Sanketh B. K
Sanketh B. K

Reputation: 837

What is the use of writing custom QuerySet classes in Django

I was going through Django documentation for model managers. I saw that in this section i saw that that model.QuerySet class is overriden by a custom QuerySet class which has two methods which returns the queryset by filtering role='A' or role='E'.

class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role='A')

    def editors(self):
        return self.filter(role='E')

class PersonManager(models.Manager):
    def get_queryset(self):
        return PersonQuerySet(self.model, using=self._db)

    def authors(self):
        return self.get_queryset().authors()

    def editors(self):
        return self.get_queryset().editors()

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=[('A', _('Author')), ('E', _('Editor'))])
    people = PersonManager()

But I don't understand what was the need of filtering in the QuerySet class when we could have done that in model manager itself like this.

class PersonManager(models.manager):
    def authors(self):
        return self.filter(role='A') 

    def editors(self):
        return self.filter(role='E') 

Please help to understand the difference. Thank you in advance.

Upvotes: 4

Views: 2407

Answers (1)

Abdul Aziz Barkat
Abdul Aziz Barkat

Reputation: 21807

While making queries using Django's ORM it is quite common to see people chaining methods on a queryset, example:

queryset.filter(...).annotate(...).order_by(...)

Each of the method here returns an instance of QuerySet upon which we can apply more methods.

Now let us assume we were to use your solution and implement our methods in the model manager. Let us consider the below snippet in that case:

print(type(Person.objects))  # prints the manager class
print(type(Person.objects.all()))  # prints the queryset class

Person.objects.authors()  # Succeeds

Person.objects.all().authors()  # likely (might be some other) raises an AttributeError

Whereas if we had done this with a custom queryset the second queryset too would have worked. This is because if we declared the method on the manager the queryset will not really have it, and hence our chaining of the method would fail.

Upvotes: 4

Related Questions