Frage

I have a little utility module, django-delegate, that lets you define methods on a QuerySet subclass and then “delegate” those method definitions to a corresponding Manager subclass, by dint of a @delegate decoration.

It looks like this, if I can quote from my own README:

from delegate import DelegateManager, delegate

class CustomQuerySet(models.query.QuerySet):

    @delegate
    def qs_method(self, some_value):
        return self.filter(some_param__icontains=some_value)

    def dont_delegate_me(self):
        return self.filter(some_other_param="something else")

class CustomManager(DelegateManager):
    __queryset__ = CustomQuerySet

class SomeModel(models.Model):
    objects = CustomManager()

… which that setup allows for method-chaining on the model’s manager reference, without any runtime dispatch vaguery, like so:

>>> SomeModel.objects.custom_query().another_custom_query()

Behind-the-scenes, the module works by using a metaclass on the Manager subclass, DelegateManager – and that worked without issue until Django turned 1.6. Now, when importing an app with models that use a DelegateManager, I get one of these maddeningly esoteric metaclass-related showstopper TypeErrors:

>>> from tika import models as tika
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/Users/fish/Praxa/TESSAR/instance/tika/models.py", line 4, in <module>
    from delegate import DelegateManager, delegate
  File "/Users/fish/Praxa/TESSAR/local/lib/python2.7/site-packages/delegate/__init__.py", line 108, in <module>
    class DelegateManager(models.Manager):
  File "/Users/fish/Praxa/TESSAR/local/lib/python2.7/site-packages/delegate/__init__.py", line 105, in __new__
    cls, name, bases, attrs)
TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
>>> 

TL,DR: what happened between versions 1.5 and 1.6 in Django’s Manager-related plumbing that could cause this?


N.B., I do understand I could solve this myself by either a) reading through endless kLOCs of Django source diffs or b) accomplishing roughly the same thing in one of the bajillion other ways that one could possibly approach the notional idea django-delegate addresses; mainly I am interested in whatever is causing this Manager metaclass situation, tho. Thank you, my fellow Djangonauts

War es hilfreich?

Lösung

In order to deprecate get_query_set in favor of get_queryset (#15363) the django.db.models.manager.Manager is now an instance of django.db.models.manager.RenameManagerMethods, a subclass of django.utils.deprecation.RenameMethodsBase.

Since delegate.DelegateSupervisor is not a (non-strict) subclass of django.db.models.manager.RenameManagerMethods Python can't resolve the correct delegate.DelegateManager metaclass.

To solve this issue you should make sure delegate.DelegateSupervisor is a subclass of type(django.db.models.manager.Manager) and not type. This should work for Django 1.5 and 1.6. I created a PR on your repository to fix this issue.

By the way, you might want to take a look at the Django 1.7 release note. A delegate alternative has been built-in into Django.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top