On Jan 3, 7:55 am, Zachary Voase <[email protected]> wrote: > At the moment it's very easy to add methods to individual models, just > by putting a method on the model class itself which accepts 'self'. > However, defining business logic on collections of records is > currently pretty complicated — you have to create at least a custom > Manager subclass, and if you want to chain those methods together > you'll need a QuerySet subclass. An example of the desired behaviour, > and the steps required for it, is shown in [1]. > > I originally created django-qmixin [2] to tackle this problem; you can > define methods which will be present on the manager and all querysets > produced therefrom, including on relations. However, the django-qmixin > approach of creating a mixin and then including that on the Manager > class object doesn't really gel with the rest of Django core. I've > worked out two approaches which are easier for novices to understand, > and match the idioms of the rest of Django. They both involve adding a > @models.querymethod decorator, which would be applied to methods which > operate on collections of records (i.e. querysets). It's an analog to > Python's @classmethod. > > The first approach [3] involves adding these querymethods to the model > class itself; the second [4] adds them to a manager subclass (but > without the trouble of the QuerySet subclass). I prefer the former, > for simplicity, but you may believe otherwise. > > I'm working on a proof-of-concept implementation, but I feel it's more > important to agree on the interface and rationale beforehand. Any > thoughts?
I haven't read all the comments in this thread, but I think I have an idea for implementation that might work, and which I haven't seen in the discussions before. The implementation idea is to add __getattr__ to QuerySet. The __getattr__ would go through managers associated with that queryset (don't know how to do that association. An API issue), and if it founds a chainable method in any associated manager, it will do the following: - create a dynamic subclass of the manager - patch that subclass so that its get_query_set() will return self._chain_to_qs - instantiate that subclass as dynamic_manager - set dynamic_manager._chain_to_qs = self - return getattr(dynamic_manager, attr) I have a very quickly crafted draft of the main idea at https://github.com/akaariai/django/compare/dynamic_man As you can see, the implementation is very simple. It is missing manager autodiscovery, and the @querymethod decorator. I have tested it with: class PublishedManager(Manager): def published(self): return self.get_query_set().filter(published_date__lte=datetime.now()) class SomeModel(models.Model): published_date = models.DateTimeField() objects = PublishedManager() print SomeModel.objects.published().query print SomeModel.objects.order_by('id').add_managers([SomeModel.objects]).published().query These simple test-cases seem to work. Note that @querymethod is not implemented. Also, add_managers is not part of the proposed API. The set of managers to look for chainable methods should somehow be autodiscovered, but I have no good ideas about how to do that. I also did some speedtesting, using objects.order_by('id').filter(published_date__lte=now()) is around 5% faster than using .order_by('id').published(). All in all, I think the above idea is worth exploring, as it is simple, yet powerful enough to allow what is wanted by the original poster. - Anssi -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
