Django中三种继承风格
- 抽象基类:仅将父类用于子类公共信息的载体,这样的父类永远都不会单独使用。
- 多表继承:继承了一个模型(可能来源其它应用),且想要每个模型都有对应的数据表。
- 代理模型:只想修改模型的 Python 级行为,而不是以任何形式修改模型字段。
抽象基类
有很多模型需要公共信息的时候,抽象基类就会有用,比如一个后台管理系统,都需要一些用于审计的字段。
例子:
python
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
关于Meta继承
当一个抽象基类被建立,Django 将所有你在基类中申明的 Meta 内部类以属性的形式提供。若子类未定义自己的 Meta 类,它会继承父类的 Meta。Django 在安装 Meta 属性前,对抽象基类的 Meta 做了一个调整------设置 abstract=False。这意味着抽象基类的子类不会自动地变成抽象类。为了继承一个抽象基类创建另一个抽象基类,你需要在子类上显式地设置 abstract=True。
由于Python继承的工作方式,如果子类从多个抽象基类继承,则默认情况下仅继承第一个列出的类的 Meta 选项。为了从多个抽象类中继承 Meta 选项,必须显式地声明 Meta 继承。
关于对 related_name 和 related_query_name 的使用
若你在 外键 或 多对多字段 使用了 related_name 或 related_query_name,你必须为该字段提供一个 独一无二 的反向名字和查询名字。这在抽象基类中一般会引发问题,因为基类中的字段都被子类继承,且保持了同样的值(包括 related_name 和 related_query_name)。
为了解决此问题,当你在抽象基类中(也只能是在抽象基类中)使用 related_name 和 related_query_name,部分值需要包含 '%(app_label)s' 和 '%(class)s'。
多表继承
Django 支持的第二种模型继承方式是层次结构中的每个模型都是一个单独的模型。每个模型都指向分离的数据表,且可被独立查询和创建,即:多表继承,它是使用隐式的 OneToOneField 连接子类和父类。
例子:
python
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
关于Meta的多表继承
多表继承情况下,子类不会继承父类的 Meta。所有 Meta 类选项已被应用至父类,在子类中再次应用会导致行为冲突(与抽象基类中应用场景对比,这种情况下,基类并不存在)。
故,子类模型无法访问父类的 Meta 类。不过,有限的几种情况下:若子类未指定 ordering 属性或 get_latest_by 属性,子类会从父类继承这些。如果父类有排序,而你并不期望子类有排序,你可以显式的禁止它:
python
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
代理模型
代理模型继承的目的:为原模型创建一个代理。你可以创建,删除和更新代理模型的实例,所以的数据都会存储的像你使用原模型(未代理的)一样。不同点是你可以修改代理默认的模型排序和默认管理器,而不需要修改原模型。
代理模型就像普通模型一样申明。你需要告诉 Django 这是一个代理模型,通过将 Meta 类的 proxy 属性设置为 True。
例子:
python
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass