Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: Give custom filtering methods to `QuerySet` objects of a specific model

Tags:

python

orm

django

I'm defining a model in Django. I want to define a few custom methods for its QuerySet objects. (i.e. I want to define custom ways to filter instances of that object, in a way peculiar only to that model.)

Now, I could define those on the Manager, but then these methods will be accessible only on the Manager and not on any single QuerySet which is the result of any kind of filtering on that model.

So: How do I give the QuerySet objects of my model custom filtering methods?

like image 253
Ram Rachum Avatar asked May 15 '13 23:05

Ram Rachum


2 Answers

If you need filter chaining extend Queryset.

One example from my current project:

from django.db import models
from django.db.models.query import QuerySet

class MemberQuerySet(QuerySet):

    def in_group(self, group):
        return self.filter(group_set__pk=group.pk)

    def not_in_group(self, group):
        return self.exclude(groups_set__pk=group.pk)

class MemberManager(models.Manager):

    def get_queryset(self):
        return MemberQuerySet(self.model, using=self._db)

    def in_group(self, group):
        return self.get_queryset().in_group(group)

    def not_in_group(self, group):
        return self.get_queryset().not_in_group(group)

class Member(models.Model):

    # ...

    objects = MemberManager()

With this you can do:

Member.objects.in_group(one_group).not_in_group(another_group)

If you don't need filter chaining a manager with custom methods would be enough. That's quite well covered in the docs.

This snippet seems nice for faster QuerySet plug-in, but it is old (4 years) and I've never tried it.

like image 82
Adrián Avatar answered Oct 06 '22 19:10

Adrián


Resurrecting this to offer a better, cleaner way to do this.

From at least django 1.8 (not tested on anything earlier..it may or may not work, not sure when this API was introduced), there's a much nicer way to do this...

from django.db import models
from django.db.models.query import QuerySet

class MemberQuerySet(QuerySet):
    def in_group(self, group):
        return self.filter(group_set__pk=group.pk)

    def not_in_group(self, group):
        return self.exclude(group_set__pk=group.pk)

    # etc..

class Member(models.Model):
    # fields..

    objects = MemberQuerySet.as_manager()

Then you can do this..

Member.objects.in_group(group1).not_in_group(group2)
like image 41
dannosaur Avatar answered Oct 06 '22 17:10

dannosaur