I think I'm in the "otherwise" camp -- primarily for reasons of namespacing.
The QuerySet and the Manager are different classes, but they effective
share a namespace. It's good practice (and a practice that your
example demonstrates) for the Manager and the QuerySet to use the same
names for their methods; it makes a certain amount of sense to me that
there should be handy shortcut to automagically perform this
duplication.
However, the same isn't true of the Model and the QuerySet. The API
for a Model and the API for a QuerySet are quite different -- and
intentionally so. Sticking QuerySet methods on a Model in order to
avoid a couple of lines of manager definition and registration seems a
little odd to me, in an "explicit is better that implicit" kinda way.
For my money, it's better to be explicit that you're defining a custom
manager.
In practical terms, there are two ways that this could manifest.
Firstly, if there is a namespace collision -- i.e., if you want
MyObject.objects.foo() and myinstance.foo() do different things. I
can't think of an obvious practical example off the top of my head,
but anything where the verb and collective noun aren't distinguishable
would be a candidate for a collision.
Secondly, if you have more than one manager, you may want a method on
one manager, but not the other. For this case, we'd need to have the
Manager-based shortcut anyway, or tell people that if you have
multiple managers, you need to do the full-manual queryset definion.
> 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?
As I mentioned to you in person at DC.eu -- details notwithstanding,
I'm +1 to this idea. QuerySet+Manager is a common pattern, and it
deserves to have a simplification in Django's core.
Yours,
Russ Magee %-)
--You received this message because you are subscribed to the Google Groups "Django developers" group.To post to this group, send email to django-d...@googlegroups.com.To unsubscribe from this group, send email to django-develop...@googlegroups.com.For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Hi Zachary,
On 01/02/2012 10:55 PM, Zachary Voase 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?
Thanks for working on this. I think the decorator API you've selected is
a pretty good one for this; I agree with Russell that the methods belong
on a custom Manager, not on the model itself.
There will be some tricky points in the implementation. In particular,
QuerySets must remain pickleable (so they can be cached), which is
problematic if you are dynamically creating the QuerySet subclass. As
Donald Stufft points out on the ticket, we previously had an approach in
django-model-utils (manager_from) that was based on dynamically-created
QuerySet subclasses, and we moved away from it because it broke pickling
of QuerySets. There may be another way around this; I didn't look at the
problem in depth.
I have to say, this all feels to me a bit like polishing a turd; adding
ever-more complex hacks with metaclasses, dynamic subclasses, etc., all
to paper over what is really a more fundamental design problem. To wit:
a Manager is no different from a QuerySet in practice - it's just a sort
of "starter" queryset accessible from the model. If in practice people
pretty much always want to pair a custom Manager with a custom QuerySet
that has the same extra methods (and that is my experience), I think
it's telling us that those shouldn't be two separate classes at all.
I can't help but wonder if it would be possible to solve the problem
more thoroughly and remove the need for magic decorators by eliminating
the Manager class entirely and making it possible to simply assign a
QuerySet to a model, perhaps with a helper method:
objects = manager(PersonQuerySet.filter(active=True))
I'm sure there are devils in the details here, too, but on cursory
inspection of the Manager code, it seems like it might be doable.
Carl
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk8DPlYACgkQ8W4rlRKtE2e+TQCffZZmaqKRc4Hyn3reUzYj6Cga
ja8AoNyIutFq4GuUfTgn5RgBMQaW/rJk
=AIQa
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi Zachary,
On 01/02/2012 10:55 PM, Zachary Voase 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
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-d...@googlegroups.com.
To unsubscribe from this group, send email to django-develop...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
On 01/03/2012 10:42 AM, Alex Gaynor wrote:
> I haven't analyzed your suggestion in detail Carl, but there is a good
> reason for managers to exist:
> querysets cache internally on iteration, managers are not directly
> evaluable, but were they you could end up doing something like:
> for instance in Model.objects:
> pass
> and you'd cache those results in global state.
Indeed, that's of those devils in the details, but it's not
insurmountable. Clearly, either the Model metaclass or the hypothetical
"manager" function would need to do something to the provided QuerySet
to make it not iterable directly. This could be a pretty simple flag on
QuerySet, it doesn't require a separate class.
Carl
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk8DQEsACgkQFRcxmeyPUXI5DQCffoTuvSuJ84HlQq1F8SGv6yRX
SFMAniE3FhaGH5eVlrgtSy3se/g2/4Nj
=lqm7
-----END PGP SIGNATURE-----
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-d...@googlegroups.com.
To unsubscribe from this group, send email to django-develop...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Zachary provided a complete example in his original post. The crux of
it -- at present, in order to support the following two expressions:
MyModel.objects.foo()
MyModel.objects.filter(...).foo()
you currently have to provide 2 definitions of foo() -- one on a
custom Manager, and one on a custom QuerySet that is generated by the
custom manager. Zachary's proposal is to provide a decorator so that
you only have to provide the Manager definition.
Yours,
Russ Magee %-)
Hi Anssi,
On 01/10/2012 04:14 AM, Anssi K��ri�inen wrote:
> However, I would like it if you could mix methods from different
> managers. Maybe add qs.set_manager(manager_name), and so you could do:
> SomeModel.objects.order_by('id').some_manager_method().set_manager('other_manager').some_manager_method_in_other_manager().
> set_manager would simply change the qs.manager. This way you could
> change the manager which is looked for chainable methods, and all
> chainable methods would be available from all managers.
>
> I hoverer do like the idea that you have all the methods defined in
> any manager directly available in the queryset, without the need to
> use set_manager. This is easy to achieve.
>
> Thoughts?
Having access to any manager method from any queryset, regardless of
which manager that queryset came from, seems to me like a highly
surprising anti-feature, not a benefit. Can you outline a realistic use
case for it?
Carl
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk8McRgACgkQ8W4rlRKtE2fAlwCgq6JcsmkEyUR4djK8bPr49Uf0
lWIAmwdn2oAVqrv7GXph/qePaCYCuVVp
=GL8K
-----END PGP SIGNATURE-----
Cheers,
Etienne