Mastering Django ORM I | Use Q objects to filter Django Queryset.

Mastering Django ORM I | Use Q objects to filter Django Queryset.

Learn to use Django Q to filter queryset like a pro.

Aashish Bhandari's photo
Aashish Bhandari

Published on Dec 24, 2021

Subscribe to my newsletter and never miss my upcoming articles

We all know that we can use Django ORM as a high-level interface to interact with databases. And we can use normal methods like get(), filter() to narrow down our search. But, there comes a stage in our project that it becomes tedious to handle looong lines and chains of filtering. Such as:

# Get the countries with name that starts with N or ends with L
countries = Country.objects.filter(name__istartswith="N") |
     Country.objects.filter(name__iendswith="l")
# Django querysets support & as and and | as or.

Here is where Q object makes an entry. While using Q objects, we can use &, | and ~ as and, or, and not.

Let's see an example:

from django.db.models import Q

countries = Country.objects.filter(
    Q(name__istartswith='N') | Q(name__iendswith='l')
)

This was a rather simple example that shows how we can use Q objects to simplify how we write queries.

Now, let's see how we can simplify a somewhat complex query using Q objects. Here, we will use the same example of countries. But let's make our logic a bit complex. Like:
Get countries that satisfy both of the given criteria:

  • Continent's name: starts from 'A' or has 'rica' BUT exclude 'Africa'.
  • Has a per-capita income of more than 10000 OR is not landlocked.

Let's do that using normal model methods.

countries = ((
        Country.objects.filter(continent__name__istartswith="A") |
        Country.objects.filter(continent__name__icontains="rica")
    ).exclude(continent__name__iexact="Africa")) & (
        Country.objects.filter(pci__gt=10000) |
        Country.objects.filter(is_landlocked=False)
    )

We have already been really overwhelmed by the complexity of this query. Let's use Q and see its magic.

from django.db.models import Q
countries = Country.objects.filter(
        (
            Q(continent__name__istartswith="A") |
            Q(continent__name__icontains="rica") &
            ~Q(continent__name__iexact="Africa")
        ) & (
            Q(pci__gt=10000) | Q(is_landlocked=False)
        )
    )

As we can see, using Q objects has the potential of refactoring a lot of complex code and making it more readable.

Thank you for your time. To know more about Q object, visit Django Docs

Happy Reading, Keep Pythoning

 
Share this