2010-11-10 50 views
4

比方說,我有以下的ORM類(字段中刪除,以簡化):與多對多場Django的ORM繼承

class Animal(models.Model): 
    say = "?" 

    def say_something(self): 
     return self.say 

class Cat(Animal): 
    self.say = "I'm a cat: miaow" 

class Dog(Animal): 
    self.say = "I'm a dog: wuff" 

class Animals(models.Model): 
    my_zoo = models.ManyToManyField("Animal") 

當我加入一些動物動物園我:

cat = Cat() 
cat.save() 
dog = Dog() 
dog.save() 

animals.my_zoo.add(cat) 
animals.my_zoo.add(dog) 

for animal in animals.my_zoo.all(): 
    print animal.say_something() 

...我所期望的結果如下:

我是一隻貓:喵的,我是狗:wuff

而是所有我得到的是一般Animal對象的實例,除了「?」之外別無他法。

如何從db中retreived對象時實現真正的對象繼承和後來的多態?

+0

您的代碼太簡單了,無法分辨。 – 2010-11-10 18:43:40

回答

4

django中的模型繼承不會向基類添加任何類型信息。所以從Animal()向下放置對象到其適當的形式是不可能的。

繼承僅用於將繼承模型上的字段映射回父模型。因此,如果Animal的字段爲name,那麼Cat上將存在相同的字段,並且當您保存Cat時,將更新animal

繼承的工作原理是將一個OneToOne關係:在您的情況

class Animal(Model): 
    name = CharField() 

class Cat(Model): 
    animal_ptr = OneToOneField(Animal) 

Cat(name='Murky').save() 

print repr(list(Animals.objects.all())) 

: [Animal(name='Murky')] 

技術上講,它甚至可以用於動物()既狗()和貓()在同一時間:

animal = Animal() 
animal.save() 

Cat(animal_ptr=animal).save() 
Dog(animal_ptr=animal).save() 

解決您的問題將是一個場subtype或類似添加到您的Animal()對象,並實施向下轉換功能的方式:

class Animal(Model): 
    subtype = CharField() 

    def downcast(self): 
     if self.subtype == 'cat': 
      return self.cat 
      # The self.cat is a automatic reverse reference created 
      # from OneToOne field with the name of the model 

for animal in Animal.objects.all().select_related('dog', 'cat', ...)]: 
    animal.downcast().say_something() 

有關類似主題的堆棧溢出幾個有用的讀取: Generic many-to-many relationships他罩。 How to do Inheritance Modeling in Relational Databases?

+1

+1有一個很好的解釋;儘管我發現添加'species_name'是一種破解,但沒有更好的方法(事實上,我自己也使用過)。另外,如果你問我,django應該在內部做這樣的事情,而不是讓我們碰到鍵盤。 – 2010-11-10 20:01:31

+0

感謝您的回答。我明白它是如何工作的,但我希望有一些魔術會爲我做一些骯髒的工作。 – edkirin 2010-11-11 07:12:24

0

您可以通過attribute on the parent class訪問descendentas。該屬性的名稱是模型名稱的小寫版本:

class Animal(models.Model): 
    say = "?" 

    def say_something(self): 
     for animal in ('cat', 'dog'): 
      try: 
       return getattr(self, animal).say 
      except: 
       pass 
      return self.say