TransWikia.com

Why does a queryset applied in a ModelForm not inherit a queryset from a ModelManager?

Stack Overflow Asked by alias51 on November 10, 2021

I have a custom queryset on a model manager:

class TenantManager(models.Manager):
    def get_queryset(self):
       return super().get_queryset().filter(myfield=myvalue)

class TenantModel(TenantModelMixin, models.Model):
    objects = TenantManager()

    class Meta:
        abstract = True

I use the abstract TenantModel as a mixin with another model to apply the TenantManager. E.g.

class MyModel(TenantModel):

This works as expected, applying the TenantManager filter every time MyModel.objects.all() is called when inside a view.

However, when I create a ModelForm with the model, the filter is not applied and all results (without the filter are returned. For example:

class AddPersonForm(forms.ModelForm):

    class Meta:
        model = MyModel
        fields = ('person', )

Why is this and how to I ensure the ModelManager is applied to the queryset in ModelForm?

Edit

@Willem suggests the reason is forms use ._base_manager and not .objects (although I can not find this in the Django source code), however the docs say not to filter this kind of manager, so how does one filter form queries?

Don’t filter away any results in this type of manager subclass

This
manager is used to access objects that are related to from some other
model. In those situations, Django has to be able to see all the
objects for the model it is fetching, so that anything which is
referred to can be retrieved.

If you override the get_queryset() method and filter out any rows,
Django will return incorrect results. Don’t do that. A manager that
filters results in get_queryset() is not appropriate for use as a base
manager.

2 Answers

You can do it in two ways:

First: When creating the form instance, add the queryset for the desired field.

person_form = AddPersonForm()
person_form.fields["myfield"].queryset = TenantModel.objects.filter(myfield="myvalue")

Second: Override the field's queryset in the AddPersonForm itself.

class AddPersonForm(forms.ModelForm):

    class Meta:
        model = MyModel
        fields = ('person', )

    def __init__(self, *args, **kwargs):
        super(AddPersonForm, self).__init__(*args, **kwargs)   
        self.fields['myfield'].queryset = TenantModel.objects.filter(myfield="myvalue")

Answered by jerrymouse on November 10, 2021

I'm not sure why your code doesn't properly works. Probably you haven't reload django app. You could load queryset in __init__ of your form class

class AddPersonForm(forms.ModelForm):

    person = forms.ModelMultipleChoiceField(queryset=None)

    class Meta:
        model = MyOtherModel
        fields = ('person', )
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['person'].queryset = MyModel.objects.all()

Answered by Zapix on November 10, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP