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

Unable to add a field to a model using a filter

anthonygoslar opened this issue · comments

I have a model called RealEstate. It has a number of fields I want dynamic choices and filter methods for. Because the ModelChoiceFilter and ModelMultipleChoiceFilter don't allow for a method on the filter, I have used a ChoiceFilter and MultipleChoiceFilter instead and have defined the choices form a Queryset.

This seems to have caused some issues:

  1. It doesn't dynamically update the field choices unless I call the method after init. It took me a while to figure out that these initialize when the server starts.
  2. I can't add model fields if I initiate the choices with my Queryset as it throws an error on makemigrations that the new model field is not found.

I suspect that I'm probably using the wrong field for the job but thought that I should raise it and share my solution by only calling the choices Queryset after the super().init(*args, **kwargs) in init. Here's an example below:

def destination_country_choices() -> tuple:
    options = []
    for obj in RegionOption.objects.filter(show=True).order_by("order"): # This queryset is causing the issue
        options.append((obj.lookup_name, obj.name))
    return tuple(options)

class PublishedDealListFilter(django_filters.FilterSet):
    
    destination_country = django_filters.ChoiceFilter(
        field_name="destination__region__country",
        label="Select a country",
        empty_label="All countries",
        choices=tuple(), # choices are set in __init__
        method="filter_destination_country",
    )
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form.fields["destination_country"].choices = destination_country_choices()
    
    def filter_destination_country(self, queryset, destination_country, value):
        if value or value != "":
            return queryset.filter(
                Q(destination__region__country=value)
                | Q(destination__region__name=value)
            )
        return queryset

Thanks

Yes, this is right. Dynamically setting choices should be done in __init__ as you've found.