7

就像在这个问题中一样,除了我希望能够拥有返回混合对象的查询集:

>>> Product.objects.all()
[<SimpleProduct: ...>, <OtherProduct: ...>, <BlueProduct: ...>, ...]

我发现我不能只设置Product.Meta.abstract为 true 或者只是将不同对象的查询集或在一起。很好,但是这些都是公共类的子类,所以如果我将它们的超类保留为非抽象类,我应该很高兴,只要我能让它的管理器返回正确类的对象。django 中的查询代码完成了它的工作,只是调用 Product()。听起来很简单,除了当我覆盖它时它会爆炸Product.__new__,我猜是因为__metaclass__模型中的......这是非django代码,其行为几乎符合我的要求:

class Top(object):
    _counter = 0
    def __init__(self, arg):
        Top._counter += 1
        print "Top#__init__(%s) called %d times" % (arg, Top._counter)
class A(Top):
    def __new__(cls, *args, **kwargs):
        if cls is A and len(args) > 0:
            if args[0] is B.fav:
                return B(*args, **kwargs)
            elif args[0] is C.fav:
                return C(*args, **kwargs)
            else:
                print "PRETENDING TO BE ABSTRACT"
                return None # or raise?
        else:
            return super(A).__new__(cls, *args, **kwargs)
class B(A):
    fav = 1
class C(A):
    fav = 2
A(0) # => None
A(1) # => <B object>
A(2) # => <C object>

但是,如果我继承 fromdjango.db.models.Model而不是object

File "/home/martin/beehive/apps/hello_world/models.py", line 50, in <module>
    A(0)
TypeError: unbound method __new__() must be called with A instance as first argument (got ModelBase instance instead)

这是一个非常糟糕的回溯;我也无法__new__在调试器中进入我的代码框架。我已经不同地尝试了super(A, cls), Top, super(A, A), 以及以上所有方法,并结合cls作为第一个参数传入__new__,但都无济于事。为什么这踢我这么狠?我是否必须弄清楚 django 的元类才能解决这个问题,还是有更好的方法来实现我的目标?

4

5 回答 5

4

基本上你要做的是返回不同的子类,同时查询一个共享的基类。那就是:你想要叶子类。检查此代码段以获取解决方案:http: //www.djangosnippets.org/snippets/1034/

还请务必查看 Django 的 Contenttypes 框架上的文档:http ://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ 一开始可能会有点混乱,但 Contenttypes 会解决您的其他问题在 Django 的 ORM 中使用非抽象基类时可能会遇到。

于 2010-03-30T09:48:25.540 回答
2

你想要其中之一:

http://code.google.com/p/django-polymorphic-models/
https://github.com/bconstantin/django_polymorphic

有缺点,即额外的查询。

于 2012-02-08T17:29:30.010 回答
1

好的,这有效:https ://gist.github.com/348872

棘手的是这个。

class A(Top):
    pass

def newA(cls, *args, **kwargs):
    # [all that code you wrote for A.__new__]

A.__new__ = staticmethod(newA)

现在,__new__我可能不太了解 Python 是如何绑定的,但它的要点是:django 的ModelBase元类创建一个新的类对象,而不是使用传递给它的__new__; 叫那个A_prime。然后它将您在类定义中的所有属性粘贴到Aon to A_prime,但__new__没有正确重新绑定。

然后,当您评估A(1),A实际上A_prime在这里时,python 调用<A.__new__>(A_prime, 1),它不匹配,并且它爆炸了。

所以解决方案是定义你的__new__ after A_prime已经定义。

也许这是 中的一个错误django.db.models.base.ModelBase.add_to_class,也许是 Python 中的一个错误,我不知道。

现在,当我之前说“这有效”时,我的意思是它与当前 SVN 版本的 Django 中的最小对象构造测试用例隔离开来。我不知道它实际上是作为模型工作还是在 QuerySet 中有用。如果你真的在生产代码中使用它,我将为 pdxpython 公开闪电般地谈论它,让他们嘲笑你,直到你给我们买所有的无麸质披萨。

于 2010-03-30T08:50:52.650 回答
1

只需在方法之前粘贴@staticmethod __new__

@staticmethod
def __new__(cls, *args, **kwargs):
    print args, kwargs
    return super(License, cls).__new__(cls, *args, **kwargs)
于 2012-02-08T13:45:34.820 回答
1

我最近发现的另一种方法:http: //jeffelmore.org/2010/11/11/automatic-downcasting-of-inherited-models-in-django/

于 2013-04-04T18:51:57.063 回答