Django模型层ORM的一个强大之处是对模型继承的支持,该技术将Python面向对象的编程方法与数据库面向关系表的数据结构有机地结合。Django支持三种风格的模型继承
l 抽象类继承继承:父类继承自models.Model,但不会在底层数据库中生成相应的数据表,父类的属性列存储在其子类的数据表中。
l 多表继承:夺标继承的每个模型类都在底层数据库中生成相应的数据表管理数据。
l 代理模型继承:父类用于在底层数据库中管理数据表;而子类不定义数据列,只定义查询数据集的排列方式等元数据
1. 抽象类继承
抽象类继承的作用是在多个表有若干相同的字段时,可以使开发者讲这些字段统一定义在抽象基类中,免于重复定义这些字段。抽象积累的定义通过在模型的Meta中定义属性abstract=True来实现。抽象基类的举例如下:
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 Meta: abstract = True class Moment(MessageBase): headline = models.CharField(max_length=50) Levels = ( ('1', 'Very good'), ('2', 'Good'), ('3', 'Normal'), ('4', 'Bad'),) class Coment(MessageBase): level = models.CharField(max_length=1, choices=Levels)
本列中定义了一个抽象基类MessageBase,用于保存消息的4个公用字段:id、content、user_name、pub_date。子类Moment和Coment继承自MessageBase,并分别定义了自己的一个字段。本列中3个类映射到数据库后,会被定义为两个数据表。
l 数据表moment:有id、content、user_name、pub_date、headline等5个字段
l 数据表comment:有id、content、user_name、pub_date、level 等5个字段
在子类模型的编程中,可以直接饮用父类定义的字段,比如:
>>> m1 = Moment(user_name="Tom", headline="hello world") # 创建数据 >>> m1.content = "reference parent field in subclass" >>> m1.save()
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)
l 数据表MessageBase:有id、content、user_name、pub_date等4个字段
l 数据表Moment:有id、headline等两个字段
l 数据表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模型继承的原因是子类中新的特性不会影响父类模型及其已有代码的行为。