Back
Jul 1, 2010

Overriding QuerySet in Django

Vladimir Sidorenko
Vladimir Sidorenko

As you know, model managers can be overriden in Django. It's convenient to add custom filtration method there:

Article.objects.published()
    Article.objects.old()

But these custom methods cannot be chained:

Article.objects.published().old()

Overriding manager doesn't allow it, because after first manager method is invoked (publishedin this case), we receive queryset as a result, which knows nothing about custom manager methods. So we have to add custom methods to queryset. This can look like this:

class ArticleQuerySet(models.query.QuerySet):

        def published(self):
            return self.filter("...")

        def old(self):
            return self.filter("...")

Now we need to make manager user this class. In order to do this, let's override manager's get_query_set method:

class ArticleManager(models.Manager):

        def get_query_set(self):
            return ArticleQuerySet(self.model, using=self._db)

    class Article(models.Model):

        # ...

        objects = ArticleManager()

This allows to make queties like:

Article.objects.all().published().old()

Here we invoke all to get queryset instance, because this time manager doesn't have custom methods - publishedand old. To avoid this, we can make manager look for undefined methods in queryset:

class ArticleManager(models.Manager):

        def get_query_set(self):
            return ArticleQuerySet(self.model, using=self._db)

        def getattr(self, key):
            return getattr(self.get_query_set(), key)

Note that getattris only invoked when attribute is not found by usual means.

Manager can be further improved to be more generic:

class QuerySetManager(models.Manager):

        def init(self, queryset_class, args, **kwargs):
            self.queryset_class = queryset_class
            super(QuerySetManager, self).init(args, **kwargs)

        def get_query_set(self):
            return self.queryset_class(self.model, using=self._db)

        def getattr(self, key):
            return getattr(self.get_query_set(), key)

       # Теперь можно писать просто:
       # objects = QuerySetManager(ArticleQuerySet)
       #

Update

After article was published, I found out that this idea is not all that new and unique :)

It's funny that only after I implemented my own solution, I've managed to make correct search query.

More thoughts

Feb 12, 2020Technology
5 Best Payment Gateways For 2020

We reviewed the best payment gateways in 2020. Here’s our comparison of their features, advantages, and disadvantages.

Vladimir Sidorenko
Vladimir Sidorenko
Jun 1, 2018Technology
Site search organization: basic concepts

Now it's time to get acquainted with Elasticsearch. This NoSQL database is used to store logs, analyze information and - most importantly - search.

Viktor Tyshchenko
Viktor Tyshchenko
Jan 22, 2017Technology
Django vs Rails Performance

This article is aimed for beginners, who are trying to choose between Ruby on Rails and Django. Let’s see which is fastest and why.

Vladimir Sidorenko
Vladimir Sidorenko
May 12, 2010Technology
Twitter API, OAuth and decorators

In my current project I had a task to use twitter API. Twitter uses OAuth for authentication, which is pretty dreary. To avoid fiddling with it all the time, I've moved authentication to decorator. If key is available - nothing happens, just view is launched as usual. It's convenient that there's no need for additional twitter settings in user profile. Code is in article.

Vladimir Sidorenko
Vladimir Sidorenko