Django 官方文档中对于关联表的关联对象的操作 https://docs.djangoproject.com/zh-hans/3.0/topics/db/queries/#related-objects
关联对象¶
当你在模型中定义了关联关系(如 ForeignKey
, OneToOneField
, 或 ManyToManyField
),该模型的实例将会自动获取一套 API,能快捷地访问关联对象。
拿本文开始的模型做例子,一个 Entry
对象 e
通过 blog
属性获取其关联的 Blog
对象: e.blog
。
(在幕后,这个函数是由 Python descriptors 实现的。这玩意一般不会麻烦你,但是我们为你指出了注意点。)
Django 也提供了从关联关系 另一边 访问的 API —— 从被关联模型到定义关联关系的模型的连接。例如,一个 Blog
对象 b
能通过 entry_set
属性 b.entry_set.all()
访问包含所有关联 Entry
对象的列表。
本章节中的所有例子都是用了本页开头定义的 Blog
, Author
和 Entry
模型。
一对多关联¶
正向访问¶
If a model has a ForeignKey
, instances of that model will have access to the related (foreign) object via an attribute of the model.
举例:
>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.
你可以通过 foreign-key 属性获取和设置值。如你所想,对外键的修改直到你调用 save()
后才会被存入数据库。例子:
>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save()
若 ForeignKey
字段配置了 null=True
(即其允许 NULL
值),你可以指定值为 None
移除关联。例子:
>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
首次通过正向一对多关联访问关联对象时会缓存关联关系。后续在同一对象上通过外键的访问也会被缓存。例子:
>>> e = Entry.objects.get(id=2)
>>> print(e.blog) # Hits the database to retrieve the associated Blog.
>>> print(e.blog) # Doesn't hit the database; uses cached version.
注意 select_related()
QuerySet
方法会预先用所有一对多关联对象填充缓存。例子:
>>> e = Entry.objects.select_related().get(id=2)
>>> print(e.blog) # Doesn't hit the database; uses cached version.
>>> print(e.blog) # Doesn't hit the database; uses cached version.
“反向” 关联¶
若模型有 ForeignKey
,外键关联的模型实例将能访问 Manager
,后者会返回第一个模型的所有实例。默认情况下,该 Manager
名为 FOO_set
, FOO
即源模型名的小写形式。 Manager
返回 QuerySets
,后者能以 “检索对象” 章节介绍的方式进行筛选和操作。
举例:
>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.
# b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()
你可以在定义 ForeignKey
时设置 related_name
参数重写这个 FOO_set
名。例如,若修改 Entry
模型为 blog =ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries')
,前文示例代码会看起来像这样:
>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.
# b.entries is a Manager that returns QuerySets.
>>> b.entries.filter(headline__contains='Lennon')
>>> b.entries.count()
使用自定义反向管理器¶
RelatedManager
反向关联的默认实现是该模型 默认管理器 一个实例。若你想为某个查询指定一个不同的管理器,可以使用如下语法:
from django.db import models
class Entry(models.Model):
#...
objects = models.Manager() # Default Manager
entries = EntryManager() # Custom Manager
b = Blog.objects.get(id=1)
b.entry_set(manager='entries').all()
若 EntryManager
在其 get_queryset()
方法执行了默认过滤行为,改行为会应用到 all()
调用中。
当然,指定一个自定义反向管理也允许你调用模型自定义方法:
b.entry_set(manager='entries').is_published()