How do I filter values in a Django form using ModelForm?

I am trying to use the ModelForm to add my data. It is working well, except that the ForeignKey dropdown list is showing all values and I only want it to display the values that a pertinent for the logged in user.

Here is my model for ExcludedDate, the record I want to add:

class ExcludedDate(models.Model):
    date = models.DateTimeField()
    reason = models.CharField(max_length=50)
    user = models.ForeignKey(User)
    category = models.ForeignKey(Category)
    recurring = models.ForeignKey(RecurringExclusion)

    def __unicode__(self):
        return self.reason

Here is the model for the category, which is the table containing the relationship that I'd like to limit by user:

class Category(models.Model):
    name = models.CharField(max_length=50)
    user = models.ForeignKey(User, unique=False)

    def __unicode__(self):
        return self.name

And finally, the form code:

class ExcludedDateForm(ModelForm):

    class Meta:
        model = models.ExcludedDate
        exclude = ('user', 'recurring',)

How do I get the form to display only the subset of categories where category.user equals the logged in user?


Solution 1:

You can customize your form in init

class ExcludedDateForm(ModelForm):
    class Meta:
        model = models.ExcludedDate
        exclude = ('user', 'recurring',)
    def __init__(self, user=None, **kwargs):
        super(ExcludedDateForm, self).__init__(**kwargs)
        if user:
            self.fields['category'].queryset = models.Category.objects.filter(user=user)

And in views, when constructing your form, besides the standard form params, you'll specify also the current user:

form = ExcludedDateForm(user=request.user)

Solution 2:

I know this is old; but its one of the first Google search results so I thought I would add how I found to do it.

class CustomModelFilter(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        return "%s %s" % (obj.column1, obj.column2)

class CustomForm(ModelForm):
    model_to_filter = CustomModelFilter(queryset=CustomModel.objects.filter(active=1))

    class Meta:
        model = CustomModel
        fields = ['model_to_filter', 'field1', 'field2']

Where 'model_to_filter' is a ForiegnKey of the "CustomModel" model

Why I like this method: in the "CustomModelFilter" you can also change the default way that the Model object is displayed in the ChoiceField that is created, as I've done above.