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]).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 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 from
https://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.