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...

code on a computerManaging Persistent Browser Data with useSyncExternalStore

Struggling to keep React state in sync across tabs and sessions? Learn how to use useSyncExternalStore to manage state persistence with localStorage and sessionStorage—without complex state management libraries. Improve performance and streamline your app’s state logic.

software developerReact Hooks 102: When to Avoid useEffect

Overusing useEffect in React can lead to inefficient components and performance issues. In this post, learn when to avoid useEffect and discover better alternatives for managing state, calculations, and user events. Optimize your React code with best practices for cleaner, faster applications.

software developer codingFintech Security with GraphQL Shield

Securing fintech applications is crucial, and GraphQL’s flexibility can be a security risk if not properly managed. GraphQL Shield helps enforce fine-grained authorization using role-based (RBAC) and attribute-based (ABAC) access control. In this post, we’ll explore why authorization matters, how to implement secure, composable rules, and walk through a demo app showcasing best practices.

Browse all Blog Articles

Ready for your new product adventure?

Let's Get Started