Back
Jul 1, 2010

Overriding QuerySet in Django

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 (published in 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 - published and 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 getattr is 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)


# Now you can simply write:
# 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.

Subscribe for the news and updates

More thoughts
Apr 3, 2024Technology
Test Analysis at the Product Level

Test analysis is an essential and crucial activity in the testing process. A well-organized test analysis provides an adequate level of confidence in the overall effectiveness of the testing and contributes to the delivery of high-quality software.

Apr 18, 2023Technology
TDD guide for busy people, with example

This quick introduction to TDD is meant to show the implementation of this practice with a real-life example.

Apr 27, 2022TechnologyBusiness
How to Choose the Best Javascript Framework: Comparison of the Top Javascript Frameworks

In our article, you will find the best JavaScript framework comparison so that you know for sure how to choose the right one for your project.

Mar 12, 2017Technology
Creating a chat with Django Channels

Nowadays, when every second large company has developed its own instant messenger, in the era of iMessages, Slack, Hipchat, Messager, Google Allo, Zulip and others, I will tell you how to keep up with the trend and write your own chat, using django-channels 0.17.3, django 1.10.x, python 3.5.x.

Mar 6, 2010TechnologyManagement
Supplementing settings in settings_local

For local project settings, I use old trick with settings_local file:try:from settings_local import \*except ImportError:passSo in settings_local.py we can override variables from settings.py. I didn't know how to supplement them. For example how to add line to INSTALLED_APPS without copying whole list.Yesterday I finally understood that I can import settings from settings_local:# settings_local.pyfrom settings import \*INSTALLED_APPS += (# ...)

Mar 6, 2010Technology
Ajax form validation

There was a task to submit form with ajax, with server side validation of course. Obvious solution is to do validation and return json with erros. I didn't like idea of writing separate view for validation and then inserting errors in form html on client side. Especially since I already had a generic template for django form with errors display. In this article I'll describe how I solved the task.