This is a slightly simplified example of the filterset I'm using, which I'm using with the DjangoFilterBackend for Django Rest Framework. I'd like to be able to send a request to /api/bookmarks/?title__contains=word1&title__contains=word2
and have results returned that contain both words, but currently it ignores the first parameter and only filters for word2.
Any help would be very appreciated!
class BookmarkFilter(django_filters.FilterSet):
class Meta:
model = Bookmark
fields = {
'title': ['startswith', 'endswith', 'contains', 'exact', 'istartswith', 'iendswith', 'icontains', 'iexact'],
}
class BookmarkViewSet(viewsets.ModelViewSet):
serializer_class = BookmarkSerializer
permission_classes = (IsAuthenticated,)
filter_backends = (DjangoFilterBackend,)
filter_class = BookmarkFilter
ordering_fields = ('title', 'date', 'modified')
ordering = '-modified'
page_size = 10
The main problem is that you need a filter that understands how to operate on multiple values. There are basically two options:
MultipleChoiceFilter
(not recommended for this instance)Using
MultipleChoiceFilter
While this retains your desired syntax, the problem is that you have to construct a list of choices. I'm not sure if you can simplify/reduce the possible choices, but off the cuff it seems like you would need to fetch all titles from the database, split the titles into distinct words, then create a set to remove duplicates. This seems like it would be expensive/slow depending on how many records you have.
Custom
Filter
Alternatively, you can create a custom filter class - something like the following:
Usage (notice that the values are comma-separated):
Result:
The syntax is changed a bit, but the CSV-based filter doesn't need to construct an unnecessary set of choices.
Note that it isn't really possible to support the
?title__contains=word1&title__contains=word2
syntax as the widget can't render a suitable html input. You would either need to useSelectMultiple
(which again, requires choices), or use javascript on the client to add/remove additional text inputs with the samename
attribute.Without going into too much detail, filters and filtersets are just an extension of Django's forms.
Filter
has a formField
, which in turn has aWidget
.FilterSet
is composed ofFilter
s.FilterSet
generates an inner form based on its filters' fields.Responsibilities of each filter component:
data
QueryDict
.filter()
call to the queryset, using the validated value.In order to apply multiple values for the same filter, you would need a filter, field, and widget that understand how to operate on multiple values.
The custom filter achieves this by mixing in
BaseCSVFilter
, which in turn mixes in a "comma-separation => list" functionality into the composed field and widget classes.I'd recommend looking at the source code for the CSV mixins, but in short:
CharField
orIntegerField
). The field also derives the mixed in widget.The CSV filter was intended to be used with
in
andrange
lookups, which accept a list of values. In this case,contains
expects a single value. Thefilter()
method fixes this by iterating over the values and chaining together individual filter calls.