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
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 2 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.