Django-filter: Default filtering when no filter parameters passed in request.GET

Created on 6 Nov 2015  ·  6Comments  ·  Source: carltongibson/django-filter

Dear all,

Is there an option to set the default filter field and value to use if no parameter that I define in the Meta.fields has been found in the request object?

If there isn't such an option, what would you suggest as the best way to do it?

Regards,
Andrius

Most helpful comment

I use a FilterView. Overriding get_filterset_kwargs works for me:

class MyFilterView(FilterView):
    filterset_class = filters.MyFilter
    # ...
    def get_filterset_kwargs(self, filterset_class):
        kwargs = super(MyFilterView, self).get_filterset_kwargs(filterset_class)
        if kwargs["data"] is None:
            kwargs["data"] = {"is_very_interesting": False}
        return kwargs

Edit: When using e.g. pagination, this is not applied (b/c data is not None). Something like this might work:

        if kwargs["data"] is None:
            kwargs["data"] = {"is_very_interesting": False}
        elif "is_very_interesting" not in kwargs["data"]:
            kwargs["data"] = kwargs["data"].copy()
            kwargs["data"]["is_very_interesting"] = False

All 6 comments

Hi @andrius-senulis. django-filter is built on top of regular django forms, so this question can be boiled down into "How do I provide default field values for bound forms?" Per this SO answer, you can add a clean_<field_name> method to your form in order to provide a default value. Unfortunately, the underlying form class isn't directly exposed, so you can't subclass it and add that method (see edit). That said, filters have an underlying form field, so you could subclass the field and override its clean() method. This is a little cumbersome. eg,

class DecimalDefaultField(forms.DecimalField):
    def clean(self, value):
        if value is None:
            return 4.0
        return super(DecimalDefaultField, self).clean(value)

class NumberDefaultFilter(filters.NumberFilter):
    field_class = DecimalDefaultField

class MyFilterSet(FilterSet):
    some_field = NumberDefaultFilter(name='some_field')
    ...

An easier method would be to just inspect the incoming data and set a default value.

def your_view(request):
    data = request.GET.copy()
    data.setdefault('some_field', 'some_value')

    f = YourFilterSet(data, queryset=YourModel.objects.all())
    ...

edit:
You actually are able to provide a base form to the FilterSet.Meta, which is then subclassed internally. So, clean_<field name> is possible.

class MyForm(forms.Form):
    clean_some_field(self, value):
        ...

class MyFilterSet(FilterSet):
    class Meta:
        model = MyModel
        form = MyForm
        fields = ['some_field']

I use a FilterView. Overriding get_filterset_kwargs works for me:

class MyFilterView(FilterView):
    filterset_class = filters.MyFilter
    # ...
    def get_filterset_kwargs(self, filterset_class):
        kwargs = super(MyFilterView, self).get_filterset_kwargs(filterset_class)
        if kwargs["data"] is None:
            kwargs["data"] = {"is_very_interesting": False}
        return kwargs

Edit: When using e.g. pagination, this is not applied (b/c data is not None). Something like this might work:

        if kwargs["data"] is None:
            kwargs["data"] = {"is_very_interesting": False}
        elif "is_very_interesting" not in kwargs["data"]:
            kwargs["data"] = kwargs["data"].copy()
            kwargs["data"]["is_very_interesting"] = False

Using __init__ worked for me. (This is with djangorestframework-filters, but I think is the same as vanilla django-filter):

class MyFilter(filters.FilterSet):
    foo = ...

    def __init__(self, data, *args, **kwargs):
        if not data.get('foo'):
            data = data.copy()
            data['foo'] = 'bar'
        super().__init__(data, *args, **kwargs)

the method with __init__ isn't recommended and the workaround documented in the link didn't work for me ... https://django-filter.readthedocs.io/en/master/guide/tips.html#using-initial-values-as-defaults
get_filterset_kwargs in the FilterView worked for me.

the method with __init__ isn't recommended

Providing default form/filter values in general is not recommended in that it's a bad practice that can have confusing/adverse effects. Implementing via __init__ or whatever other method is fine (given that you're okay with the effects).

I use a FilterView. Overriding get_filterset_kwargs works for me:

class MyFilterView(FilterView):
    filterset_class = filters.MyFilter
    # ...
    def get_filterset_kwargs(self, filterset_class):
        kwargs = super(MyFilterView, self).get_filterset_kwargs(filterset_class)
        if kwargs["data"] is None:
            kwargs["data"] = {"is_very_interesting": False}
        return kwargs

Edit: When using e.g. pagination, this is not applied (b/c data is not None). Something like this might work:

        if kwargs["data"] is None:
            kwargs["data"] = {"is_very_interesting": False}
        elif "is_very_interesting" not in kwargs["data"]:
            kwargs["data"] = kwargs["data"].copy()
            kwargs["data"]["is_very_interesting"] = False

You made my day!

Was this page helpful?
0 / 5 - 0 ratings