2010-03-30 73 views
7

this question,但我希望能有返回對象的混合體的查詢集:子類Django模型集成了查詢集

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

我想通了,我不能只設置Product.Meta.abstract爲真或者以其他方式將或不同對象的查詢集合在一起。好,但這些都是普通類的所有子類,所以如果我把它們的超類作爲非抽象類,我應該很高興,只要我能讓它的管理器返回適當類的對象。 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> 

但失敗如果我從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的metaclasses能夠解決這個問題還是有更好的方法來完成我的目標嗎?

+1

人們很容易揣摩的困擾,但直覺告訴我,你就錯了。這就像對Django可憐的小ORM的折磨一樣。 – keturn 2010-03-30 06:44:09

回答

4

基本上你想要做的是返回不同的子類,同時查詢共享基類。那就是:你想要葉類。檢查這個片斷的解決方案:http://www.djangosnippets.org/snippets/1034/

此外,一定要檢查出Django的CONTENTTYPES框架文檔:http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/它可以在第一有點混亂,但CONTENTTYPES將解決使用非時,你可能會面臨更多的問題用Django的ORM抽象基類。

+0

。 – keturn 2010-03-30 18:20:23

+0

代碼段鏈接已損壞。 – freyley 2016-09-16 15:37:02

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) 

現在,有一些有關Python如何結合__new__,我可能不太瞭解,但它的主要內容是這樣的:Django的ModelBase元類創建一個新的類對象,而不是使用在通過一個到其__new__;致電A_prime。然後它將A的類定義中的所有屬性保留爲A_prime,但__new__不會正確重新綁定。

然後當你評估A(1)時,A實際上是A_prime這裏,python調用<A.__new__>(A_prime, 1),它不匹配,並爆炸。

因此,解決方案是在A_prime已定義之後定義您的__new__

也許這是django.db.models.base.ModelBase.add_to_class中的一個錯誤,也許這是Python中的一個錯誤,我不知道。

現在,當我之前說過「這可行」時,我的意思是這與當前SVN版本的Django中的最小對象構造測試用例是分離的。我不知道它實際上是作爲一個Model還是在QuerySet中有用。如果你真的在生產代碼中使用它,我會爲pdxpython做一個公開的閃電講話,讓他們嘲笑你,直到你買完所有無麩質比薩餅。

+0

heh。那麼,這就讓我得到了需要的子類行爲,但是它會破壞我所有的關係!然後,我的任何對象都不能與其他模型相關聯。我會進一步研究,看看我能否解決這個問題。 upvoted不加'__new__'或'__metaclass__'的 – outofculture 2010-03-30 17:34:21

1

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

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