DEV Community

Cover image for Custom QuerySets in Django: Writing Cleaner, Reusable Queries
Ezeana Micheal
Ezeana Micheal

Posted on

Custom QuerySets in Django: Writing Cleaner, Reusable Queries

Custom QuerySets in Django: Writing Cleaner, Reusable Queries

When building Django applications, especially as they scale when creating views and similar components, it's easy to find yourself using the same queries repeatedly in different logical steps. The repetition makes the code hard to maintain and can cause some confusion.

This is where querysets come in. Custom querysets allow us to write cleaner code, keeping some business logic close to your models while avoiding duplication.

But first, what is a queryset?

A queryset is simply a collection of database queries in Django, it allows us to make queries through Django’s ORM to the database. By default, objects is Django’s built-in Manager that returns a QuerySet. But what if you always need the query? Rewriting the filter everywhere does not follow the DRY (Don’t Repeat Yourself) principle.

from blog.models import Post

# Default QuerySets  
all_posts = Post.objects.all()  
published_posts = Post.objects.filter(status="published")  
Enter fullscreen mode Exit fullscreen mode

Now, let's get into the programming aspect. How do you define a custom queryset? We use the models imported from Django’s DB and inherit from the Queryset. Here’s an example, Suppose you have a Post model and you want to make some repeated queries.

from django.db import models

class PostQuerySet(models.QuerySet):  
    def published(self):  
        return self.filter(status="published")

    def drafts(self):  
        return self.filter(status="draft")

    def by_author(self, author):  
        return self.filter(author=author)  
Enter fullscreen mode Exit fullscreen mode

Let's now attach this to our model.

class Post(models.Model):  
    STATUS_CHOICES = (  
        ("draft", "Draft"),  
        ("published", "Published"),  
    )

    title = models.CharField(max_length=200)  
    content = models.TextField()  
    status = models.CharField(max_length=10, choices=STATUS_CHOICES)  
    author = models.ForeignKey("auth.User", on_delete=models.CASCADE)

    # Attach the custom QuerySet  
    objects = PostQuerySet.as_manager()

Enter fullscreen mode Exit fullscreen mode

Now we can make queries like

# All published posts  
Post.objects.published()

# All drafts by a specific author  
Post.objects.drafts().by_author(user)  
Enter fullscreen mode Exit fullscreen mode

We can even chain them,

# Get published posts by a specific author  
Post.objects.published().by_author(user)  
Enter fullscreen mode Exit fullscreen mode

In a way, querysets and managers are similar and different. How would you know when to use either?

Use Custom QuerySets when:

  • You want reusable filters (like .published(), .active()).
  • You need chainable queries.

Use Custom Managers when:

  • You want to override get_queryset() itself.
  • You need queries that return something other than a QuerySet (like creating objects or aggregations).

In this article, We’ve seen how qureysets helps us in keeping the DRY principle and writing cleaner code, in the next one we’ll consider when and how the 3 powerhouses in our models.py can be used together in Custom Model Methods vs. Managers vs. QuerySets: When to Use Each.

Top comments (0)