“Django/docs/3.0.x/topics/db/queries”的版本间差异

来自菜鸟教程
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|<span class="doc">数据模型</span>]] 后,Django 自动给予你一套数据库抽象 API,允许你创建,检索,更新和删除对象。本页介绍如何使用这些 API。参考 [[../../../ref/models/index|<span class="doc">数据模型参考</span>]] 获取所有查询选项的完整细节。
+
一旦您创建了 [[../models|数据模型]] ,Django 会自动为您提供一个数据库抽象 API,让您可以创建、检索、更新和删除对象。 本文档解释了如何使用此 API。 有关所有各种模型查找选项的完整详细信息,请参阅 [[../../../ref/models/index|数据模型参考]]
  
在本指南中(以及相关参考资料中),我们将引用以下模型,这些模型包含了一个 Webblog 应用:
+
在本指南(以及参考资料)中,我们将参考以下模型,它们构成了一个 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">
  
<pre>from django.db import models
+
<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</pre>
+
         return self.headline</syntaxhighlight>
  
 
</div>
 
</div>
第48行: 第49行:
 
== 创建对象 ==
 
== 创建对象 ==
  
为了用 Python 对象展示数据表对象,Django 使用了一套直观的系统:一个模型类代表一张数据表,一个模型类的实例代表数据库表中的一行记录。
+
为了在 Python 对象中表示数据库表数据,Django 使用了一个直观的系统:模型类表示数据库表,该类的实例表示数据库表中的特定记录。
  
要创建一个对象,用关键字参数初始化它,然后调用 [[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 将其存入数据库。
+
要创建对象,请使用模型类的关键字参数将其实例化,然后调用 [[../../../ref/models/instances#django.db.models.Model|save()]] 将其保存到数据库中。
  
假设模型都位于文件 <code>mysite/blog/models.py</code> 中,这是一个例子:
+
假设模型存在于文件 <code>mysite/blog/models.py</code> 中,这是一个示例:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第58行: 第59行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from blog.models import Blog
+
<syntaxhighlight lang="python">>>> from blog.models import Blog
&gt;&gt;&gt; b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
+
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
&gt;&gt;&gt; b.save()</pre>
+
>>> b.save()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
这在幕后执行了 <code>INSERT</code> SQL 语句。Django 在你显式调用 [[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 才操作数据库。
+
这会在幕后执行 <code>INSERT</code> SQL 语句。 在您明确调用 [[../../../ref/models/instances#django.db.models.Model|save()]] 之前,Django 不会访问数据库。
  
[[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 方法没有返回值。
+
[[../../../ref/models/instances#django.db.models.Model|save()]] 方法没有返回值。
  
 
<div class="admonition seealso">
 
<div class="admonition seealso">
  
参见
+
也可以看看
  
[[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 接受很多此处未介绍的高级选项。参考文档 [[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 获取完整细节。
+
[[../../../ref/models/instances#django.db.models.Model|save()]] 采用了许多此处未描述的高级选项。 有关完整的详细信息,请参阅 [[../../../ref/models/instances#django.db.models.Model|save()]] 的文档。
  
要一步创建并保存一个对象,使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>create()</code>]] 方法。
+
要在单个步骤中创建和保存对象,请使用 [[../../../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|<code>save()</code>]]。
+
要保存对数据库中已有对象的更改,请使用 [[../../../ref/models/instances#django.db.models.Model|save()]]。
  
有一个已被存入数据库中的 <code>Blog</code> 实例 <code>b5</code>,本例将其改名,并在数据库中更新其记录:
+
给定一个已经保存到数据库的 <code>Blog</code> 实例 <code>b5</code>,这个例子改变了它的名字并更新了它在数据库中的记录:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第93行: 第94行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; b5.name = 'New name'
+
<syntaxhighlight lang="python">>>> b5.name = 'New name'
&gt;&gt;&gt; b5.save()</pre>
+
>>> b5.save()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
这在幕后执行了 <code>UPDATE</code> SQL 语句。Django 在你显示调用 [[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 后才操作数据库。
+
这会在幕后执行 <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">
  
=== 保存 <code>ForeignKey</code> <code>ManyToManyField</code> 字段 ===
+
=== 保存 ForeignKey 和 ManyToManyField 字段 ===
  
更新 [[../../../ref/models/fields#django.db.models|<code>ForeignKey</code>]] 字段的方式与保存普通字段的方式相同——只需将正确类型的实例分配给相关字段。本例为 <code>Entry</code> 类的实例 <code>entry</code> 更新了 <code>blog</code> 属性,假设 <code>Entry</code> 和 <code>Blog</code> 的实例均已保存在数据库中(因此能在下面检索它们):
+
更新 [[../../../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">
  
<pre>&gt;&gt;&gt; from blog.models import Blog, Entry
+
<syntaxhighlight lang="python">>>> from blog.models import Blog, Entry
&gt;&gt;&gt; entry = Entry.objects.get(pk=1)
+
>>> entry = Entry.objects.get(pk=1)
&gt;&gt;&gt; cheese_blog = Blog.objects.get(name=&quot;Cheddar Talk&quot;)
+
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
&gt;&gt;&gt; entry.blog = cheese_blog
+
>>> entry.blog = cheese_blog
&gt;&gt;&gt; entry.save()</pre>
+
>>> entry.save()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
更新 [[../../../ref/models/fields#django.db.models|<code>ManyToManyField</code>]] 字段有点不同——在字段上使用 [[../../../ref/models/relations#django.db.models.fields.related.RelatedManager|<code>add()</code>]] 方法为关联关系添加一条记录。本例将 <code>Author</code> 实例 <code>joe</code> 添加至 <code>entry</code> 对象:
+
更新 [[../../../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">
  
<pre>&gt;&gt;&gt; from blog.models import Author
+
<syntaxhighlight lang="python">>>> from blog.models import Author
&gt;&gt;&gt; joe = Author.objects.create(name=&quot;Joe&quot;)
+
>>> joe = Author.objects.create(name="Joe")
&gt;&gt;&gt; entry.authors.add(joe)</pre>
+
>>> entry.authors.add(joe)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
要一次添加多行记录至 [[../../../ref/models/fields#django.db.models|<code>ManyToManyField</code>]] 字段,在一次调用 [[../../../ref/models/relations#django.db.models.fields.related.RelatedManager|<code>add()</code>]] 时传入多个参数,像这样:
+
要将多条记录一次性添加到 [[../../../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">
  
<pre>&gt;&gt;&gt; john = Author.objects.create(name=&quot;John&quot;)
+
<syntaxhighlight lang="python">>>> john = Author.objects.create(name="John")
&gt;&gt;&gt; paul = Author.objects.create(name=&quot;Paul&quot;)
+
>>> paul = Author.objects.create(name="Paul")
&gt;&gt;&gt; george = Author.objects.create(name=&quot;George&quot;)
+
>>> george = Author.objects.create(name="George")
&gt;&gt;&gt; ringo = Author.objects.create(name=&quot;Ringo&quot;)
+
>>> ringo = Author.objects.create(name="Ringo")
&gt;&gt;&gt; entry.authors.add(john, paul, george, ringo)</pre>
+
>>> entry.authors.add(john, paul, george, ringo)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Django 会在添加或指定错误类型的对象时报错。
+
如果您尝试分配或添加错误类型的对象,Django 会抱怨。
  
  
第159行: 第160行:
 
== 检索对象 ==
 
== 检索对象 ==
  
要从数据库检索对象,要通过模型类的 [[../managers#django.db.models|<code>Manager</code>]] 构建一个 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]]。
+
要从数据库中检索对象,请通过模型类上的 [[../managers#django.db.models|Manager]] 构造 [[../../../ref/models/querysets#django.db.models.query|QuerySet]]。
  
一个 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 代表来自数据库中对象的一个集合。它可以有 0 个,1 个或者多个 ''filters''. Filters,可以根据给定参数缩小查询结果量。在 SQL 的层面上, [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 对应 <code>SELECT</code> 语句,而*filters*对应类似 <code>WHERE</code> 或 <code>LIMIT</code> 的限制子句。
+
[[../../../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|<code>Manager</code>]] 获取 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]]。每个模型至少有一个 [[../managers#django.db.models|<code>Manager</code>]],默认名称是 [[../../../ref/models/class#django.db.models.Model|<code>objects</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">
  
<pre>&gt;&gt;&gt; Blog.objects
+
<syntaxhighlight lang="python">>>> Blog.objects
&lt;django.db.models.manager.Manager object at ...&gt;
+
<django.db.models.manager.Manager object at ...>
&gt;&gt;&gt; b = Blog(name='Foo', tagline='Bar')
+
>>> b = Blog(name='Foo', tagline='Bar')
&gt;&gt;&gt; b.objects
+
>>> b.objects
 
Traceback:
 
Traceback:
 
     ...
 
     ...
AttributeError: &quot;Manager isn't accessible via Blog instances.&quot;</pre>
+
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|<code>Manager</code>]] 是模型的 <code>QuerySets</code> 主要来源。例如 <code>Blog.objects.all()</code> 返回了一个 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],后者包含了数据库中所有的 <code>Blog</code> 对象。
+
[[../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|<code>Manager</code>]] 上调用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>all()</code>]] 方法:
+
从表中检索对象的最简单方法是获取所有对象。 为此,请在 [[../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">
  
<pre>&gt;&gt;&gt; all_entries = Entry.objects.all()</pre>
+
<syntaxhighlight lang="python">>>> all_entries = Entry.objects.all()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
方法 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>all()</code>]] 返回了一个包含数据库中所有对象的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 对象。
+
[[../../../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|<code>all()</code>]] 返回的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 包含了数据表中所有的对象。虽然,大多数情况下,你只需要完整对象集合的一个子集。
+
[[../../../ref/models/querysets#django.db.models.query.QuerySet|all()]] 返回的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 描述了数据库表中的所有对象。 但是,通常您只需要选择完整对象集的一个子集。
  
要创建一个这样的子集,你需要通过添加过滤条件精炼原始 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]]。两种最常见的精炼 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 的方式是:
+
要创建这样的子集,您需要优化初始 [[../../../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|<code>QuerySet</code>]],包含的对象满足给定查询参数。
+
: 返回一个新的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 包含与给定查找参数匹配的对象。
 
; <code>exclude(**kwargs)</code>
 
; <code>exclude(**kwargs)</code>
: 返回一个新的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],包含的对象 '''' 满足给定查询参数。
+
: 返回一个新的 [[../../../ref/models/querysets#django.db.models.query|QuerySet]],其中包含 '''' 不匹配给定查找参数的对象。
  
查询参数(<code>**kwargs</code>)应该符合下面的 [[#field-lookups|Field lookups]] 的要求。
+
查找参数(上述函数定义中的 <code>**kwargs</code>)应采用以下 [[#field-lookups|字段查找]] 中描述的格式。
  
例如,要包含获取 2006 年的博客条目(entries blog)的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],像这样使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]]:
+
例如,要获取 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">
  
<pre>Entry.objects.filter(pub_date__year=2006)</pre>
+
<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">
  
<pre>Entry.objects.all().filter(pub_date__year=2006)</pre>
+
<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|<code>QuerySet</code>]] 的结果本身还是一个 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],所以能串联精炼过程。例子:
+
细化 [[../../../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">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(
+
<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)
... )</pre>
+
... )</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
这个先获取包含数据库所有条目(entry)的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],然后排除一些,再进入另一个过滤器。最终的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 包含标题以 &quot;What&quot; 开头的,发布日期介于 2005 年 1 月 30 日与今天之间的所有条目。
+
这需要数据库中所有条目的初始 [[../../../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>
==== 每个 <code>QuerySet</code> 都是唯一的 ====
+
==== 过滤后的 QuerySet 是唯一的 ====
  
每次精炼一个 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],你就会获得一个全新的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],后者与前者毫无关联。每次精炼都会创建一个单独的、不同的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],能被存储,使用和复用。
+
每次细化 [[../../../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">
  
<pre>&gt;&gt;&gt; q1 = Entry.objects.filter(headline__startswith=&quot;What&quot;)
+
<syntaxhighlight lang="python">>>> q1 = Entry.objects.filter(headline__startswith="What")
&gt;&gt;&gt; q2 = q1.exclude(pub_date__gte=datetime.date.today())
+
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
&gt;&gt;&gt; q3 = q1.filter(pub_date__gte=datetime.date.today())</pre>
+
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
这三个 <code>QuerySets</code> 是独立的。第一个是基础 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],包含了所有标题以 &quot;What&quot; 开头的条目。第二个是第一个的子集,带有额外条件,排除了 <code>pub_date</code> 是今天和今天之后的所有记录。第三个是第一个的子集,带有额外条件,只筛选 <code>pub_date</code> 是今天或未来的所有记录。最初的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] (<code>q1</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>
==== <code>QuerySet</code> 是惰性的 ====
+
==== QuerySet很懒 ====
  
<code>QuerySet</code> 是惰性的 —— 创建 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 并不会引发任何数据库活动。你可以将一整天的过滤器都堆积在一起,Django 只会在 [[../../../ref/models/querysets#django.db.models.query|<code>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">
  
<pre>&gt;&gt;&gt; q = Entry.objects.filter(headline__startswith=&quot;What&quot;)
+
<syntaxhighlight lang="python">>>> q = Entry.objects.filter(headline__startswith="What")
&gt;&gt;&gt; q = q.filter(pub_date__lte=datetime.date.today())
+
>>> q = q.filter(pub_date__lte=datetime.date.today())
&gt;&gt;&gt; q = q.exclude(body_text__icontains=&quot;food&quot;)
+
>>> q = q.exclude(body_text__icontains="food")
&gt;&gt;&gt; print(q)</pre>
+
>>> print(q)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
虽然这看起来像是三次数据库操作,实际上只在最后一行 (<code>print(q)</code>) 做了一次。一般来说, [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 的结果直到你 “要使用” 时才会从数据库中拿出。当你要用时,才通过数据库 ''计算'' 出 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]]。关于何时才真的执行计算的更多细节,参考 [[../../../ref/models/querysets#when-querysets-are-evaluated|<span class="std std-ref">When QuerySets are evaluated</span>]]。
+
虽然这看起来像是三个数据库命中,但实际上它只命中数据库一次,在最后一行 (<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>
=== 用 <code>get()</code> 检索单个对象 ===
+
=== 使用 get() 检索单个对象 ===
  
[[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 总是返回一个 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],即便只有一个对象满足查询条件 —— 这种情况下, [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 只包含了一个元素。
+
[[../../../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|<code>Manager</code>]] 上使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>get()</code>]] 方法,它会直接返回这个对象:
+
如果您知道只有一个对象与您的查询匹配,您可以在直接返回对象的 [[../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">
  
<pre>&gt;&gt;&gt; one_entry = Entry.objects.get(pk=1)</pre>
+
<syntaxhighlight lang="python">>>> one_entry = Entry.objects.get(pk=1)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
你可以对 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>get()</code>]] 使用与 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 类似的所有查询表达式 —— 同样的,参考下面的 [[#field-lookups|Field lookups]]。
+
您可以将任何查询表达式与 [[../../../ref/models/querysets#django.db.models.query.QuerySet|get()]] 一起使用,就像使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|filter()]] 一样 - 再次参见下面的 [[#field-lookups|字段查找]] 。
  
注意, 使用切片 <code>[0]</code> 时的 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>get()</code>]] 和 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 有点不同。如果没有满足查询条件的结果, [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>get()</code>]] 会抛出一个 <code>DoesNotExist</code> 异常。该异常是执行查询的模型类的一个属性 —— 所有,上述代码中,若没有哪个 <code>Entry</code> 对象的主键是 1,Django 会抛出 <code>Entry.DoesNotExist</code>
+
请注意,使用 [[../../../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> .
  
类似了,Django 会在有不止一个记录满足 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>get()</code>]] 查询条件时发出警告。这时,Django 会抛出 [[../../../ref/exceptions#django.core.exceptions|<code>MultipleObjectsReturned</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">
  
=== 其它 <code>QuerySet</code> 方法 ===
+
=== 其他 QuerySet 方法 ===
  
大多数情况下,你会在需要从数据库中检索对象时使用 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>all()</code>]][[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>get()</code>]][[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 和 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>exclude()</code>]]。然而,这样远远不够;完整的各种 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 方法请参阅 [[../../../ref/models/querysets#queryset-api|<span class="std std-ref">QuerySet API 参考</span>]]。
+
大多数情况下,您会在需要时使用 [[../../../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>
=== 限制 <code>QuerySet</code> 条目数 ===
+
=== 限制 QuerySets ===
  
利用 Python 的数组切片语法将 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 切成指定长度。这等价于 SQL 的 <code>LIMIT</code> 和 <code>OFFSET</code> 子句。
+
使用 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">
  
<pre>&gt;&gt;&gt; Entry.objects.all()[:5]</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.all()[:5]</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
这会返回第 6 至第 10 个对象 (<code>OFFSET 5 LIMIT 5</code>):
+
这将返回第六到第十个对象 (<code>OFFSET 5 LIMIT 5</code>)
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第379行: 第380行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; Entry.objects.all()[5:10]</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.all()[5:10]</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
不支持负索引 (例如 <code>Entry.objects.all()[-1]</code>)
+
负索引(即 不支持 <code>Entry.objects.all()[-1]</code>)
  
一般情况下, [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 的切片返回一个新的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] —— 其并未执行查询。一个特殊情况是使用了的 Python 切片语法的 “步长”。例如,这将会实际的执行查询命令,为了获取从前 10 个对象中,每隔一个抽取的对象组成的列表:
+
通常,切片 [[../../../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">
  
<pre>&gt;&gt;&gt; Entry.objects.all()[:10:2]</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.all()[:10:2]</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
由于对 queryset 切片工作方式的模糊性,禁止对其进行进一步的排序或过滤。
+
由于其工作方式的模糊性,禁止对切片查询集进行进一步过滤或排序。
  
要检索 ''单个'' 对象而不是一个列表时(例如 <code>SELECT foo FROM bar LIMIT 1</code>),请使用索引,而不是切片。例如,这会返回按标题字母排序后的第一个 <code>Entry</code>:
+
要检索 ''单个'' 对象而不是列表(例如 <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">
  
<pre>&gt;&gt;&gt; Entry.objects.order_by('headline')[0]</pre>
+
<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">
  
<pre>&gt;&gt;&gt; Entry.objects.order_by('headline')[0:1].get()</pre>
+
<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|<code>get()</code>]] 获取更多细节。
+
但是请注意,如果没有对象符合给定条件,第一个将引发 <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|<code>QuerySet</code>]] 方法 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]][[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>exclude()</code>]] 和 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>get()</code>]]
+
字段查找是您指定 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>。(有个双下划线)。例如:
+
基本查找关键字参数采用 <code>field__lookuptype=value</code> 的形式。 (这是一个双下划线)。 例如:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第438行: 第439行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(pub_date__lte='2006-01-01')</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.filter(pub_date__lte='2006-01-01')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
转换为 SQL 语句大致如下:
+
转换(大致)为以下 SQL:
  
 
<div class="highlight-sql notranslate">
 
<div class="highlight-sql notranslate">
第449行: 第450行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>SELECT * FROM blog_entry WHERE pub_date &lt;= '2006-01-01';</pre>
+
<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 能定义可接受任意数量 name-value 参数的函数,参数名和值均在运行时计算。更多信息,请参考官方 Python 教程中的 <span class="xref std std-ref">tut-keywordargs</span>。
+
Python 能够定义接受任意名称-值参数的函数,其名称和值在运行时进行评估。 更多信息参见Python官方教程中的<span class="xref std std-ref">tut-keywordargs</span>。
  
  
 
</div>
 
</div>
查询子句中指定的字段必须是模型的一个字段名。不过也有个例外,在 [[../../../ref/models/fields#django.db.models|<code>ForeignKey</code>]] 中,你可以指定以 <code>_id</code> 为后缀的字段名。这种情况下,value 参数需要包含 foreign 模型的主键的原始值。例子:
+
查找中指定的字段必须是模型字段的名称。 但有一个例外,在 [[../../../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">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(blog_id=4)</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.filter(blog_id=4)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
若你传入了无效的关键字参数,查询函数会抛出 <code>TypeError</code>。
+
如果传递无效的关键字参数,查找函数将引发 <code>TypeError</code>。
  
数据库 API 支持两套查询类型;完整参考文档位于 [[../../../ref/models/querysets#field-lookups|<span class="std std-ref">字段查询参考</span>]]。为了让你了解能干啥,以下是一些常见的查询:
+
数据库 API 支持大约二打查找类型; 完整的参考可以在 [[../../../ref/models/querysets#field-lookups|字段查找参考]] 中找到。 为了让您了解可用的内容,以下是您可能会使用的一些更常见的查找:
  
 
<dl>
 
<dl>
<dt>[[../../../ref/models/querysets#std-fieldlookup-exact|<code>exact</code>]]</dt>
+
<dt>[[#id6|<span id="id7" class="problematic">:查找:`精确`</span>]]</dt>
<dd><p>一个 &quot;exact&quot; 匹配的例子:</p>
+
<dd><p>“精确”匹配。 例如:</p>
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; Entry.objects.get(headline__exact=&quot;Cat bites dog&quot;)</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.get(headline__exact="Cat bites dog")</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>会生成这些 SQL:</p>
+
<p>将按照以下方式生成 SQL:</p>
 
<div class="highlight-sql notranslate">
 
<div class="highlight-sql notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>SELECT ... WHERE headline = 'Cat bites dog';</pre>
+
<syntaxhighlight lang="sql">SELECT ... WHERE headline = 'Cat bites dog';</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>若你为提供查询类型 —— 也就说,若关键字参数未包含双下划线 —— 查询类型会被指定为 <code>exact</code>。</p>
+
<p>如果您不提供查找类型——也就是说,如果您的关键字参数不包含双下划线——则假定查找类型为 <code>exact</code>。</p>
<p>例如,以下两条语句是等价的:</p>
+
<p>例如,以下两个语句是等效的:</p>
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; Blog.objects.get(id__exact=14)  # Explicit form
+
<syntaxhighlight lang="python">>>> Blog.objects.get(id__exact=14)  # Explicit form
&gt;&gt;&gt; Blog.objects.get(id=14)        # __exact is implied</pre>
+
>>> Blog.objects.get(id=14)        # __exact is implied</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>这是为了方便,因为 <code>exact</code> 查询是最常见的。</p></dd>
+
<p>这是为了方便起见,因为 <code>exact</code> 查找是常见情况。</p></dd>
<dt>[[../../../ref/models/querysets#std-fieldlookup-iexact|<code>iexact</code>]]</dt>
+
<dt>[[#id8|<span id="id9" class="problematic">:lookup:`iexact`</span>]]</dt>
<dd><p>不分大小写的匹配,查询语句:</p>
+
<dd><p>不区分大小写的匹配。 所以,查询:</p>
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; Blog.objects.get(name__iexact=&quot;beatles blog&quot;)</pre>
+
<syntaxhighlight lang="python">>>> Blog.objects.get(name__iexact="beatles blog")</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>会匹配标题为 <code>&quot;Beatles Blog&quot;</code><code>&quot;beatles blog&quot;</code>甚至 <code>&quot;BeAtlES blOG&quot;</code> 的 <code>Blog</code>。</p></dd>
+
<p>将匹配标题为 <code>&quot;Beatles Blog&quot;</code><code>&quot;beatles blog&quot;</code> 甚至 <code>&quot;BeAtlES blOG&quot;</code> 的 <code>Blog</code>。</p></dd>
<dt>[[../../../ref/models/querysets#std-fieldlookup-contains|<code>contains</code>]]</dt>
+
<dt>[[#id10|<span id="id11" class="problematic">:查找:`包含`</span>]]</dt>
<dd><p>大小写敏感的包含测试。例子:</p>
+
<dd><p>区分大小写的包含测试。 例如:</p>
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>Entry.objects.get(headline__contains='Lennon')</pre>
+
<syntaxhighlight lang="python">Entry.objects.get(headline__contains='Lennon')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>粗略地转为 SQL:</p>
+
<p>大致翻译成这个 SQL:</p>
 
<div class="highlight-sql notranslate">
 
<div class="highlight-sql notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>SELECT ... WHERE headline LIKE '%Lennon%';</pre>
+
<syntaxhighlight lang="sql">SELECT ... WHERE headline LIKE '%Lennon%';</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>注意这将匹配标题 <code>'Today Lennon honored'</code>,而不是 <code>'today lennon honored'</code>。</p>
+
<p>请注意,这将匹配标题 <code>'Today Lennon honored'</code> 但不匹配 <code>'today lennon honored'</code>。</p>
<p>这也有个大小写不敏感的版本, [[../../../ref/models/querysets#std-fieldlookup-icontains|<code>icontains</code>]]。</p></dd>
+
<p>还有一个不区分大小写的版本,[[#id12|:lookup:`icontains`]]。</p></dd>
<dt>[[../../../ref/models/querysets#std-fieldlookup-startswith|<code>startswith</code>]], [[../../../ref/models/querysets#std-fieldlookup-endswith|<code>endswith</code>]]</dt>
+
<dt>[[#id14|:lookup:`startswith`]], [[#id16|:lookup:`endswith`]]</dt>
<dd><p>以……开头和以……结尾的查找。当然也有大小写不敏感的版本,名为 [[../../../ref/models/querysets#std-fieldlookup-istartswith|<code>istartswith</code>]] 和 [[../../../ref/models/querysets#std-fieldlookup-iendswith|<code>iendswith</code>]]。</p></dd></dl>
+
<dd><p>分别开始于和结束于搜索。 还有一些不区分大小写的版本,称为 [[#id18|:lookup:`istartswith`]] 和 [[#id20|:lookup:`iendswith`]]。</p></dd></dl>
  
同样,这只介绍了皮毛。完整的参考能在 [[../../../ref/models/querysets#field-lookups|<span class="std std-ref">field 查询参考</span>]] 找到。
+
同样,这只会划伤表面。 完整的参考可以在 [[../../../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="id6"></span>
+
<span id="id22"></span>
=== 跨关系查询 ===
+
=== 跨越关系的查找 ===
  
Django 提供了一种强大而直观的方式来“追踪”查询中的关系,在幕后自动为你处理 SQL <code>JOIN</code> 关系。为了跨越关系,跨模型使用关联字段名,字段名由双下划线分割,直到拿到想要的字段。
+
Django 提供了一种强大而直观的方式来“跟踪”查找中的关系,在幕后自动为您处理 SQL <code>JOIN</code>。 要跨越关系,请使用跨模型的相关字段的字段名称,用双下划线分隔,直到到达所需的字段。
  
本例检索出所有的 <code>Entry</code> 对象,其 <code>Blog``的 ``name</code> 为 <code>'Beatles Blog'</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">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(blog__name='Beatles Blog')</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.filter(blog__name='Beatles Blog')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
跨域的深度随你所想。
+
这个跨越可以像你想要的那样深。
  
反向操作也能行。要指向一个“反向的”关联关系,需使用模型名的小写。
+
它也向后工作。 要引用“反向”关系,请使用模型的小写名称。
  
本例检索的所有 <code>Blog</code> 对象均拥有少一个 <code>标题</code> 含有 <code>'Lennon'</code> 的条目:
+
此示例检索所有 <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">
  
<pre>&gt;&gt;&gt; Blog.objects.filter(entry__headline__contains='Lennon')</pre>
+
<syntaxhighlight lang="python">>>> Blog.objects.filter(entry__headline__contains='Lennon')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
如果你在跨多个关系进行筛选,而某个中间模型的没有满足筛选条件的值,Django 会将它当做一个空的(所有值都是 <code>NULL</code>)但是有效的对象。这样就意味着不会抛出错误。例如,在这个过滤器中:
+
如果你在多个关系之间进行过滤并且其中一个中间模型没有满足过滤条件的值,Django 会将其视为有空(所有值都是 <code>NULL</code>),但有效,对象那里。 所有这一切都意味着不会引发任何错误。 例如,在这个过滤器中:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第593行: 第594行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>Blog.objects.filter(entry__authors__name='Lennon')</pre>
+
<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> 而抛出错误。大多数情况下,这就是你期望的。唯一可能使你迷惑的场景是在使用 [[../../../ref/models/querysets#std-fieldlookup-isnull|<code>isnull</code>]] 时。因此:
+
(如果有相关的 <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">
  
<pre>Blog.objects.filter(entry__authors__name__isnull=True)</pre>
+
<syntaxhighlight lang="python">Blog.objects.filter(entry__authors__name__isnull=True)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
将会返回 <code>Blog</code> 对象,包含 <code>author</code> 的 <code>name</code> 为空的对象,以及那些 <code>entry</code> <code>author</code> 为空的对象。若你不想要后面的对象,你可以这样写:
+
将返回在 <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">
  
<pre>Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)</pre>
+
<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|<code>ManyToManyField</code>]] 或反向 [[../../../ref/models/fields#django.db.models|<code>ForeignKey</code>]] 筛选某对象时,你可能对两种不同类型的过滤器感兴趣。假设 <code>Blog</code>/<code>Entry</code> 关联关系(<code>Blog</code> <code>Entry</code> 是一种一对多关联关系)。我们可能对那些条目标题同时含有 ''&quot;Lennon&quot;'' 且发布于 2008 年的博客感兴趣。或者,我们可能想找到那些条目标题包含 ''&quot;Lennon&quot;'' 或发布于 2008 的博客。由于多个 <code>Entry``能同时关联至一个 ``Blog</code>,两种查询都是可行的,且在某些场景下非常有用。
+
当您根据 [[../../../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|<code>ManyToManyField</code>]]。例如,若有个 <code>Entry</code> 拥有一个叫做 <code>tags</code> 的 [[../../../ref/models/fields#django.db.models|<code>ManyToManyField</code>]],要从关联至 tags 中的条目中找到名为 ''&quot;music&quot;'' 和 ''&quot;bands&quot;'' 的条目,或要找到某个标签名为 ''&quot;music&quot;'' 且状态为 ''&quot;public&quot;'' 的条目。
+
[[../../../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|<code>filter()</code>]] 调用。配置给某次 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 的所有条件会在调用时同时生效,筛选出满足条件的项目。连续的 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 调用进一步限制了对象结果集,但对于多值关联来说,限制条件作用于链接至主模型的对象,而不一定是那些被前置 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 调用筛选的对象。
+
为了处理这两种情况,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|过滤器选择的对象()]] 调用。
  
这听起来可能有点迷糊,所以需要一个例子来解释一下。要筛选出所有关联条目同时满足标题含有 ''&quot;Lennon&quot;'' 且发布于 2008 (同一个条目,同时满足两个条件)年的博客,我们会这样写:
+
这可能听起来有点令人困惑,所以希望有一个例子可以澄清。 要选择标题中同时包含 ''“Lennon”'' 条目且发布于 2008 年的所有博客(满足两个条件的同一条目),我们将编写:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第636行: 第637行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)</pre>
+
<syntaxhighlight lang="python">Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
要筛选所有条目标题包含 ''&quot;Lennon&quot;'' 或条目发布于 2008 年的博客,我们会这样写:
+
要选择标题中包含 ''“Lennon”'' 的条目以及 2008 年发布的条目的所有博客,我们将编写:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第647行: 第648行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)</pre>
+
<syntaxhighlight lang="python">Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
假设只有一个博客,拥有的条目同时满足标题含有 ''&quot;Lennon&quot;'' 且发布于 2008 年,但是发布于 2008 年的条目的标题均不含有 ''&quot;Lennon&quot;''。第一项查询不会返回任何博客,而第二项查询会返回此博客。
+
假设只有一个博客同时包含 ''“Lennon”'' 2008 年的条目,但 2008 年的条目都没有包含 ''“Lennon”''。 第一个查询不会返回任何博客,但第二个查询会返回那个博客。
  
在第二个例子中,第一个过滤器限制结果集为那些关联了标题包含 ''&quot;Lennon&quot;'' 的条目的博客。第二个过滤器进一步要求结果集中的博客要发布于 2008 年。第二个过滤器筛选的条目与第一个过滤器筛选的可能不尽相同。我们是用过滤器语句筛选 <code>Blog</code>,而不是 <code>Entry</code>
+
在第二个示例中,第一个过滤器将查询集限制为链接到标题中带有 ''“Lennon”'' 的条目的所有博客。 第二个过滤器将博客集 ''进一步'' 限制为那些也链接到 2008 年发布的条目的博客。 第二个过滤器选择的条目可能与第一个过滤器中的条目相同,也可能不同。 我们使用每个过滤器语句过滤 <code>Blog</code> 项,而不是 <code>Entry</code> 项。
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
[[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 的查询行为会跨越多值关联,就像前文说的那样,并不与 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>exclude()</code>]] 相同。相反,一次 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>exclude()</code>]] 调用的条件并不需要指向同一项目。
+
[[../../../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()]] 调用中的条件不一定指向同一个项目。
  
例如,以下查询会排除那些关联条目标题包含 ''&quot;Lennon&quot;'' 且发布于 2008 年的博客:
+
例如,以下查询将排除包含 ''both'' 条目的博客,其中 ''“Lennon”'' 在标题 ''和'' 条目发布于 2008 年:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第668行: 第669行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>Blog.objects.exclude(
+
<syntaxhighlight lang="python">Blog.objects.exclude(
 
     entry__headline__contains='Lennon',
 
     entry__headline__contains='Lennon',
 
     entry__pub_date__year=2008,
 
     entry__pub_date__year=2008,
)</pre>
+
)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
但是,与 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 的行为不同,其并不会限制博客同时满足这两种条件。要这么做的话,也就是筛选出所有条目标题不带 ''&quot;Lennon&quot;'' 且发布年不是 2008 的博客,你需要做两次查询:
+
但是,与使用 [[../../../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">
  
<pre>Blog.objects.exclude(
+
<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,
 
     ),
 
     ),
)</pre>
+
)</syntaxhighlight>
  
 
</div>
 
</div>
第701行: 第702行:
  
 
<span id="using-f-expressions-in-filters"></span>
 
<span id="using-f-expressions-in-filters"></span>
=== 过滤器可以为模型指定字段 ===
+
=== 过滤器可以引用模型上的字段 ===
  
在之前的例子中,我们已经构建过的 <code>filter</code> 都是将模型字段值与常量做比较。但是,要怎么做才能将模型字段值与同一模型中的另一字段做比较呢?
+
在到目前为止给出的示例中,我们构建了过滤器,将模型字段的值与常量进行比较。 但是,如果您想将模型字段的值与同一模型上的另一个字段的值进行比较,该怎么办?
  
Django 提供了 [[../../../ref/models/expressions#django.db.models|<code>F 表达式</code>]] 实现这种比较。 <code>F()</code> 的实例充当查询中的模型字段的引用。这些引用可在查询过滤器中用于在同一模型实例中比较两个不同的字段。
+
Django 提供了 [[../../../ref/models/expressions#django.db.models|F 表达式]] 来允许这样的比较。 <code>F()</code> 的实例充当对查询中模型字段的引用。 然后可以在查询过滤器中使用这些引用来比较同一模型实例上两个不同字段的值。
  
例如,要查出所有评论数大于 pingbacks 的博客条目,我们构建了一个 <code>F()</code> 对象,指代 pingback 的数量,然后在查询中使用该 <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">
  
<pre>&gt;&gt;&gt; from django.db.models import F
+
<syntaxhighlight lang="python">>>> from django.db.models import F
&gt;&gt;&gt; Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))</pre>
+
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Django 支持对 <code>F()</code> 对象进行加、减、乘、除、求余和次方,另一操作数既可以是常量,也可以是其它 <code>F()</code> 对象。要找到那些评论数两倍于 pingbacks 的博客条目,我们这样修改查询条件:
+
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">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
要找出所有评分低于 pingback 和评论总数之和的条目,修改查询条件:
+
要查找条目评分小于 pingback 计数和评论计数之和的所有条目,我们将发出查询:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第736行: 第737行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(rating__lt=F('number_of_comments') + F('number_of_pingbacks'))</pre>
+
<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> 对象将引入访问关联对象所需的任何连接。例如,要检索出所有作者名与博客名相同的博客,这样修改查询条件:
+
您还可以使用双下划线表示法来跨越 <code>F()</code> 对象中的关系。 带有双下划线的 <code>F()</code> 对象将引入访问相关对象所需的任何连接。 例如,要检索作者姓名与博客名称相同的所有条目,我们可以发出查询:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第747行: 第748行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(authors__name=F('blog__name'))</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.filter(authors__name=F('blog__name'))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
对于 date 和 date/time 字段,你可以加上或减去一个 <code>timedelta</code> 对象。以下会返回所有发布 3 天后被修改的条目:
+
对于日期和日期/时间字段,您可以添加或减去 <code>timedelta</code> 对象。 以下将返回在发布后超过 3 天修改的所有条目:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第758行: 第759行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from datetime import timedelta
+
<syntaxhighlight lang="python">>>> from datetime import timedelta
&gt;&gt;&gt; Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))</pre>
+
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<code>F()</code> 对象通过 <code>.bitand()</code><code>.bitor()</code><code>.bitrightshift()</code> 和 <code>.bitleftshift()</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">
  
<pre>&gt;&gt;&gt; F('somefield').bitand(16)</pre>
+
<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">
  
=== 主键 (<code>pk</code>) 查询快捷方式 ===
+
=== pk 查找快捷方式 ===
  
出于方便的目的,Django 提供了一种 <code>pk</code> 查询快捷方式, <code>pk</code> 表示主键 &quot;primary key&quot;。
+
为方便起见,Django 提供了一个 <code>pk</code> 查找快捷方式,它代表“主键”。
  
示例 <code>Blog</code> 模型中,主键是 <code>id</code> 字段,所以这 3 个语句是等效的:
+
在示例 <code>Blog</code> 模型中,主键是 <code>id</code> 字段,因此这三个语句是等效的:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第789行: 第790行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; Blog.objects.get(id__exact=14) # Explicit form
+
<syntaxhighlight lang="python">>>> Blog.objects.get(id__exact=14) # Explicit form
&gt;&gt;&gt; Blog.objects.get(id=14) # __exact is implied
+
>>> Blog.objects.get(id=14) # __exact is implied
&gt;&gt;&gt; Blog.objects.get(pk=14) # pk implies id__exact</pre>
+
>>> Blog.objects.get(pk=14) # pk implies id__exact</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<code>pk</code> 的使用并不仅限于 <code>__exact</code> 查询——任何的查询项都能接在 <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">
  
<pre># Get blogs entries with id 1, 4 and 7
+
<syntaxhighlight lang="python"># Get blogs entries with id 1, 4 and 7
&gt;&gt;&gt; Blog.objects.filter(pk__in=[1,4,7])
+
>>> Blog.objects.filter(pk__in=[1,4,7])
  
# Get all blog entries with id &gt; 14
+
# Get all blog entries with id > 14
&gt;&gt;&gt; Blog.objects.filter(pk__gt=14)</pre>
+
>>> Blog.objects.filter(pk__gt=14)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<code>pk</code> 查找也支持跨连接。例如,以下 3 个语句是等效的:
+
<code>pk</code> 查找也适用于连接。 例如,这三个语句是等效的:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第817行: 第818行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(blog__id__exact=3) # Explicit form
+
<syntaxhighlight lang="python">>>> Entry.objects.filter(blog__id__exact=3) # Explicit form
&gt;&gt;&gt; Entry.objects.filter(blog__id=3)        # __exact is implied
+
>>> Entry.objects.filter(blog__id=3)        # __exact is implied
&gt;&gt;&gt; Entry.objects.filter(blog__pk=3)        # __pk implies __id__exact</pre>
+
>>> 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">
  
=== 在 <code>LIKE</code> 语句中转义百分号和下划线 ===
+
=== 在 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> 语句中,百分号匹配多个任意字符,而下划线匹配一个任意字符。)
+
等同于 <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">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(headline__contains='%')</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.filter(headline__contains='%')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Django 为你小心处理了引号;生成的 SQL 语句看起来像这样:
+
Django 会为您处理报价; 生成的 SQL 将如下所示:
  
 
<div class="highlight-sql notranslate">
 
<div class="highlight-sql notranslate">
第849行: 第850行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>SELECT ... WHERE headline LIKE '%\%%';</pre>
+
<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="id7"></span>
+
<span id="id25"></span>
=== 缓存和 <code>QuerySet</code> ===
+
=== 缓存和 QuerySets ===
  
每个 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 都带有缓存,尽量减少数据库访问。理解它是如何工作的能让你编写更高效的代码。
+
每个 [[../../../ref/models/querysets#django.db.models.query|QuerySet]] 都包含一个缓存以最小化数据库访问。 了解它的工作原理将使您能够编写最有效的代码。
  
新创建的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 缓存是空的。一旦要计算 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 的值,就会执行数据查询,随后,Django 就会将查询结果保存在 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 的缓存中,并返回这些显式请求的缓存(例如,下一个元素,若 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 正在被迭代)。后续针对 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 的计算会复用缓存结果。
+
在新创建的 [[../../../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|<code>QuerySet</code>]] 时可能会被它咬一下。例如,以下会创建两个 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],计算它们,丢掉它们:
+
记住这种缓存行为,因为如果你没有正确使用你的 [[../../../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">
  
<pre>&gt;&gt;&gt; print([e.headline for e in Entry.objects.all()])
+
<syntaxhighlight lang="python">>>> print([e.headline for e in Entry.objects.all()])
&gt;&gt;&gt; print([e.pub_date for e in Entry.objects.all()])</pre>
+
>>> print([e.pub_date for e in Entry.objects.all()])</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
这意味着同样的数据库查询会被执行两次,实际加倍了数据库负载。同时,有可能这两个列表不包含同样的记录,因为在两次请求间,可能有 <code>Entry</code> 被添加或删除了。
+
这意味着相同的数据库查询将执行两次,有效地使您的数据库负载加倍。 此外,这两个列表可能不包含相同的数据库记录,因为 <code>Entry</code> 可能已在两个请求之间的瞬间添加或删除。
  
要避免此问题,保存 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</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">
  
<pre>&gt;&gt;&gt; queryset = Entry.objects.all()
+
<syntaxhighlight lang="python">>>> queryset = Entry.objects.all()
&gt;&gt;&gt; print([p.headline for p in queryset]) # Evaluate the query set.
+
>>> print([p.headline for p in queryset]) # Evaluate the query set.
&gt;&gt;&gt; print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.</pre>
+
>>> 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">
  
==== 当 <code>QuerySet</code> 未被缓存时 ====
+
==== 当 QuerySet 未缓存时 ====
  
查询结果集并不总是缓存结果。当仅计算查询结果集的 ''部分'' 时,会校验缓存,若没有填充缓存,则后续查询返回的项目不会被缓存。特别地说,这意味着使用数组切片或索引的 [[#limiting-querysets|<span class="std std-ref">限制查询结果集</span>]] 不会填充缓存。
+
查询集并不总是缓存它们的结果。 当仅评估查询集的 ''part'' 时,会检查缓存,但如果未填充,则不会缓存后续查询返回的项目。 具体来说,这意味着 [[#limiting-querysets|使用数组切片或索引限制查询集]] 不会填充缓存。
  
例如,重复的从某个查询结果集对象中取指定索引的对象会每次都查询数据库:
+
例如,重复获取查询集对象中的某个索引,每次都会查询数据库:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第906行: 第907行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; queryset = Entry.objects.all()
+
<syntaxhighlight lang="python">>>> queryset = Entry.objects.all()
&gt;&gt;&gt; print(queryset[5]) # Queries the database
+
>>> print(queryset[5]) # Queries the database
&gt;&gt;&gt; print(queryset[5]) # Queries the database again</pre>
+
>>> 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">
  
<pre>&gt;&gt;&gt; queryset = Entry.objects.all()
+
<syntaxhighlight lang="python">>>> queryset = Entry.objects.all()
&gt;&gt;&gt; [entry for entry in queryset] # Queries the database
+
>>> [entry for entry in queryset] # Queries the database
&gt;&gt;&gt; print(queryset[5]) # Uses cache
+
>>> print(queryset[5]) # Uses cache
&gt;&gt;&gt; print(queryset[5]) # Uses cache</pre>
+
>>> 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">
  
<pre>&gt;&gt;&gt; [entry for entry in queryset]
+
<syntaxhighlight lang="python">>>> [entry for entry in queryset]
&gt;&gt;&gt; bool(queryset)
+
>>> bool(queryset)
&gt;&gt;&gt; entry in queryset
+
>>> entry in queryset
&gt;&gt;&gt; list(queryset)</pre>
+
>>> list(queryset)</syntaxhighlight>
  
 
</div>
 
</div>
第943行: 第944行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
只是打印查询结果集不会填充缓存。因为调用 <code>__repr__()</code> 仅返回了完整结果集的一个切片。
+
简单地打印查询集不会填充缓存。 这是因为对 <code>__repr__()</code> 的调用只返回整个查询集的一部分。
  
  
第958行: 第959行:
  
 
<span id="complex-lookups-with-q"></span>
 
<span id="complex-lookups-with-q"></span>
== 通过 <code>Q</code> 对象完成复杂查询 ==
+
== Q 对象的复杂查找 ==
  
在类似 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>filter()</code>]] 中,查询使用的关键字参数是通过 &quot;AND&quot; 连接起来的。如果你要执行更复杂的查询(例如,由 <code>OR</code> 语句连接的查询),你可以使用 [[../../../ref/models/querysets#django.db.models|<code>Q 对象</code>]]。
+
关键字参数查询 - 在 [[../../../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|<code>Q 对象</code>]] (<code>django.db.models.Q</code>) 用于压缩关键字参数集合。这些关键字参数由前文 &quot;Field lookups&quot; 指定。
+
[[../../../ref/models/querysets#django.db.models|Q 对象]] (<code>django.db.models.Q</code>) 是用于封装关键字参数集合的对象。 这些关键字参数在上面的“字段查找”中指定。
  
例如,该 <code>Q</code> 对象压缩了一个 <code>LIKE</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">
  
<pre>from django.db.models import Q
+
<syntaxhighlight lang="python">from django.db.models import Q
Q(question__startswith='What')</pre>
+
Q(question__startswith='What')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<code>Q</code> 对象能通过 <code>&amp;</code> 和 <code>|</code> 操作符连接起来。当操作符被用于两个 <code>Q</code> 对象之间时会生成一个新的 <code>Q</code> 对象。
+
<code>Q</code> 对象可以使用 <code>&amp;</code> 和 <code>|</code> 运算符进行组合。 当一个运算符用于两个 <code>Q</code> 对象时,它会产生一个新的 <code>Q</code> 对象。
  
例如,该语句生成一个 <code>Q</code> 对象,表示两个 <code>&quot;question_startswith&quot;</code> 查询语句之间的 &quot;OR&quot; 关系:
+
例如,此语句生成一个 <code>Q</code> 对象,表示两个 <code>&quot;question__startswith&quot;</code> 查询的“或”:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第984行: 第985行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>Q(question__startswith='Who') | Q(question__startswith='What')</pre>
+
<syntaxhighlight lang="python">Q(question__startswith='Who') | Q(question__startswith='What')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
这等价于以下 SQL <code>WHERE</code> 字句:
+
这等效于以下 SQL <code>WHERE</code> 子句:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第995行: 第996行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>WHERE question LIKE 'Who%' OR question LIKE 'What%'</pre>
+
<syntaxhighlight lang="python">WHERE question LIKE 'Who%' OR question LIKE 'What%'</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
你能通过 <code>&amp;</code> 和 <code>|</code> 操作符和括号分组,组合任意复杂度的语句。当然, <code>Q</code> 对象也可通过 <code>~</code> 操作符反转,允许在组合查询中组合普通查询或反向 (<code>NOT</code>) 查询:
+
您可以通过将 <code>Q</code> 对象与 <code>&amp;</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">
  
<pre>Q(question__startswith='Who') | ~Q(pub_date__year=2005)</pre>
+
<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|<code>filter()</code>]][[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>exclude()</code>]][[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>get()</code>]]) 也同时接受一个或多个 <code>Q</code> 对象作为位置(未命名的)参数。若你为查询函数提供了多个 <code>Q</code> 对象参数,这些参数会通过 &quot;AND&quot; 连接。例子:
+
每个采用关键字参数的查找函数(例如 [[../../../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">
  
<pre>Poll.objects.get(
+
<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))
)</pre>
+
)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
...粗略地转为 SQL:
+
... 大致翻译成 SQL:
  
 
<div class="highlight-sql notranslate">
 
<div class="highlight-sql notranslate">
第1,031行: 第1,032行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>SELECT * from polls WHERE question LIKE 'Who%'
+
<syntaxhighlight lang="sql">SELECT * from polls WHERE question LIKE 'Who%'
     AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')</pre>
+
     AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
查询函数能混合使用 <code>Q</code> 对象和关键字参数。所有提供给查询函数的参数(即关键字参数或 <code>Q</code> 对象)均通过 &quot;AND&quot; 连接。然而,若提供了 <code>Q</code> 对象,那么它必须位于所有关键字参数之前。例子:
+
查找函数可以混合使用 <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">
  
<pre>Poll.objects.get(
+
<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',
)</pre>
+
)</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">
  
<pre># INVALID QUERY
+
<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))
)</pre>
+
)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
……却是无效的。
+
……无效。
  
 
<div class="admonition seealso">
 
<div class="admonition seealso">
  
参见
+
也可以看看
  
Django 单元测试中的 [https://github.com/django/django/blob/master/tests/or_lookups/tests.py OR 查询实例] 展示了 <code>Q</code> 的用法。
+
这 [[#id26|:source:`OR 查找示例 `]] 在 Django 的单元测试中显示了一些可能的用途<code>Q</code> .
  
  
第1,082行: 第1,083行:
 
== 比较对象 ==
 
== 比较对象 ==
  
要比较两个模型实例,使用标准的 Python 比较操作符,两个等号: <code>==</code>。实际上,这比较了两个模型实例的主键值。
+
要比较两个模型实例,请使用标准 Python 比较运算符,即双等号:<code>==</code>。 在幕后,它比较了两个模型的主键值。
  
使用前文的 <code>Entry</code>,以下的两个语句是等效的:
+
使用上面的 <code>Entry</code> 示例,以下两个语句是等效的:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第1,090行: 第1,091行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; some_entry == other_entry
+
<syntaxhighlight lang="python">>>> some_entry == other_entry
&gt;&gt;&gt; some_entry.id == other_entry.id</pre>
+
>>> some_entry.id == other_entry.id</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
若模型主键名不是 <code>id</code>,没问题。比较时总会使用主键,不管它叫啥。例如,若模型的主键字段名为 <code>name</code>,以下两个语句是等效的:
+
如果模型的主键不是 <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">
  
<pre>&gt;&gt;&gt; some_obj == other_obj
+
<syntaxhighlight lang="python">>>> some_obj == other_obj
&gt;&gt;&gt; some_obj.name == other_obj.name</pre>
+
>>> some_obj.name == other_obj.name</syntaxhighlight>
  
 
</div>
 
</div>
第1,115行: 第1,116行:
 
== 删除对象 ==
 
== 删除对象 ==
  
通常,删除方法被命名为 [[../../../ref/models/instances#django.db.models.Model|<code>delete()</code>]]。该方法立刻删除对象,并返回被删除的对象数量和一个包含了每个被删除对象类型的数量的字典。例子:
+
删除方法方便地命名为 [[../../../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">
  
<pre>&gt;&gt;&gt; e.delete()
+
<syntaxhighlight lang="python">>>> e.delete()
(1, {'weblog.Entry': 1})</pre>
+
(1, {'weblog.Entry': 1})</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
你也能批量删除对象。所有 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 都有个 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>delete()</code>]] 方法,它会删除 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 中的所有成员。
+
您还可以批量删除对象。 每个 [[../../../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]] 的所有成员。
  
例如,这会删除 2005 发布的所有 <code>Entry</code> 对象:
+
例如,这将删除 <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">
  
<pre>&gt;&gt;&gt; Entry.objects.filter(pub_date__year=2005).delete()
+
<syntaxhighlight lang="python">>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})</pre>
+
(5, {'webapp.Entry': 5})</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
请记住,只要有机会的话,这会通过纯 SQL 语句执行,所以就无需在过程中调用每个对象的删除方法了。若你为模型类提供了自定义的 <code>delete()</code> 方法,且希望确保调用了该方法,你需要 “手动” 删除该模型的实例(例如,如,遍历 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]],在每个对象上分别调用 <code>delete()</code> 方法),而不是使用 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 的批量删除方法 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>delete()</code>]]
+
请记住,只要有可能,这将完全在 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 删除某个对象时,默认会模仿 SQL 约束 <code>ON DELETE CASCADE</code> 的行为——换而言之,某个对象被删除时,关联对象也会被删除。例子:
+
当 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">
  
<pre>b = Blog.objects.get(pk=1)
+
<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()</pre>
+
b.delete()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
这种约束行为由 [[../../../ref/models/fields#django.db.models|<code>ForeignKey</code>]] 的 [[../../../ref/models/fields#django.db.models.ForeignKey|<code>on_delete</code>]] 参数指定。
+
此级联行为可通过 [[../../../ref/models/fields#django.db.models|ForeignKey]] 的 [[../../../ref/models/fields#django.db.models.ForeignKey|on_delete]] 参数进行自定义。
  
注意 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>delete()</code>]] 是唯一未在 [[../managers#django.db.models|<code>Manager</code>]] 上暴漏的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 方法。这是一种安全机制,避免你不小心调用了 <code>Entry.objects.delete()</code>,删除了 ''所有的'' 条目。若你 ''确实'' 想要删除所有对象,你必须显示请求完整结果集合:
+
请注意, [[../../../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">
  
<pre>Entry.objects.all().delete()</pre>
+
<syntaxhighlight lang="python">Entry.objects.all().delete()</syntaxhighlight>
  
 
</div>
 
</div>
第1,176行: 第1,177行:
 
== 复制模型实例 ==
 
== 复制模型实例 ==
  
虽然没有用于拷贝模型实例的内置方法,但仍能很简单的拷贝所有字段值创建新实例。最简单的例子,你可以将 <code>pk</code> 设为 <code>None</code>。使用博客示例:
+
尽管没有用于复制模型实例的内置方法,但可以轻松地创建新实例并复制所有字段的值。 在最简单的情况下,您可以将 <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">
  
<pre>blog = Blog(name='My blog', tagline='Blogging is easy')
+
<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</pre>
+
blog.save() # blog.pk == 2</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
若你使用了集成,事情会更复杂。考虑下 <code>Blog</code> 的一个子类:
+
如果您使用继承,事情会变得更加复杂。 考虑 <code>Blog</code> 的子类:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第1,197行: 第1,198行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>class ThemeBlog(Blog):
+
<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</pre>
+
django_blog.save() # django_blog.pk == 3</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
根据继承的原理,你必须将 <code>pk</code> 和 <code>id</code> 都设置为 None:
+
由于继承的工作方式,您必须将 <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">
  
<pre>django_blog.pk = None
+
<syntaxhighlight lang="python">django_blog.pk = None
 
django_blog.id = None
 
django_blog.id = None
django_blog.save() # django_blog.pk == 4</pre>
+
django_blog.save() # django_blog.pk == 4</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
该方法不会拷贝不是模型数据表中的关联关系。例如, <code>Entry</code> 有一个对 <code>Author</code> <code>ManyToManyField</code> 关联关系。在复制条目后,你必须为新条目设置多对多关联关系。
+
此过程不会复制不属于模型数据库表的关系。 例如,<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">
  
<pre>entry = Entry.objects.all()[0] # some previous entry
+
<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)</pre>
+
entry.authors.set(old_authors)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
对于 <code>OneToOneField</code> 关联,你必须拷贝关联对象,并将其指定给新对象的关联字段,避免违反一对一唯一性约束。例如,指定前文复制的 <code>entry</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">
  
<pre>detail = EntryDetail.objects.all()[0]
+
<syntaxhighlight lang="python">detail = EntryDetail.objects.all()[0]
 
detail.pk = None
 
detail.pk = None
 
detail.entry = entry
 
detail.entry = entry
detail.save()</pre>
+
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|<code>QuerySet</code>]] 中的所有对象的某个字段。你可以通过 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>update()</code>]] 达到目的。例子:
+
有时,您希望为 [[../../../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">
  
<pre># Update all the headlines with pub_date in 2007.
+
<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')</pre>
+
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
你仅能用此方法设置非关联字段和 [[../../../ref/models/fields#django.db.models|<code>ForeignKey</code>]] 字段。要修改非关联字段,需要用常量提供新值。要修改 [[../../../ref/models/fields#django.db.models|<code>ForeignKey</code>]] 字段,将新值置为目标模型的新实例。例子:
+
使用此方法只能设置非关系字段和 [[../../../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">
  
<pre>&gt;&gt;&gt; b = Blog.objects.get(pk=1)
+
<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.
&gt;&gt;&gt; Entry.objects.all().update(blog=b)</pre>
+
>>> Entry.objects.all().update(blog=b)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
方法 <code>update()</code> 立刻被运行,并返回匹配查询调节的行数(若某些行早已是新值,则可能不等于实际匹配的行数)。更新 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 的唯一限制即它只能操作一个数据表:该模型的主表。你可以基于关联字段进行筛选,但你只能更新模型主表中的列。例子:
+
<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">
  
<pre>&gt;&gt;&gt; b = Blog.objects.get(pk=1)
+
<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.
&gt;&gt;&gt; Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same')</pre>
+
>>> 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|<code>save()</code>]] 方法,或发射 <code>pre_save</code> 或 <code>post_save</code> 信号(调用 [[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 会触发信号),或使用 [[../../../ref/models/fields#django.db.models.DateField|<code>auto_now</code>]] 字段选项。若想保存 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 中的每项,并确保调用了每个实例的 [[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 方法,你并不需要任何特殊的函数来处理此问题。迭代它们,并调用它们的 [[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 方法:
+
请注意,<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">
  
<pre>for item in my_queryset:
+
<syntaxhighlight lang="python">for item in my_queryset:
     item.save()</pre>
+
     item.save()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
调用更新方法时也能使用 [[../../../ref/models/expressions#django.db.models|<code>F 表达式</code>]] 基于同一模型另一个字段的值更新某个字段。这在基于计数器的当前值增加其值时特别有用。例如,要增加针对博客中每项条目的 pingback 技术:
+
调用 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">
  
<pre>&gt;&gt;&gt; Entry.objects.all().update(number_of_pingbacks=F('number_of_pingbacks') + 1)</pre>
+
<syntaxhighlight lang="python">>>> Entry.objects.all().update(number_of_pingbacks=F('number_of_pingbacks') + 1)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
然而,与过滤器中的 <code>F()</code> 对象和排除字句不同,你不能在更新方法中使用 <code>F()</code> 对象的同时使用 join——你只能引用被更新模型的内部字段。若你试着在使用 join 字句时使用 <code>F()</code> 对象,会抛出一个 <code>FieldError()</code>:
+
但是,与 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">
  
<pre># This will raise a FieldError
+
<syntaxhighlight lang="python"># This will raise a FieldError
&gt;&gt;&gt; Entry.objects.update(headline=F('blog__name'))</pre>
+
>>> 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|<code>ForeignKey</code>]][[../../../ref/models/fields#django.db.models|<code>OneToOneField</code>]]或 [[../../../ref/models/fields#django.db.models|<code>ManyToManyField</code>]]),该模型的实例将会自动获取一套 API,能快捷地访问关联对象。
+
当您在模型中定义关系(即 [[../../../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> 对象: <code>e.blog</code>
+
使用本页顶部的模型,例如,<code>Entry</code> 对象 <code>e</code> 可以通过访问 <code>blog</code> 属性来获取其关联的 <code>Blog</code> 对象:[ X163X]
  
(在幕后,这个函数是由 Python <span class="xref std std-doc">descriptors</span> 实现的。这玩意一般不会麻烦你,但是我们为你指出了注意点。)
+
(在幕后,此功能由 Python <span class="xref std std-doc"> 描述符 </span> 实现。 这对你来说并不重要,但我们在这里指出它是为了好奇。)
  
Django 也提供了从关联关系 ''另一边'' 访问的 API —— 从被关联模型到定义关联关系的模型的连接。例如,一个 <code>Blog</code> 对象 <code>b</code> 能通过 <code>entry_set</code> 属性 <code>b.entry_set.all()</code> 访问包含所有关联 <code>Entry</code> 对象的列表。
+
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> 模型。
+
本节中的所有示例均使用本页顶部定义的示例 <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|<code>ForeignKey</code>]],该模型的实例能通过其属性访问关联(外部的)对象。
+
如果模型具有 [[../../../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">
  
<pre>&gt;&gt;&gt; e = Entry.objects.get(id=2)
+
<syntaxhighlight lang="python">>>> e = Entry.objects.get(id=2)
&gt;&gt;&gt; e.blog # Returns the related Blog object.</pre>
+
>>> e.blog # Returns the related Blog object.</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
你可以通过 foreign-key 属性获取和设置值。如你所想,对外键的修改直到你调用 [[../../../ref/models/instances#django.db.models.Model|<code>save()</code>]] 后才会被存入数据库。例子:
+
您可以通过外键属性获取和设置。 正如您所料,在您调用 [[../../../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">
  
<pre>&gt;&gt;&gt; e = Entry.objects.get(id=2)
+
<syntaxhighlight lang="python">>>> e = Entry.objects.get(id=2)
&gt;&gt;&gt; e.blog = some_blog
+
>>> e.blog = some_blog
&gt;&gt;&gt; e.save()</pre>
+
>>> e.save()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
[[../../../ref/models/fields#django.db.models|<code>ForeignKey</code>]] 字段配置了 <code>null=True</code> (即其允许 <code>NULL</code> 值),你可以指定值为 <code>None</code> 移除关联。例子:
+
如果 [[../../../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">
  
<pre>&gt;&gt;&gt; e = Entry.objects.get(id=2)
+
<syntaxhighlight lang="python">>>> e = Entry.objects.get(id=2)
&gt;&gt;&gt; e.blog = None
+
>>> e.blog = None
&gt;&gt;&gt; e.save() # &quot;UPDATE blog_entry SET blog_id = NULL ...;&quot;</pre>
+
>>> 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">
  
<pre>&gt;&gt;&gt; e = Entry.objects.get(id=2)
+
<syntaxhighlight lang="python">>>> e = Entry.objects.get(id=2)
&gt;&gt;&gt; print(e.blog)  # Hits the database to retrieve the associated Blog.
+
>>> print(e.blog)  # Hits the database to retrieve the associated Blog.
&gt;&gt;&gt; print(e.blog)  # Doesn't hit the database; uses cached version.</pre>
+
>>> print(e.blog)  # Doesn't hit the database; uses cached version.</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
注意 [[../../../ref/models/querysets#django.db.models.query.QuerySet|<code>select_related()</code>]] [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 方法会预先用所有一对多关联对象填充缓存。例子:
+
请注意, [[../../../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">
  
<pre>&gt;&gt;&gt; e = Entry.objects.select_related().get(id=2)
+
<syntaxhighlight lang="python">>>> e = Entry.objects.select_related().get(id=2)
&gt;&gt;&gt; print(e.blog)  # Doesn't hit the database; uses cached version.
+
>>> print(e.blog)  # Doesn't hit the database; uses cached version.
&gt;&gt;&gt; print(e.blog)  # Doesn't hit the database; uses cached version.</pre>
+
>>> 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|<code>ForeignKey</code>]],外键关联的模型实例将能访问 [[../managers#django.db.models|<code>Manager</code>]],后者会返回第一个模型的所有实例。默认情况下,该 [[../managers#django.db.models|<code>Manager</code>]] 名为 <code>FOO_set</code><code>FOO</code> 即源模型名的小写形式。 [[../managers#django.db.models|<code>Manager</code>]] 返回 <code>QuerySets</code>,后者能以 “检索对象” 章节介绍的方式进行筛选和操作。
+
如果模型具有 [[../../../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">
  
<pre>&gt;&gt;&gt; b = Blog.objects.get(id=1)
+
<syntaxhighlight lang="python">>>> b = Blog.objects.get(id=1)
&gt;&gt;&gt; b.entry_set.all() # Returns all Entry objects related to Blog.
+
>>> 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.
&gt;&gt;&gt; b.entry_set.filter(headline__contains='Lennon')
+
>>> b.entry_set.filter(headline__contains='Lennon')
&gt;&gt;&gt; b.entry_set.count()</pre>
+
>>> b.entry_set.count()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
你可以在定义 [[../../../ref/models/fields#django.db.models|<code>ForeignKey</code>]] 时设置 [[../../../ref/models/fields#django.db.models.ForeignKey|<code>related_name</code>]] 参数重写这个 <code>FOO_set</code> 名。例如,若修改 <code>Entry</code> 模型为 <code>blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries')</code>,前文示例代码会看起来像这样:
+
您可以通过设置 [[../../../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">
  
<pre>&gt;&gt;&gt; b = Blog.objects.get(id=1)
+
<syntaxhighlight lang="python">>>> b = Blog.objects.get(id=1)
&gt;&gt;&gt; b.entries.all() # Returns all Entry objects related to Blog.
+
>>> 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.
&gt;&gt;&gt; b.entries.filter(headline__contains='Lennon')
+
>>> b.entries.filter(headline__contains='Lennon')
&gt;&gt;&gt; b.entries.count()</pre>
+
>>> b.entries.count()</syntaxhighlight>
  
 
</div>
 
</div>
第1,469行: 第1,470行:
 
==== 使用自定义反向管理器 ====
 
==== 使用自定义反向管理器 ====
  
[[../../../ref/models/relations#django.db.models.fields.related|<code>RelatedManager</code>]] 反向关联的默认实现是该模型 [[../managers#manager-names|<span class="std std-ref">默认管理器</span>]] 一个实例。若你想为某个查询指定一个不同的管理器,可以使用如下语法:
+
默认情况下,用于反向关系的 [[../../../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">
  
<pre>from django.db import models
+
<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()</pre>
+
b.entry_set(manager='entries').all()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<code>EntryManager</code> 在其 <code>get_queryset()</code> 方法执行了默认过滤行为,改行为会应用到 <code>all()</code> 调用中。
+
如果 <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">
  
<pre>b.entry_set(manager='entries').is_published()</pre>
+
<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/fields#django.db.models|<code>ForeignKey</code>]] [[../managers#django.db.models|<code>Manager</code>]] 还有方法能处理关联对象集合。除了上面的 “检索对象” 中定义的 [[../../../ref/models/querysets#django.db.models.query|<code>QuerySet</code>]] 方法以外,以下是每项的简要介绍,而完整的细节能在 [[../../../ref/models/relations|<span class="doc">关联对象参考</span>]] 中找到。
+
除了上面“检索对象”中定义的 [[../../../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> 实例:
+
要分配相关集合的成员,请使用具有可迭代对象实例的 <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">
  
<pre>b = Blog.objects.get(id=1)
+
<syntaxhighlight lang="python">b = Blog.objects.get(id=1)
b.entry_set.set([e1, e2])</pre>
+
b.entry_set.set([e1, e2])</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
若能使用 <code>clear()</code> 方法, <code>entry_set</code> 中所有旧对象会在将可迭代集合(本例中是个列表)中的对象加入其中之前被删除。若 ''不能'' 使用 <code>clear()</code> 方法,添加新对象时不会删除旧对象。
+
如果 <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 的工作方式类似上面的 “反向” 一对多关联。
+
多对多关系的两端都可以自动访问另一端的 API。 API 的工作原理类似于上面的“向后”一对多关系。
  
不同点在为属性命名上:定义了 [[../../../ref/models/fields#django.db.models|<code>ManyToManyField</code>]] 的模型使用字段名作为属性名,而 “反向” 模型使用源模型名的小写形式,加上 <code>'_set'</code> (就像反向一对多关联一样)。
+
一个区别在于属性命名:定义 [[../../../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">
  
<pre>e = Entry.objects.get(id=3)
+
<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.</pre>
+
a.entry_set.all() # Returns all Entry objects for this Author.</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
和 [[../../../ref/models/fields#django.db.models|<code>ForeignKey</code>]] 一样, [[../../../ref/models/fields#django.db.models|<code>ManyToManyField</code>]] 能指定 [[../../../ref/models/fields#django.db.models.ManyToManyField|<code>related_name</code>]]。在上面的例子中,若 <code>Entry</code> 中的 [[../../../ref/models/fields#django.db.models|<code>ManyToManyField</code>]] 已指定了 <code>related_name='entries'</code>,随后每个 <code>Author</code> 实例会拥有一个 <code>entries</code> 属性,而不是 <code>entry_set</code>。
+
和[[../../../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>e</code> 和 <code>e2</code> 是 <code>Entry</code> 的实例,以下两种 <code>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">
  
<pre>a = Author.objects.get(id=5)
+
<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])</pre>
+
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|<code>OneToOneField</code>]],该模型的实例只需通过其属性就能访问关联对象。
+
一对一关系与多对一关系非常相似。 如果您在模型上定义 [[../../../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">
  
<pre>class EntryDetail(models.Model):
+
<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.</pre>
+
ed.entry # Returns the related Entry object.</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
不同点在于 “反向” 查询。一对一关联所关联的对象也能访问 [[../managers#django.db.models|<code>Manager</code>]] 对象,但这个 [[../managers#django.db.models|<code>Manager</code>]] 仅代表一个对象,而不是对象的集合:
+
区别在于“反向”查询。 一对一关系中的相关模型也可以访问 [[../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">
  
<pre>e = Entry.objects.get(id=2)
+
<syntaxhighlight lang="python">e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object</pre>
+
e.entrydetail # returns the related EntryDetail object</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
若未为关联关系指定对象,Django 会抛出 <code>DoesNotExist</code> 异常。
+
如果没有对象被分配给这个关系,Django 将引发一个 <code>DoesNotExist</code> 异常。
  
实例能通过为正向关联指定关联对象一样的方式指定给反向关联:
+
可以使用与分配正向关系相同的方式将实例分配给反向关系:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第1,625行: 第1,626行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>e.entrydetail = ed</pre>
+
<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 仅要求你在一端定义关联关系。
+
其他对象关系映射器要求您定义双方的关系。 Django 开发者认为这违反了 DRY(不要重复自己)原则,因此 Django 只要求您在一端定义关系。
  
但这是如何实现的呢,给你一个模型类,模型类并不知道是否有其它模型类关联它,直到其它模型类被加载?
+
但是,这怎么可能,因为在加载其他模型类之前,模型类不知道哪些其他模型类与其相关?
  
答案位于 [[../../../ref/applications#django.apps|<code>应用注册</code>]]。 Django 启动时,它会导入 [[../../../ref/settings#std-setting-INSTALLED_APPS|<code>INSTALLED_APPS</code>]] 列出的每个应用,和每个应用中的 <code>model</code> 模块。无论何时创建了一个新模型类,Django 为每个关联模型添加反向关联。若被关联的模型未被导入,Django 会持续追踪这些关联,并在关联模型被导入时添加关联关系。
+
答案在于 [[../../../ref/applications#django.apps|应用程序注册表]] 。 Django 启动时,它会导入 [[#id28|:setting:`INSTALLED_APPS`]] 中列出的每个应用程序,然后是每个应用程序内部的 <code>models</code> 模块。 每当创建新的模型类时,Django 都会向任何相关模型添加后向关系。 如果尚未导入相关模型,Django 会跟踪关系并在最终导入相关模型时添加它们。
  
出于这个原因,包含你所使用的所有模型的应用必须列在 [[../../../ref/settings#std-setting-INSTALLED_APPS|<code>INSTALLED_APPS</code>]] 中。否则,反向关联可能不会正常工作。
+
因此,在 [[#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>b</code>,其 <code>id=5</code>,以下三种查询是一样的:
+
例如,如果您有一个带有 <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">
  
<pre>Entry.objects.filter(blog=b) # Query using object instance
+
<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</pre>
+
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 ==
+
== 回退到原始 SQL ==
  
若你发现需要编写的 SQL 查询语句太过复杂,以至于 Django 的数据库映射无法处理,你可以回归手动编写 SQL。Django 针对编写原生 SQL 有几个选项;参考 [[../sql|<span class="doc">执行原生 SQL 查询</span>]]。
+
如果您发现自己需要编写一个 Django 的数据库映射器无法处理的过于复杂的 SQL 查询,您可以退回到手动编写 SQL。 Django 有几个用于编写原始 SQL 查询的选项; 请参阅 [[../sql|执行原始 SQL 查询]] 。
  
最后,Django 数据库层只是一种访问数据库的接口,理解这点非常重要。你也可以通过其它工具,编程语言或数据库框架访问数据库;Django 并没有对数据库数据库做啥独有的操作。
+
最后,重要的是要注意 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 应用程序:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    number_of_comments = models.IntegerField()
    number_of_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline

创建对象

为了在 Python 对象中表示数据库表数据,Django 使用了一个直观的系统:模型类表示数据库表,该类的实例表示数据库表中的特定记录。

要创建对象,请使用模型类的关键字参数将其实例化,然后调用 save() 将其保存到数据库中。

假设模型存在于文件 mysite/blog/models.py 中,这是一个示例:

>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

这会在幕后执行 INSERT SQL 语句。 在您明确调用 save() 之前,Django 不会访问数据库。

save() 方法没有返回值。

也可以看看

save() 采用了许多此处未描述的高级选项。 有关完整的详细信息,请参阅 save() 的文档。

要在单个步骤中创建和保存对象,请使用 create() 方法。


保存对对象的更改

要保存对数据库中已有对象的更改,请使用 save()

给定一个已经保存到数据库的 Blog 实例 b5,这个例子改变了它的名字并更新了它在数据库中的记录:

>>> b5.name = 'New name'
>>> b5.save()

这会在幕后执行 UPDATE SQL 语句。 在您明确调用 save() 之前,Django 不会访问数据库。

保存 ForeignKey 和 ManyToManyField 字段

更新 ForeignKey 字段的工作方式与保存普通字段的方式完全相同——将正确类型的对象分配给相关字段。 此示例更新 Entry 实例 entryblog 属性,假设 EntryBlog 的适当实例已保存到数据库中(所以我们可以在下面检索它们):

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

更新 ManyToManyField 的工作方式略有不同 - 在字段上使用 add() 方法向关系添加记录。 本示例将 Author 实例 joe 添加到 entry 对象:

>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)

要将多条记录一次性添加到 ManyToManyField,请在对 add() 的调用中包含多个参数,如下所示:

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

如果您尝试分配或添加错误类型的对象,Django 会抱怨。


检索对象

要从数据库中检索对象,请通过模型类上的 Manager 构造 QuerySet

QuerySet 表示数据库中的对象集合。 它可以有零个、一个或多个 过滤器 。 过滤器根据给定的参数缩小查询结果的范围。 在 SQL 术语中,QuerySet 等同于 SELECT 语句,过滤器是一个限制子句,例如 WHERELIMIT

您可以使用模型的 Manager 获得 QuerySet。 每个模型至少有一个Manager,默认叫做objects。 通过模型类直接访问它,如下所示:

>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
    ...
AttributeError: "Manager isn't accessible via Blog instances."

笔记

Managers 只能通过模型类访问,而不是从模型实例访问,以强制分离“表级”操作和“记录级”操作。


Manager 是模型的 QuerySets 的主要来源。 例如,Blog.objects.all() 返回一个 QuerySet,其中包含数据库中的所有 Blog 对象。

检索所有对象

从表中检索对象的最简单方法是获取所有对象。 为此,请在 Manager 上使用 all() 方法:

>>> all_entries = Entry.objects.all()

all() 方法返回数据库中所有对象的 QuerySet


使用过滤器检索特定对象

all() 返回的 QuerySet 描述了数据库表中的所有对象。 但是,通常您只需要选择完整对象集的一个子集。

要创建这样的子集,您需要优化初始 QuerySet,添加过滤条件。 优化 QuerySet 的两种最常见方法是:

filter(**kwargs)
返回一个新的 QuerySet 包含与给定查找参数匹配的对象。
exclude(**kwargs)
返回一个新的 QuerySet,其中包含 不匹配给定查找参数的对象。

查找参数(上述函数定义中的 **kwargs)应采用以下 字段查找 中描述的格式。

例如,要获取 2006 年博客条目的 QuerySet,请使用 filter(),如下所示:

Entry.objects.filter(pub_date__year=2006)

使用默认管理器类,它与:

Entry.objects.all().filter(pub_date__year=2006)

链接过滤器

细化 QuerySet 的结果本身就是一个 QuerySet,因此可以将细化链接在一起。 例如:

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime.date(2005, 1, 30)
... )

这需要数据库中所有条目的初始 QuerySet,添加一个过滤器,然后是一个排除,然后是另一个过滤器。 最终结果是一个 QuerySet,其中包含所有标题以“What”开头的条目,这些条目在 2005 年 1 月 30 日和当天之间发布。


过滤后的 QuerySet 是唯一的

每次细化 QuerySet 时,都会得到一个全新的 QuerySet,它与之前的 QuerySet 没有任何关系。 每次细化都会创建一个单独且不同的 QuerySet,可以存储、使用和重用。

例子:

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

这三个QuerySets是分开的。 第一个是基本的 QuerySet,其中包含所有包含以“What”开头的标题的条目。 第二个是第一个的子集,附加条件排除 pub_date 是今天或将来的记录。 第三个是第一个的子集,附加条件仅选择 pub_date 是今天或将来的记录。 初始的 QuerySet (q1) 不受细化过程的影响。


QuerySet很懒

QuerySets 是懒惰的——创建 QuerySet 的行为不涉及任何数据库活动。 您可以整天将过滤器堆叠在一起,并且在 QuerySet评估 之前,Django 不会实际运行查询。 看看这个例子:

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

虽然这看起来像是三个数据库命中,但实际上它只命中数据库一次,在最后一行 (print(q))。 通常,QuerySet 的结果不会从数据库中获取,直到您“请求”它们。 当您这样做时,QuerySet 通过访问数据库被 评估 。 有关评估发生的确切时间的更多详细信息,请参阅 查询集评估时


使用 get() 检索单个对象

filter() 总是会给你一个 QuerySet,即使只有一个对象与查询匹配 - 在这种情况下,它将是一个 QuerySet 包含一个元素。

如果您知道只有一个对象与您的查询匹配,您可以在直接返回对象的 Manager 上使用 get() 方法:

>>> one_entry = Entry.objects.get(pk=1)

您可以将任何查询表达式与 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 的 LIMITOFFSET 子句。

例如,这将返回前 5 个对象 (LIMIT 5):

>>> Entry.objects.all()[:5]

这将返回第六到第十个对象 (OFFSET 5 LIMIT 5):

>>> Entry.objects.all()[5:10]

负索引(即 不支持 Entry.objects.all()[-1])。

通常,切片 QuerySet 会返回一个新的 QuerySet——它不会评估查询。 一个例外是,如果您使用 Python 切片语法的“step”参数。 例如,这实际上会执行查询以返回前 10 个每 对象的列表:

>>> Entry.objects.all()[:10:2]

由于其工作方式的模糊性,禁止对切片查询集进行进一步过滤或排序。

要检索 单个 对象而不是列表(例如 SELECT foo FROM bar LIMIT 1),使用索引而不是切片。 例如,在按标题按字母顺序排列条目后,这将返回数据库中的第一个 Entry

>>> Entry.objects.order_by('headline')[0]

这大致相当于:

>>> Entry.objects.order_by('headline')[0:1].get()

但是请注意,如果没有对象符合给定条件,第一个将引发 IndexError 而第二个将引发 DoesNotExist。 有关更多详细信息,请参阅 get()


字段查找

字段查找是您指定 SQL WHERE 子句内容的方式。 它们被指定为 QuerySet 方法 filter()exclude()get() 的关键字参数。

基本查找关键字参数采用 field__lookuptype=value 的形式。 (这是一个双下划线)。 例如:

>>> Entry.objects.filter(pub_date__lte='2006-01-01')

转换(大致)为以下 SQL:

SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';

这怎么可能

Python 能够定义接受任意名称-值参数的函数,其名称和值在运行时进行评估。 更多信息参见Python官方教程中的tut-keywordargs


查找中指定的字段必须是模型字段的名称。 但有一个例外,在 ForeignKey 的情况下,您可以指定后缀为 _id 的字段名称。 在这种情况下, value 参数应该包含外部模型主键的原始值。 例如:

>>> Entry.objects.filter(blog_id=4)

如果传递无效的关键字参数,查找函数将引发 TypeError

数据库 API 支持大约二打查找类型; 完整的参考可以在 字段查找参考 中找到。 为了让您了解可用的内容,以下是您可能会使用的一些更常见的查找:

:查找:`精确`

“精确”匹配。 例如:

>>> Entry.objects.get(headline__exact="Cat bites dog")

将按照以下方式生成 SQL:

SELECT ... WHERE headline = 'Cat bites dog';

如果您不提供查找类型——也就是说,如果您的关键字参数不包含双下划线——则假定查找类型为 exact

例如,以下两个语句是等效的:

>>> Blog.objects.get(id__exact=14)  # Explicit form
>>> Blog.objects.get(id=14)         # __exact is implied

这是为了方便起见,因为 exact 查找是常见情况。

:lookup:`iexact`

不区分大小写的匹配。 所以,查询:

>>> Blog.objects.get(name__iexact="beatles blog")

将匹配标题为 "Beatles Blog""beatles blog" 甚至 "BeAtlES blOG"Blog

:查找:`包含`

区分大小写的包含测试。 例如:

Entry.objects.get(headline__contains='Lennon')

大致翻译成这个 SQL:

SELECT ... WHERE headline LIKE '%Lennon%';

请注意,这将匹配标题 'Today Lennon honored' 但不匹配 'today lennon honored'

还有一个不区分大小写的版本,:lookup:`icontains`

:lookup:`startswith`, :lookup:`endswith`

分别开始于和结束于搜索。 还有一些不区分大小写的版本,称为 :lookup:`istartswith`:lookup:`iendswith`

同样,这只会划伤表面。 完整的参考可以在 字段查找参考 中找到。


跨越关系的查找

Django 提供了一种强大而直观的方式来“跟踪”查找中的关系,在幕后自动为您处理 SQL JOIN。 要跨越关系,请使用跨模型的相关字段的字段名称,用双下划线分隔,直到到达所需的字段。

此示例检索所有具有 Blogname'Beatles Blog'Entry 对象:

>>> Entry.objects.filter(blog__name='Beatles Blog')

这个跨越可以像你想要的那样深。

它也向后工作。 要引用“反向”关系,请使用模型的小写名称。

此示例检索所有 Blog 对象,这些对象至少有一个 Entry,其 headline 包含 'Lennon'

>>> Blog.objects.filter(entry__headline__contains='Lennon')

如果你在多个关系之间进行过滤并且其中一个中间模型没有满足过滤条件的值,Django 会将其视为有空(所有值都是 NULL),但有效,对象那里。 所有这一切都意味着不会引发任何错误。 例如,在这个过滤器中:

Blog.objects.filter(entry__authors__name='Lennon')

(如果有相关的 Author 模型),如果没有与条目关联的 author,则将被视为没有附加 name,而不是由于缺少 author 而引发错误。 通常这正是您希望发生的事情。 唯一可能令人困惑的情况是,如果您使用 :lookup:`isnull`。 因此:

Blog.objects.filter(entry__authors__name__isnull=True)

将返回在 author 上有空 nameBlog 对象,以及在 entry 上有空 author 的对象。 如果你不想要那些后面的对象,你可以写:

Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)

跨越多值关系

当您根据 ManyToManyField 或反向 ForeignKey 过滤对象时,您可能会对两种不同类型的过滤器感兴趣。 考虑 Blog/Entry 关系(BlogEntry 是一对多关系)。 我们可能有兴趣查找标题中包含 “Lennon” 且发表于 2008 年的条目的博客。 或者,我们可能想要查找标题中包含 “Lennon” 条目以及 2008 年发布的条目的博客。 由于有多个条目与单个 Blog 相关联,因此这两种查询都是可能的,并且在某些情况下是有意义的。

ManyToManyField 也会出现相同类型的情况。 例如,如果 Entry 有一个名为 tagsManyToManyField,我们可能想要找到链接到名为 “music”的标签的条目]“bands” 或者我们可能想要一个包含名称为 “music” 和状态为 “public” 的标签的条目。

为了处理这两种情况,Django 有处理 filter() 调用的一致方式。 单个 filter() 调用中的所有内容都会同时应用以过滤出符合所有这些要求的项目。 连续的 filter() 调用进一步限制了对象集,但对于多值关系,它们适用于链接到主模型的任何对象,不一定是那些由较早的 过滤器选择的对象() 调用。

这可能听起来有点令人困惑,所以希望有一个例子可以澄清。 要选择标题中同时包含 “Lennon” 条目且发布于 2008 年的所有博客(满足两个条件的同一条目),我们将编写:

Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)

要选择标题中包含 “Lennon” 的条目以及 2008 年发布的条目的所有博客,我们将编写:

Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)

假设只有一个博客同时包含 “Lennon” 和 2008 年的条目,但 2008 年的条目都没有包含 “Lennon”。 第一个查询不会返回任何博客,但第二个查询会返回那个博客。

在第二个示例中,第一个过滤器将查询集限制为链接到标题中带有 “Lennon” 的条目的所有博客。 第二个过滤器将博客集 进一步 限制为那些也链接到 2008 年发布的条目的博客。 第二个过滤器选择的条目可能与第一个过滤器中的条目相同,也可能不同。 我们使用每个过滤器语句过滤 Blog 项,而不是 Entry 项。

笔记

filter() 对于跨越多值关系的查询的行为,如上所述,对于 exclude() 没有等效实现。 相反,单个 exclude() 调用中的条件不一定指向同一个项目。

例如,以下查询将排除包含 both 条目的博客,其中 “Lennon” 在标题 条目发布于 2008 年:

Blog.objects.exclude(
    entry__headline__contains='Lennon',
    entry__pub_date__year=2008,
)

但是,与使用 filter() 时的行为不同,这不会基于满足这两个条件的条目来限制博客。 为了做到这一点,即 要选择所有不包含 2008 年发布的 “Lennon” 发布的条目的博客,您需要进行两个查询:

Blog.objects.exclude(
    entry__in=Entry.objects.filter(
        headline__contains='Lennon',
        pub_date__year=2008,
    ),
)

过滤器可以引用模型上的字段

在到目前为止给出的示例中,我们构建了过滤器,将模型字段的值与常量进行比较。 但是,如果您想将模型字段的值与同一模型上的另一个字段的值进行比较,该怎么办?

Django 提供了 F 表达式 来允许这样的比较。 F() 的实例充当对查询中模型字段的引用。 然后可以在查询过滤器中使用这些引用来比较同一模型实例上两个不同字段的值。

例如,要查找评论数多于 pingback 的所有博客条目的列表,我们构造一个 F() 对象来引用 pingback 计数,并在查询中使用该 F() 对象:

>>> from django.db.models import F
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))

Django 支持对 F() 对象使用加法、减法、乘法、除法、取模和幂运算,包括常量和其他 F() 对象。 要查找评论数超过 pingback 的 的所有博客条目,我们修改查询:

>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)

要查找条目评分小于 pingback 计数和评论计数之和的所有条目,我们将发出查询:

>>> Entry.objects.filter(rating__lt=F('number_of_comments') + F('number_of_pingbacks'))

您还可以使用双下划线表示法来跨越 F() 对象中的关系。 带有双下划线的 F() 对象将引入访问相关对象所需的任何连接。 例如,要检索作者姓名与博客名称相同的所有条目,我们可以发出查询:

>>> Entry.objects.filter(authors__name=F('blog__name'))

对于日期和日期/时间字段,您可以添加或减去 timedelta 对象。 以下将返回在发布后超过 3 天修改的所有条目:

>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

F() 对象支持 .bitand().bitor().bitrightshift().bitleftshift() 的按位运算。 例如:

>>> F('somefield').bitand(16)

pk 查找快捷方式

为方便起见,Django 提供了一个 pk 查找快捷方式,它代表“主键”。

在示例 Blog 模型中,主键是 id 字段,因此这三个语句是等效的:

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

pk 的使用不限于 __exact 查询 - 任何查询词都可以与 pk 组合以对模型的主键执行查询:

# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])

# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)

pk 查找也适用于连接。 例如,这三个语句是等效的:

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

在 LIKE 语句中转义百分号和下划线

等同于 LIKE SQL 语句的字段查找(iexactcontainsicontainsstartswithistartswithendswithiendswith) 将自动转义 LIKE 语句中使用的两个特殊字符 - 百分号和下划线。 (在 LIKE 语句中,百分号表示多字符通配符,下划线表示单字符通配符。)

这意味着事情应该直观地工作,所以抽象不会泄漏。 例如,要检索包含百分号的所有条目,请将百分号用作任何其他字符:

>>> Entry.objects.filter(headline__contains='%')

Django 会为您处理报价; 生成的 SQL 将如下所示:

SELECT ... WHERE headline LIKE '%\%%';

下划线也是如此。 百分号和下划线都为您透明处理。


缓存和 QuerySets

每个 QuerySet 都包含一个缓存以最小化数据库访问。 了解它的工作原理将使您能够编写最有效的代码。

在新创建的 QuerySet 中,缓存为空。 第一次计算 QuerySet 时——因此,发生数据库查询——Django 将查询结果保存在 QuerySet 的缓存中并返回已明确请求的结果(例如,下一个元素,如果 QuerySet 正在迭代)。 QuerySet 的后续评估重用缓存的结果。

记住这种缓存行为,因为如果你没有正确使用你的 QuerySets,它可能会咬你。 例如,以下将创建两个 QuerySet ,评估它们,然后将它们丢弃:

>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])

这意味着相同的数据库查询将执行两次,有效地使您的数据库负载加倍。 此外,这两个列表可能不包含相同的数据库记录,因为 Entry 可能已在两个请求之间的瞬间添加或删除。

为了避免这个问题,保存 QuerySet 并重用它:

>>> 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.

当 QuerySet 未缓存时

查询集并不总是缓存它们的结果。 当仅评估查询集的 part 时,会检查缓存,但如果未填充,则不会缓存后续查询返回的项目。 具体来说,这意味着 使用数组切片或索引限制查询集 不会填充缓存。

例如,重复获取查询集对象中的某个索引,每次都会查询数据库:

>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again

但是,如果已经评估了整个查询集,则会改为检查缓存:

>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache

以下是其他操作的一些示例,这些操作将导致评估整个查询集并因此填充缓存:

>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)

笔记

简单地打印查询集不会填充缓存。 这是因为对 __repr__() 的调用只返回整个查询集的一部分。


Q 对象的复杂查找

关键字参数查询 - 在 filter() 等中。 – 被“AND”在一起。 如果您需要执行更复杂的查询(例如,使用 OR 语句的查询),您可以使用 Q 对象

Q 对象 (django.db.models.Q) 是用于封装关键字参数集合的对象。 这些关键字参数在上面的“字段查找”中指定。

例如,这个 Q 对象封装了单个 LIKE 查询:

from django.db.models import Q
Q(question__startswith='What')

Q 对象可以使用 &| 运算符进行组合。 当一个运算符用于两个 Q 对象时,它会产生一个新的 Q 对象。

例如,此语句生成一个 Q 对象,表示两个 "question__startswith" 查询的“或”:

Q(question__startswith='Who') | Q(question__startswith='What')

这等效于以下 SQL WHERE 子句:

WHERE question LIKE 'Who%' OR question LIKE 'What%'

您可以通过将 Q 对象与 &| 运算符组合并使用括号分组来组合任意复杂的语句。 此外,Q 对象可以使用 ~ 运算符进行否定,允许组合查找,将普通查询和否定 (NOT) 查询结合起来:

Q(question__startswith='Who') | ~Q(pub_date__year=2005)

每个采用关键字参数的查找函数(例如 filter(), exclude(), get()) 也可以传递一个或多个 Q 对象作为位置(未命名) ) 论点。 如果您向查找函数提供多个 Q 对象参数,则这些参数将被“与”在一起。 例如:

Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

... 大致翻译成 SQL:

SELECT * from polls WHERE question LIKE 'Who%'
    AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

查找函数可以混合使用 Q 对象和关键字参数。 提供给查找函数的所有参数(无论是关键字参数还是 Q 对象)都被“AND”在一起。 但是,如果提供了 Q 对象,则它必须在任何关键字参数的定义之前。 例如:

Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who',
)

... 将是一个有效的查询,相当于前面的例子; 但:

# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

……无效。

也可以看看

:source:`OR 查找示例 ` 在 Django 的单元测试中显示了一些可能的用途Q .


比较对象

要比较两个模型实例,请使用标准 Python 比较运算符,即双等号:==。 在幕后,它比较了两个模型的主键值。

使用上面的 Entry 示例,以下两个语句是等效的:

>>> some_entry == other_entry
>>> some_entry.id == other_entry.id

如果模型的主键不是 id,没问题。 比较将始终使用主键,无论它叫什么。 例如,如果模型的主键字段称为 name,则这两个语句是等效的:

>>> some_obj == other_obj
>>> some_obj.name == other_obj.name

删除对象

删除方法方便地命名为 delete()。 此方法立即删除对象并返回删除的对象数和包含每个对象类型删除数的字典。 例子:

>>> e.delete()
(1, {'weblog.Entry': 1})

您还可以批量删除对象。 每个 QuerySet 都有一个 delete() 方法,该方法删除该 QuerySet 的所有成员。

例如,这将删除 pub_date 年为 2005 的所有 Entry 对象:

>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})

请记住,只要有可能,这将完全在 SQL 中执行,因此在此过程中不一定会调用各个对象实例的 delete() 方法。 如果您在模型类上提供了自定义 delete() 方法并希望确保它被调用,您将需要“手动”删除该模型的实例(例如,通过迭代 QuerySet 并分别在每个对象上调用 delete()),而不是使用 QuerySet 的批量 delete() 方法。

当 Django 删除一个对象时,默认情况下它会模拟 SQL 约束 ON DELETE CASCADE 的行为——换句话说,任何具有指向要删除的对象的外键的对象都将被删除。 例如:

b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()

此级联行为可通过 ForeignKeyon_delete 参数进行自定义。

请注意, delete() 是唯一未在 Manager 本身上公开的 QuerySet 方法。 这是一种安全机制,可防止您意外请求 Entry.objects.delete(),并删除 all 条目。 如果您 do 想要删除所有对象,那么您必须明确请求一个完整的查询集:

Entry.objects.all().delete()

复制模型实例

尽管没有用于复制模型实例的内置方法,但可以轻松地创建新实例并复制所有字段的值。 在最简单的情况下,您可以将 pk 设置为 None。 使用我们的博客示例:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

如果您使用继承,事情会变得更加复杂。 考虑 Blog 的子类:

class ThemeBlog(Blog):
    theme = models.CharField(max_length=200)

django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save() # django_blog.pk == 3

由于继承的工作方式,您必须将 pkid 都设置为 None:

django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4

此过程不会复制不属于模型数据库表的关系。 例如,EntryManyToManyFieldAuthor。 复制条目后,您必须为新条目设置多对多关系:

entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors.set(old_authors)

对于 OneToOneField,您必须复制相关对象并将其分配给新对象的字段,以避免违反一对一唯一约束。 例如,假设 entry 已经如上复制:

detail = EntryDetail.objects.all()[0]
detail.pk = None
detail.entry = entry
detail.save()

一次更新多个对象

有时,您希望为 QuerySet 中的所有对象将字段设置为特定值。 您可以使用 update() 方法执行此操作。 例如:

# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')

使用此方法只能设置非关系字段和 ForeignKey 字段。 要更新非关系字段,请将新值作为常量提供。 要更新 ForeignKey 字段,请将新值设置为要指向的新模型实例。 例如:

>>> b = Blog.objects.get(pk=1)

# Change every Entry so that it belongs to this Blog.
>>> Entry.objects.all().update(blog=b)

update() 方法立即应用并返回查询匹配的行数(如果某些行已经具有新值,则可能不等于更新的行数)。 QuerySet 更新的唯一限制是它只能访问一个数据库表:模型的主表。 您可以根据相关字段进行过滤,但您只能更新模型主表中的列。 例子:

>>> b = Blog.objects.get(pk=1)

# Update all the headlines belonging to this Blog.
>>> Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same')

请注意,update() 方法直接转换为 SQL 语句。 这是直接更新的批量操作。 它不会在您的模型上运行任何 save() 方法,也不会发出 pre_savepost_save 信号(这是调用 save() 的结果) ),或使用 auto_now 字段选项。 如果您想将每个项目保存在 QuerySet 中并确保在每个实例上调用 save() 方法,您不需要任何特殊函数来处理它。 遍历它们并调用 save()

for item in my_queryset:
    item.save()

调用 update 还可以使用 F 表达式 根据模型中另一个字段的值更新一个字段。 这对于根据当前值递增计数器特别有用。 例如,要增加博客中每个条目的 pingback 计数:

>>> Entry.objects.all().update(number_of_pingbacks=F('number_of_pingbacks') + 1)

但是,与 filter 和 exclude 子句中的 F() 对象不同,在更新中使用 F() 对象时不能引入连接——您只能引用正在更新的模型的本地字段。 如果您尝试使用 F() 对象引入连接,则会引发 FieldError

# This will raise a FieldError
>>> Entry.objects.update(headline=F('blog__name'))

回退到原始 SQL

如果您发现自己需要编写一个 Django 的数据库映射器无法处理的过于复杂的 SQL 查询,您可以退回到手动编写 SQL。 Django 有几个用于编写原始 SQL 查询的选项; 请参阅 执行原始 SQL 查询

最后,重要的是要注意 Django 数据库层只是数据库的接口。 您可以通过其他工具、编程语言或数据库框架访问您的数据库; 您的数据库没有任何特定于 Django 的内容。