栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

0x0e -- Django -- 模型介绍 -- 14 -- QuerySet -- 执行查询 -- 关联对象

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

0x0e -- Django -- 模型介绍 -- 14 -- QuerySet -- 执行查询 -- 关联对象

目录
  • 0x00 -- 关联对象
  • 0x01 -- 一对多关联
    • 0x01 -- 1 -- 正向访问
  • 0x01 -- 2 -- 反相关联
    • 0x01 -- 3 -- 使用自定义反相管理器
    • 0x01 -- 4 -- 管理关联对象的额外方法
  • 0x02 -- 多对多关联
  • 0x03 -- 一对一关联
  • 0x04 -- 反向关联如何实现
  • 0x05 -- 查询关联对象
  • 0x06 -- 回归原生SQL


0x00 – 关联对象

当你在模型中定义了关联关系(如 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 模型。


0x01 – 一对多关联 0x01 – 1 – 正向访问

若模型有个 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.

0x01 – 2 – 反相关联

若模型有 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()

0x01 – 3 – 使用自定义反相管理器

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()

0x01 – 4 – 管理关联对象的额外方法

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() 方法,添加新对象时不会删除旧对象。

本节介绍的所有 “反向” 操作对数据库都是立刻生效的。每次的增加,创建和删除都是及时自动地保存至数据库。


0x02 – 多对多关联

多对多关联的两端均自动获取访问另一端的 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])

0x03 – 一对一关联

一对一关联与多对一关联非常类似。若在模型中定义了 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

0x04 – 反向关联如何实现

其它对象关联映射实现要求你在两边都定义关联关系。而 Django 开发者坚信这违反了 DRY 原则(不要自我重复),故 Django 仅要求你在一端定义关联关系。

但这是如何实现的呢,给你一个模型类,模型类并不知道是否有其它模型类关联它,直到其它模型类被加载?

答案位于 应用注册。 Django 启动时,它会导入 INSTALLED_APPS 列出的每个应用,和每个应用中的 model 模块。无论何时创建了一个新模型类,Django 为每个关联模型添加反向关联。若被关联的模型未被导入,Django 会持续追踪这些关联,并在关联模型被导入时添加关联关系。

出于这个原因,包含你所使用的所有模型的应用必须列在 INSTALLED_APPS 中。否则,反向关联可能不会正常工作。


0x05 – 查询关联对象

涉及关联对象的查询与涉及普通字段的查询遵守同样的规则。未查询条件指定值时,你可以使用对象实例,或该实例的主键。

例如,若有个博客对象 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

0x06 – 回归原生SQL

若你发现需要编写的 SQL 查询语句太过复杂,以至于 Django 的数据库映射无法处理,你可以回归手动编写 SQL。Django 针对编写原生 SQL 有几个选项;参考 执行原生 SQL 查询。

最后,Django 数据库层只是一种访问数据库的接口,理解这点非常重要。你也可以通过其它工具,编程语言或数据库框架访问数据库;Django 并没有对数据库数据库做啥独有的操作。


2021年10月11日

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/317317.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号