Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reuse filter() queryset in Django

I want to write a filter once and reuse it everywhere, how can I do it?

For example: there is a model Student with flag field. I want to write a filter to get non-graduate students (flag=0). But in many views and functions we need list non-graduate students, and I am lazy and don't want to write the filter again and again in these views and functions, as this will make it hard to maintain the source code.

Can I use meta in model Student? I did not find any filter related meta options. Or can I write a function to filter in model Student? In my mind, function in model only works in one Student object instead of list.

like image 231
JordanChina Avatar asked Dec 11 '22 22:12

JordanChina


1 Answers

Using a custom QuerySet and QuerySet.as_manager() is the best solution now. Jamie Matthews of Dabapps has discussed reusable filters in detail in his blog post Building a higher-level query API: the right way to use Django's ORM.

“Using Django's low-level ORM query methods directly in a view is (usually) an anti-pattern”
— Jamie Matthews

The blog post is written before Django got the .as_manager() method for QuerySet though.

I'd use something like this now (based on the currently accepted answer):

class StudentQuerySet(models.query.QuerySet):
    def graduate(self):
        return self.filter(graduated=True)

    def undergraduate(self):
        return self.filter(graduated=False)

class Student(models.Model):
    graduated = BooleanField()

    objects = StudentQuerySet.as_manager()

In other, more complex cases, it's useful to be able to define complex filters in the custom query set since filters are chainable when implemented like this.

If you want this to be made available to related managers in other models (ie. if you had another model referencing the Student model and were using otherinstance.student_set.all()), see Django: Using managers for related object access. In short, do this:

class Student(models.Model):
    graduated = BooleanField()

    objects = StudentQuerySet.as_manager()
    objects.use_for_related_fields = True
like image 195
joneskoo Avatar answered Jan 05 '23 15:01

joneskoo