# Django 三种风格的模型继承
-
抽象类继承:父类继承自 models.Model,但不会在底层数据库中生成相应的数据表(在
Meta
方法中添加abstract=True
),父类的属性列存储在其子类的数据表中。 -
多表继承:夺标继承的每个模型类都在底层数据库中生成相应的数据表管理数据。
-
代理模型继承:父类用于在底层数据库中管理数据表;而子类不定义数据列,只定义查询数据集的排列方式等元数据
# 1. 抽象类 (基类,公共类) 继承
- 抽象类,指的是继承了 model.Model 且没有生成表,而是作为基类或公共类被其他类继承
- 抽象类继承的作用是在多个表有若干相同的字段时,可以使开发者讲这些字段统一定义在抽象基类中,免于重复定义这些字段。抽象积累的定义通过在模型的 Meta 中定义属性 abstract=True 来实现。抽象基类的举例如下:
from django.db import models | |
class Base(models.Model): | |
create_time = models.TimeField(auto_now_add=True) | |
update_time = models.TimeField(auto_now=True) | |
class Meta: | |
# 添加关键字,不会产生新的表 | |
abstract = True | |
# 这张表继承了 Base,哪怕没有字段 create_time 和 update_time,也同样会展示出来 | |
class CourseType(Base): | |
title = models.CharField('课程类别', max_length=16) | |
sequence = models.IntegerField('展示顺序', default=18) | |
class Meta: | |
db_table = 'tb_coursetype' |
本列中定义了一个抽象基类 Base,用于保存 2 个公用字段:create_time、update_time 。子类 CourseType 继承自 Base,并分别定义了自己的俩个字段。本列中 2 个类映射到数据库后,会被定义为 1 个数据表。
- 数据表 tb_coursetype:有 id、title 、sequence 、create_time 、update_time 5 个字段
# 2. 多表继承
-
在多表继承技术中心,无论是父表还是子表都会用数据库中相对应的数据表维护模型数据,父类中字段不会重复地在多个子类的相关数据表中进行定义。从这种意义上讲,多表继承才是真正面向对象的 ORM 技术。
-
多表继承的定义不需要特殊的关键字。在 Django 内部通过在父模型和子模型之间建立一对一关系来实现多表继承技术。如下代码定义了 MessageBase 及其子类的多表继承版本:
from django.db import models | |
class MessageBase(models.Model): | |
id = models.AutoField() | |
content = models.CharField(max_length=100) | |
user_name = models.CharField(max_length=80) | |
pub_date = models.DateField() | |
class Moment(MessageBase): | |
headline = models.CharField(max_length=50) | |
class Coment(MessageBase): | |
level = models.CharField(max_length=1, choices=Levels) |
本例在数据库中会实际生成 3 个数据表。
- 数据表 MessageBase:有 id、content、user_name、pub_date 等 4 个字段
- 数据表 Moment:有 id、headline 等两个字段
- 数据表 Comment:有 id、level 等两个字段
在对模型的编程过程中,子类仍然可以直接引用父类定义的字段;同时子类可以通过父类对象引用访问父类实例,比如:
# 新建 Moment 对象,直接在子类中引用父类字段 | |
>>> m1 = Moment(user_name="Tom", headline="hello world") | |
>>> m1.content = "reference parent field in subclass" | |
>>> m1.save() | |
# 通过父类实例引用父类字段 | |
>>> print m1.messagebase.content | |
eference parent field in subclass |
技巧:在多表继承时,在子类实例中通过小写的父类的名字可以引用父类的实例
# 3. 代理模型继承
- 前两种继承模型中子类模型都有实际存储数据的作用;而代理模型继承中子类只用于管理父类的数据,而不实际存储数据。代理模型继承通过在子类的 Meta 中定义 proxy=True 属性来实现。举例如下:
class Moment(models.Model): | |
id = models.AutoField() | |
content = models.CharField(max_length=100) | |
user_name = models.CharField(max_length=80) | |
pub_date = models.DateField() | |
headline = models.CharField(max_length=50) | |
class OrderdMoment(Moment): | |
class Meta: | |
proxy = True | |
ordering = ["-pub_date"] |
在本例中定义了父类模型 Moment 用于存储数据,而后定义了子类模型 OrderedMoment 用于管理根据 pub_date 倒序排列的 Moment。使用 dialing 模型继承的原因是子类中新的特性不会影响父类模型及其已有代码的行为。