Filtering the queryset/context being passed to a CreateView/UpdateView form

In my application, the models Bet and Tag have a many to many relationship, in which tags are used to label bets. One tag can label zero, one or more bets, and a bet can have zero, one or more tags as labels. Both bets and tags have a bet_owner and tag_owner Foreign Key (the CustomUser model).

It is of course trivial to filter ListViews so that only objects belonging to the current user are displayed. However, in my NewTagView (CreateView) and UpdateTagView (UpdateView), a form is rendered that includes a multiple select widget where the user can pick zero, one or more bets to be immediately (upon submission) labeled with the tag being created/updated. However, it displays every single bet in the database, and not just those belonging to the current user. As the forms are rendered by the generic CBVs, I’m a bit confused as to how can I have them display only a filtered queryset (or if I should pass a custom context in the view).

The model:

from django.db import models
from django.conf import settings
from django.urls import reverse
from bets.models import Bet
from users.models import CustomUser

class Tag(models.Model):
    """Model a single Tag, which is a label associated with one or more bets."""

    associated_bets = models.ManyToManyField(Bet, related_name='tags', blank=True)
    label = models.CharField(max_length=25)
    description = models.CharField(max_length=50, blank=True)
    tag_owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE)

    def __str__(self):
        return self.label

The views:

class NewTagView(LoginRequiredMixin, CreateView):
    model = Tag
    form_class = TagForm
    template_name = "tags/new_tag.html"
    login_url = "/users/login/"
    success_url = reverse_lazy("tag_list")

    def form_valid(self, form):
        # Set the owner of the bet to the current user
        form.instance.tag_owner = self.request.user
        return super().form_valid(form)
    

        
class UpdateTagView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = Tag
    form_class = TagForm
    template_name = "tags/update_tag.html"
    login_url = "/users/login/"
    success_url = reverse_lazy("tag_list")

    def test_func(self):
        """Checks if bet owner is the same as the current user."""
        obj = self.get_object()
        return obj.tag_owner == self.request.user

The form:

from django import forms
from .models import Tag

class TagForm(forms.ModelForm):
    class Meta:
        model = Tag
        fields = ['label', 'description', 'associated_bets']

The templates:

{% block content %}


<form action="" method="post">
    {% csrf_token %}
    {{ form|crispy }}
    <button class="btn btn-warning active ml-2 border border-dark" type="submit">Add Tag</button>
</form>


{% endblock content %}

This is kind of off the top of my head, and I haven’t tried it. But I think you could try setting the queryset for the bets field, maybe something like:

In your views, add this method:

def get_form_kwargs(self):
    "Add the current User object to the kwargs that will be passed to the Form"
    kwargs = super().get_form_kwargs()
    kwargs.update("user", self.request.user)
    return kwargs

And in your form add:

def __init__(self, user, *args, **kwargs):
    super.__init__(*args, **kwargs)
    self.fields["associated_bets"].queryset = Bet.objects.filter(user=user)

Thank you for your response. I just got it to finally work, actually.

Apparently with the get_form method we can manipulate the form before it is rendered. Wrote in this method override in the views: (suggested by ChatGPT)

def get_form(self, *args, **kwargs):
        # Get the form and modify the queryset for the associated_bets field
        form = super().get_form(*args, **kwargs)
        form.fields['associated_bets'].queryset = Bet.objects.filter(bet_owner=self.request.user)
        return form