The idea of `QuerySet .as_manager()` sounded sane to me at first—especially in the light of the failure of various other attempts—but it creates a circular dependency between `Manager` and `QuerySet`. Creating the `Manager` class now requires the `QuerySet` class, and `QuerySet` needs a reference to `Manager` for `as_manager()`.
Is it possible to untangle this by writing a `Manager.from_queryset(queryset_class)` method instead? Or at least to put the code that creates a Manager from a QuerySet inside the Manager base class? QuerySet shouldn't even know what a Manager is! I also dislike the two ad-hoc APIs spilt over the QuerySet class: the `manager` flag on methods and the `base_manager_class` class attribute. I'd really like to keep the API self-contained within the `as_manager()` or `from_queryset()` method. I had started a detailed review, but I'm now thinking we should address these design decisions first. -- Aymeric. On 22 juil. 2013, at 10:27, Anssi Kääriäinen <[email protected]> wrote: > Ticket #20625 deals with the problem of writing custom QuerySet methods. The > problem is that one needs to write some boilerplate code to have methods > available on both the Manager and the QuerySet. The ticket has a patch for > having custom QuerySet methods automatically available on the model's > Manager, too. > > The reason for this post is that different ideas for implementing chainable > manager/queryset methods have been proposed multiple times. So, I want to > make sure we agree on the approach. > > The API idea in #20625 is simple: > > class MyQuerySet(models.QuerySet): > def published(self): > return self.filter(published_date__lte=now()) > > class MyModel(models.Model): > published_date = models.DateTimeField() > > objects = MyQuerySet.as_manager() > > The manager created by as_manager() will automatically have a published() > method available. The method is created dynamically, and is effectively this: > > def published(self, *args, **kwargs): > getattr(self.get_query_set(), 'published')(*args, **kwargs) > > The pull request contains more details. Pull request is available from > https://github.com/django/django/pull/1328, ticket is > https://code.djangoproject.com/ticket/20625. > > The other proposed approaches for chainable manager methods usually use > overridden __getattr__() on either manager or queryset and memoizes the other > part's class. The __getattr__ then delegates calls to the memoized class. > This approach has problems with super() calls and dynamic inspection in pdb. > Both of those should work with the proposed approach as the created Manager > class really has the methods available. > > Another somewhat common idea is to make Manager a QuerySet subclass and thus > avoid this whole Manager/QuerySet split problem. I agree on this idea, but > the problem is that this seems to be really hard to do in a way that is even > remotely backwards compatible. I tried a couple of different approaches and > failed miserably. If somebody has a concrete idea of how to do this, now is a > good time to present it. > > It should be noted that the proposed patch doesn't prevent making Manager a > QuerySet subclass later on. > > I am planning to do a final review & commit the patch soonish (likely this > week). > > - Anssi > > -- > You received this message because you are subscribed to the Google Groups > "Django developers" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. > Visit this group at http://groups.google.com/group/django-developers. > For more options, visit https://groups.google.com/groups/opt_out. > > -- You received this message because you are subscribed to the Google Groups "Django developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/django-developers. For more options, visit https://groups.google.com/groups/opt_out.
