How to add Search functionality to a Django REST Framework powered app
Agenda
We will discuss how search can be accomplished in Django REST Framework using query parameter.
Setup
Let’s use Django polls models as reference.
Let’s add the following serializer:
Let’s add the following view:
Let’s add the following urlpattern:
A GET request to /api/polls/questions/
returns a list with all the questions.
Searching
DRF provides a filter called SearchFilter
which can filter the queryset based on a query parameter named search
.
We want to search for questions which have Samsung
in the question_text.
This requires adding two attributes on QuestionsAPIView. They are search_fields
and filter_backends
. Let’s add these attributes.
Make a GET request with search
query parameter Samsung
. GET /api/polls/questions/?search=Samsung
.
The question with term Samsung
in it’s question_text is returned in the response.
Now we want to find questions authored by charles. Let’s make another request having query parameter charles.
We didn’t get the questions authored by charles because we have only question_text
in search_fields
. DRF is only looking for the search query parameter in question_text
.
We need to add author
to search_fields
too if we want search query parameter to be looked into author
. Modify search_fields
.
search_fields = ['question_text', 'author']
Make api request again and you should see charles
authored question in response.
Searching on relationship
Question has a many relationship to Choice. Let’s create a Question with an associated Choice.
In [1]: from polls.models import Question, ChoiceIn [2]: q = Question.objects.create(question_text='Who is your favorite GoT character?')In [3]: Choice.objects.create(question=q, choice_text='Ned')
Out[3]: <Choice: Ned>
We want associated choices’ choice_text to be considered too when we search for a particular term. Search for Ned
in questions endpoint.
We didn’t get any result.
Since we want associated choices’ choice_text to be considered we would need to add choice__choice_text
to search_fields
.
search_fields = ['question_text', 'author', 'choice__choice_text']
Search for Ned
again and you should see the relevant question in the returned response.
More restrictive search
Let’s revert search_fields
back to [‘question_text', ‘author’]
.
Behind the scenes, DRF uses icontains
by default. Search for GoT
.
DRF essentially applied the following filter:
Q(question_text__icontains='GoT') | Q(author__icontains='GoT')
You might want to enforce that an object should only be returned if it starts with the searched text. In such case, the filtering needed would be istartswith
instead of icontains
.
DRF SearchFilter
has support for istartswith
too. We need to prepend ^
to the field for istartswith
. Change search_fields
to:
search_fields = ['^question_text', 'author']
The GoT
search wouldn’t return any question in the response now.
Search for Who
and you would see the question in response.
DRF under the hood performed the following filter in this case
Q(question_text__istartswith='Who') | Q(author__icontains='Who')
Exact search can be performed by prepending @
in the attribute name. eg: search_fields=['@question_text', ‘@author']
.
Searching on multiple terms
Let’s revert search_fields
back to [‘question_text', ‘author']
.
DRF supports multiple terms in search query parameter. As DRF documentation states:
The search parameter may contain multiple search terms, which should be whitespace and/or comma separated. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched.
Let’s send two terms Who
and GoT
in the search parameter.
Because both the terms are in the question Who is your favorite GoT character?, so this object is returned in the list.
Search for terms Who
and Samsung
.
Because both the terms do not appear in any of the questions, so we got an empty list in the response.
If you regularly work with AWS, take this highly engaging AWS Quiz to check your AWS knowledge.
Happy Coding!