Django 1.6 分支中发生了啥影响了 Manager 元类的工作方式?

Posted

技术标签:

【中文标题】Django 1.6 分支中发生了啥影响了 Manager 元类的工作方式?【英文标题】:What happened in the Django 1.6 branch that affected how Manager metaclasses work?Django 1.6 分支中发生了什么影响了 Manager 元类的工作方式? 【发布时间】:2014-02-07 05:53:00 【问题描述】:

我有一个小实用模块 django-delegate,它允许您在 QuerySet 子类上定义方法,然后通过 @delegate 装饰将这些方法定义“委托”给相应的 Manager 子类。

看起来像这样,如果我可以引用我自己的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()

...该设置允许在模型的管理器引用上进行方法链接,而没有任何运行时调度模糊,就像这样:

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

在幕后,该模块通过使用 Manager 子类 DelegateManager 上的元类来工作 - 并且在 Django 1.6 之前一直没有问题。现在,当导入带有使用 DelegateManager 的模型的应用程序时,我得到了这些令人发狂的深奥元类相关的 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: Django 的 Manager 相关管道中的 1.5 和 1.6 版本之间发生了什么可能导致这种情况?


NB,我明白我可以自己解决这个问题,方法是 a) 阅读 Django 源差异的无穷 kLOC 或 b) 在其中一个 bajillion 中完成大致相同的事情其他可能接近概念想法 django-delegate 地址的方式;主要是我对导致此 Manager 元类情况的任何原因感兴趣。谢谢你,我的 Djangonauts 伙伴

【问题讨论】:

【参考方案1】:

为了弃用 get_query_set 以支持 get_queryset (#15363),django.db.models.manager.Manager 现在是 django.db.models.manager.RenameManagerMethods 的实例,django.utils.deprecation.RenameMethodsBase 的子类。

由于delegate.DelegateSupervisor 不是django.db.models.manager.RenameManagerMethods 的(非严格)子类,Python 无法解析正确的delegate.DelegateManager 元类。

要解决此问题,您应确保 delegate.DelegateSupervisortype(django.db.models.manager.Manager) 的子类,而不是 type。这应该适用于 Django 1.5 和 1.6。我在您的存储库上创建了一个 PR 来解决这个问题。

顺便说一句,您可能想看看Django 1.7 release note。 Django 中内置了一个委托替代方案。

【讨论】:

就是这样!毫无疑问,这是我记得收到的最全面有用和最有帮助的 SO 回复,就像任何问题一样。谢谢!

以上是关于Django 1.6 分支中发生了啥影响了 Manager 元类的工作方式?的主要内容,如果未能解决你的问题,请参考以下文章

django - Ajax 实时搜索,我做错了啥?

View.post @Runnable 到底发生了啥 [重复]

从 Django 1.6 升级到 1.9:python manage.py migrate 失败

这个 WinMain() 声明中发生了啥?

在 numpy 添加过程中隐含发生了啥?

我的 UIButton 在 xib 中发生了啥?