yeti logo icon
Close Icon
contact us
Yeti postage stamp
We'll reply within 24 hours.
Thank you! Your message has been received!
A yeti hand giving a thumb's up
Oops! Something went wrong while submitting the form.

Global Search in Django Rest Framework

By
-
November 26, 2014

My first few months of working with Django Rest Framework was a delight because so much functionality comes out of the box.

For example, let's say you have a user model and you want to search for a user via their username.

You can add this functionality by defining a filtering backend (in our case, using DRF's SearchFilter) and a search field specifying which field to search upon like this:

class UserViewSet(viewsets.ReadOnlyModelViewSet):   queryset = User.objects.all()   serializer_class = UserSerializer   filter_backends = (filters.SearchFilter,)   search_fields = ('username',)

Now if you want to search for users who’s username contains the word ‘john’ can just make GET request to the endpoint,  /users?search=john.

Easy enough. But what if you want a single endpoint that will make queries over multiple models? Because the search functionality we just introduced is applied per view level, we have to make some slight customizations in order to get global search over many models.

Global Search Over Multiple Models

In this blog post we’ll base our example off the Snippets tutorial from the Django Rest Framework website. The snippets app lets a user create and save code snippets as well as highlight them. What we want to add is a custom endpoint that will search across all our user and snippet models and return our list of model instances that matches the input query. So, if we make a GET request on the endpoint /search/?query=your_query, it will search and return serialized json data from both the user and snippet models that will match our query.

Customizing Django Rest Framework

To begin, we’ve set up a basic /search/ endpoint that will list all our search results.

urlpatterns = patterns('',   ...   url(r'^search/$', GlobalSearchList.as_view(), name="search"),)

Then we built a GobalSearchList view set that returns the appropriate data. GlobalSearchList inherits from ListAPIView, one of many of DRF’s view classes, that provides us with a read-only endpoint on a collection of model instances. We define this collection within our get_queryset method. Within this method, we capture the query parameter and make two separate queries, filtering against fields in both our snippets and users model instances, and the results are then returned in a combined list.

class GlobalSearchList(generics.ListAPIView):   serializer_class = GlobalSearchSerializer   def get_queryset(self):      query = self.request.QUERY_PARAMS.get('query', None)      snippets = Snippet.objects.filter(Q(code__icontains=query) | Q(highlighted__icontains=query) | Q(language__icontains=query))      users = User.objects.filter(username__icontains=query)      all_results = list(chain(snippets, users))       all_results.sort(key=lambda x: x.created)      return all_results

In the above example, you may have also noticed that we specified a custom serializer class called GlobalSearchSerializer. When we return our data from our GlobalSearchList viewset, it's important to apply the appropriate serializer to the model instances returned. We can customize this behavior by overriding the to_native method so that Snippet instances are serialized using a SnippetSerializer, and User instances are serialized using a UserSerializer.

Here’s an example of a global search serializer that may do this:

class GlobalSearchSerializer(serializers.ModelSerializer):   class Meta:      model = User   def to_native(self, obj):      if isinstance(obj, Snippet):          serializer = SnippetSerializer(obj)      elif isinstance(obj, User):         serializer = UserSerializer(obj)      else:         raise Exception("Neither a Snippet nor User instance!")      return serializer.data

If you'd like to follow along, you can start with the snippets tutorial from the Django Rest Framework website here.

Also, special thanks to Baylee for the original code and for being an amazing mentor.

You Might also like...

VIDEO: Building API's with Django and GraphQL

At our last Django Meetup Group event, Jayden Windle, the lead engineer at Jetpack, an on demand delivery company, talks building APIs with Django and GraphQL. Watch the video to learn more.

Using Pytest to Write Beautiful Tests and a Bulletproof Django App

At the last meeting of the San Francisco Django Meetup Group, Wes Kendall gave a talk on how to make a bulletproof Django application by testing it with pytest. Check out his talk here!

Creating a Reusable Component Library: Yeti Lunch and Learn

Part of the Yeti Lunch and Learn series - our amazing developer, Resdan, gives a presentation on creating a reusable component library. Enjoy the video!

Browse all Blog Articles

Ready for your new product adventure?

Let's Get Started