django模型继承需要一致性

时间:2013-11-29 13:38:26

标签: python django inheritance orm model

让我们考虑两个简单模型(假设为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)

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

问题是 - 当我明确指定此模型的实例时,为什么django需要祖先模型的字段?

我知道可以通过这样的小解决方法解决这个问题:

class Cafe(StreetAddress):
    name = models.CharField(max_length=64)

    def save(self, *args, **kwargs):
        if self.streetaddress_ptr is not None:
            for field in self.streetaddress_ptr._meta.local_fields:
                if field.name != 'id':
                setattr(
                    self,
                    field.name,
                    getattr(self.streetaddress_ptr, field.name)
                )

        return super(Cafe, self).save(*args, **kwargs)

现在它按预期工作:

>>> 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>

请解释一下 - 为什么没有任何解决方法它不起作用?这不是最一致和可预测的行为吗?

非常感谢。

2 个答案:

答案 0 :(得分:0)

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

这是Django v.1.5.x的一个小补丁(它也可以很容易地适用于Django master分支)。

我建议在django.db.models.Model类的save_base方法中替换these two lines 通过遵循代码(只是一个想法):

        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>

答案 1 :(得分:0)

您的主张将允许创建分享其父部分的单独对象(考虑一个看起来就像您的咖啡馆的模型餐厅)。也就是说,在面向对象的世界中,通常是奇怪的,但在关系世界中则很好。

然而,您会发现自己可以在同一街道地址中找到咖啡馆和餐厅,但不是两家咖啡馆,也不是两家餐馆。这比Django目前的期望更令人惊讶,(基本上)每个对象都有自己的身份。