“Django/docs/3.0.x/topics/db/queries”的版本间差异
(autoload) |
小 (Page commit) |
||
第1行: | 第1行: | ||
+ | {{DISPLAYTITLE:进行查询 — Django 文档}} | ||
<div id="making-queries" class="section"> | <div id="making-queries" class="section"> | ||
− | = | + | = 进行查询 = |
− | + | 一旦您创建了 [[../models|数据模型]] ,Django 会自动为您提供一个数据库抽象 API,让您可以创建、检索、更新和删除对象。 本文档解释了如何使用此 API。 有关所有各种模型查找选项的完整详细信息,请参阅 [[../../../ref/models/index|数据模型参考]] 。 | |
− | + | 在本指南(以及参考资料)中,我们将参考以下模型,它们构成了一个 Weblog 应用程序: | |
<div id="queryset-model-example" class="highlight-python notranslate"> | <div id="queryset-model-example" class="highlight-python notranslate"> | ||
第11行: | 第12行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.db import models |
class Blog(models.Model): | class Blog(models.Model): | ||
第39行: | 第40行: | ||
def __str__(self): | def __str__(self): | ||
− | return self.headline</ | + | return self.headline</syntaxhighlight> |
</div> | </div> | ||
第48行: | 第49行: | ||
== 创建对象 == | == 创建对象 == | ||
− | + | 为了在 Python 对象中表示数据库表数据,Django 使用了一个直观的系统:模型类表示数据库表,该类的实例表示数据库表中的特定记录。 | |
− | + | 要创建对象,请使用模型类的关键字参数将其实例化,然后调用 [[../../../ref/models/instances#django.db.models.Model|save()]] 将其保存到数据库中。 | |
− | + | 假设模型存在于文件 <code>mysite/blog/models.py</code> 中,这是一个示例: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第58行: | 第59行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from blog.models import Blog |
− | + | >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') | |
− | + | >>> b.save()</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这会在幕后执行 <code>INSERT</code> SQL 语句。 在您明确调用 [[../../../ref/models/instances#django.db.models.Model|save()]] 之前,Django 不会访问数据库。 | |
− | [[../../../ref/models/instances#django.db.models.Model| | + | [[../../../ref/models/instances#django.db.models.Model|save()]] 方法没有返回值。 |
<div class="admonition seealso"> | <div class="admonition seealso"> | ||
− | + | 也可以看看 | |
− | [[../../../ref/models/instances#django.db.models.Model| | + | [[../../../ref/models/instances#django.db.models.Model|save()]] 采用了许多此处未描述的高级选项。 有关完整的详细信息,请参阅 [[../../../ref/models/instances#django.db.models.Model|save()]] 的文档。 |
− | + | 要在单个步骤中创建和保存对象,请使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|create()]] 方法。 | |
第83行: | 第84行: | ||
<div id="saving-changes-to-objects" class="section"> | <div id="saving-changes-to-objects" class="section"> | ||
− | == | + | == 保存对对象的更改 == |
− | + | 要保存对数据库中已有对象的更改,请使用 [[../../../ref/models/instances#django.db.models.Model|save()]]。 | |
− | + | 给定一个已经保存到数据库的 <code>Blog</code> 实例 <code>b5</code>,这个例子改变了它的名字并更新了它在数据库中的记录: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第93行: | 第94行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> b5.name = 'New name' |
− | + | >>> b5.save()</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这会在幕后执行 <code>UPDATE</code> SQL 语句。 在您明确调用 [[../../../ref/models/instances#django.db.models.Model|save()]] 之前,Django 不会访问数据库。 | |
<div id="saving-foreignkey-and-manytomanyfield-fields" class="section"> | <div id="saving-foreignkey-and-manytomanyfield-fields" class="section"> | ||
− | === 保存 | + | === 保存 ForeignKey 和 ManyToManyField 字段 === |
− | 更新 [[../../../ref/models/fields#django.db.models| | + | 更新 [[../../../ref/models/fields#django.db.models|ForeignKey]] 字段的工作方式与保存普通字段的方式完全相同——将正确类型的对象分配给相关字段。 此示例更新 <code>Entry</code> 实例 <code>entry</code> 的 <code>blog</code> 属性,假设 <code>Entry</code> 和 <code>Blog</code> 的适当实例已保存到数据库中(所以我们可以在下面检索它们): |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第111行: | 第112行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from blog.models import Blog, Entry |
− | + | >>> entry = Entry.objects.get(pk=1) | |
− | + | >>> cheese_blog = Blog.objects.get(name="Cheddar Talk") | |
− | + | >>> entry.blog = cheese_blog | |
− | + | >>> entry.save()</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | 更新 [[../../../ref/models/fields#django.db.models| | + | 更新 [[../../../ref/models/fields#django.db.models|ManyToManyField]] 的工作方式略有不同 - 在字段上使用 [[../../../ref/models/relations#django.db.models.fields.related.RelatedManager|add()]] 方法向关系添加记录。 本示例将 <code>Author</code> 实例 <code>joe</code> 添加到 <code>entry</code> 对象: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第126行: | 第127行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from blog.models import Author |
− | + | >>> joe = Author.objects.create(name="Joe") | |
− | + | >>> entry.authors.add(joe)</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 要将多条记录一次性添加到 [[../../../ref/models/fields#django.db.models|ManyToManyField]],请在对 [[../../../ref/models/relations#django.db.models.fields.related.RelatedManager|add()]] 的调用中包含多个参数,如下所示: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第139行: | 第140行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> john = Author.objects.create(name="John") |
− | + | >>> paul = Author.objects.create(name="Paul") | |
− | + | >>> george = Author.objects.create(name="George") | |
− | + | >>> ringo = Author.objects.create(name="Ringo") | |
− | + | >>> entry.authors.add(john, paul, george, ringo)</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您尝试分配或添加错误类型的对象,Django 会抱怨。 | |
第159行: | 第160行: | ||
== 检索对象 == | == 检索对象 == | ||
− | + | 要从数据库中检索对象,请通过模型类上的 [[../managers#django.db.models|Manager]] 构造 [[../../../ref/models/querysets#django.db.models.query|QuerySet]]。 | |
− | + | [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 表示数据库中的对象集合。 它可以有零个、一个或多个 ''过滤器'' 。 过滤器根据给定的参数缩小查询结果的范围。 在 SQL 术语中,[[../../../ref/models/querysets#django.db.models.query|QuerySet]] 等同于 <code>SELECT</code> 语句,过滤器是一个限制子句,例如 <code>WHERE</code> 或 <code>LIMIT</code>。 | |
− | + | 您可以使用模型的 [[../managers#django.db.models|Manager]] 获得 [[../../../ref/models/querysets#django.db.models.query|QuerySet]]。 每个模型至少有一个[[../managers#django.db.models|Manager]],默认叫做[[../../../ref/models/class#django.db.models.Model|objects]]。 通过模型类直接访问它,如下所示: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第169行: | 第170行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Blog.objects |
− | + | <django.db.models.manager.Manager object at ...> | |
− | + | >>> b = Blog(name='Foo', tagline='Bar') | |
− | + | >>> b.objects | |
Traceback: | Traceback: | ||
... | ... | ||
− | AttributeError: | + | AttributeError: "Manager isn't accessible via Blog instances."</syntaxhighlight> |
</div> | </div> | ||
第182行: | 第183行: | ||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | <code>Managers</code> | + | <code>Managers</code> 只能通过模型类访问,而不是从模型实例访问,以强制分离“表级”操作和“记录级”操作。 |
</div> | </div> | ||
− | [[../managers#django.db.models| | + | [[../managers#django.db.models|Manager]] 是模型的 <code>QuerySets</code> 的主要来源。 例如,<code>Blog.objects.all()</code> 返回一个 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],其中包含数据库中的所有 <code>Blog</code> 对象。 |
<div id="retrieving-all-objects" class="section"> | <div id="retrieving-all-objects" class="section"> | ||
− | === | + | === 检索所有对象 === |
− | + | 从表中检索对象的最简单方法是获取所有对象。 为此,请在 [[../managers#django.db.models|Manager]] 上使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|all()]] 方法: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第200行: | 第201行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> all_entries = Entry.objects.all()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | [[../../../ref/models/querysets#django.db.models.query.QuerySet|all()]] 方法返回数据库中所有对象的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]]。 | |
第211行: | 第212行: | ||
<div id="retrieving-specific-objects-with-filters" class="section"> | <div id="retrieving-specific-objects-with-filters" class="section"> | ||
− | === | + | === 使用过滤器检索特定对象 === |
− | [[../../../ref/models/querysets#django.db.models.query.QuerySet| | + | [[../../../ref/models/querysets#django.db.models.query.QuerySet|all()]] 返回的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 描述了数据库表中的所有对象。 但是,通常您只需要选择完整对象集的一个子集。 |
− | + | 要创建这样的子集,您需要优化初始 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],添加过滤条件。 优化 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 的两种最常见方法是: | |
; <code>filter(**kwargs)</code> | ; <code>filter(**kwargs)</code> | ||
− | : 返回一个新的 [[../../../ref/models/querysets#django.db.models.query| | + | : 返回一个新的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 包含与给定查找参数匹配的对象。 |
; <code>exclude(**kwargs)</code> | ; <code>exclude(**kwargs)</code> | ||
− | : 返回一个新的 [[../../../ref/models/querysets#django.db.models.query| | + | : 返回一个新的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],其中包含 ''与'' 不匹配给定查找参数的对象。 |
− | + | 查找参数(上述函数定义中的 <code>**kwargs</code>)应采用以下 [[#field-lookups|字段查找]] 中描述的格式。 | |
− | + | 例如,要获取 2006 年博客条目的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],请使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]],如下所示: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第230行: | 第231行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__year=2006)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 使用默认管理器类,它与: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第241行: | 第242行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.all().filter(pub_date__year=2006)</syntaxhighlight> |
</div> | </div> | ||
第249行: | 第250行: | ||
<span id="id2"></span> | <span id="id2"></span> | ||
− | ==== | + | ==== 链接过滤器 ==== |
− | + | 细化 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 的结果本身就是一个 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],因此可以将细化链接在一起。 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第257行: | 第258行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter( |
... headline__startswith='What' | ... headline__startswith='What' | ||
... ).exclude( | ... ).exclude( | ||
第263行: | 第264行: | ||
... ).filter( | ... ).filter( | ||
... pub_date__gte=datetime.date(2005, 1, 30) | ... pub_date__gte=datetime.date(2005, 1, 30) | ||
− | ... )</ | + | ... )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这需要数据库中所有条目的初始 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],添加一个过滤器,然后是一个排除,然后是另一个过滤器。 最终结果是一个 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],其中包含所有标题以“What”开头的条目,这些条目在 2005 年 1 月 30 日和当天之间发布。 | |
第275行: | 第276行: | ||
<span id="id3"></span> | <span id="id3"></span> | ||
− | ==== | + | ==== 过滤后的 QuerySet 是唯一的 ==== |
− | + | 每次细化 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 时,都会得到一个全新的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],它与之前的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 没有任何关系。 每次细化都会创建一个单独且不同的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],可以存储、使用和重用。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第285行: | 第286行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> q1 = Entry.objects.filter(headline__startswith="What") |
− | + | >>> q2 = q1.exclude(pub_date__gte=datetime.date.today()) | |
− | + | >>> q3 = q1.filter(pub_date__gte=datetime.date.today())</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | 这三个 <code>QuerySets</code> | + | 这三个<code>QuerySets</code>是分开的。 第一个是基本的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],其中包含所有包含以“What”开头的标题的条目。 第二个是第一个的子集,附加条件排除 <code>pub_date</code> 是今天或将来的记录。 第三个是第一个的子集,附加条件仅选择 <code>pub_date</code> 是今天或将来的记录。 初始的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] (<code>q1</code>) 不受细化过程的影响。 |
第299行: | 第300行: | ||
<span id="id4"></span> | <span id="id4"></span> | ||
− | ==== | + | ==== QuerySet很懒 ==== |
− | <code> | + | <code>QuerySets</code> 是懒惰的——创建 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 的行为不涉及任何数据库活动。 您可以整天将过滤器堆叠在一起,并且在 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 被 ''评估'' 之前,Django 不会实际运行查询。 看看这个例子: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第307行: | 第308行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> q = Entry.objects.filter(headline__startswith="What") |
− | + | >>> q = q.filter(pub_date__lte=datetime.date.today()) | |
− | + | >>> q = q.exclude(body_text__icontains="food") | |
− | + | >>> print(q)</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 虽然这看起来像是三个数据库命中,但实际上它只命中数据库一次,在最后一行 (<code>print(q)</code>)。 通常,[[../../../ref/models/querysets#django.db.models.query|QuerySet]] 的结果不会从数据库中获取,直到您“请求”它们。 当您这样做时,[[../../../ref/models/querysets#django.db.models.query|QuerySet]] 通过访问数据库被 ''评估'' 。 有关评估发生的确切时间的更多详细信息,请参阅 [[../../../ref/models/querysets#when-querysets-are-evaluated|查询集评估时]] 。 | |
第324行: | 第325行: | ||
<span id="retrieving-single-object-with-get"></span> | <span id="retrieving-single-object-with-get"></span> | ||
− | === | + | === 使用 get() 检索单个对象 === |
− | [[../../../ref/models/querysets#django.db.models.query.QuerySet| | + | [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 总是会给你一个 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],即使只有一个对象与查询匹配 - 在这种情况下,它将是一个 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 包含一个元素。 |
− | + | 如果您知道只有一个对象与您的查询匹配,您可以在直接返回对象的 [[../managers#django.db.models|Manager]] 上使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]] 方法: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第334行: | 第335行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> one_entry = Entry.objects.get(pk=1)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以将任何查询表达式与 [[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]] 一起使用,就像使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 一样 - 再次参见下面的 [[#field-lookups|字段查找]] 。 | |
− | + | 请注意,使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]] 和使用带有 <code>[0]</code> 切片的 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 之间存在差异。 如果没有与查询匹配的结果,[[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]] 将引发 <code>DoesNotExist</code> 异常。 这个异常是正在执行查询的模型类的一个属性——所以在上面的代码中,如果没有主键为 1 的 <code>Entry</code> 对象,Django 将引发 <code>Entry.DoesNotExist</code> . | |
− | + | 类似地,如果超过一项与 [[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]] 查询匹配,Django 也会抱怨。 在这种情况下,它将引发 [[../../../ref/exceptions#django.core.exceptions|MultipleObjectsReturned]],这也是模型类本身的一个属性。 | |
第349行: | 第350行: | ||
<div id="other-queryset-methods" class="section"> | <div id="other-queryset-methods" class="section"> | ||
− | === | + | === 其他 QuerySet 方法 === |
− | + | 大多数情况下,您会在需要时使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|all()]]、[[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]]、[[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 和 [[../../../ref/models/querysets#django.db.models.query.QuerySet|exclude()]]从数据库中查找对象。 然而,这远非全部。 有关所有各种 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 方法的完整列表,请参阅 [[../../../ref/models/querysets#queryset-api|QuerySet API 参考]] 。 | |
第358行: | 第359行: | ||
<span id="id5"></span> | <span id="id5"></span> | ||
− | === 限制 | + | === 限制 QuerySets === |
− | + | 使用 Python 的数组切片语法的子集将 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 限制为一定数量的结果。 这相当于 SQL 的 <code>LIMIT</code> 和 <code>OFFSET</code> 子句。 | |
− | 例如,这将返回前 5 个对象 (<code>LIMIT 5</code>) | + | 例如,这将返回前 5 个对象 (<code>LIMIT 5</code>): |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第368行: | 第369行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.all()[:5]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这将返回第六到第十个对象 (<code>OFFSET 5 LIMIT 5</code>): | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第379行: | 第380行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.all()[5:10]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 负索引(即 不支持 <code>Entry.objects.all()[-1]</code>)。 | |
− | + | 通常,切片 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 会返回一个新的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]]——它不会评估查询。 一个例外是,如果您使用 Python 切片语法的“step”参数。 例如,这实际上会执行查询以返回前 10 个每 ''秒'' 对象的列表: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第392行: | 第393行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.all()[:10:2]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 由于其工作方式的模糊性,禁止对切片查询集进行进一步过滤或排序。 | |
− | 要检索 ''单个'' | + | 要检索 ''单个'' 对象而不是列表(例如 <code>SELECT foo FROM bar LIMIT 1</code>),使用索引而不是切片。 例如,在按标题按字母顺序排列条目后,这将返回数据库中的第一个 <code>Entry</code>: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第405行: | 第406行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.order_by('headline')[0]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这大致相当于: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第416行: | 第417行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.order_by('headline')[0:1].get()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 但是请注意,如果没有对象符合给定条件,第一个将引发 <code>IndexError</code> 而第二个将引发 <code>DoesNotExist</code>。 有关更多详细信息,请参阅 [[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]]。 | |
第428行: | 第429行: | ||
<span id="field-lookups-intro"></span> | <span id="field-lookups-intro"></span> | ||
− | === | + | === 字段查找 === |
− | + | 字段查找是您指定 SQL <code>WHERE</code> 子句内容的方式。 它们被指定为 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 方法 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]]、[[../../../ref/models/querysets#django.db.models.query.QuerySet|exclude()]] 和 [[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]] 的关键字参数。 | |
− | + | 基本查找关键字参数采用 <code>field__lookuptype=value</code> 的形式。 (这是一个双下划线)。 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第438行: | 第439行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(pub_date__lte='2006-01-01')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 转换(大致)为以下 SQL: | |
<div class="highlight-sql notranslate"> | <div class="highlight-sql notranslate"> | ||
第449行: | 第450行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="sql">SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';</syntaxhighlight> |
</div> | </div> | ||
第456行: | 第457行: | ||
<div class="admonition-how-this-is-possible admonition"> | <div class="admonition-how-this-is-possible admonition"> | ||
− | + | 这怎么可能 | |
− | Python | + | Python 能够定义接受任意名称-值参数的函数,其名称和值在运行时进行评估。 更多信息参见Python官方教程中的<span class="xref std std-ref">tut-keywordargs</span>。 |
</div> | </div> | ||
− | + | 查找中指定的字段必须是模型字段的名称。 但有一个例外,在 [[../../../ref/models/fields#django.db.models|ForeignKey]] 的情况下,您可以指定后缀为 <code>_id</code> 的字段名称。 在这种情况下, value 参数应该包含外部模型主键的原始值。 例如: | |
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第468行: | 第469行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(blog_id=4)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果传递无效的关键字参数,查找函数将引发 <code>TypeError</code>。 | |
− | 数据库 API | + | 数据库 API 支持大约二打查找类型; 完整的参考可以在 [[../../../ref/models/querysets#field-lookups|字段查找参考]] 中找到。 为了让您了解可用的内容,以下是您可能会使用的一些更常见的查找: |
<dl> | <dl> | ||
− | <dt>[[ | + | <dt>[[#id6|<span id="id7" class="problematic">:查找:`精确`</span>]]</dt> |
− | <dd><p> | + | <dd><p>“精确”匹配。 例如:</p> |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.get(headline__exact="Cat bites dog")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>将按照以下方式生成 SQL:</p> |
<div class="highlight-sql notranslate"> | <div class="highlight-sql notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="sql">SELECT ... WHERE headline = 'Cat bites dog';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>如果您不提供查找类型——也就是说,如果您的关键字参数不包含双下划线——则假定查找类型为 <code>exact</code>。</p> |
− | <p> | + | <p>例如,以下两个语句是等效的:</p> |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Blog.objects.get(id__exact=14) # Explicit form |
− | + | >>> Blog.objects.get(id=14) # __exact is implied</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>这是为了方便起见,因为 <code>exact</code> 查找是常见情况。</p></dd> |
− | <dt>[[ | + | <dt>[[#id8|<span id="id9" class="problematic">:lookup:`iexact`</span>]]</dt> |
− | <dd><p> | + | <dd><p>不区分大小写的匹配。 所以,查询:</p> |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Blog.objects.get(name__iexact="beatles blog")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>将匹配标题为 <code>"Beatles Blog"</code>、<code>"beatles blog"</code> 甚至 <code>"BeAtlES blOG"</code> 的 <code>Blog</code>。</p></dd> |
− | <dt>[[ | + | <dt>[[#id10|<span id="id11" class="problematic">:查找:`包含`</span>]]</dt> |
− | <dd><p> | + | <dd><p>区分大小写的包含测试。 例如:</p> |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.get(headline__contains='Lennon')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>大致翻译成这个 SQL:</p> |
<div class="highlight-sql notranslate"> | <div class="highlight-sql notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="sql">SELECT ... WHERE headline LIKE '%Lennon%';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>请注意,这将匹配标题 <code>'Today Lennon honored'</code> 但不匹配 <code>'today lennon honored'</code>。</p> |
− | <p> | + | <p>还有一个不区分大小写的版本,[[#id12|:lookup:`icontains`]]。</p></dd> |
− | <dt>[[ | + | <dt>[[#id14|:lookup:`startswith`]], [[#id16|:lookup:`endswith`]]</dt> |
− | <dd><p> | + | <dd><p>分别开始于和结束于搜索。 还有一些不区分大小写的版本,称为 [[#id18|:lookup:`istartswith`]] 和 [[#id20|:lookup:`iendswith`]]。</p></dd></dl> |
− | + | 同样,这只会划伤表面。 完整的参考可以在 [[../../../ref/models/querysets#field-lookups|字段查找参考]] 中找到。 | |
第556行: | 第557行: | ||
<div id="lookups-that-span-relationships" class="section"> | <div id="lookups-that-span-relationships" class="section"> | ||
− | <span id=" | + | <span id="id22"></span> |
− | === | + | === 跨越关系的查找 === |
− | Django | + | Django 提供了一种强大而直观的方式来“跟踪”查找中的关系,在幕后自动为您处理 SQL <code>JOIN</code>。 要跨越关系,请使用跨模型的相关字段的字段名称,用双下划线分隔,直到到达所需的字段。 |
− | + | 此示例检索所有具有 <code>Blog</code> 且 <code>name</code> 为 <code>'Beatles Blog'</code> 的 <code>Entry</code> 对象: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第567行: | 第568行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(blog__name='Beatles Blog')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这个跨越可以像你想要的那样深。 | |
− | + | 它也向后工作。 要引用“反向”关系,请使用模型的小写名称。 | |
− | + | 此示例检索所有 <code>Blog</code> 对象,这些对象至少有一个 <code>Entry</code>,其 <code>headline</code> 包含 <code>'Lennon'</code>: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第582行: | 第583行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Blog.objects.filter(entry__headline__contains='Lennon')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果你在多个关系之间进行过滤并且其中一个中间模型没有满足过滤条件的值,Django 会将其视为有空(所有值都是 <code>NULL</code>),但有效,对象那里。 所有这一切都意味着不会引发任何错误。 例如,在这个过滤器中: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第593行: | 第594行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.filter(entry__authors__name='Lennon')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (如果有相关的 <code>Author</code> 模型),如果没有与条目关联的 <code>author</code>,则将被视为没有附加 <code>name</code>,而不是由于缺少 <code>author</code> 而引发错误。 通常这正是您希望发生的事情。 唯一可能令人困惑的情况是,如果您使用 [[#id23|:lookup:`isnull`]]。 因此: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第604行: | 第605行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.filter(entry__authors__name__isnull=True)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 将返回在 <code>author</code> 上有空 <code>name</code> 的 <code>Blog</code> 对象,以及在 <code>entry</code> 上有空 <code>author</code> 的对象。 如果你不想要那些后面的对象,你可以写: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第615行: | 第616行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)</syntaxhighlight> |
</div> | </div> | ||
第622行: | 第623行: | ||
<div id="spanning-multi-valued-relationships" class="section"> | <div id="spanning-multi-valued-relationships" class="section"> | ||
− | ==== | + | ==== 跨越多值关系 ==== |
− | + | 当您根据 [[../../../ref/models/fields#django.db.models|ManyToManyField]] 或反向 [[../../../ref/models/fields#django.db.models|ForeignKey]] 过滤对象时,您可能会对两种不同类型的过滤器感兴趣。 考虑 <code>Blog</code>/<code>Entry</code> 关系(<code>Blog</code> 到 <code>Entry</code> 是一对多关系)。 我们可能有兴趣查找标题中包含 ''“Lennon”'' 且发表于 2008 年的条目的博客。 或者,我们可能想要查找标题中包含 ''“Lennon”'' 条目以及 2008 年发布的条目的博客。 由于有多个条目与单个 <code>Blog</code> 相关联,因此这两种查询都是可能的,并且在某些情况下是有意义的。 | |
− | + | [[../../../ref/models/fields#django.db.models|ManyToManyField]] 也会出现相同类型的情况。 例如,如果 <code>Entry</code> 有一个名为 <code>tags</code> 的 [[../../../ref/models/fields#django.db.models|ManyToManyField]],我们可能想要找到链接到名为 ''“music”'' 和 ''的标签的条目]“bands”'' 或者我们可能想要一个包含名称为 ''“music”'' 和状态为 ''“public”'' 的标签的条目。 | |
− | + | 为了处理这两种情况,Django 有处理 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 调用的一致方式。 单个 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 调用中的所有内容都会同时应用以过滤出符合所有这些要求的项目。 连续的 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 调用进一步限制了对象集,但对于多值关系,它们适用于链接到主模型的任何对象,不一定是那些由较早的 [[../../../ref/models/querysets#django.db.models.query.QuerySet|过滤器选择的对象()]] 调用。 | |
− | + | 这可能听起来有点令人困惑,所以希望有一个例子可以澄清。 要选择标题中同时包含 ''“Lennon”'' 条目且发布于 2008 年的所有博客(满足两个条件的同一条目),我们将编写: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第636行: | 第637行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 要选择标题中包含 ''“Lennon”'' 的条目以及 2008 年发布的条目的所有博客,我们将编写: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第647行: | 第648行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 假设只有一个博客同时包含 ''“Lennon”'' 和 2008 年的条目,但 2008 年的条目都没有包含 ''“Lennon”''。 第一个查询不会返回任何博客,但第二个查询会返回那个博客。 | |
− | + | 在第二个示例中,第一个过滤器将查询集限制为链接到标题中带有 ''“Lennon”'' 的条目的所有博客。 第二个过滤器将博客集 ''进一步'' 限制为那些也链接到 2008 年发布的条目的博客。 第二个过滤器选择的条目可能与第一个过滤器中的条目相同,也可能不同。 我们使用每个过滤器语句过滤 <code>Blog</code> 项,而不是 <code>Entry</code> 项。 | |
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | [[../../../ref/models/querysets#django.db.models.query.QuerySet| | + | [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 对于跨越多值关系的查询的行为,如上所述,对于 [[../../../ref/models/querysets#django.db.models.query.QuerySet|exclude()]] 没有等效实现。 相反,单个 [[../../../ref/models/querysets#django.db.models.query.QuerySet|exclude()]] 调用中的条件不一定指向同一个项目。 |
− | + | 例如,以下查询将排除包含 ''both'' 条目的博客,其中 ''“Lennon”'' 在标题 ''和'' 条目发布于 2008 年: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第668行: | 第669行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.exclude( |
entry__headline__contains='Lennon', | entry__headline__contains='Lennon', | ||
entry__pub_date__year=2008, | entry__pub_date__year=2008, | ||
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 但是,与使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 时的行为不同,这不会基于满足这两个条件的条目来限制博客。 为了做到这一点,即 要选择所有不包含 2008 年发布的 ''“Lennon”'' 发布的条目的博客,您需要进行两个查询: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第682行: | 第683行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.exclude( |
entry__in=Entry.objects.filter( | entry__in=Entry.objects.filter( | ||
headline__contains='Lennon', | headline__contains='Lennon', | ||
pub_date__year=2008, | pub_date__year=2008, | ||
), | ), | ||
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
第701行: | 第702行: | ||
<span id="using-f-expressions-in-filters"></span> | <span id="using-f-expressions-in-filters"></span> | ||
− | === | + | === 过滤器可以引用模型上的字段 === |
− | + | 在到目前为止给出的示例中,我们构建了过滤器,将模型字段的值与常量进行比较。 但是,如果您想将模型字段的值与同一模型上的另一个字段的值进行比较,该怎么办? | |
− | Django 提供了 [[../../../ref/models/expressions#django.db.models| | + | Django 提供了 [[../../../ref/models/expressions#django.db.models|F 表达式]] 来允许这样的比较。 <code>F()</code> 的实例充当对查询中模型字段的引用。 然后可以在查询过滤器中使用这些引用来比较同一模型实例上两个不同字段的值。 |
− | + | 例如,要查找评论数多于 pingback 的所有博客条目的列表,我们构造一个 <code>F()</code> 对象来引用 pingback 计数,并在查询中使用该 <code>F()</code> 对象: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第713行: | 第714行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import F |
− | + | >>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | Django 支持对 <code>F()</code> | + | Django 支持对 <code>F()</code> 对象使用加法、减法、乘法、除法、取模和幂运算,包括常量和其他 <code>F()</code> 对象。 要查找评论数超过 pingback 的 ''倍'' 的所有博客条目,我们修改查询: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第725行: | 第726行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 要查找条目评分小于 pingback 计数和评论计数之和的所有条目,我们将发出查询: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第736行: | 第737行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(rating__lt=F('number_of_comments') + F('number_of_pingbacks'))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您还可以使用双下划线表示法来跨越 <code>F()</code> 对象中的关系。 带有双下划线的 <code>F()</code> 对象将引入访问相关对象所需的任何连接。 例如,要检索作者姓名与博客名称相同的所有条目,我们可以发出查询: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第747行: | 第748行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(authors__name=F('blog__name'))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 对于日期和日期/时间字段,您可以添加或减去 <code>timedelta</code> 对象。 以下将返回在发布后超过 3 天修改的所有条目: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第758行: | 第759行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from datetime import timedelta |
− | + | >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | <code>F()</code> | + | <code>F()</code> 对象支持 <code>.bitand()</code>、<code>.bitor()</code>、<code>.bitrightshift()</code> 和 <code>.bitleftshift()</code> 的按位运算。 例如: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第770行: | 第771行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> F('somefield').bitand(16)</syntaxhighlight> |
</div> | </div> | ||
第779行: | 第780行: | ||
<div id="the-pk-lookup-shortcut" class="section"> | <div id="the-pk-lookup-shortcut" class="section"> | ||
− | === | + | === pk 查找快捷方式 === |
− | + | 为方便起见,Django 提供了一个 <code>pk</code> 查找快捷方式,它代表“主键”。 | |
− | + | 在示例 <code>Blog</code> 模型中,主键是 <code>id</code> 字段,因此这三个语句是等效的: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第789行: | 第790行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Blog.objects.get(id__exact=14) # Explicit form |
− | + | >>> Blog.objects.get(id=14) # __exact is implied | |
− | + | >>> Blog.objects.get(pk=14) # pk implies id__exact</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | <code>pk</code> | + | <code>pk</code> 的使用不限于 <code>__exact</code> 查询 - 任何查询词都可以与 <code>pk</code> 组合以对模型的主键执行查询: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第802行: | 第803行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Get blogs entries with id 1, 4 and 7 |
− | + | >>> Blog.objects.filter(pk__in=[1,4,7]) | |
− | # Get all blog entries with id | + | # Get all blog entries with id > 14 |
− | + | >>> Blog.objects.filter(pk__gt=14)</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | <code>pk</code> | + | <code>pk</code> 查找也适用于连接。 例如,这三个语句是等效的: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第817行: | 第818行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(blog__id__exact=3) # Explicit form |
− | + | >>> Entry.objects.filter(blog__id=3) # __exact is implied | |
− | + | >>> Entry.objects.filter(blog__pk=3) # __pk implies __id__exact</syntaxhighlight> | |
</div> | </div> | ||
第828行: | 第829行: | ||
<div id="escaping-percent-signs-and-underscores-in-like-statements" class="section"> | <div id="escaping-percent-signs-and-underscores-in-like-statements" class="section"> | ||
− | === 在 | + | === 在 LIKE 语句中转义百分号和下划线 === |
− | + | 等同于 <code>LIKE</code> SQL 语句的字段查找(<code>iexact</code>、<code>contains</code>、<code>icontains</code>、<code>startswith</code>、<code>istartswith</code>、 <code>endswith</code> 和 <code>iendswith</code>) 将自动转义 <code>LIKE</code> 语句中使用的两个特殊字符 - 百分号和下划线。 (在 <code>LIKE</code> 语句中,百分号表示多字符通配符,下划线表示单字符通配符。) | |
− | + | 这意味着事情应该直观地工作,所以抽象不会泄漏。 例如,要检索包含百分号的所有条目,请将百分号用作任何其他字符: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第838行: | 第839行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(headline__contains='%')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | Django | + | Django 会为您处理报价; 生成的 SQL 将如下所示: |
<div class="highlight-sql notranslate"> | <div class="highlight-sql notranslate"> | ||
第849行: | 第850行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="sql">SELECT ... WHERE headline LIKE '%\%%';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 下划线也是如此。 百分号和下划线都为您透明处理。 | |
第860行: | 第861行: | ||
<div id="caching-and-querysets" class="section"> | <div id="caching-and-querysets" class="section"> | ||
− | <span id=" | + | <span id="id25"></span> |
− | === 缓存和 | + | === 缓存和 QuerySets === |
− | 每个 [[../../../ref/models/querysets#django.db.models.query| | + | 每个 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 都包含一个缓存以最小化数据库访问。 了解它的工作原理将使您能够编写最有效的代码。 |
− | + | 在新创建的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 中,缓存为空。 第一次计算 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 时——因此,发生数据库查询——Django 将查询结果保存在 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 的缓存中并返回已明确请求的结果(例如,下一个元素,如果 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 正在迭代)。 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 的后续评估重用缓存的结果。 | |
− | + | 记住这种缓存行为,因为如果你没有正确使用你的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]]s,它可能会咬你。 例如,以下将创建两个 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] ,评估它们,然后将它们丢弃: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第873行: | 第874行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> print([e.headline for e in Entry.objects.all()]) |
− | + | >>> print([e.pub_date for e in Entry.objects.all()])</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这意味着相同的数据库查询将执行两次,有效地使您的数据库负载加倍。 此外,这两个列表可能不包含相同的数据库记录,因为 <code>Entry</code> 可能已在两个请求之间的瞬间添加或删除。 | |
− | + | 为了避免这个问题,保存 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 并重用它: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第887行: | 第888行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> queryset = Entry.objects.all() |
− | + | >>> print([p.headline for p in queryset]) # Evaluate the query set. | |
− | + | >>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.</syntaxhighlight> | |
</div> | </div> | ||
第896行: | 第897行: | ||
<div id="when-querysets-are-not-cached" class="section"> | <div id="when-querysets-are-not-cached" class="section"> | ||
− | ==== 当 | + | ==== 当 QuerySet 未缓存时 ==== |
− | + | 查询集并不总是缓存它们的结果。 当仅评估查询集的 ''part'' 时,会检查缓存,但如果未填充,则不会缓存后续查询返回的项目。 具体来说,这意味着 [[#limiting-querysets|使用数组切片或索引限制查询集]] 不会填充缓存。 | |
− | + | 例如,重复获取查询集对象中的某个索引,每次都会查询数据库: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第906行: | 第907行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> queryset = Entry.objects.all() |
− | + | >>> print(queryset[5]) # Queries the database | |
− | + | >>> print(queryset[5]) # Queries the database again</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 但是,如果已经评估了整个查询集,则会改为检查缓存: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第919行: | 第920行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> queryset = Entry.objects.all() |
− | + | >>> [entry for entry in queryset] # Queries the database | |
− | + | >>> print(queryset[5]) # Uses cache | |
− | + | >>> print(queryset[5]) # Uses cache</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 以下是其他操作的一些示例,这些操作将导致评估整个查询集并因此填充缓存: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第933行: | 第934行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> [entry for entry in queryset] |
− | + | >>> bool(queryset) | |
− | + | >>> entry in queryset | |
− | + | >>> list(queryset)</syntaxhighlight> | |
</div> | </div> | ||
第943行: | 第944行: | ||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 简单地打印查询集不会填充缓存。 这是因为对 <code>__repr__()</code> 的调用只返回整个查询集的一部分。 | |
第958行: | 第959行: | ||
<span id="complex-lookups-with-q"></span> | <span id="complex-lookups-with-q"></span> | ||
− | == | + | == Q 对象的复杂查找 == |
− | + | 关键字参数查询 - 在 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 等中。 – 被“AND”在一起。 如果您需要执行更复杂的查询(例如,使用 <code>OR</code> 语句的查询),您可以使用 [[../../../ref/models/querysets#django.db.models|Q 对象]] 。 | |
− | + | [[../../../ref/models/querysets#django.db.models|Q 对象]] (<code>django.db.models.Q</code>) 是用于封装关键字参数集合的对象。 这些关键字参数在上面的“字段查找”中指定。 | |
− | + | 例如,这个 <code>Q</code> 对象封装了单个 <code>LIKE</code> 查询: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第970行: | 第971行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.db.models import Q |
− | Q(question__startswith='What')</ | + | Q(question__startswith='What')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <code>Q</code> | + | <code>Q</code> 对象可以使用 <code>&</code> 和 <code>|</code> 运算符进行组合。 当一个运算符用于两个 <code>Q</code> 对象时,它会产生一个新的 <code>Q</code> 对象。 |
− | + | 例如,此语句生成一个 <code>Q</code> 对象,表示两个 <code>"question__startswith"</code> 查询的“或”: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第984行: | 第985行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Q(question__startswith='Who') | Q(question__startswith='What')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这等效于以下 SQL <code>WHERE</code> 子句: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第995行: | 第996行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">WHERE question LIKE 'Who%' OR question LIKE 'What%'</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以通过将 <code>Q</code> 对象与 <code>&</code> 和 <code>|</code> 运算符组合并使用括号分组来组合任意复杂的语句。 此外,<code>Q</code> 对象可以使用 <code>~</code> 运算符进行否定,允许组合查找,将普通查询和否定 (<code>NOT</code>) 查询结合起来: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,006行: | 第1,007行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Q(question__startswith='Who') | ~Q(pub_date__year=2005)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 每个采用关键字参数的查找函数(例如 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]], [[../../../ref/models/querysets#django.db.models.query.QuerySet|exclude()]], [[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]]) 也可以传递一个或多个 <code>Q</code> 对象作为位置(未命名) ) 论点。 如果您向查找函数提供多个 <code>Q</code> 对象参数,则这些参数将被“与”在一起。 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,017行: | 第1,018行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Poll.objects.get( |
Q(question__startswith='Who'), | Q(question__startswith='Who'), | ||
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) | Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) | ||
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | ... | + | ... 大致翻译成 SQL: |
<div class="highlight-sql notranslate"> | <div class="highlight-sql notranslate"> | ||
第1,031行: | 第1,032行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="sql">SELECT * from polls WHERE question LIKE 'Who%' |
− | AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')</ | + | AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 查找函数可以混合使用 <code>Q</code> 对象和关键字参数。 提供给查找函数的所有参数(无论是关键字参数还是 <code>Q</code> 对象)都被“AND”在一起。 但是,如果提供了 <code>Q</code> 对象,则它必须在任何关键字参数的定义之前。 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,043行: | 第1,044行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Poll.objects.get( |
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), | Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), | ||
question__startswith='Who', | question__startswith='Who', | ||
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | ... 将是一个有效的查询,相当于前面的例子; 但: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,057行: | 第1,058行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># INVALID QUERY |
Poll.objects.get( | Poll.objects.get( | ||
question__startswith='Who', | question__startswith='Who', | ||
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) | Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) | ||
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | ……无效。 | |
<div class="admonition seealso"> | <div class="admonition seealso"> | ||
− | + | 也可以看看 | |
− | + | 这 [[#id26|:source:`OR 查找示例 `]] 在 Django 的单元测试中显示了一些可能的用途<code>Q</code> . | |
第1,082行: | 第1,083行: | ||
== 比较对象 == | == 比较对象 == | ||
− | + | 要比较两个模型实例,请使用标准 Python 比较运算符,即双等号:<code>==</code>。 在幕后,它比较了两个模型的主键值。 | |
− | + | 使用上面的 <code>Entry</code> 示例,以下两个语句是等效的: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,090行: | 第1,091行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> some_entry == other_entry |
− | + | >>> some_entry.id == other_entry.id</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果模型的主键不是 <code>id</code>,没问题。 比较将始终使用主键,无论它叫什么。 例如,如果模型的主键字段称为 <code>name</code>,则这两个语句是等效的: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,102行: | 第1,103行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> some_obj == other_obj |
− | + | >>> some_obj.name == other_obj.name</syntaxhighlight> | |
</div> | </div> | ||
第1,115行: | 第1,116行: | ||
== 删除对象 == | == 删除对象 == | ||
− | + | 删除方法方便地命名为 [[../../../ref/models/instances#django.db.models.Model|delete()]]。 此方法立即删除对象并返回删除的对象数和包含每个对象类型删除数的字典。 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,121行: | 第1,122行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> e.delete() |
− | (1, {'weblog.Entry': 1})</ | + | (1, {'weblog.Entry': 1})</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您还可以批量删除对象。 每个 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 都有一个 [[../../../ref/models/querysets#django.db.models.query.QuerySet|delete()]] 方法,该方法删除该 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 的所有成员。 | |
− | + | 例如,这将删除 <code>pub_date</code> 年为 2005 的所有 <code>Entry</code> 对象: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,135行: | 第1,136行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(pub_date__year=2005).delete() |
− | (5, {'webapp.Entry': 5})</ | + | (5, {'webapp.Entry': 5})</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请记住,只要有可能,这将完全在 SQL 中执行,因此在此过程中不一定会调用各个对象实例的 <code>delete()</code> 方法。 如果您在模型类上提供了自定义 <code>delete()</code> 方法并希望确保它被调用,您将需要“手动”删除该模型的实例(例如,通过迭代 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 并分别在每个对象上调用 <code>delete()</code>),而不是使用 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 的批量 [[../../../ref/models/querysets#django.db.models.query.QuerySet|delete()]] 方法。 | |
− | 当 Django | + | 当 Django 删除一个对象时,默认情况下它会模拟 SQL 约束 <code>ON DELETE CASCADE</code> 的行为——换句话说,任何具有指向要删除的对象的外键的对象都将被删除。 例如: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,149行: | 第1,150行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">b = Blog.objects.get(pk=1) |
# This will delete the Blog and all of its Entry objects. | # This will delete the Blog and all of its Entry objects. | ||
− | b.delete()</ | + | b.delete()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此级联行为可通过 [[../../../ref/models/fields#django.db.models|ForeignKey]] 的 [[../../../ref/models/fields#django.db.models.ForeignKey|on_delete]] 参数进行自定义。 | |
− | + | 请注意, [[../../../ref/models/querysets#django.db.models.query.QuerySet|delete()]] 是唯一未在 [[../managers#django.db.models|Manager]] 本身上公开的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 方法。 这是一种安全机制,可防止您意外请求 <code>Entry.objects.delete()</code>,并删除 ''all'' 条目。 如果您 ''do'' 想要删除所有对象,那么您必须明确请求一个完整的查询集: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,164行: | 第1,165行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.all().delete()</syntaxhighlight> |
</div> | </div> | ||
第1,176行: | 第1,177行: | ||
== 复制模型实例 == | == 复制模型实例 == | ||
− | + | 尽管没有用于复制模型实例的内置方法,但可以轻松地创建新实例并复制所有字段的值。 在最简单的情况下,您可以将 <code>pk</code> 设置为 <code>None</code>。 使用我们的博客示例: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,182行: | 第1,183行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">blog = Blog(name='My blog', tagline='Blogging is easy') |
blog.save() # blog.pk == 1 | blog.save() # blog.pk == 1 | ||
blog.pk = None | blog.pk = None | ||
− | blog.save() # blog.pk == 2</ | + | blog.save() # blog.pk == 2</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您使用继承,事情会变得更加复杂。 考虑 <code>Blog</code> 的子类: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,197行: | 第1,198行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">class ThemeBlog(Blog): |
theme = models.CharField(max_length=200) | theme = models.CharField(max_length=200) | ||
django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python') | django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python') | ||
− | django_blog.save() # django_blog.pk == 3</ | + | django_blog.save() # django_blog.pk == 3</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 由于继承的工作方式,您必须将 <code>pk</code> 和 <code>id</code> 都设置为 None: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,212行: | 第1,213行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">django_blog.pk = None |
django_blog.id = None | django_blog.id = None | ||
− | django_blog.save() # django_blog.pk == 4</ | + | django_blog.save() # django_blog.pk == 4</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此过程不会复制不属于模型数据库表的关系。 例如,<code>Entry</code> 有 <code>ManyToManyField</code> 到 <code>Author</code>。 复制条目后,您必须为新条目设置多对多关系: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,225行: | 第1,226行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">entry = Entry.objects.all()[0] # some previous entry |
old_authors = entry.authors.all() | old_authors = entry.authors.all() | ||
entry.pk = None | entry.pk = None | ||
entry.save() | entry.save() | ||
− | entry.authors.set(old_authors)</ | + | entry.authors.set(old_authors)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | 对于 <code>OneToOneField</code> | + | 对于 <code>OneToOneField</code>,您必须复制相关对象并将其分配给新对象的字段,以避免违反一对一唯一约束。 例如,假设 <code>entry</code> 已经如上复制: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,240行: | 第1,241行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">detail = EntryDetail.objects.all()[0] |
detail.pk = None | detail.pk = None | ||
detail.entry = entry | detail.entry = entry | ||
− | detail.save()</ | + | detail.save()</syntaxhighlight> |
</div> | </div> | ||
第1,253行: | 第1,254行: | ||
<span id="topics-db-queries-update"></span> | <span id="topics-db-queries-update"></span> | ||
− | == | + | == 一次更新多个对象 == |
− | + | 有时,您希望为 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 中的所有对象将字段设置为特定值。 您可以使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|update()]] 方法执行此操作。 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,261行: | 第1,262行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Update all the headlines with pub_date in 2007. |
− | Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')</ | + | Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 使用此方法只能设置非关系字段和 [[../../../ref/models/fields#django.db.models|ForeignKey]] 字段。 要更新非关系字段,请将新值作为常量提供。 要更新 [[../../../ref/models/fields#django.db.models|ForeignKey]] 字段,请将新值设置为要指向的新模型实例。 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,273行: | 第1,274行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> b = Blog.objects.get(pk=1) |
# Change every Entry so that it belongs to this Blog. | # Change every Entry so that it belongs to this Blog. | ||
− | + | >>> Entry.objects.all().update(blog=b)</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>update()</code> 方法立即应用并返回查询匹配的行数(如果某些行已经具有新值,则可能不等于更新的行数)。 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 更新的唯一限制是它只能访问一个数据库表:模型的主表。 您可以根据相关字段进行过滤,但您只能更新模型主表中的列。 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,287行: | 第1,288行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> b = Blog.objects.get(pk=1) |
# Update all the headlines belonging to this Blog. | # Update all the headlines belonging to this Blog. | ||
− | + | >>> Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same')</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意,<code>update()</code> 方法直接转换为 SQL 语句。 这是直接更新的批量操作。 它不会在您的模型上运行任何 [[../../../ref/models/instances#django.db.models.Model|save()]] 方法,也不会发出 <code>pre_save</code> 或 <code>post_save</code> 信号(这是调用 [[../../../ref/models/instances#django.db.models.Model|save() 的结果)]] ),或使用 [[../../../ref/models/fields#django.db.models.DateField|auto_now]] 字段选项。 如果您想将每个项目保存在 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 中并确保在每个实例上调用 [[../../../ref/models/instances#django.db.models.Model|save()]] 方法,您不需要任何特殊函数来处理它。 遍历它们并调用 [[../../../ref/models/instances#django.db.models.Model|save()]]: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,301行: | 第1,302行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">for item in my_queryset: |
− | item.save()</ | + | item.save()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 调用 update 还可以使用 [[../../../ref/models/expressions#django.db.models|F 表达式]] 根据模型中另一个字段的值更新一个字段。 这对于根据当前值递增计数器特别有用。 例如,要增加博客中每个条目的 pingback 计数: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,313行: | 第1,314行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.all().update(number_of_pingbacks=F('number_of_pingbacks') + 1)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 但是,与 filter 和 exclude 子句中的 <code>F()</code> 对象不同,在更新中使用 <code>F()</code> 对象时不能引入连接——您只能引用正在更新的模型的本地字段。 如果您尝试使用 <code>F()</code> 对象引入连接,则会引发 <code>FieldError</code>: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,324行: | 第1,325行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># This will raise a FieldError |
− | + | >>> Entry.objects.update(headline=F('blog__name'))</syntaxhighlight> | |
</div> | </div> | ||
第1,335行: | 第1,336行: | ||
<span id="topics-db-queries-related"></span> | <span id="topics-db-queries-related"></span> | ||
− | == | + | == 相关对象 == |
− | + | 当您在模型中定义关系(即 [[../../../ref/models/fields#django.db.models|ForeignKey]]、[[../../../ref/models/fields#django.db.models|OneToOneField]] 或 [[../../../ref/models/fields#django.db.models|ManyToManyField]])时,该模型的实例将有一个方便的 API 来访问相关对象。 | |
− | + | 使用本页顶部的模型,例如,<code>Entry</code> 对象 <code>e</code> 可以通过访问 <code>blog</code> 属性来获取其关联的 <code>Blog</code> 对象:[ X163X]。 | |
− | + | (在幕后,此功能由 Python <span class="xref std std-doc"> 描述符 </span> 实现。 这对你来说并不重要,但我们在这里指出它是为了好奇。) | |
− | Django | + | Django 还为关系的“另一方”创建 API 访问器——从相关模型到定义关系的模型的链接。 例如,<code>Blog</code> 对象 <code>b</code> 可以通过 <code>entry_set</code> 属性访问所有相关 <code>Entry</code> 对象的列表:<code>b.entry_set.all()</code>。 |
− | + | 本节中的所有示例均使用本页顶部定义的示例 <code>Blog</code>、<code>Author</code> 和 <code>Entry</code> 模型。 | |
<div id="one-to-many-relationships" class="section"> | <div id="one-to-many-relationships" class="section"> | ||
− | === | + | === 一对多关系 === |
<div id="forward" class="section"> | <div id="forward" class="section"> | ||
− | ==== | + | ==== 前进 ==== |
− | + | 如果模型具有 [[../../../ref/models/fields#django.db.models|ForeignKey]],则该模型的实例将可以通过模型的属性访问相关(外部)对象。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,363行: | 第1,364行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> e = Entry.objects.get(id=2) |
− | + | >>> e.blog # Returns the related Blog object.</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以通过外键属性获取和设置。 正如您所料,在您调用 [[../../../ref/models/instances#django.db.models.Model|save()]] 之前,对外键的更改不会保存到数据库中。 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,375行: | 第1,376行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> e = Entry.objects.get(id=2) |
− | + | >>> e.blog = some_blog | |
− | + | >>> e.save()</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果 [[../../../ref/models/fields#django.db.models|ForeignKey]] 字段设置了 <code>null=True</code>(即它允许 <code>NULL</code> 值),您可以分配 <code>None</code> 以删除关系。 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,388行: | 第1,389行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> e = Entry.objects.get(id=2) |
− | + | >>> e.blog = None | |
− | + | >>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 对一对多关系的前向访问在第一次访问相关对象时被缓存。 对同一对象实例上的外键的后续访问被缓存。 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,401行: | 第1,402行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> 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.</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意, [[../../../ref/models/querysets#django.db.models.query.QuerySet|select_related()]] [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 方法会提前递归地预填充所有一对多关系的缓存。 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,414行: | 第1,415行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> 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.</syntaxhighlight> | |
</div> | </div> | ||
第1,426行: | 第1,427行: | ||
<span id="backwards-related-objects"></span> | <span id="backwards-related-objects"></span> | ||
− | ==== | + | ==== 跟随关系“向后” ==== |
− | + | 如果模型具有 [[../../../ref/models/fields#django.db.models|ForeignKey]],则外键模型的实例将有权访问返回第一个模型的所有实例的 [[../managers#django.db.models|Manager]]。 默认情况下,这个 [[../managers#django.db.models|Manager]] 被命名为 <code>FOO_set</code>,其中 <code>FOO</code> 是源模型名称,小写。 这个 [[../managers#django.db.models|Manager]] 返回 <code>QuerySets</code>,可以按照上面“检索对象”部分中的描述进行过滤和操作。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,436行: | 第1,437行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> 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 is a Manager that returns QuerySets. | ||
− | + | >>> b.entry_set.filter(headline__contains='Lennon') | |
− | + | >>> b.entry_set.count()</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以通过设置 [[../../../ref/models/fields#django.db.models|ForeignKey]] 定义中的 [[../../../ref/models/fields#django.db.models.ForeignKey|related_name]] 参数来覆盖 <code>FOO_set</code> 名称。 例如,如果将 <code>Entry</code> 模型更改为 <code>blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries')</code>,则上面的示例代码将如下所示: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,452行: | 第1,453行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> 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 is a Manager that returns QuerySets. | ||
− | + | >>> b.entries.filter(headline__contains='Lennon') | |
− | + | >>> b.entries.count()</syntaxhighlight> | |
</div> | </div> | ||
第1,469行: | 第1,470行: | ||
==== 使用自定义反向管理器 ==== | ==== 使用自定义反向管理器 ==== | ||
− | [[../../../ref/models/relations#django.db.models.fields.related| | + | 默认情况下,用于反向关系的 [[../../../ref/models/relations#django.db.models.fields.related|RelatedManager]] 是该模型的 [[../managers#manager-names|default manager]] 的子类。 如果您想为给定查询指定不同的管理器,您可以使用以下语法: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,475行: | 第1,476行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.db import models |
class Entry(models.Model): | class Entry(models.Model): | ||
第1,483行: | 第1,484行: | ||
b = Blog.objects.get(id=1) | b = Blog.objects.get(id=1) | ||
− | b.entry_set(manager='entries').all()</ | + | b.entry_set(manager='entries').all()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果 <code>EntryManager</code> 在其 <code>get_queryset()</code> 方法中执行默认过滤,则该过滤将应用于 <code>all()</code> 调用。 | |
− | + | 当然,指定自定义反向管理器还可以让您调用其自定义方法: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,496行: | 第1,497行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">b.entry_set(manager='entries').is_published()</syntaxhighlight> |
</div> | </div> | ||
第1,505行: | 第1,506行: | ||
<div id="additional-methods-to-handle-related-objects" class="section"> | <div id="additional-methods-to-handle-related-objects" class="section"> | ||
− | ==== | + | ==== 处理相关对象的其他方法 ==== |
− | [[../../../ref/models/ | + | 除了上面“检索对象”中定义的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 方法之外,[[../../../ref/models/fields#django.db.models|ForeignKey]] [[../managers#django.db.models|Manager]] 还有其他方法用于处理相关对象集。 每个的概要如下,完整的细节可以在[[../../../ref/models/relations|相关对象参考]]中找到。 |
; <code>add(obj1, obj2, ...)</code> | ; <code>add(obj1, obj2, ...)</code> | ||
− | : | + | : 将指定的模型对象添加到相关对象集。 |
; <code>create(**kwargs)</code> | ; <code>create(**kwargs)</code> | ||
− | : | + | : 创建一个新对象,保存它并将其放入相关对象集中。 返回新创建的对象。 |
; <code>remove(obj1, obj2, ...)</code> | ; <code>remove(obj1, obj2, ...)</code> | ||
− | : | + | : 从相关对象集中移除指定的模型对象。 |
; <code>clear()</code> | ; <code>clear()</code> | ||
− | : | + | : 从相关对象集中删除所有对象。 |
; <code>set(objs)</code> | ; <code>set(objs)</code> | ||
− | : | + | : 替换相关对象集。 |
− | + | 要分配相关集合的成员,请使用具有可迭代对象实例的 <code>set()</code> 方法。 例如,如果 <code>e1</code> 和 <code>e2</code> 是 <code>Entry</code> 实例: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,526行: | 第1,527行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">b = Blog.objects.get(id=1) |
− | b.entry_set.set([e1, e2])</ | + | b.entry_set.set([e1, e2])</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果 <code>clear()</code> 方法可用,任何预先存在的对象都将从 <code>entry_set</code> 中删除,然后再将迭代中的所有对象(在本例中为列表)添加到集合中。 如果 <code>clear()</code> 方法 ''not'' 可用,则将添加迭代中的所有对象,而不会删除任何现有元素。 | |
− | + | 本节中描述的每个“反向”操作都会对数据库产生直接影响。 每次添加、创建和删除都会立即自动保存到数据库中。 | |
第1,543行: | 第1,544行: | ||
<span id="m2m-reverse-relationships"></span> | <span id="m2m-reverse-relationships"></span> | ||
− | === | + | === 多对多关系 === |
− | + | 多对多关系的两端都可以自动访问另一端的 API。 API 的工作原理类似于上面的“向后”一对多关系。 | |
− | + | 一个区别在于属性命名:定义 [[../../../ref/models/fields#django.db.models|ManyToManyField]] 的模型使用该字段本身的属性名称,而“反向”模型使用原始模型的小写模型名称,加上 [ X232X](就像反向一对多关系)。 | |
− | + | 一个例子使这更容易理解: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,555行: | 第1,556行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">e = Entry.objects.get(id=3) |
e.authors.all() # Returns all Author objects for this Entry. | e.authors.all() # Returns all Author objects for this Entry. | ||
e.authors.count() | e.authors.count() | ||
第1,561行: | 第1,562行: | ||
a = Author.objects.get(id=5) | a = Author.objects.get(id=5) | ||
− | a.entry_set.all() # Returns all Entry objects for this Author.</ | + | a.entry_set.all() # Returns all Entry objects for this Author.</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | 和 [[../../../ref/models/fields#django.db.models| | + | 和[[../../../ref/models/fields#django.db.models|ForeignKey]]一样,[[../../../ref/models/fields#django.db.models|ManyToManyField]]可以指定[[../../../ref/models/fields#django.db.models.ManyToManyField|related_name]]。 在上面的例子中,如果 <code>Entry</code> 中的 [[../../../ref/models/fields#django.db.models|ManyToManyField]] 指定了 <code>related_name='entries'</code>,那么每个 <code>Author</code> 实例将具有 <code>entries</code> 属性<code>entry_set</code>。 |
− | + | 与一对多关系的另一个区别是,除了模型实例之外,多对多关系上的 <code>add()</code>、<code>set()</code> 和 <code>remove()</code> 方法接受主键值。 例如,如果 <code>e1</code> 和 <code>e2</code> 是 <code>Entry</code> 实例,那么这些 <code>set()</code> 调用的工作方式相同: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,574行: | 第1,575行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">a = Author.objects.get(id=5) |
a.entry_set.set([e1, e2]) | a.entry_set.set([e1, e2]) | ||
− | a.entry_set.set([e1.pk, e2.pk])</ | + | a.entry_set.set([e1.pk, e2.pk])</syntaxhighlight> |
</div> | </div> | ||
第1,585行: | 第1,586行: | ||
<div id="one-to-one-relationships" class="section"> | <div id="one-to-one-relationships" class="section"> | ||
− | === | + | === 一对一的关系 === |
− | + | 一对一关系与多对一关系非常相似。 如果您在模型上定义 [[../../../ref/models/fields#django.db.models|OneToOneField]],则该模型的实例将可以通过模型的属性访问相关对象。 | |
− | + | 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,595行: | 第1,596行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">class EntryDetail(models.Model): |
entry = models.OneToOneField(Entry, on_delete=models.CASCADE) | entry = models.OneToOneField(Entry, on_delete=models.CASCADE) | ||
details = models.TextField() | details = models.TextField() | ||
ed = EntryDetail.objects.get(id=2) | ed = EntryDetail.objects.get(id=2) | ||
− | ed.entry # Returns the related Entry object.</ | + | ed.entry # Returns the related Entry object.</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 区别在于“反向”查询。 一对一关系中的相关模型也可以访问 [[../managers#django.db.models|Manager]] 对象,但 [[../managers#django.db.models|Manager]] 表示单个对象,而不是一组对象: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,611行: | 第1,612行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">e = Entry.objects.get(id=2) |
− | e.entrydetail # returns the related EntryDetail object</ | + | e.entrydetail # returns the related EntryDetail object</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果没有对象被分配给这个关系,Django 将引发一个 <code>DoesNotExist</code> 异常。 | |
− | + | 可以使用与分配正向关系相同的方式将实例分配给反向关系: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,625行: | 第1,626行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">e.entrydetail = ed</syntaxhighlight> |
</div> | </div> | ||
第1,634行: | 第1,635行: | ||
<div id="how-are-the-backward-relationships-possible" class="section"> | <div id="how-are-the-backward-relationships-possible" class="section"> | ||
− | === | + | === 反向关系怎么可能? === |
− | + | 其他对象关系映射器要求您定义双方的关系。 Django 开发者认为这违反了 DRY(不要重复自己)原则,因此 Django 只要求您在一端定义关系。 | |
− | + | 但是,这怎么可能,因为在加载其他模型类之前,模型类不知道哪些其他模型类与其相关? | |
− | + | 答案在于 [[../../../ref/applications#django.apps|应用程序注册表]] 。 当 Django 启动时,它会导入 [[#id28|:setting:`INSTALLED_APPS`]] 中列出的每个应用程序,然后是每个应用程序内部的 <code>models</code> 模块。 每当创建新的模型类时,Django 都会向任何相关模型添加后向关系。 如果尚未导入相关模型,Django 会跟踪关系并在最终导入相关模型时添加它们。 | |
− | + | 因此,在 [[#id30|:setting:`INSTALLED_APPS`]] 中列出的应用程序中定义您使用的所有模型尤为重要。 否则,向后关系可能无法正常工作。 | |
第1,648行: | 第1,649行: | ||
<div id="queries-over-related-objects" class="section"> | <div id="queries-over-related-objects" class="section"> | ||
− | === | + | === 对相关对象的查询 === |
− | + | 涉及相关对象的查询与涉及普通值字段的查询遵循相同的规则。 在指定要匹配的查询的值时,您可以使用对象实例本身或对象的主键值。 | |
− | + | 例如,如果您有一个带有 <code>id=5</code> 的博客对象 <code>b</code>,则以下三个查询将相同: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,658行: | 第1,659行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(blog=b) # Query using object instance |
Entry.objects.filter(blog=b.id) # Query using id from instance | Entry.objects.filter(blog=b.id) # Query using id from instance | ||
− | Entry.objects.filter(blog=5) # Query using id directly</ | + | Entry.objects.filter(blog=5) # Query using id directly</syntaxhighlight> |
</div> | </div> | ||
第1,671行: | 第1,672行: | ||
<div id="falling-back-to-raw-sql" class="section"> | <div id="falling-back-to-raw-sql" class="section"> | ||
− | == | + | == 回退到原始 SQL == |
− | + | 如果您发现自己需要编写一个 Django 的数据库映射器无法处理的过于复杂的 SQL 查询,您可以退回到手动编写 SQL。 Django 有几个用于编写原始 SQL 查询的选项; 请参阅 [[../sql|执行原始 SQL 查询]] 。 | |
− | + | 最后,重要的是要注意 Django 数据库层只是数据库的接口。 您可以通过其他工具、编程语言或数据库框架访问您的数据库; 您的数据库没有任何特定于 Django 的内容。 | |
第1,681行: | 第1,682行: | ||
</div> | </div> | ||
+ | <div class="clearer"> | ||
− | [[Category:Django 3.0.x | + | |
+ | |||
+ | </div> | ||
+ | |||
+ | [[Category:Django 3.0.x 文档]] |
2021年10月31日 (日) 04:12的最新版本
进行查询
一旦您创建了 数据模型 ,Django 会自动为您提供一个数据库抽象 API,让您可以创建、检索、更新和删除对象。 本文档解释了如何使用此 API。 有关所有各种模型查找选项的完整详细信息,请参阅 数据模型参考 。
在本指南(以及参考资料)中,我们将参考以下模型,它们构成了一个 Weblog 应用程序:
创建对象
为了在 Python 对象中表示数据库表数据,Django 使用了一个直观的系统:模型类表示数据库表,该类的实例表示数据库表中的特定记录。
要创建对象,请使用模型类的关键字参数将其实例化,然后调用 save() 将其保存到数据库中。
假设模型存在于文件 mysite/blog/models.py
中,这是一个示例:
这会在幕后执行 INSERT
SQL 语句。 在您明确调用 save() 之前,Django 不会访问数据库。
save() 方法没有返回值。
保存对对象的更改
要保存对数据库中已有对象的更改,请使用 save()。
给定一个已经保存到数据库的 Blog
实例 b5
,这个例子改变了它的名字并更新了它在数据库中的记录:
这会在幕后执行 UPDATE
SQL 语句。 在您明确调用 save() 之前,Django 不会访问数据库。
保存 ForeignKey 和 ManyToManyField 字段
更新 ForeignKey 字段的工作方式与保存普通字段的方式完全相同——将正确类型的对象分配给相关字段。 此示例更新 Entry
实例 entry
的 blog
属性,假设 Entry
和 Blog
的适当实例已保存到数据库中(所以我们可以在下面检索它们):
更新 ManyToManyField 的工作方式略有不同 - 在字段上使用 add() 方法向关系添加记录。 本示例将 Author
实例 joe
添加到 entry
对象:
要将多条记录一次性添加到 ManyToManyField,请在对 add() 的调用中包含多个参数,如下所示:
如果您尝试分配或添加错误类型的对象,Django 会抱怨。
检索对象
要从数据库中检索对象,请通过模型类上的 Manager 构造 QuerySet。
QuerySet 表示数据库中的对象集合。 它可以有零个、一个或多个 过滤器 。 过滤器根据给定的参数缩小查询结果的范围。 在 SQL 术语中,QuerySet 等同于 SELECT
语句,过滤器是一个限制子句,例如 WHERE
或 LIMIT
。
您可以使用模型的 Manager 获得 QuerySet。 每个模型至少有一个Manager,默认叫做objects。 通过模型类直接访问它,如下所示:
笔记
Managers
只能通过模型类访问,而不是从模型实例访问,以强制分离“表级”操作和“记录级”操作。
Manager 是模型的 QuerySets
的主要来源。 例如,Blog.objects.all()
返回一个 QuerySet,其中包含数据库中的所有 Blog
对象。
使用过滤器检索特定对象
all() 返回的 QuerySet 描述了数据库表中的所有对象。 但是,通常您只需要选择完整对象集的一个子集。
要创建这样的子集,您需要优化初始 QuerySet,添加过滤条件。 优化 QuerySet 的两种最常见方法是:
filter(**kwargs)
- 返回一个新的 QuerySet 包含与给定查找参数匹配的对象。
exclude(**kwargs)
- 返回一个新的 QuerySet,其中包含 与 不匹配给定查找参数的对象。
查找参数(上述函数定义中的 **kwargs
)应采用以下 字段查找 中描述的格式。
例如,要获取 2006 年博客条目的 QuerySet,请使用 filter(),如下所示:
使用默认管理器类,它与:
链接过滤器
细化 QuerySet 的结果本身就是一个 QuerySet,因此可以将细化链接在一起。 例如:
这需要数据库中所有条目的初始 QuerySet,添加一个过滤器,然后是一个排除,然后是另一个过滤器。 最终结果是一个 QuerySet,其中包含所有标题以“What”开头的条目,这些条目在 2005 年 1 月 30 日和当天之间发布。
使用 get() 检索单个对象
filter() 总是会给你一个 QuerySet,即使只有一个对象与查询匹配 - 在这种情况下,它将是一个 QuerySet 包含一个元素。
如果您知道只有一个对象与您的查询匹配,您可以在直接返回对象的 Manager 上使用 get() 方法:
您可以将任何查询表达式与 get() 一起使用,就像使用 filter() 一样 - 再次参见下面的 字段查找 。
请注意,使用 get() 和使用带有 [0]
切片的 filter() 之间存在差异。 如果没有与查询匹配的结果,get() 将引发 DoesNotExist
异常。 这个异常是正在执行查询的模型类的一个属性——所以在上面的代码中,如果没有主键为 1 的 Entry
对象,Django 将引发 Entry.DoesNotExist
.
类似地,如果超过一项与 get() 查询匹配,Django 也会抱怨。 在这种情况下,它将引发 MultipleObjectsReturned,这也是模型类本身的一个属性。
其他 QuerySet 方法
大多数情况下,您会在需要时使用 all()、get()、filter() 和 exclude()从数据库中查找对象。 然而,这远非全部。 有关所有各种 QuerySet 方法的完整列表,请参阅 QuerySet API 参考 。
限制 QuerySets
使用 Python 的数组切片语法的子集将 QuerySet 限制为一定数量的结果。 这相当于 SQL 的 LIMIT
和 OFFSET
子句。
例如,这将返回前 5 个对象 (LIMIT 5
):
这将返回第六到第十个对象 (OFFSET 5 LIMIT 5
):
负索引(即 不支持 Entry.objects.all()[-1]
)。
通常,切片 QuerySet 会返回一个新的 QuerySet——它不会评估查询。 一个例外是,如果您使用 Python 切片语法的“step”参数。 例如,这实际上会执行查询以返回前 10 个每 秒 对象的列表:
由于其工作方式的模糊性,禁止对切片查询集进行进一步过滤或排序。
要检索 单个 对象而不是列表(例如 SELECT foo FROM bar LIMIT 1
),使用索引而不是切片。 例如,在按标题按字母顺序排列条目后,这将返回数据库中的第一个 Entry
:
这大致相当于:
但是请注意,如果没有对象符合给定条件,第一个将引发 IndexError
而第二个将引发 DoesNotExist
。 有关更多详细信息,请参阅 get()。
字段查找
字段查找是您指定 SQL WHERE
子句内容的方式。 它们被指定为 QuerySet 方法 filter()、exclude() 和 get() 的关键字参数。
基本查找关键字参数采用 field__lookuptype=value
的形式。 (这是一个双下划线)。 例如:
转换(大致)为以下 SQL:
这怎么可能
Python 能够定义接受任意名称-值参数的函数,其名称和值在运行时进行评估。 更多信息参见Python官方教程中的tut-keywordargs。
查找中指定的字段必须是模型字段的名称。 但有一个例外,在 ForeignKey 的情况下,您可以指定后缀为 _id
的字段名称。 在这种情况下, value 参数应该包含外部模型主键的原始值。 例如:
如果传递无效的关键字参数,查找函数将引发 TypeError
。
数据库 API 支持大约二打查找类型; 完整的参考可以在 字段查找参考 中找到。 为了让您了解可用的内容,以下是您可能会使用的一些更常见的查找:
- :查找:`精确`
“精确”匹配。 例如:
将按照以下方式生成 SQL:
如果您不提供查找类型——也就是说,如果您的关键字参数不包含双下划线——则假定查找类型为
exact
。例如,以下两个语句是等效的:
这是为了方便起见,因为
exact
查找是常见情况。- :lookup:`iexact`
不区分大小写的匹配。 所以,查询:
将匹配标题为
"Beatles Blog"
、"beatles blog"
甚至"BeAtlES blOG"
的Blog
。- :查找:`包含`
区分大小写的包含测试。 例如:
大致翻译成这个 SQL:
请注意,这将匹配标题
'Today Lennon honored'
但不匹配'today lennon honored'
。还有一个不区分大小写的版本,:lookup:`icontains`。
- :lookup:`startswith`, :lookup:`endswith`
分别开始于和结束于搜索。 还有一些不区分大小写的版本,称为 :lookup:`istartswith` 和 :lookup:`iendswith`。
同样,这只会划伤表面。 完整的参考可以在 字段查找参考 中找到。
跨越关系的查找
Django 提供了一种强大而直观的方式来“跟踪”查找中的关系,在幕后自动为您处理 SQL JOIN
。 要跨越关系,请使用跨模型的相关字段的字段名称,用双下划线分隔,直到到达所需的字段。
此示例检索所有具有 Blog
且 name
为 'Beatles Blog'
的 Entry
对象:
这个跨越可以像你想要的那样深。
它也向后工作。 要引用“反向”关系,请使用模型的小写名称。
此示例检索所有 Blog
对象,这些对象至少有一个 Entry
,其 headline
包含 'Lennon'
:
如果你在多个关系之间进行过滤并且其中一个中间模型没有满足过滤条件的值,Django 会将其视为有空(所有值都是 NULL
),但有效,对象那里。 所有这一切都意味着不会引发任何错误。 例如,在这个过滤器中:
(如果有相关的 Author
模型),如果没有与条目关联的 author
,则将被视为没有附加 name
,而不是由于缺少 author
而引发错误。 通常这正是您希望发生的事情。 唯一可能令人困惑的情况是,如果您使用 :lookup:`isnull`。 因此:
将返回在 author
上有空 name
的 Blog
对象,以及在 entry
上有空 author
的对象。 如果你不想要那些后面的对象,你可以写:
跨越多值关系
当您根据 ManyToManyField 或反向 ForeignKey 过滤对象时,您可能会对两种不同类型的过滤器感兴趣。 考虑 Blog
/Entry
关系(Blog
到 Entry
是一对多关系)。 我们可能有兴趣查找标题中包含 “Lennon” 且发表于 2008 年的条目的博客。 或者,我们可能想要查找标题中包含 “Lennon” 条目以及 2008 年发布的条目的博客。 由于有多个条目与单个 Blog
相关联,因此这两种查询都是可能的,并且在某些情况下是有意义的。
ManyToManyField 也会出现相同类型的情况。 例如,如果 Entry
有一个名为 tags
的 ManyToManyField,我们可能想要找到链接到名为 “music” 和 的标签的条目]“bands” 或者我们可能想要一个包含名称为 “music” 和状态为 “public” 的标签的条目。
为了处理这两种情况,Django 有处理 filter() 调用的一致方式。 单个 filter() 调用中的所有内容都会同时应用以过滤出符合所有这些要求的项目。 连续的 filter() 调用进一步限制了对象集,但对于多值关系,它们适用于链接到主模型的任何对象,不一定是那些由较早的 过滤器选择的对象() 调用。
这可能听起来有点令人困惑,所以希望有一个例子可以澄清。 要选择标题中同时包含 “Lennon” 条目且发布于 2008 年的所有博客(满足两个条件的同一条目),我们将编写:
要选择标题中包含 “Lennon” 的条目以及 2008 年发布的条目的所有博客,我们将编写:
假设只有一个博客同时包含 “Lennon” 和 2008 年的条目,但 2008 年的条目都没有包含 “Lennon”。 第一个查询不会返回任何博客,但第二个查询会返回那个博客。
在第二个示例中,第一个过滤器将查询集限制为链接到标题中带有 “Lennon” 的条目的所有博客。 第二个过滤器将博客集 进一步 限制为那些也链接到 2008 年发布的条目的博客。 第二个过滤器选择的条目可能与第一个过滤器中的条目相同,也可能不同。 我们使用每个过滤器语句过滤 Blog
项,而不是 Entry
项。
过滤器可以引用模型上的字段
在到目前为止给出的示例中,我们构建了过滤器,将模型字段的值与常量进行比较。 但是,如果您想将模型字段的值与同一模型上的另一个字段的值进行比较,该怎么办?
Django 提供了 F 表达式 来允许这样的比较。 F()
的实例充当对查询中模型字段的引用。 然后可以在查询过滤器中使用这些引用来比较同一模型实例上两个不同字段的值。
例如,要查找评论数多于 pingback 的所有博客条目的列表,我们构造一个 F()
对象来引用 pingback 计数,并在查询中使用该 F()
对象:
Django 支持对 F()
对象使用加法、减法、乘法、除法、取模和幂运算,包括常量和其他 F()
对象。 要查找评论数超过 pingback 的 倍 的所有博客条目,我们修改查询:
要查找条目评分小于 pingback 计数和评论计数之和的所有条目,我们将发出查询:
您还可以使用双下划线表示法来跨越 F()
对象中的关系。 带有双下划线的 F()
对象将引入访问相关对象所需的任何连接。 例如,要检索作者姓名与博客名称相同的所有条目,我们可以发出查询:
对于日期和日期/时间字段,您可以添加或减去 timedelta
对象。 以下将返回在发布后超过 3 天修改的所有条目:
F()
对象支持 .bitand()
、.bitor()
、.bitrightshift()
和 .bitleftshift()
的按位运算。 例如:
pk 查找快捷方式
为方便起见,Django 提供了一个 pk
查找快捷方式,它代表“主键”。
在示例 Blog
模型中,主键是 id
字段,因此这三个语句是等效的:
pk
的使用不限于 __exact
查询 - 任何查询词都可以与 pk
组合以对模型的主键执行查询:
pk
查找也适用于连接。 例如,这三个语句是等效的:
在 LIKE 语句中转义百分号和下划线
等同于 LIKE
SQL 语句的字段查找(iexact
、contains
、icontains
、startswith
、istartswith
、 endswith
和 iendswith
) 将自动转义 LIKE
语句中使用的两个特殊字符 - 百分号和下划线。 (在 LIKE
语句中,百分号表示多字符通配符,下划线表示单字符通配符。)
这意味着事情应该直观地工作,所以抽象不会泄漏。 例如,要检索包含百分号的所有条目,请将百分号用作任何其他字符:
Django 会为您处理报价; 生成的 SQL 将如下所示:
下划线也是如此。 百分号和下划线都为您透明处理。
缓存和 QuerySets
每个 QuerySet 都包含一个缓存以最小化数据库访问。 了解它的工作原理将使您能够编写最有效的代码。
在新创建的 QuerySet 中,缓存为空。 第一次计算 QuerySet 时——因此,发生数据库查询——Django 将查询结果保存在 QuerySet 的缓存中并返回已明确请求的结果(例如,下一个元素,如果 QuerySet 正在迭代)。 QuerySet 的后续评估重用缓存的结果。
记住这种缓存行为,因为如果你没有正确使用你的 QuerySets,它可能会咬你。 例如,以下将创建两个 QuerySet ,评估它们,然后将它们丢弃:
这意味着相同的数据库查询将执行两次,有效地使您的数据库负载加倍。 此外,这两个列表可能不包含相同的数据库记录,因为 Entry
可能已在两个请求之间的瞬间添加或删除。
为了避免这个问题,保存 QuerySet 并重用它:
当 QuerySet 未缓存时
查询集并不总是缓存它们的结果。 当仅评估查询集的 part 时,会检查缓存,但如果未填充,则不会缓存后续查询返回的项目。 具体来说,这意味着 使用数组切片或索引限制查询集 不会填充缓存。
例如,重复获取查询集对象中的某个索引,每次都会查询数据库:
但是,如果已经评估了整个查询集,则会改为检查缓存:
以下是其他操作的一些示例,这些操作将导致评估整个查询集并因此填充缓存:
笔记
简单地打印查询集不会填充缓存。 这是因为对 __repr__()
的调用只返回整个查询集的一部分。
Q 对象的复杂查找
关键字参数查询 - 在 filter() 等中。 – 被“AND”在一起。 如果您需要执行更复杂的查询(例如,使用 OR
语句的查询),您可以使用 Q 对象 。
Q 对象 (django.db.models.Q
) 是用于封装关键字参数集合的对象。 这些关键字参数在上面的“字段查找”中指定。
例如,这个 Q
对象封装了单个 LIKE
查询:
Q
对象可以使用 &
和 |
运算符进行组合。 当一个运算符用于两个 Q
对象时,它会产生一个新的 Q
对象。
例如,此语句生成一个 Q
对象,表示两个 "question__startswith"
查询的“或”:
这等效于以下 SQL WHERE
子句:
您可以通过将 Q
对象与 &
和 |
运算符组合并使用括号分组来组合任意复杂的语句。 此外,Q
对象可以使用 ~
运算符进行否定,允许组合查找,将普通查询和否定 (NOT
) 查询结合起来:
每个采用关键字参数的查找函数(例如 filter(), exclude(), get()) 也可以传递一个或多个 Q
对象作为位置(未命名) ) 论点。 如果您向查找函数提供多个 Q
对象参数,则这些参数将被“与”在一起。 例如:
... 大致翻译成 SQL:
查找函数可以混合使用 Q
对象和关键字参数。 提供给查找函数的所有参数(无论是关键字参数还是 Q
对象)都被“AND”在一起。 但是,如果提供了 Q
对象,则它必须在任何关键字参数的定义之前。 例如:
... 将是一个有效的查询,相当于前面的例子; 但:
……无效。
比较对象
要比较两个模型实例,请使用标准 Python 比较运算符,即双等号:==
。 在幕后,它比较了两个模型的主键值。
使用上面的 Entry
示例,以下两个语句是等效的:
如果模型的主键不是 id
,没问题。 比较将始终使用主键,无论它叫什么。 例如,如果模型的主键字段称为 name
,则这两个语句是等效的:
删除对象
删除方法方便地命名为 delete()。 此方法立即删除对象并返回删除的对象数和包含每个对象类型删除数的字典。 例子:
您还可以批量删除对象。 每个 QuerySet 都有一个 delete() 方法,该方法删除该 QuerySet 的所有成员。
例如,这将删除 pub_date
年为 2005 的所有 Entry
对象:
请记住,只要有可能,这将完全在 SQL 中执行,因此在此过程中不一定会调用各个对象实例的 delete()
方法。 如果您在模型类上提供了自定义 delete()
方法并希望确保它被调用,您将需要“手动”删除该模型的实例(例如,通过迭代 QuerySet 并分别在每个对象上调用 delete()
),而不是使用 QuerySet 的批量 delete() 方法。
当 Django 删除一个对象时,默认情况下它会模拟 SQL 约束 ON DELETE CASCADE
的行为——换句话说,任何具有指向要删除的对象的外键的对象都将被删除。 例如:
此级联行为可通过 ForeignKey 的 on_delete 参数进行自定义。
请注意, delete() 是唯一未在 Manager 本身上公开的 QuerySet 方法。 这是一种安全机制,可防止您意外请求 Entry.objects.delete()
,并删除 all 条目。 如果您 do 想要删除所有对象,那么您必须明确请求一个完整的查询集:
复制模型实例
尽管没有用于复制模型实例的内置方法,但可以轻松地创建新实例并复制所有字段的值。 在最简单的情况下,您可以将 pk
设置为 None
。 使用我们的博客示例:
如果您使用继承,事情会变得更加复杂。 考虑 Blog
的子类:
由于继承的工作方式,您必须将 pk
和 id
都设置为 None:
此过程不会复制不属于模型数据库表的关系。 例如,Entry
有 ManyToManyField
到 Author
。 复制条目后,您必须为新条目设置多对多关系:
对于 OneToOneField
,您必须复制相关对象并将其分配给新对象的字段,以避免违反一对一唯一约束。 例如,假设 entry
已经如上复制:
一次更新多个对象
有时,您希望为 QuerySet 中的所有对象将字段设置为特定值。 您可以使用 update() 方法执行此操作。 例如:
使用此方法只能设置非关系字段和 ForeignKey 字段。 要更新非关系字段,请将新值作为常量提供。 要更新 ForeignKey 字段,请将新值设置为要指向的新模型实例。 例如:
update()
方法立即应用并返回查询匹配的行数(如果某些行已经具有新值,则可能不等于更新的行数)。 QuerySet 更新的唯一限制是它只能访问一个数据库表:模型的主表。 您可以根据相关字段进行过滤,但您只能更新模型主表中的列。 例子:
请注意,update()
方法直接转换为 SQL 语句。 这是直接更新的批量操作。 它不会在您的模型上运行任何 save() 方法,也不会发出 pre_save
或 post_save
信号(这是调用 save() 的结果) ),或使用 auto_now 字段选项。 如果您想将每个项目保存在 QuerySet 中并确保在每个实例上调用 save() 方法,您不需要任何特殊函数来处理它。 遍历它们并调用 save():
调用 update 还可以使用 F 表达式 根据模型中另一个字段的值更新一个字段。 这对于根据当前值递增计数器特别有用。 例如,要增加博客中每个条目的 pingback 计数:
但是,与 filter 和 exclude 子句中的 F()
对象不同,在更新中使用 F()
对象时不能引入连接——您只能引用正在更新的模型的本地字段。 如果您尝试使用 F()
对象引入连接,则会引发 FieldError
:
回退到原始 SQL
如果您发现自己需要编写一个 Django 的数据库映射器无法处理的过于复杂的 SQL 查询,您可以退回到手动编写 SQL。 Django 有几个用于编写原始 SQL 查询的选项; 请参阅 执行原始 SQL 查询 。
最后,重要的是要注意 Django 数据库层只是数据库的接口。 您可以通过其他工具、编程语言或数据库框架访问您的数据库; 您的数据库没有任何特定于 Django 的内容。