具有集成查询集的子类化 django 模型
就像这个问题,除了我希望能够有返回混合对象体的查询集:
>>> 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)
、Top
、super(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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
基本上,您想要做的是返回不同的子类,同时查询共享基类。那就是:您想要叶类。检查此代码片段以获取解决方案: 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.
您需要其中之一:
http://code.google.com/p/django-多态模型/
https://github.com/bconstantin/django_polymorphic
有缺点,即额外的查询。
You want one of these:
http://code.google.com/p/django-polymorphic-models/
https://github.com/bconstantin/django_polymorphic
There are downsides, namely extra queries.
好的,这有效: https://gist.github.com/348872
棘手的一点是这个。
现在,关于 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.
Now, there's something about how Python binds
__new__
that I maybe don't quite understand, but the gist of it is this: django'sModelBase
metaclass creates a new class object, rather than using the one that's passed in to its__new__
; call thatA_prime
. Then it sticks all the attributes you had in the class definition forA
on toA_prime
, but__new__
doesn't get re-bound correctly.Then when you evaluate
A(1)
,A
is actuallyA_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__
afterA_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.
只需将 @staticmethod 粘贴在 __new__ 方法之前即可。
Simply stick @staticmethod before the
__new__
method.我最近发现的另一种方法: http:// /jeffelmore.org/2010/11/11/automatic-downcasting-of-inherited-models-in-django/
Another approach that I recently found: http://jeffelmore.org/2010/11/11/automatic-downcasting-of-inherited-models-in-django/