I built up a REST endpoint using Django REST Framework.
class PersonFilter(django_filters.FilterSet):
id = django_filters.NumberFilter(name="id", lookup_type="gt")
first_name = django_filters.CharFilter(name="first_name", lookup_type="icontains")
last_name = django_filters.CharFilter(name="last_name", lookup_type="icontains")
class Meta:
model = Person
fields = ('id', 'first_name', 'last_name', 'last_mod')
class PersonModelViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Person.objects.none()
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
pagination_class = StandardResultsSetPagination
ordering_fields = ('id', 'first_name', 'last_name', 'last_mod')
ordering = ('last_mod', 'id')
filter_class = PersonFilter
Now if I make a request like this:
/api/rest/v1/Person?first_name=foo&last_name=foo&page_size=10
This returns only those objects where both the first name and the last name contains "foo". I want to return those objects where first name contains "foo" OR last name contains "foo".
I wonder if there's a symbol usable in the URL parameters, which will mean logical or relationship between the filters.
One workaround can be to issue two separate AJAX queries to the endpoint, but that requires extra work to unify the result.
Unfortunately, it's not possible with the current django_filter
implementation. Every single filter modifies the queryset in-place instead of returning the Q
object, which could be joined to your taste. You could try overriding the FilterSet.qs()
method and doing some black magic on self._qs.query.where
to recombine clauses using OR
. See also a question on editing the queryset filters.
Update: As long as Django handles SQL injection attempts really well, you could just use something like:
qs.filter(map(operators.or_, [Q(k=v) for k, v in request.GET.items()]))
, but surely it needs some validation before putting it to production.
I also wanted to do something similar to this and ended up creating a custom filter using django-filter to do it, hope this helps:
class NameFilter(django_filters.CharFilter):
def filter(self, qs, value):
if value:
return qs.filter(Q(**{first_name+'__'+self.lookup_expr: value}) |
Q(**{last_name+'__'+self.lookup_expr: value}))
return qs
class PersonFilter(django_filters.rest_framework.FilterSet):
name = NameFilter(lookup_expr='icontains')
/api/rest/v1/Person?name=foo&page_size=10
not a very generic solution but it is an example of how to create your own filters, how generic it is depends on your code implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With