在Django中,模型继承是否允许覆盖父模型的属性?
我想要做的是这个:
class Place(models.Model):
name = models.CharField(max_length=20)
rating = models.DecimalField()
class LongNamedRestaurant(Place): # Subclassing `Place`.
name = models.CharField(max_length=255) # Notice, I'm overriding `Place.name` to give it a longer length.
food_type = models.CharField(max_length=25)
这是我想用的版本(不过我也愿意听听其他建议):http://docs.djangoproject.com/en/dev/topics/db/models/#id7
这个在Django中支持吗?如果不支持,有没有其他方法可以达到类似的效果?
10 个回答
这件事是不可能的,除非是抽象的,原因如下:LongNamedRestaurant
不仅是一个类,它在数据库中也是一个 Place
。地方表(place-table)里每个纯粹的 Place
和每个 LongNamedRestaurant
都有一条记录。LongNamedRestaurant
只是多创建了一个表,里面有 food_type
和对地方表的引用。
如果你执行 Place.objects.all()
,你也会得到每个作为 LongNamedRestaurant
的地方,而它会是一个 Place
的实例(没有 food_type
)。所以 Place.name
和 LongNamedRestaurant.name
共享同一个数据库列,因此它们必须是同一种类型。
我觉得这对于普通模型来说是有道理的:每个餐馆都是一个地方,应该至少拥有地方所具备的所有属性。也许这种一致性就是为什么在1.10之前抽象模型无法实现,尽管那样不会导致数据库问题。正如 @lampslave 所说,在1.10中实现了这一点。我个人建议要小心:如果 Sub.x 重写了 Super.x,确保 Sub.x 是 Super.x 的子类,否则 Sub 就不能替代 Super。
解决方法:你可以创建一个自定义用户模型(AUTH_USER_MODEL
),但如果你只是想改邮箱字段,这样会涉及到很多代码重复。或者你可以保持邮箱字段不变,并确保在所有表单中都是必填的。这并不能保证数据库的完整性,如果其他应用使用它的话,而且如果你想让用户名不是必填的,这种方法也不适用。
不可以,这是不允许的:
字段名称“隐藏”是不允许的
在普通的Python类继承中,子类可以覆盖父类的任何属性。但在Django中,对于那些是
Field
实例的属性,这是不允许的(至少目前是这样)。如果一个基类有一个叫author
的字段,那么在任何继承自这个基类的类中,你都不能再创建一个叫author
的模型字段。
更新的回答:正如评论中提到的,原来的回答并没有正确回答问题。实际上,数据库中只创建了 LongNamedRestaurant
模型,而 Place
并没有被创建。
一个解决方案是创建一个抽象模型来表示“地点”,比如 AbstractPlace
,然后从这个模型继承:
class AbstractPlace(models.Model):
name = models.CharField(max_length=20)
rating = models.DecimalField()
class Meta:
abstract = True
class Place(AbstractPlace):
pass
class LongNamedRestaurant(AbstractPlace):
name = models.CharField(max_length=255)
food_type = models.CharField(max_length=25)
请还阅读 @Mark 的 回答,他很好地解释了为什么你不能修改从非抽象类继承的属性。
(注意,这种情况自 Django 1.10 开始才可以:在 Django 1.10 之前,修改从抽象类继承的属性是不可能的。)
原始回答
自 Django 1.10 起,这是可能的!你只需要做你想做的事情:
class Place(models.Model): name = models.CharField(max_length=20) rating = models.DecimalField() class Meta: abstract = True class LongNamedRestaurant(Place): # Subclassing `Place`. name = models.CharField(max_length=255) # Notice, I'm overriding `Place.name` to give it a longer length. food_type = models.CharField(max_length=25)