Here is a reusable solution using a custom Filter and a custom Field.
The custom Field reuses Django's MultipleChoiceField but replaces the validation functions.
Instead, it validates using another Field class that we pass to the constructor.
from django.forms.fields import MultipleChoiceField
class MultipleValueField(MultipleChoiceField):
def __init__(self, *args, field_class, **kwargs):
self.inner_field = field_class()
super().__init__(*args, **kwargs)
def valid_value(self, value):
return self.inner_field.validate(value)
def clean(self, values):
return values and [self.inner_field.clean(value) for value in values]
The custom Filter uses MultipleValueField and forwards the field_class argument.
It also sets the default value of lookup_expr to in.
from django_filters.filters import Filter
class MultipleValueFilter(Filter):
field_class = MultipleValueField
def __init__(self, *args, field_class, **kwargs):
kwargs.setdefault('lookup_expr', 'in')
super().__init__(*args, field_class=field_class, **kwargs)
To use this filter, simply create a MultipleValueFilter with the appropriate field_class. For example, to filter City by id, we can use a IntegerField, like so:
from django.forms.fields import IntegerField
class CityFilterSet(FilterSet):
id = MultipleValueFilter(field_class=IntegerField)
name = filters.CharFilter(lookup_expr='icontains')
class Meta:
model = City
fields = ['name']
id=1&id=2and resource with ID 2 does not exist, only the first one would be returned in the list of results. If neither exist, an empty list would be returned.