Python django模型继承中的一致性缺失 让我们考虑两个简单的模型(Django V1.5.5假设),一个继承了另一个模型: from django.db import models class StreetAddress(models.Model): street_name = models.CharField(max_length=64) building_number = models.PositiveSmallIntegerField() def __str__(self): return "%s %d" % (self.street_name, self.building_number) class Cafe(StreetAddress): name = models.CharField(max_length=64)

Python django模型继承中的一致性缺失 让我们考虑两个简单的模型(Django V1.5.5假设),一个继承了另一个模型: from django.db import models class StreetAddress(models.Model): street_name = models.CharField(max_length=64) building_number = models.PositiveSmallIntegerField() def __str__(self): return "%s %d" % (self.street_name, self.building_number) class Cafe(StreetAddress): name = models.CharField(max_length=64),python,django,inheritance,orm,model,Python,Django,Inheritance,Orm,Model,StreetAddress故意创建为非抽象的,因为我认为它的实例是独立于cafe实例或其他可能的后代创建的。 让我们尝试创建一些位于某个地址的咖啡馆: Python 2.7.4 (default, Apr 19 2013, 18:28:01) [GCC 4.7.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>>

StreetAddress故意创建为非抽象的,因为我认为它的实例是独立于cafe实例或其他可能的后代创建的。 让我们尝试创建一些位于某个地址的咖啡馆:

Python 2.7.4 (default, Apr 19 2013, 18:28:01) 
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from heir_demo.models import StreetAddress, Cafe
>>> addr = StreetAddress.objects.create(street_name='Piccadilly', building_number=5)
>>> addr
<StreetAddress: Piccadilly 5>

>>> mollys = Cafe.objects.create(streetaddress_ptr=addr, name='Mollys')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  ...  stacktrace goes here ...
Warning: Column 'building_number' cannot be null
现在,它的工作原理与预期一样:

>>> from heir_demo.models import StreetAddress, Cafe
>>> addr = StreetAddress.objects.all()[0]
>>> mollys = Cafe.objects.create(streetaddress_ptr=addr, name='Mollys')
>>> mollys
<Cafe: Piccadilly 5>
来自heir_demo.models导入街道地址,咖啡馆 >>>addr=StreetAddress.objects.all()[0] >>>mollys=Cafe.objects.create(streetaddress\u ptr=addr,name='mollys') >>>莫利斯 请给我解释一下——为什么没有任何解决办法它就不能工作?这不是最一致、最可预测的行为吗


非常感谢。

我敢自己回答。虽然,这可能是一个命题,而不是答案

这里是Django v.1.5.x的一个小补丁(它也可以很容易地适应Django主分支)

我建议替换django.db.models.Model类的save_base方法 通过以下代码(只是一个想法):

这个小补丁允许基于父模型的现有实例创建派生模型的实例

>>> from heir_demo.models import StreetAddress, Cafe
>>> addr = StreetAddress.objects.create(street_name='Piccadilly', building_number=5)
>>> mollys = Cafe.objects.create(streetaddress_ptr=addr, name='Mollys')
>>> mollys
<Cafe: Piccadilly 5>
来自heir_demo.models导入街道地址,咖啡馆 >>>addr=StreetAddress.objects.create(street\u name='Piccadilly',building\u number=5) >>>mollys=Cafe.objects.create(streetaddress\u ptr=addr,name='mollys') >>>莫利斯
您的提议将允许创建共享其父部分的独立对象(考虑一个看起来像您的咖啡馆的模型餐厅)。这在面向对象的世界中通常很奇怪,但在关系世界中很好


然而,你会发现你可以在同一条街上有一家咖啡馆和一家餐馆,但不是两个咖啡馆或两个餐馆。这比Django目前的预期要令人惊讶得多,即(本质上)每个对象都有自己的身份。

如果你想要这种行为,试着使用
OneToOne
关系而不是继承。是的,它是有效的,但根据:继承关系引入了子模型与其每个父模型之间的链接(通过自动创建的OneToOneField)。因此,我看不到模型继承的另一种行为的原因。你的模型(没有双关语)是有缺陷的。咖啡馆不是一个街道地址,它有一个街道地址。封装,而不是继承。组合!我是说作文!垂头丧气shame@IgnacioVazquez-艾布拉姆斯我很抱歉,如果这个例子让你感到困惑,它只是为了演示的目的。问题是——为什么django在明确指定此模型的实例时需要祖先模型的字段?我不建议违反继承所基于的一对一关系。我相信没有必要担心这种违反的可能性,因为在DB级别上对这个外键应用了唯一的约束。顺便说一句,这个例子(街道和咖啡馆)只是一个例子,考虑到你的反应,它似乎是绝对低效和尴尬的。我还建议扩展而不是改变当前的行为。唯一约束在每个表中单独实施。如果有餐馆和咖啡馆,没有什么独特的约束会阻止它们之间共享id——除非你不能突然在现有的街道地址上添加咖啡馆;然后由唯一的街道地址id来处理。现在我明白你的意思了。然而,在我看来,某些模型的这种行为可以显式启用。无论如何,谢谢你的解释。
        related_object = getattr(self, field.name)
        if related_object is None:
            self.save_base(cls=parent, origin=org, using=using,
                           update_fields=update_fields)
        else:
            for related_field in parent._meta.local_fields:
                if related_field is not parent._meta.pk:
                    setattr(
                        self,
                        related_field.attname,
                        getattr(related_object, related_field.attname)
                    )
>>> from heir_demo.models import StreetAddress, Cafe
>>> addr = StreetAddress.objects.create(street_name='Piccadilly', building_number=5)
>>> mollys = Cafe.objects.create(streetaddress_ptr=addr, name='Mollys')
>>> mollys
<Cafe: Piccadilly 5>