It should be relatively simple to allow QuerySets to know which
manager they came from, and then override __getattr__ to use the
declared @querymethod from that manager (instead of the other way
around). This avoids the unpickleable QuerySet subclass problem.

On Jan 10, 8:54 am, Anssi Kääriäinen <[email protected]> wrote:
> On Jan 9, 10:51 pm, Anssi Kääriäinen <[email protected]> wrote:
>
>
>
>
>
>
>
>
>
> > I have a very quickly crafted draft of the main idea 
> > athttps://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]).publishe 
> > d().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 did some more testing, and I think this will work. Now, I also have
> a suggestion about the manager  autodiscovery API:
>   - All managers defined for the model are added into the
> queryset.managers list. That is, the method resolution order is the
> same order in which the managers are defined. Current model's managers
> first, then parent model managers.
>   - The managers list should be _meta option. Currently this is not
> available in the above order, but it should be possible to
> have  ._meta.ordered_managers attribute. This could then be used in
> __getattr__. Instead of doing for manager in self.managers do: for
> manager in self.model._meta.ordered_managers.
>   - You can also access managers directly, if you had:
>
> class M1(Manager):
>      some_constant = 'bar'
>      def get_foo_objs(self):
>             return self.get_query_set().filter(foo=self.some_constant)
>
> class M2(Manager):
>      def get_foo_objs(self):
>             return self.get_query_set().filter(foo='baz')
>
> class SomeModel(Model):
>     ...
>     objects = M1()
>     other_objects = M2()
>
> and you do SomeModel.objects.order_by('id').get_foo_objs(), you will
> get M1 get_foo_objs(). However, you can also do
> SomeModel.objects.order_by('id').other_objects.get_foo_objs(), that
> is, you can get the dynamically created manager by using other_objects
> on the queryset. The only nasty thing about this is that now _all_
> methods of other_objects manager are available to you, not only those
> which are actually chainable.
>
> I think the base idea (dynamic manager) has some merit which can not
> be easily achieved by adding the chainable methods to QuerySet
> (dynamic) subclasses:
>   - You can easily upgrade your current managers. Verify that your
> manager method is chainable (it is based on .get_query_set()
> and .get_query_set() is not overridden). If so, add @chainable and
> things should just work.
>   - You can call other methods of the manager, or access class
> variables. This is not achievable in the QuerySet subclassing idea,
> unless you transfer all the methods, and all the variables to the
> QuerySet subclass. And in this case, @chainable decorator is
> pointless. For example M1.get_foo_objs() would not work, because it
> accesses self.some_constant, and it would be hard to make that
> available in the queryset subclass.
>   - You can't access duplicate manager methods. In the above example
> M2.get_foo_objs() would not be available through the queryset.
>
> It could be possible to make the "dynamic manager" idea work even
> without creating dynamic manager subclasses. Just define
> Manager.get_query_set() as:
>     if self._chain_to_qs is not None:
>         return self._chain_to_qs
>     else:
>         # return new queryset as normally.
>
> And the only thing you need to do in __getattr__ is instantiating the
> manager, set its _chain_to_qs to self. And return the asked method by
> getattr.
>
> This would make even overridden get_query_set() based managers work,
> as long as your overriding implementation calls
> super().get_query_set().
>
> I have updated the draft patch according to the above idea of
> modifying manager.get_query_set(). The patch is even more simple than
> before. The manager resolution order is not implemented (I use
> chain(_meta.abstract_managers, _meta.concrete_managers).  @chainable
> is still not implemented.  The patch is available 
> fromhttps://github.com/akaariai/django/compare/dynamic_man
>
> It would be pretty easy to return the "dynamic managers" from
> __getattr__, but I am not sure that is a good idea, so I have left
> that out.
>
>  - 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