django-url-filter icon indicating copy to clipboard operation
django-url-filter copied to clipboard

Fallback behavior for the callable filter

Open selimt opened this issue 7 years ago • 3 comments
trafficstars

Hi I have a callable filter that handles a custom lookup that I implemented:

class StatusFilter(CallableFilter):
    @form_field_for_filter(forms.CharField())
    def filter_has_any_keys_for_django(self, queryset, spec):
        value = spec.value.split(',')
        if spec.is_negated:
            return queryset.exclude(status__has_any_keys=value)
        else:
            return queryset.filter(status__has_any_keys=value)

    @form_field_for_filter(forms.CharField())
    def filter_contains_for_django(self, queryset, spec):
        if spec.is_negated:
            return queryset.exclude(status__contains=spec.value)
        else:
            return queryset.filter(status__contains=spec.value)

And it works great. The issue is that if the callable filter doesn't support other types of lookups, I want it to fall back to the default way of handling the filters, so for example if I try to specify a query parameter like ".../?status=["complete","succeeded"] then I get an error:

AssertionError: StatusFilter was not provided form_field parameter in initialization (e.g. StatusFilter(form_field=CharField)) and form_field was not provided for the lookup. If the lookup is a custom filter callable you should provide form_field by using @form_field_for_filter decorator. If the lookup is a normal lookup, then please either provide form_field parameter or overwrite get_form_field().

Is there a mechanism for this?

Thanks -Selim

selimt avatar Nov 19 '18 20:11 selimt

not any simple way at the moment

miki725 avatar Nov 19 '18 21:11 miki725

That's ok, it was as simple as adding the following:

    @form_field_for_filter(forms.CharField())
    def filter_exact_for_django(self, queryset, spec):
        value = json.loads(spec.value)
        f = queryset.filter if not spec.is_negated else queryset.exclude
        return f(status=value)

selimt avatar Nov 19 '18 22:11 selimt

In order to solve this problem, I wrote FallbackCallableFilter:

class FallbackCallableFilter(CallableFilter):
    def get_spec(self, config):
        spec = super(CallableFilter, self).get_spec(config)
        try:
            spec.filter_callable = self._get_filter_method_for_lookup(spec.lookup)
        except AttributeError:
            pass
        return spec

Perhaps this could be part of the project?

Note that @selimt's solution is not complete, as there are many other lookups besides __exact (__icontains, __in etc.).

tbitai avatar Jun 24 '20 15:06 tbitai