在Django模型中使用多表继承时,每个子类模型都会创建一张新表,这是因为子类需要一个地方存储基类中不存在的数据字段。但是,有时候如果只想修改模型的Python级行为(例如修改默认管理器或添加一个方法),这时就需要使用代理模型了。
使用代理模型继承的目标就是为原模型创建一个“代理”。在设计时可以创建、删除或更新代理模型的实例,全部数据都会存储成与使用原模型(未代理)一样的形式。这里稍微有些不同的是,在设计时可以修改代理默认的模型排序和管理器,而不需要修改原模型。
使用代理模型时就像使用普通模型一样进行声明,只需要告诉Django框架这是一个代理模型,通过将Meta类的proxy属性设置为True即可。
例如,如果打算为一个Person模型添加一个方法,可以参照下面的代码示例。
【代码3-25】
01 from django.db import models
02
03 class Person(models.Model):
04 first_name = models.CharField(max_length=30)
05 last_name = models.CharField(max_length=30)
06
07 class Child(Person):
08 class Meta:
09 proxy = True
10
11 def do_something(self):
12 # ...
13 pass
14 #...
15 pass
【代码分析】
在第03~05行代码中,定义了一个Person类,它是一个用于描述人的模型。其中,在第04、05行代码中定义了两个属性,即first_name和last_name。
在第07~15行代码中,定义了一个Child类,它继承自父类Person,是一个用于描述孩子的模型。具体内容说明如下:
- 在第08、09行代码中,通过Meta类定义了“proxy=True”属性,表明该类是一个代理类。
- 在第11~13行代码中,为Person模型添加了一个方法do_something。
根据上面的代码,子类Child与父类Person操作同一张数据表。另外,Person模型的实例能通过Child模型访问,反之亦然。
>>> p = Person.objects.create(first_name="king")
>>> Child.objects.get(first_name="king")
<Child: king>
使用代理模型还可以定义模型的另一种默认排序方法。比如在【代码3-25】中,也许不期望总是对Person进行排序,但在使用代理时总是会依据last_name属性进行排序,解决方法可参看下面的代码示例。
【代码3-26】
01 class OrderedPerson(Person):
02 class Meta:
03 ordering = ["last_name"]
04 proxy = True
05 #...
06 pass
【代码分析】
通过上面的定义,普通Person模型的查询结果就不会被排序了。而通过第03行代码的定义,OrderedPerson模型的查询结果会按照last_name属性排序。
再次回看一下【代码3-24】,当使用Person模型对象进行查询时,Django框架是不会返回Child模型对象的,对于Person模型对象的查询结果集,总是返回相对应的类型(QuerySet仍会返回请求的模型)。
代理对象存在的意义是帮设计人员复用原Person模型所提供的代码,以及自定义的功能代码(并未依赖其他代码)。对于代理模型,是不存在任何方法来保证在设计时创建完代理后,能够替换所有Person(或其他)模型。
在使用代理模型时,对于其继承的基类是有约束条件的。一个代理模型必须继承一个非抽象模型类,而不能继承多个非抽象模型类的。原因在于,代理模型无法在不同数据表之间提供任何行间连接。一个代理模型既可以继承任意数量的抽象模型类(假设它没有定义任何模型字段),也可以继承任意数量的代理模型(只需共享同一个非抽象父类)。
另外,如果未在代理模型中指定模型管理器,则默认会从父类模型中继承。而如果在代理模型中指定了管理器,则该管理器就会成为默认的管理器,同时父类中所定义的管理器也仍可使用。
基于【代码3-25】和【代码3-26】的示例,我们可以在查询Person模型时修改默认管理器,示例代码如下:
【代码3-27】
01 from django.db import models
02
03 class NewManager(models.Manager):
04 # ...
05 pass
06
07 class Child(Person):
08 objects = NewManager()
09
10 class Meta:
11 proxy = True
12 #...
13 pass
【代码分析】
在第03~05行代码中,在不替换已存在的默认管理器情况下,为代理模型添加了新管理器NewManager。
另外,官方文档中对“自定义管理器”介绍了一种技巧,即先创建一个包含新管理器的基类,然后在继承列表中的主类后追加这个新管理器的基类。不过,通常情况下可能是不需要这么做的。
本文节选自《Django 5企业级Web应用开发实战(视频教学版)》,获出版社和作者授权发布。