philipn/django-rest-framework-filters

AllLookupsFilter by default for specific model field

Eloi-Marin opened this issue · 1 comments

Hi !

I'd like to do something like:

class OrdersFilter(filters.FilterSet):
   class Meta:
      model = OrderView
      fields = '__all__'

   @classmethod
   def filter_for_lookup(cls, f, lookup_type):
      if isinstance(f, models.FloatField):
         return filters.AllLookupsFilter, {}

but it is not working :S Is there any reason for that ? I am doing the same for DateFields in order to activare range filtering for any date and it works well.

        if isinstance(f, models.DateField):
            return DateRangeRangeFilter, {'lookup_expr': 'range'}
        if isinstance(f, models.CharField):
            return ChoicesInFilter, {'lookup_expr': 'in'}

Also, would there be a way to set the allowed lookup_expr for AllLookupsFilter, something like

        if isinstance(f, models.FloatField):
             return filters.AllLookupsFilter, {'lookup_expr': ['gt','lt','gte','lte','exact']}

Thanks!!


(@rpkilby) edited for formatting

Hi @Eloi-Marin. I can't dive into an in-depth explanation at the moment, but the short version is:

  • None of the magic occurs within AllLookupsFilter - it doesn't implement any real functionality. Instead, these filters act like a... virtual filter (?)... that is processed by the filterset and converted into the 20 or 30 concrete filters (one for each lookup supported by that model field).
  • Overriding filter_for_lookup to return AllLookupsFilter occurs too late. At this point, the AllLookupsFilter instances have already been expanded into the concrete filters, and they are not processed any further.

In general, AllLookupsFilter is useful for prototyping, but I highly recommend against using it in production code. Writing code that generates AllLookupsFilters should be avoided. The real solution here is to use the Meta.fields dict syntax.

class OrdersFilter(filters.FilterSet):
    class Meta:
        model = Order
        fields = {
            'date_field': ['exact', '...'],
            'float_field': ['etc', '...'],
        }

If you're not a fan of copy+pasting the list of lookups for every field, then just create a few common lists

DATE_LOOKUPS = ['...']
FLOAT_LOOKUPS = ['...']

class OrdersFilter(filters.FilterSet):
    class Meta:
        model = Order
        fields = {
            'date_field': DATE_LOOKUPS,
            'float_field': FLOAT_LOOKUPS,
        }