Login

CustomQueryManager

Author:
zvoase
Posted:
July 13, 2008
Language:
Python
Version:
.96
Tags:
models q manager query custom
Score:
2 (after 2 ratings)

A models.Manager subclass that helps to remove some of the boilerplate involved in creating managers from certain queries. Usually, a manager would be created by doing this:

class MyManager(models.Manager):
    def get_query_set(self):
        return super(MyManager, self).get_query_set().filter(query=blah)

Other managers may return other query sets, but this is especially useful as one may define queries on a table which would be used a lot. Since the only part that ever changes is the query=blah set of keyword arguments, I decided to abstract that into a class which, besides taking the repetition out of manager definition, allows them to be and'd and or'd in a manner similar to the Q objects used for complex database queries.

CustomQueryManager instances may be defined in one of two ways. The first, more laborious but reusable manner, is to subclass it, like so:

class MyManager(CustomQueryManager):
    query = Q(some=query)

Then, MyManager is instantiated with no arguments on a model, like normal managers. This allows a query to be reused without extra typing and copying, and keeps code DRY.

Another way to do this is to pass a Q object to the __init__ method of the CustomQueryManager class itself, on the model. This would be done like so:

class MyModel(models.Model):
    field1 = models.CharField(maxlength=100)
    field2 = models.PositiveIntegerField()

    my_mgr = CustomQueryManager(Q(field1='Hello, World'))

This should mainly be used when a query is only used once, on a particular model. Either way, the definition of __and__ and __or__ methods on the CustomQueryManager class allow the use of the & and | operators on instances of the manager and on queries. For example:

class Booking(models.Model):
    start_date = models.DateField()
    end_date = models.DateField()
    public = models.BooleanField()
    confirmed = models.BooleanField()

    public_bookings = CustomQueryManager(Q(public=True))
    private_bookings = public_bookings.not_()
    confirmed_bookings = CustomQueryManager(Q(confirmed=True))
    public_confirmed = public_bookings & confirmed_bookings
    public_unconfirmed = public_bookings & confirmed_bookings.not_()
    public_or_confirmed = public_bookings | confirmed_bookings
    public_past = public_bookings & Q(end_date__lt=models.LazyDate())
    public_present = public_bookings & Q(start_date__lte=models.LazyDate(), end_date__gte=models.LazyDate())
    public_future = public_bookings & Q(start_date__gt=models.LazyDate())

As you can see, CustomQueryManager instances can be manipulated much like Q objects, including combination, via & (and) and | (or), with other managers (currently only other CustomQueryManager instances) and even Q objects. This makes it easy to define a set of prepared queries on the set of data represented by a model, and removes a lot of the boilerplate of usual manager definition.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from django.db.models import Manager, Q, QAnd, QOr, QNot

def get_query(obj):
    if isinstance(obj, Manager):
        return obj.query
    return obj

class CustomQueryManager(Manager):
    
    def __init__(self, query=None):
        if query:
            self.query = query
        Manager.__init__(self)
    
    def __and__(self, q):
        return self.__class__(self.query & get_query(q))
    
    def __or__(self, q):
        return self.__class__(self.query | get_query(q))
    
    def not_(self):
        return self.__class__(QNot(self.query))
    
    def get_query_set(self):
        return Manager.get_query_set(self).filter(self.query)

More like this

  1. RandomObjectManager by jjdelc 3 years, 12 months ago
  2. ActiveManager: filter objects depending on publication and/or expiration dates by haplo 6 years, 8 months ago
  3. common model privacy by teepark 7 years, 7 months ago
  4. Custom managers with chainable filters by itavor 7 years, 1 month ago
  5. Filter on Multiple M2M Objects Simultaneously by axiak 7 years, 10 months ago

Comments

Please login first before commenting.