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
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!
Most helpful comment
I use a FilterView. Overriding get_filterset_kwargs works for me:
Edit: When using e.g. pagination, this is not applied (b/c data is not None). Something like this might work: