在Django模型继承中,假如在外键或多对多字段中使用了related_name属性或related_query_name属性,则必须为该字段提供一个独一无二的反向名字和查询名字。但是,这样在抽象基类中一般会引发问题,因为基类中的字段都被子类继承并且保持了同样的值,这其中当然也包括related_name属性和related_query_name属性。
为了解决上述问题,当在抽象基类中(也只能是在抽象基类中)使用related_name属性和related_query_name属性时,部分值需要包含"%(app_label)s"和"%(class)s",具体说明如下:
- %(class)s:用该字段子类的小写类名替换。
- %(app_label)s:用小写的、包含子类的应用名替换。每个安装的应用名必须唯一,应用内的每个模型类名也必须唯一,因此替换后的名字也是唯一的。
关于related_name属性或related_query_name属性的使用,示例代码如下:
【代码3-19】
01 # --- common app --- #
02 # common/models.py:
03
04 from django.db import models
05
06 class Base(models.Model):
07 m2m = models.ManyToManyField(
08 OtherModel,
09 related_name="%(app_label)s_%(class)s_related",
10 related_query_name="%(app_label)s_%(class)ss",
11 )
12
13 class Meta:
14 abstract = True
15
16 class ChildA(Base):
17 pass
18
19 class ChildB(Base):
20 pass
21
22 # --- another app --- #
23 # another/models.py:
24
25 from common.models import Base
26
27 class ChildB(Base):
28 pass
【代码分析】
在第01~20行代码中,定义了第一个Python应用common app,具体内容说明如下:
- 在第06~14行代码中,定义了一个抽象基类Base:
- 在第07~11行代码中,定义了一个多对多属性m2m,并使用了related_name属性和related_query_name属性。
- 在第13、14行代码中,在Meta类中添加了属性"abstract=True",表明Base类为抽象 基类。
- 在第16、17行和第19、20行代码中,定义了两个继承自抽象基类Base的子类ChildA和ChildB,具体内容说明如下:
- common.ChildA.m2m字段的反转名是common_childa_related,反转查询名是common_childas。
- common.ChildB.m2m字段的反转名是common_childb_related,反转查询名是common_childbs。
在第22~28行代码中,定义了第二个Python应用another app,具体内容说明如下:
- 在第27、28行代码中,定义了一个继承自抽象基类Base的子类ChildB。其中,another.ChildB.m2m字段的反转名是another_childb_related,反转查询名是another_childbs。
如何使用"%(class)s"和"%(app_label)s"构建关联名字和关联查询名,取决于设计人员。不过,如果在设计时忘了使用"%(class)s"和"%(app_label)s",那么Django会在执行系统检查或运行迁移时抛出错误。如果设计时未指定抽象基类中的related_name属性,那么默认的反转名会是子类名后接"_set"。
本文节选自《Django 5企业级Web应用开发实战(视频教学版)》,获出版社和作者授权发布。