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.

Reply via email to