How to reuse custom defined 'models.Manager' QuerySets
Django recommends to define custom query sets on your sublcassed models.Manager, there is one problem however that these are not chainable, that is you can only call your custom queryset once - assuming I have defined an '.active()' queryset the following will not work:
>> PaymentTerm.objects.active().active() Traceback (most recent call last): File "", line 1, in AttributeError: 'PaymentTermManager' object has no attribute 'active' >>>
Current solutions on the web involve creating a subclassed 'models.Manager' and a custom 'QuerySet' which do work but are to code verbose for my linking. I believe the below approach is simpler, cleaner and more succinct.
class OfferManager(models.Manager): ... STATUS_CHOICES = ( (STATUS_DISABLED, "Disabled"), (STATUS_ENABLED, "Enabled"), (STATUS_NEGOTIATED, "Negotiated"), (STATUS_ARCHIVED, "Archived"), ) QUERYSET_PUBLIC_KWARGS = {'status__gte': STATUS_ENABLED} QUERYSET_ACTIVE_KWARGS = {'status': STATUS_ENABLED} def get_query_set(self): """ Assing a modified QuerySet to include our chainable QuerySets """ class OfferManagerQuerySet(QuerySet): # clone QuerySet pass # explictly copy our QuerySet methods OfferManagerQuerySet.public = self.public OfferManagerQuerySet.active = self.active ... return QuerySetDynamicallyCreated(self.model, using=self._db) def public(self): """ Returns all entries accessible through front end site""" return self.all().filter(**OfferManager.QUERYSET_PUBLIC_KWARGS) public.chainable = True def active(self): """ returns offers that are open to negotiation """ return self.public().filter(**OfferManager.QUERYSET_ACTIVE_KWARGS) ...
Django patch for model.Manager chainable QuerySets
Please take a look at Django Ticket #20625 with a github Django branch for new proposed method for defining chainable filters based on this idea which works through a 'chainable == True' attribute as shown below:
class OfferManager(models.Manager): ... def public(self): """ Returns all entries accessible through front end site""" return self.all().filter(...) public.chainable = True # instructs to dynamically tranplat this method onto # returned QuerySet as.public(...) # effectively providing chainable custom QuerySets def active(self): """ Returns offers that are open to negotiation """ return self.public().filter(**OfferManager.QUERYSET_ACTIVE_KWARGS) # an example of how to reffer to OfferManager # constants as 'self' context changes active.chainable = True ...
As always feel free to comment or follow me on twitter @danielsokolowski
Comments
Post a Comment