2010-11-10 51 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