【问题标题】:Django ORM inheritance with ManyToMany field带有 ManyToMany 字段的 Django ORM 继承
【发布时间】:2011-05-08 01:25:28
【问题描述】:

假设我有以下 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()

...我希望得到以下结果:

我是猫:喵呜,我是狗:呜呜呜

但相反,我所拥有的只是一般 Animal 对象的实例,除了“?”之外什么都不能说。

从db中获取对象时如何实现真正的对象继承和后期的多态性?

【问题讨论】:

  • 您的代码过于简单,无法分辨。

标签: django orm inheritance


【解决方案1】:

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() 甚至可能同时是 Dog() 和 Cat():

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 很好的解释;尽管我发现添加 species_name 是一种 hack,但没有更好的方法(事实上,我自己也使用过)。另外,如果你问我,django 应该在内部做类似的事情,而不是让我们用头撞键盘。
  • 感谢您的回答。我知道它是如何工作的,但我希望有一些魔法可以为我做脏活。
【解决方案2】:

您可以通过attribute on the parent class 访问后代。属性名称是模型名称的小写版本:

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

【讨论】:

    猜你喜欢
    • 2020-02-18
    • 1970-01-01
    • 1970-01-01
    • 2021-09-24
    • 2019-09-04
    • 1970-01-01
    • 1970-01-01
    • 2017-11-25
    • 2017-01-15
    相关资源
    最近更新 更多