carltongibson / django-filter

A generic system for filtering Django QuerySets based on user selections
https://django-filter.readthedocs.io/en/main/
Other
4.39k stars 760 forks source link

Docs are incorrect about filtering by a Model[Multiple]ChoiceFilter #1661

Open george-silva opened 2 months ago

george-silva commented 2 months ago

In the current documentation we have this following example:

class FooFilter(BaseFilterSet):
    foo = django_filters.filters.ModelMultipleChoiceFilter(
        field_name='attr__uuid',
        to_field_name='uuid',
        queryset=Foo.objects.all(),
    )

However, it's not necessary to set the filter field_name to point to the uuid field. The only needed step is to add the to_field_name and point to the correct relationship.

In my case I've tried doing what is described above, but this fails here:

location: django_filters.filters.ChoiceFilter#158

class ChoiceFilter(Filter):
    field_class = ChoiceField

    def __init__(self, *args, **kwargs):
        self.null_value = kwargs.get('null_value', settings.NULL_CHOICE_VALUE)
        super().__init__(*args, **kwargs)

    def filter(self, qs, value):
        if value != self.null_value:
            return super().filter(qs, value)

        qs = self.get_method(qs)(**{'%s__%s' % (self.field_name, self.lookup_expr): None})
        return qs.distinct() if self.distinct else qs

The reason it fails is that value in this particular code location is already the related model. If you use related_model__uuid as the field name, the lookup becomes: related_model__uuid and value is the model itself. Django fails when it tries to convert the related model instance to an UUID.

The only necessary change seems to be changing the field_name to remove the extra __uuid.

nils-van-zuijlen commented 1 month ago

Thank you for your issue, I just bumped in the same issue, this also solved it for me.

george-silva commented 1 month ago

Created a simple pull request fixing the example.