carltongibson / django-filter

A generic system for filtering Django QuerySets based on user selections

Home Page:https://django-filter.readthedocs.io/en/main/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"form" definition on "class Meta" of a FilterSet is not honored

DanielSwain opened this issue · comments

I need to modify what is displayed to the user for each item in a <select> by including in parentheses the count of a set of items that ForeignKey to the main model. To do this, I defined a custom form and fields (including a queryset definition on the field displayed in the <select> - see the very end of this question for additional information). I was getting KeyError on the first of the field names, so I then stripped down the form definition by simply declaring a filter form of type forms.ModelForm with exclude:

class MyModelFilterForm(forms.ModelForm):

    class Meta:
        model = MyModel
        exclude = ['field1', 'field2']
        # fields = ['field1', 'field2', 'field3', 'field4', 'field5', etc.]

class MyModelFilter(django_filters.FilterSet):

    class Meta:
        form = MyModelFilterForm

As I add each subsequent field from the fields in the model definition to exclude, I then get a KeyError on the next, non-excluded field. It seems like the form declaration isn't being honored. I looked at your source code in BaseFilterSet.get_fields().
It appears that the form argument is possibly not being taken into account in get_fields(). Is that right, or am I missing something?

Incidentally, in order to modify what is displayed for each item in the <select>, I defined a custom ModelChoiceField and used the label_from_instance method as mentioned here.

class MyCustomChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        return f"{obj.name} ({obj.relatedmodel_set.count()})"

There will only ever be a small number of items in the <select>, and I included select_related for related_model on the queryset definition.

Hi @DanielSwain. The Meta.form attribute is used in get_form_class(), where it's used as the base for a new class built with fields for the declared filters.

You may want to override that, e.g. if your form has exact fields specified, or to control the dynamic generation.

Thank you @carltongibson. I did notice get_form_class() and the comment in the code there:

image

A search in the docs on get_form_class turns up nothing, so this appears to be an undocumented feature(?). 😊 Also, while setting a form attribute seems like the most acceptable and most standard way to use a custom form in a FilterSet, the statement in the docs that one can set a form attribute on a FilterSet appears to not be applicable. Is that correct? None of this is a criticism of this excellent library. I just want to make sure things are clear for the record. In my ideal world, I would be able to assign a custom form to the form attribute as is currently stated in the docs.

... the statement in the docs that one can set a form attribute on a FilterSet appears to not be applicable.

I would be able to assign a custom form to the form attribute as is currently stated in the docs.

You can. You can see in the code there that self._meta.form is used as the base class for the dynamically generated form class used by the filterset.

this appears to be an undocumented feature(?)

Yes, good. Adding that to the FilterSet reference would be great.