当你在模型中定义了关联关系(如 ForeignKey
, OneToOneField
或 ManyToManyField
),该模型的实例将会自动获取一套 API,能快捷地访问关联对象。
拿本文开始的模型做例子,一个 Entry
对象 e
通过 blog
属性获取其关联的 Blog
对象: e.blog
。
Django 也提供了从关联关系 另一边 访问的 API —— 从被关联模型到定义关联关系的模型的连接。例如,一个 Blog
对象 b
能通过 entry_set
属性 b.entry_set.all()
访问包含所有关联 Entry
对象的列表。
本章节中的所有例子都是用了本页开头定义的 Blog
, Author
和 Entry
模型。
若模型有个 ForeignKey
,该模型的实例能通过其属性访问关联(外部的)对象。例如:
>>> 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()
ForeignKey Manager
还有方法能处理关联对象集合。除了上面的 “检索对象” 中定义的 QuerySet
方法以外,以下是每项的简要介绍:
add(obj1, obj2, ...)
:将特定的模型对象加入关联对象集合。create(**kwargs)
:创建一个新对象,保存,并将其放入关联对象集合中。返回新创建的对象。remove(obj1, obj2, ...)
:从关联对象集合删除指定模型对象。clear()
:从关联对象集合删除所有对象。set(objs)
:替换关联对象集合要指定关联集合的成员,调用 set()
方法,并传入可迭代的对象实例集合。例如,若 e1
和 e2
都是 Entry
实例:
b = Blog.objects.get(id=1)
b.entry_set.set([e1, e2])
若能使用 clear()
方法, entry_set
中所有旧对象会在将可迭代集合(本例中是个列表)中的对象加入其中之前被删除。若 不能 使用 clear()
方法,添加新对象时不会删除旧对象。
多对多关联的两端均自动获取访问另一端的 API。该 API 的工作方式类似上面的 “反向” 一对多关联。
不同点在为属性命名上:定义了 ManyToManyField
的模型使用字段名作为属性名,而 “反向” 模型使用源模型名的小写形式,加上 "_set"
(就像反向一对多关联一样)。
例如:
e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains="John")
a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.
和 ForeignKey
一样, ManyToManyField
能指定 related_name
。在上面的例子中,若 Entry
中的 ManyToManyField
已指定了 related_name="entries"
,随后每个 Author
实例会拥有一个 entries
属性,而不是
entry_set
。
另一个与一对多关联不同的地方是,除了模型实例以外,多对多关联中的 add()
, set()
和 remove()
方法能接收主键值。例如,若 e
和 e2
是 Entry
的实例,以下两种 set()
调用结果一致:
a = Author.objects.get(id=5)
a.entry_set.set([e1, e2])
a.entry_set.set([e1.pk, e2.pk])
一对一关联与多对一关联非常类似。若在模型中定义了 OneToOneField
,该模型的实例只需通过其属性就能访问关联对象。
例如:
class EntryDetail(models.Model):
entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
details = models.TextField()
ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.
不同点在于 “反向” 查询。一对一关联所关联的对象也能访问 Manager
对象,但这个 Manager
仅代表一个对象,而不是对象的集合:
e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object
若未为关联关系指定对象,Django 会抛出 DoesNotExist
异常。
实例能通过为正向关联指定关联对象一样的方式指定给反向关联:
e.entrydetail = ed
其它对象关联映射实现要求你在两边都定义关联关系。而 Django 开发者坚信这违反了 DRY
原则(不要自我重复),故 Django 仅要求你在一端定义关联关系。
但这是如何实现的呢,给你一个模型类,模型类并不知道是否有其它模型类关联它,直到其它模型类被加载?
答案在于 应用注册。 Django 启动时,它会导入 INSTALLED_APPS
列出的每个应用,和每个应用中的 model
模块。无论何时创建了一个新模型类,Django 为每个关联模型添加反向关联。若被关联的模型未被导入,Django 会持续追踪这些关联,并在关联模型被导入时添加关联关系。
出于这个原因,包含你所使用的所有模型的应用必须列在 INSTALLED_APPS
中。否则,反向关联可能不会正常工作。
涉及关联对象的查询与涉及普通字段的查询遵守同样的规则。未查询条件指定值时,你可以使用对象实例,或该实例的主键。
例如,若有个博客对象 b
,其 id=5
,以下三种查询是一样的:
Entry.objects.filter(blog=b) # Query using object instance
Entry.objects.filter(blog=b.id) # Query using id from instance
Entry.objects.filter(blog=5) # Query using id directly
ASP.NET Calendar NextMonthText 属性 Calendar 控件定义和用法 NextMonthText 属性用于规定日历中下一月的链接所显示的文本。 ...
ASP.NET Calendar ShowNextPrevMonth 属性 Calendar 控件定义和用法 ShowNextPrevMonth 属性用于规定是否显示日历中下个月和上个...
ASP.NET ImageButton CausesValidation 属性 ImageButton 控件定义和用法 CausesValidation 属性规定了当 ImageButton 控件被点...
WebSecurity - UserExists()WebSecurity 对象定义 UserExists() 方法指示用户是否已存在于 WebSecurity 数据库中。C# 和 VB 语法...