让我们考虑两个简单模型(假设为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>
请解释一下 - 为什么没有任何解决方法它不起作用?这不是最一致和可预测的行为吗?
非常感谢。
答案 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目前的期望更令人惊讶,(基本上)每个对象都有自己的身份。