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, Choice In [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.

Dynamic filter

My next post talks about implementing dynamic filters in DRF.

Connect with me on Twitter, I tweet about informative and valuable programming articles and advices.