具有集成查询集的子类化 django 模型

发布于 2024-08-26 21:07:31 字数 1941 浏览 5 评论 0原文

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

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

我发现我不能只设置 Product.Meta.abstract 为 true 或仅将不同对象的查询集或在一起。很好,但这些都是公共类的子类,所以如果我将它们的超类保留为非抽象,我应该很高兴,只要我能让它的管理器返回正确类的对象。 django 中的查询代码完成其任务,只是调用 Product()。听起来很简单,除了当我覆盖 Product.__new__ 时它会爆炸,我猜是因为 Model 中的 __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>

但是如果我从 django.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)Topsuper(A, A),以及以上所有方法与传递的结合cls 作为 __new__ 的第一个参数,一切都无济于事。为什么这会这么狠狠地踢我?我是否必须弄清楚 django 的元类才能解决此问题,或者是否有更好的方法来实现我的目的?

Like in this question, except I want to be able to have querysets that return a mixed body of objects:

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

I figured out that I can't just set Product.Meta.abstract to true or otherwise just OR together querysets of differing objects. Fine, but these are all subclasses of a common class, so if I leave their superclass as non-abstract I should be happy, so long as I can get its manager to return objects of the proper class. The query code in django does its thing, and just makes calls to Product(). Sounds easy enough, except it blows up when I override Product.__new__, I'm guessing because of the __metaclass__ in Model... Here's non-django code that behaves pretty much how I want it:

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>

But that fails if I inherit from django.db.models.Model instead of 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)

Which is a notably crappy backtrace; I can't step into the frame of my __new__ code in the debugger, either. I have variously tried super(A, cls), Top, super(A, A), and all of the above in combination with passing cls in as the first argument to __new__, all to no avail. Why is this kicking me so hard? Do I have to figure out django's metaclasses to be able to fix this or is there a better way to accomplish my ends?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

抹茶夏天i‖ 2024-09-02 21:07:31

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

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

Basically what you're trying to do is to return the different child classes, while querying a shared base class. That is: you want the leaf classes. Check this snippet for a solution: http://www.djangosnippets.org/snippets/1034/

Also be sure to check out the docs on Django's Contenttypes framework: http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ It can be a bit confusing at first, but Contenttypes will solve additional problems you'll probably face when using non-abstract base classes with Django's ORM.

南薇 2024-09-02 21:07:31

好的,这有效: 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)

现在,关于 Python 如何绑定 __new__ 的一些事情我可能不太明白,但其要点是:django 的 ModelBase 元类创建一个新的类对象,而不是而不是使用传入其 __new__ 的那个;称之为A_prime。然后,它将 A 的类定义中的所有属性粘贴到 A_prime 上,但 __new__ 无法正确重新绑定。

那么当你计算A(1)时,这里A实际上是A_prime,python调用(A_prime, 1),不匹配,就会爆炸。

因此,解决方案是在定义 A_prime 后定义 __new__

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

现在,当我之前说“这有效”时,我的意思是这与当前 Django SVN 版本中的最小对象构造测试用例无关。我不知道它是否真的可以作为模型工作或者在查询集中有用。如果你真的在生产代码中使用它,我会为 pdxpython 进行一次公开的闪电演讲,让他们嘲笑你,直到你给我们买了所有无麸质披萨。

Okay, this works: https://gist.github.com/348872

The tricky bit was this.

class A(Top):
    pass

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

A.__new__ = staticmethod(newA)

Now, there's something about how Python binds __new__ that I maybe don't quite understand, but the gist of it is this: django's ModelBase metaclass creates a new class object, rather than using the one that's passed in to its __new__; call that A_prime. Then it sticks all the attributes you had in the class definition for A on to A_prime, but __new__ doesn't get re-bound correctly.

Then when you evaluate A(1), A is actually A_prime here, python calls <A.__new__>(A_prime, 1), which doesn't match up, and it explodes.

So the solution is to define your __new__ after A_prime has been defined.

Maybe this is a bug in django.db.models.base.ModelBase.add_to_class, maybe it's a bug in Python, I don't know.

Now, when I said "this works" earlier, I meant this works in isolation with the minimal object construction test case in the current SVN version of Django. I don't know if it actually works as a Model or is useful in a QuerySet. If you actually use this in production code, I will make a public lightning talk out of it for pdxpython and have them mock you until you buy us all gluten-free pizza.

冰火雁神 2024-09-02 21:07:31

只需将 @staticmethod 粘贴在 __new__ 方法之前即可。

@staticmethod
def __new__(cls, *args, **kwargs):
    print args, kwargs
    return super(License, cls).__new__(cls, *args, **kwargs)

Simply stick @staticmethod before the __new__ method.

@staticmethod
def __new__(cls, *args, **kwargs):
    print args, kwargs
    return super(License, cls).__new__(cls, *args, **kwargs)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文