models.ForeignKey()
可以使当前模型与单个其他模型建立外键关系,例如我们通过 creator 字段记录资源的创建者:
class Resource(models.Model):
...
creator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
如果我们需要通过某个字段与多个模型建立外键关系,就需要用到 通用外键。
from django.contrib.contenttypes.fields import GenericForeignKey
通用外键是一种特殊的外键,它允许模型与多个其他模型建立关联,而不需要在定义字段时明确关联的模型。
GenericForeignKey
是一个虚拟字段(它只存在于 Python 内存中,不存在于数据库中),它主要有两个参数:
ct_field
: 关联的模型,通常定义 content_type 字段外键关联到内置的 ContentType 模型fk_field
: 关联的实例,通常定义一个 object_id,类型是 PositiveIntegerField
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
class Resource(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey("content_type", "object_id")
content_object 并不存在于数据库中,但是我们可以在 ORM 中通过 resource.content_object
取出对应的实例对象。
django.contrib.contenttypes.models.ContentType 用于存储模型的元数据。
通常与 GenericForeignKey 一起使用,用于创建通用外键,动态绑定模型。
常见的 ContentType 实例方法:
get_for_model(model)
: 查询模型类的 ContentType 实例get_object_for_this_type(**kwargs)
: 基于指定的 ContentType 实例查询关联的模型实例model
: 查询当前 ContentType 实例关联的模型类app_label
: 查询当前 ContentType 实例关联的模型所属的应用名称
到此为止,resource.content_object
可以取出关联的模型实例。
但是我们怎么通过关联的模型实例取出所有的 Resource 实例呢?
例如我们定义了另一个模型 Module,我们期望以 module.resources
的形式取出 Module 实例关联的所有 Resource 实例。
我们还需要在 Module 模型上定义获取 reosurces 的方法:
from django.contrib.contenttypes.fields import GenericRelation
class Module(models.Model):
resources = GenericRelation(Resource)
resources 也是一个虚拟字段,此时 module.resources
是一个查询集对象。
综上:
- 使用通用外键的模型需要
- 定义 content_type 字段并外键关联到 ContentType 模型
- 定义 object_id 字段,类型为 PositiveIntegerField
- 定义content_object 虚拟字段,通过 GenericForeignKey 配合上面的 content_type 和 object_id 动态关联其他模型的实例
- 反向查询关联的实例需要通过 GenericRelation 声明虚拟字段
评论区