“Django/docs/2.2.x/ref/models/querysets”的版本间差异
(autoload) |
小 (Page commit) |
||
第1行: | 第1行: | ||
+ | {{DISPLAYTITLE:QuerySet API 参考 — Django 文档}} | ||
<div id="queryset-api-reference" class="section"> | <div id="queryset-api-reference" class="section"> | ||
− | = | + | = QuerySet API 参考 = |
− | + | 本文档描述了 <code>QuerySet</code> API 的详细信息。 它建立在 [[../../../topics/db/models|模型]] 和 [[../../../topics/db/queries|数据库查询]] 指南中提供的材料之上,因此您可能希望在阅读本文之前先阅读并理解这些文档。 | |
− | + | 在本参考文献中,我们将使用 [[../../../topics/db/queries|数据库查询指南]] 中介绍的 [[../../../topics/db/queries#queryset-model-example|示例网络日志模型]] 。 | |
<div id="when-querysets-are-evaluated" class="section"> | <div id="when-querysets-are-evaluated" class="section"> | ||
<span id="id1"></span> | <span id="id1"></span> | ||
− | == | + | == 当评估 QuerySets 时 == |
− | + | 在内部,<code>QuerySet</code> 可以被构造、过滤、切片,并且通常在不实际访问数据库的情况下传递。 在您执行某些操作来评估查询集之前,实际上不会发生数据库活动。 | |
− | |||
− | |||
− | + | 您可以通过以下方式评估 <code>QuerySet</code>: | |
<ul> | <ul> | ||
− | <li><p>'''Iteration.''' | + | <li><p>'''Iteration.''' <code>QuerySet</code> 是可迭代的,它会在您第一次对其进行迭代时执行其数据库查询。 例如,这将打印数据库中所有条目的标题:</p> |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">for e in Entry.objects.all(): |
− | print(e.headline)</ | + | print(e.headline)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>注意:如果您只想确定是否存在至少一个结果,请不要使用此选项。 使用 [[#django.db.models.query.QuerySet.exists|exists()]] 效率更高。</p></li> |
− | + | <li><p>'''切片。''' 如 [[../../../topics/db/queries#limiting-querysets|Limiting QuerySets]] 中所述,可以使用 Python 的数组切片语法对 <code>QuerySet</code> 进行切片。 切片一个未赋值的 <code>QuerySet</code> 通常会返回另一个未赋值的 <code>QuerySet</code>,但如果使用切片语法的“step”参数,Django 将执行数据库查询,并返回一个列表。 对已求值的 <code>QuerySet</code> 进行切片也会返回一个列表。</p> | |
− | <li><p>''' | + | <p>另请注意,即使切片未计算的 <code>QuerySet</code> 返回另一个未计算的 <code>QuerySet</code>,也不允许进一步修改(例如,添加更多过滤器或修改排序),因为这不能很好地转换为 SQL它也不会有明确的含义。</p></li> |
− | + | <li><p>'''Pickling/Caching.''' [[#pickling-querysets|pickling QuerySets]] 时所涉及的详细信息,请参阅以下部分。 就本节而言,重要的是从数据库中读取结果。</p></li> | |
− | <code>QuerySet</code> | + | <li><p>'''repr().''' 当您调用 <code>repr()</code> 时,会评估 <code>QuerySet</code>。 这是为了方便 Python 交互式解释器,因此您可以在交互式使用 API 时立即看到结果。</p></li> |
− | + | <li><p>'''len().''' 当您调用 <code>len()</code> 时,会评估 <code>QuerySet</code>。 正如您所料,这将返回结果列表的长度。</p> | |
− | + | <p>注意:如果您只需要确定集合中的记录数(而不需要实际对象),使用 SQL 的 <code>SELECT COUNT(*)</code> 在数据库级别处理计数会更有效。 正是出于这个原因,Django 提供了一个 [[#django.db.models.query.QuerySet.count|count()]] 方法。</p></li> | |
− | + | <li><p>'''list().''' 通过调用 <code>list()</code> 强制评估 <code>QuerySet</code>。 例如:</p> | |
− | <p> | ||
− | |||
− | |||
− | |||
− | <li><p>'''Pickling/Caching.''' | ||
− | |||
− | |||
− | <li><p>'''repr().''' | ||
− | |||
− | |||
− | <li><p>'''len().''' | ||
− | |||
− | <p> | ||
− | |||
− | |||
− | [[#django.db.models.query.QuerySet.count| | ||
− | <li><p>'''list().''' | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">entry_list = list(Entry.objects.all())</syntaxhighlight> |
</div> | </div> | ||
</div></li> | </div></li> | ||
− | <li><p>'''bool().''' | + | <li><p>'''bool().''' 在布尔上下文中测试 <code>QuerySet</code>,例如使用 <code>bool()</code>、<code>or</code>、<code>and</code> 或 <code>if</code> 语句,将导致执行查询。 如果至少有一个结果,则<code>QuerySet</code>为<code>True</code>,否则为<code>False</code>。 例如:</p> |
− | <code>bool()</code> | ||
− | |||
− | <code>True</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">if Entry.objects.filter(headline="Test"): |
− | print( | + | print("There is at least one Entry with the headline Test")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>注意:如果您只想确定是否至少存在一个结果(并且不需要实际对象),则使用 [[#django.db.models.query.QuerySet.exists|exists()]] 更有效。</p></li></ul> |
− | |||
<div id="pickling-querysets" class="section"> | <div id="pickling-querysets" class="section"> | ||
<span id="id2"></span> | <span id="id2"></span> | ||
− | === | + | === 酸洗 QuerySets === |
− | + | 如果你 <code>pickle</code> a <code>QuerySet</code>,这将强制所有结果在酸洗之前加载到内存中。 酸洗通常用作缓存的前兆,当重新加载缓存的查询集时,您希望结果已经存在并可供使用(从数据库中读取可能需要一些时间,违背了缓存的目的)。 这意味着当你 unpickle 一个 <code>QuerySet</code> 时,它包含它被pickle 时的结果,而不是当前在数据库中的结果。 | |
− | |||
− | |||
− | |||
− | |||
− | <code>QuerySet</code> | ||
− | |||
− | + | 如果您只想在稍后从数据库中腌制必要的信息以重新创建 <code>QuerySet</code>,请腌制 <code>QuerySet</code> 的 <code>query</code> 属性。 然后,您可以使用如下代码重新创建原始 <code>QuerySet</code>(不加载任何结果): | |
− | <code>QuerySet</code> | ||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第106行: | 第72行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> import pickle |
− | + | >>> query = pickle.loads(s) # Assuming 's' is the pickled string. | |
− | + | >>> qs = MyModel.objects.all() | |
− | + | >>> qs.query = query # Restore the original 'query'.</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>query</code> 属性是一个不透明的对象。 它代表查询构造的内部结构,而不是公共 API 的一部分。 但是,按照此处所述对属性的内容进行pickle 和unpickle 是安全的(并且完全受支持)。 | |
− | |||
− | |||
− | |||
<div class="admonition-you-can-t-share-pickles-between-versions admonition"> | <div class="admonition-you-can-t-share-pickles-between-versions admonition"> | ||
− | + | 您不能在版本之间共享泡菜 | |
− | + | <code>QuerySets</code> 的泡菜仅对用于生成它们的 Django 版本有效。 如果您使用 Django 版本 N 生成 pickle,则无法保证使用 Django N+1 版本可以读取 pickle。 不应将泡菜用作长期存档策略的一部分。 | |
− | |||
− | |||
− | Django | ||
− | |||
− | + | 由于pickle 兼容性错误可能难以诊断,例如静默损坏的对象,因此当您尝试在与pickle 版本不同的Django 版本中对查询集进行unpickle 时,会引发<code>RuntimeWarning</code>。 | |
− | |||
− | |||
− | |||
第143行: | 第99行: | ||
<span id="id3"></span> | <span id="id3"></span> | ||
− | == | + | == QuerySet API == |
− | + | 这是 <code>QuerySet</code> 的正式声明: | |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">QuerySet</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">model</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">query</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">using</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">hints</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>通常,当您与 <code>QuerySet</code> 交互时,您会通过 [[../../../topics/db/queries#chaining-filters|链接过滤器]] 来使用它。 为了完成这项工作,大多数 <code>QuerySet</code> 方法返回新的查询集。 本节稍后将详细介绍这些方法。</p> |
− | [[../../../topics/db/queries#chaining-filters| | + | <p><code>QuerySet</code> 类有两个可用于自省的公共属性:</p> |
− | <code>QuerySet</code> | ||
− | |||
− | <p> | ||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">ordered</span></span></dt> |
− | <dd><p><code>True</code> | + | <dd><p><code>True</code> 如果订购了 <code>QuerySet</code> — 即 在模型上有一个 [[#django.db.models.query.QuerySet.order_by|order_by()]] 子句或默认排序。 <code>False</code> 否则。</p></dd></dl> |
− | [[#django.db.models.query.QuerySet.order_by| | ||
− | <code>False</code> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">db</span></span></dt> |
− | <dd><p> | + | <dd><p>如果现在执行这个查询,将使用的数据库。</p></dd></dl> |
<div class="admonition note"> | <div class="admonition note"> | ||
<p>注解</p> | <p>注解</p> | ||
− | <p> | + | <p>[[#django.db.models.query.QuerySet|QuerySet]] 的 <code>query</code> 参数存在以便专门的查询子类可以重建内部查询状态。 该参数的值是该查询状态的不透明表示,不是公共 API 的一部分。 简单地说:如果你需要问,你不需要使用它。</p> |
− | |||
− | |||
− | |||
− | |||
</div></dd></dl> | </div></dd></dl> | ||
第178行: | 第124行: | ||
<div id="methods-that-return-new-querysets" class="section"> | <div id="methods-that-return-new-querysets" class="section"> | ||
− | === | + | === 返回新 QuerySet 的方法 === |
− | Django | + | Django 提供了一系列 <code>QuerySet</code> 细化方法,可以修改 <code>QuerySet</code> 返回的结果类型或其 SQL 查询的执行方式。 |
− | |||
− | |||
<div id="filter" class="section"> | <div id="filter" class="section"> | ||
− | ==== | + | ==== filter() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">filter</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回包含与给定查找参数匹配的对象的新 <code>QuerySet</code>。 | |
− | |||
− | + | 查找参数 (<code>**kwargs</code>) 应采用以下 [[#id10|字段查找]] 中描述的格式。 多个参数通过底层 SQL 语句中的 <code>AND</code> 连接。 | |
− | [[# | ||
− | |||
− | + | 如果您需要执行更复杂的查询(例如,使用 <code>OR</code> 语句的查询),您可以使用 [[#django.db.models.Q|Q 对象]] 。 | |
− | |||
第205行: | 第145行: | ||
<div id="exclude" class="section"> | <div id="exclude" class="section"> | ||
− | ==== | + | ==== exclude() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">exclude</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回一个新的 <code>QuerySet</code>,其中包含 ''与'' 不匹配给定查找参数的对象。 | |
− | |||
− | + | 查找参数 (<code>**kwargs</code>) 应采用以下 [[#id10|字段查找]] 中描述的格式。 多个参数通过底层 SQL 语句中的 <code>AND</code> 连接起来,整个内容包含在一个 <code>NOT()</code> 中。 | |
− | [[# | ||
− | |||
− | + | 此示例排除 <code>pub_date</code> 晚于 2005-1-3 且 <code>headline</code> 为“Hello”的所有条目: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第224行: | 第160行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在 SQL 术语中,计算结果为: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第235行: | 第171行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... |
− | WHERE NOT (pub_date | + | WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此示例排除 <code>pub_date</code> 晚于 2005-1-3 或标题为“您好”的所有条目: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第248行: | 第183行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在 SQL 术语中,计算结果为: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第259行: | 第194行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... |
− | WHERE NOT pub_date | + | WHERE NOT pub_date > '2005-1-3' |
− | AND NOT headline = 'Hello'</ | + | AND NOT headline = 'Hello'</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意,第二个例子的限制性更强。 | |
− | + | 如果您需要执行更复杂的查询(例如,使用 <code>OR</code> 语句的查询),您可以使用 [[#django.db.models.Q|Q 对象]] 。 | |
− | |||
第275行: | 第209行: | ||
<div id="annotate" class="section"> | <div id="annotate" class="section"> | ||
− | ==== | + | ==== annotate() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">annotate</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 使用提供的 [[../expressions|查询表达式]] 列表注释 <code>QuerySet</code> 中的每个对象。 表达式可以是一个简单的值、对模型(或任何相关模型)上的字段的引用,或者是对与模型中的对象相关的对象进行计算的聚合表达式(平均值、总和等)。 <code>QuerySet</code>。 | |
− | |||
− | |||
− | |||
− | |||
− | + | <code>annotate()</code> 的每个参数都是一个注释,将添加到返回的 <code>QuerySet</code> 中的每个对象。 | |
− | |||
− | + | Django 提供的聚合函数在下面的 [[#id41|Aggregation Functions]] 中描述。 | |
− | |||
− | + | 使用关键字参数指定的注释将使用关键字作为注释的别名。 匿名参数将根据聚合函数的名称和正在聚合的模型字段为它们生成一个别名。 只有引用单个字段的聚合表达式才能是匿名参数。 其他所有内容都必须是关键字参数。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 例如,如果您正在操作一个博客列表,您可能想要确定每个博客中已经创建了多少条目: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第306行: | 第228行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import Count |
− | + | >>> q = Blog.objects.annotate(Count('entry')) | |
# The name of the first blog | # The name of the first blog | ||
− | + | >>> q[0].name | |
'Blogasaurus' | 'Blogasaurus' | ||
# The number of entries on the first blog | # The number of entries on the first blog | ||
− | + | >>> q[0].entry__count | |
− | 42</ | + | 42</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>Blog</code> 模型本身并没有定义 <code>entry__count</code> 属性,但是通过使用关键字参数指定聚合函数,您可以控制注解的名称: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第326行: | 第246行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> q = Blog.objects.annotate(number_of_entries=Count('entry')) |
# The number of entries on the first blog, using the name provided | # The number of entries on the first blog, using the name provided | ||
− | + | >>> q[0].number_of_entries | |
− | 42</ | + | 42</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有关聚合的深入讨论,请参阅 [[../../../topics/db/aggregation|有关聚合的主题指南]] 。 | |
− | |||
第341行: | 第260行: | ||
<div id="order-by" class="section"> | <div id="order-by" class="section"> | ||
− | ==== | + | ==== order_by() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">order_by</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">fields</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 默认情况下,<code>QuerySet</code> 返回的结果按模型的 <code>Meta</code> 中的 <code>ordering</code> 选项给出的排序元组排序。 您可以使用 <code>order_by</code> 方法在每个 <code>QuerySet</code> 的基础上覆盖它。 | |
− | |||
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第356行: | 第273行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 上面的结果将按 <code>pub_date</code> 降序排列,然后按 <code>headline</code> 升序排列。 <code>"-pub_date"</code>前面的负号表示''降序''。 升序是隐含的。 要随机排序,请使用 <code>"?"</code>,如下所示: | |
− | <code>headline</code> | ||
− | '' | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第370行: | 第284行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.order_by('?')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 注意:<code>order_by('?')</code> 查询可能既昂贵又缓慢,具体取决于您使用的数据库后端。 | |
− | |||
− | + | 要按不同模型中的字段排序,请使用与跨模型关系查询时相同的语法。 即,字段名称,后跟双下划线 (<code>__</code>),后跟新模型中的字段名称,依此类推,适用于您想要加入的模型。 例如: | |
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第387行: | 第297行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.order_by('blog__name', 'headline')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您尝试按与另一个模型相关的字段排序,Django 将使用相关模型的默认排序,或者如果未指定 [[../options#django.db.models.Options|Meta.ordering]],则按相关模型的主键排序。 例如,由于 <code>Blog</code> 模型没有指定默认排序: | |
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第401行: | 第308行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.order_by('blog')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | ……等同于: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第412行: | 第319行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.order_by('blog__id')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果 <code>Blog</code> 有 <code>ordering = ['name']</code>,那么第一个查询集将等同于: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第424行: | 第330行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.order_by('blog__name')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您还可以通过在表达式上调用 [[../expressions#django.db.models.Expression|asc()]] 或 [[../expressions#django.db.models.Expression|desc()]] 来按 [[../expressions|查询表达式]] 排序: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第437行: | 第341行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.order_by(Coalesce('summary', 'headline').desc())</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | [[../expressions#django.db.models.Expression| | + | [[../expressions#django.db.models.Expression|asc()]] 和 [[../expressions#django.db.models.Expression|desc()]] 具有控制空值排序方式的参数(<code>nulls_first</code> 和 <code>nulls_last</code>)。 |
− | |||
− | + | 如果您还使用 [[#django.db.models.query.QuerySet.distinct|distinct()]],则在按相关模型中的字段排序时要小心。 有关相关模型排序如何改变预期结果的说明,请参阅 [[#django.db.models.query.QuerySet.distinct|distinct()]] 中的注释。 | |
− | [[#django.db.models.query.QuerySet.distinct| | ||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
第453行: | 第354行: | ||
注解 | 注解 | ||
− | + | 允许指定多值字段以对结果进行排序(例如,[[../fields#django.db.models|ManyToManyField]] 字段,或 [[../fields#django.db.models|ForeignKey]] 字段的反向关系)。 | |
− | |||
− | |||
− | + | 考虑这个案例: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第463行: | 第362行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">class Event(Model): |
parent = models.ForeignKey( | parent = models.ForeignKey( | ||
'self', | 'self', | ||
第471行: | 第370行: | ||
date = models.DateField() | date = models.DateField() | ||
− | Event.objects.order_by('children__date')</ | + | Event.objects.order_by('children__date')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在这里,每个 <code>Event</code> 可能有多个排序数据; 每个带有多个 <code>children</code> 的 <code>Event</code> 将多次返回到 <code>order_by()</code> 创建的新 <code>QuerySet</code> 中。 换句话说,在 <code>QuerySet</code> 上使用 <code>order_by()</code> 可能会返回比您开始时更多的项目 - 这可能既不符合预期也没有用。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 因此,在使用多值字段对结果进行排序时要小心。 '''如果'''您可以确定您订购的每件商品只有一个订购数据,那么这种方法应该不会出现问题。 如果没有,请确保结果符合您的预期。 | |
− | |||
− | |||
− | |||
</div> | </div> | ||
− | + | 无法指定排序是否应区分大小写。 关于区分大小写,Django 会对结果进行排序,但您的数据库后端通常会对其进行排序。 | |
− | |||
− | |||
− | + | 您可以使用 [[../database-functions#django.db.models.functions|Lower]] 将字段转换为小写,这将实现大小写一致的排序: | |
− | [[../database-functions#django.db.models.functions| | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第502行: | 第389行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.order_by(Lower('headline').desc())</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您不希望对查询应用任何排序,甚至不希望应用默认排序,请调用不带参数的 [[#django.db.models.query.QuerySet.order_by|order_by()]]。 | |
− | |||
− | + | 您可以通过检查 [[#django.db.models.query.QuerySet.ordered|QuerySet.ordered]] 属性来判断查询是否已排序,如果 <code>QuerySet</code> 已以任何方式排序,则该属性将为 <code>True</code>。 | |
− | [[#django.db.models.query.QuerySet.ordered| | ||
− | <code> | ||
− | + | 每个 <code>order_by()</code> 调用将清除任何先前的排序。 例如,此查询将按 <code>pub_date</code> 而不是 <code>headline</code> 排序: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第521行: | 第404行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.order_by('headline').order_by('pub_date')</syntaxhighlight> |
</div> | </div> | ||
第530行: | 第413行: | ||
警告 | 警告 | ||
− | + | 订购不是免费的操作。 您添加到排序中的每个字段都会对您的数据库产生成本。 您添加的每个外键也将隐式包含其所有默认排序。 | |
− | |||
− | |||
− | + | 如果查询未指定排序,则以未指定的顺序从数据库返回结果。 只有在按唯一标识结果中每个对象的一组字段进行排序时,才能保证特定的排序。 例如,如果 <code>name</code> 字段不是唯一的,按它排序并不能保证具有相同名称的对象总是以相同的顺序出现。 | |
− | |||
− | |||
− | |||
− | |||
第548行: | 第425行: | ||
==== reverse() ==== | ==== reverse() ==== | ||
− | ; < | + | ; <span class="sig-name descname"><span class="pre">reverse</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
: | : | ||
− | + | 使用 <code>reverse()</code> 方法反转查询集元素的返回顺序。 再次调用 <code>reverse()</code> 会将排序恢复到正常方向。 | |
− | |||
− | |||
− | + | 要检索查询集中的“最后”五个项目,您可以这样做: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第561行: | 第436行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">my_queryset.reverse()[:5]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意,这与在 Python 中从序列末尾进行切片并不完全相同。 上面的例子将首先返回最后一项,然后是倒数第二项,依此类推。 如果我们有一个 Python 序列并查看 <code>seq[-5:]</code>,我们将首先看到倒数第五项。 Django 不支持这种访问模式(从末尾切片),因为在 SQL 中不可能有效地做到这一点。 | |
− | Python | ||
− | |||
− | <code>seq[-5:]</code> | ||
− | |||
− | |||
− | + | 另外,请注意 <code>reverse()</code> 通常只应在具有定义排序的 <code>QuerySet</code> 上调用(例如,当查询定义默认排序的模型时,或使用 [[#django.db.models.query.QuerySet.order_by|order_by( )]])。 如果没有为给定的 <code>QuerySet</code> 定义这样的排序,则对其调用 <code>reverse()</code> 没有实际影响(排序在调用 <code>reverse()</code> 之前未定义,之后将保持未定义) . | |
− | |||
− | |||
− | |||
− | |||
− | |||
第584行: | 第449行: | ||
<div id="distinct" class="section"> | <div id="distinct" class="section"> | ||
− | ==== | + | ==== distinct() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">distinct</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">fields</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回在其 SQL 查询中使用 <code>SELECT DISTINCT</code> 的新 <code>QuerySet</code>。 这消除了查询结果中的重复行。 | |
− | |||
− | + | 默认情况下,<code>QuerySet</code> 不会消除重复行。 在实践中,这很少成为问题,因为像 <code>Blog.objects.all()</code> 这样的简单查询不会引入重复结果行的可能性。 但是,如果您的查询跨越多个表,则在评估 <code>QuerySet</code> 时可能会得到重复的结果。 那时你会使用 <code>distinct()</code>。 | |
− | |||
− | |||
− | |||
− | <code>QuerySet</code> | ||
<div class="admonition note"> | <div class="admonition note"> | ||
第602行: | 第462行: | ||
注解 | 注解 | ||
− | + | [[#django.db.models.query.QuerySet.order_by|order_by()]] 调用中使用的任何字段都包含在 SQL <code>SELECT</code> 列中。 当与 <code>distinct()</code> 结合使用时,有时会导致意外结果。 如果您按相关模型中的字段排序,这些字段将被添加到选定的列中,否则它们可能会使重复的行看起来不同。 由于额外的列不会出现在返回的结果中(它们仅用于支持排序),因此有时看起来返回的是非不同的结果。 | |
− | <code>SELECT</code> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 同样,如果您使用 [[#django.db.models.query.QuerySet.values|values()]] 查询来限制选择的列,则任何 [[#django.db.models.query.QuerySet.order_by|order_by()]](或默认模型排序)中使用的列仍将涉及并可能影响唯一性的结果。 | |
− | |||
− | |||
− | + | 这里的寓意是,如果您使用 <code>distinct()</code>,请注意按相关型号排序。 同样,当一起使用 <code>distinct()</code> 和 [[#django.db.models.query.QuerySet.values|values()]] 时,按不在 [[#django.db.models.query.QuerySet.values|values()]] 调用中的字段排序时要小心。 | |
− | |||
− | [[#django.db.models.query.QuerySet.values| | ||
− | [[#django.db.models.query.QuerySet.values| | ||
</div> | </div> | ||
− | + | 仅在 PostgreSQL 上,您可以传递位置参数 (<code>*fields</code>) 以指定 <code>DISTINCT</code> 应适用的字段名称。 这将转换为 <code>SELECT DISTINCT ON</code> SQL 查询。 这就是不同之处。 对于正常的 <code>distinct()</code> 调用,数据库在确定哪些行不同时比较每行中的 ''each'' 字段。 对于具有指定字段名称的 <code>distinct()</code> 调用,数据库将仅比较指定的字段名称。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
第632行: | 第476行: | ||
注解 | 注解 | ||
− | + | 指定字段名称时,''必须''在<code>QuerySet</code>中提供<code>order_by()</code>,<code>order_by()</code>中的字段必须以<code>distinct()</code>,顺序相同。 | |
− | <code> | ||
− | <code>distinct()</code> | ||
− | + | 例如,<code>SELECT DISTINCT ON (a)</code> 为您提供 <code>a</code> 列中每个值的第一行。 如果您不指定顺序,您将得到一些任意行。 | |
− | |||
− | |||
</div> | </div> | ||
− | + | 示例(第一个之后的仅适用于 PostgreSQL): | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第648行: | 第488行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Author.objects.distinct() |
[...] | [...] | ||
− | + | >>> Entry.objects.order_by('pub_date').distinct('pub_date') | |
[...] | [...] | ||
− | + | >>> Entry.objects.order_by('blog').distinct('blog') | |
[...] | [...] | ||
− | + | >>> Entry.objects.order_by('author', 'pub_date').distinct('author', 'pub_date') | |
[...] | [...] | ||
− | + | >>> Entry.objects.order_by('blog__name', 'mod_date').distinct('blog__name', 'mod_date') | |
[...] | [...] | ||
− | + | >>> Entry.objects.order_by('author', 'pub_date').distinct('author') | |
− | [...]</ | + | [...]</syntaxhighlight> |
</div> | </div> | ||
第673行: | 第513行: | ||
注解 | 注解 | ||
− | + | 请记住, [[#django.db.models.query.QuerySet.order_by|order_by()]] 使用已定义的任何默认相关模型排序。 您可能必须按关系 <code>_id</code> 或引用字段显式排序,以确保 <code>DISTINCT ON</code> 表达式与 <code>ORDER BY</code> 子句开头的表达式匹配。 例如,如果 <code>Blog</code> 模型通过 <code>name</code> 定义了一个 [[../options#django.db.models.Options|ordering]]: | |
− | |||
− | <code>_id</code> | ||
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第684行: | 第519行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.order_by('blog').distinct('blog')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | ……不起作用,因为查询将按 <code>blog__name</code> 排序,从而与 <code>DISTINCT ON</code> 表达式不匹配。 您必须按关系 _id 字段(在本例中为 <code>blog_id</code>)或引用的字段(<code>blog__pk</code>)明确排序,以确保两个表达式匹配。 | |
− | |||
− | |||
− | |||
第700行: | 第532行: | ||
<div id="values" class="section"> | <div id="values" class="section"> | ||
− | ==== | + | ==== values() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">values</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">fields</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">expressions</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回一个 <code>QuerySet</code> 返回字典,而不是模型实例。 | |
− | |||
− | + | 其中每一个字典都代表一个对象,键与模型对象的属性名相对应。 | |
− | |||
− | + | 本示例将 <code>values()</code> 的字典与普通模型对象进行比较: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第718行: | 第547行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># This list contains a Blog object. |
− | + | >>> Blog.objects.filter(name__startswith='Beatles') | |
− | + | <QuerySet [<Blog: Beatles Blog>]> | |
# This list contains a dictionary. | # This list contains a dictionary. | ||
− | + | >>> Blog.objects.filter(name__startswith='Beatles').values() | |
− | + | <QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>values()</code> 方法采用可选的位置参数 <code>*fields</code>,它指定 <code>SELECT</code> 应限制为的字段名称。 如果您指定字段,则每个字典将仅包含您指定的字段的字段键/值。 如果不指定字段,则每个字典将包含数据库表中每个字段的键和值。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第741行: | 第566行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Blog.objects.values() |
− | + | <QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]> | |
− | + | >>> Blog.objects.values('id', 'name') | |
− | + | <QuerySet [{'id': 1, 'name': 'Beatles Blog'}]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>values()</code> 方法还接受可选的关键字参数,<code>**expressions</code>,这些参数被传递给 [[#django.db.models.query.QuerySet.annotate|annotate()]]: | |
− | <code>**expressions</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第756行: | 第580行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models.functions import Lower |
− | + | >>> Blog.objects.values(lower_name=Lower('name')) | |
− | + | <QuerySet [{'lower_name': 'beatles blog'}]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以在排序中使用内置和 [[../../../howto/custom-lookups|自定义查找]] 。 例如: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第770行: | 第593行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import CharField |
− | + | >>> from django.db.models.functions import Lower | |
− | + | >>> CharField.register_lookup(Lower) | |
− | + | >>> Blog.objects.values('name__lower') | |
− | + | <QuerySet [{'name__lower': 'beatles blog'}]></syntaxhighlight> | |
</div> | </div> | ||
第781行: | 第604行: | ||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | + | <span class="versionmodified changed"> 2.1 版更改: </span> 添加了对查找的支持。 | |
</div> | </div> | ||
− | + | <code>values()</code> 子句中的聚合应用在同一 <code>values()</code> 子句中的其他参数之前。 如果您需要按另一个值分组,请将其添加到较早的 <code>values()</code> 子句中。 例如: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第793行: | 第614行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import Count |
− | + | >>> Blog.objects.values('entry__authors', entries=Count('entry')) | |
− | + | <QuerySet [{'entry__authors': 1, 'entries': 20}, {'entry__authors': 1, 'entries': 13}]> | |
− | + | >>> Blog.objects.values('entry__authors').annotate(entries=Count('entry')) | |
− | + | <QuerySet [{'entry__authors': 1, 'entries': 33}]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有几个微妙的地方值得一提: | |
<ul> | <ul> | ||
− | <li><p> | + | <li><p>如果您有一个名为 <code>foo</code> 的字段,它是一个 [[../fields#django.db.models|ForeignKey]],默认的 <code>values()</code> 调用将返回一个名为 <code>foo_id</code> 的字典键,因为这是名称存储实际值的隐藏模型属性(<code>foo</code> 属性指的是相关模型)。 当你调用 <code>values()</code> 并传入字段名称时,你可以传入 <code>foo</code> 或 <code>foo_id</code> 并且你会得到同样的东西(字典键将匹配字段您传入的名称)。</p> |
− | [[../fields#django.db.models| | + | <p>例如:</p> |
− | |||
− | |||
− | |||
− | <code>values()</code> | ||
− | |||
− | |||
− | <p> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.values() |
− | + | <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]> | |
− | + | >>> Entry.objects.values('blog') | |
− | + | <QuerySet [{'blog': 1}, ...]> | |
− | + | >>> Entry.objects.values('blog_id') | |
− | + | <QuerySet [{'blog_id': 1}, ...]></syntaxhighlight> | |
</div> | </div> | ||
</div></li> | </div></li> | ||
− | <li><p> | + | <li><p>将 <code>values()</code> 与 [[#django.db.models.query.QuerySet.distinct|distinct()]] 一起使用时,请注意排序会影响结果。 有关详细信息,请参阅 [[#django.db.models.query.QuerySet.distinct|distinct()]] 中的注释。</p></li> |
− | + | <li><p>如果在 [[#django.db.models.query.QuerySet.extra|extra()]] 调用后使用 <code>values()</code> 子句,则 [[#django.db.models.query.QuerySet.extra|extra()]] 中由 <code>select</code> 参数定义的任何字段必须明确包含在 <code>values()</code> 调用中。 在 <code>values()</code> 调用之后进行的任何 [[#django.db.models.query.QuerySet.extra|extra()]] 调用都将忽略其额外的选定字段。</p></li> | |
− | + | <li><p>在 <code>values()</code> 之后调用 [[#django.db.models.query.QuerySet.only|only()]] 和 [[#django.db.models.query.QuerySet.defer|defer()]] 没有意义,因此这样做会引发 <code>NotImplementedError</code>。</p></li> | |
− | <li><p> | + | <li><p>组合转换和聚合需要使用两个 [[#django.db.models.query.QuerySet.annotate|annotate()]] 调用,明确地或作为 [[#django.db.models.query.QuerySet.values|values()]] 的关键字参数。 如上,如果转换已在相关字段类型上注册,则第一个 [[#django.db.models.query.QuerySet.annotate|annotate()]] 可以省略,因此以下示例是等效的:</p> |
− | |||
− | |||
− | |||
− | |||
− | <li><p> | ||
− | |||
− | <li><p> | ||
− | |||
− | |||
− | [[#django.db.models.query.QuerySet.annotate| | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import CharField, Count |
− | + | >>> from django.db.models.functions import Lower | |
− | + | >>> CharField.register_lookup(Lower) | |
− | + | >>> Blog.objects.values('entry__authors__name__lower').annotate(entries=Count('entry')) | |
− | + | <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]> | |
− | + | >>> Blog.objects.values( | |
... entry__authors__name__lower=Lower('entry__authors__name') | ... entry__authors__name__lower=Lower('entry__authors__name') | ||
... ).annotate(entries=Count('entry')) | ... ).annotate(entries=Count('entry')) | ||
− | + | <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]> | |
− | + | >>> Blog.objects.annotate( | |
... entry__authors__name__lower=Lower('entry__authors__name') | ... entry__authors__name__lower=Lower('entry__authors__name') | ||
... ).values('entry__authors__name__lower').annotate(entries=Count('entry')) | ... ).values('entry__authors__name__lower').annotate(entries=Count('entry')) | ||
− | + | <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]></syntaxhighlight> | |
</div> | </div> | ||
第866行: | 第670行: | ||
</div></li></ul> | </div></li></ul> | ||
− | + | 当您知道只需要少量可用字段中的值并且不需要模型实例对象的功能时,它很有用。 仅选择需要使用的字段会更有效率。 | |
− | |||
− | |||
− | + | 最后,注意可以调用<code>filter()</code>、<code>order_by()</code>等。 在 <code>values()</code> 调用之后,这意味着这两个调用是相同的: | |
− | <code>values()</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第877行: | 第678行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.values().order_by('id') |
− | Blog.objects.order_by('id').values()</ | + | Blog.objects.order_by('id').values()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 制作 Django 的人更喜欢将所有影响 SQL 的方法放在第一位,然后(可选)任何影响输出的方法(例如 <code>values()</code>),但这并不重要。 这是你真正炫耀个人主义的机会。 | |
− | |||
− | |||
− | |||
− | + | 您还可以通过 <code>OneToOneField</code>、<code>ForeignKey</code> 和 <code>ManyToManyField</code> 属性引用具有反向关系的相关模型上的字段: | |
− | <code>OneToOneField</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第895行: | 第692行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Blog.objects.values('name', 'entry__headline') |
− | + | <QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'}, | |
− | {'name': 'My blog', 'entry__headline': 'Another entry'}, ...] | + | {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]></syntaxhighlight> |
</div> | </div> | ||
第906行: | 第703行: | ||
警告 | 警告 | ||
− | + | 因为 [[../fields#django.db.models|ManyToManyField]] 属性和反向关系可以有多个相关行,包括这些可以对结果集的大小产生乘数效应。 如果您在 <code>values()</code> 查询中包含多个这样的字段,这将特别明显,在这种情况下,将返回所有可能的组合。 | |
− | |||
− | |||
− | |||
− | |||
第918行: | 第711行: | ||
<div id="values-list" class="section"> | <div id="values-list" class="section"> | ||
− | ==== | + | ==== values_list() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">values_list</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">fields</span></span>'', ''<span class="n"><span class="pre">flat</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">named</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 这类似于 <code>values()</code> 不同之处在于它在迭代时返回元组而不是返回字典。 每个元组包含来自传递到 <code>values_list()</code> 调用的相应字段或表达式的值——所以第一项是第一个字段,依此类推。 例如: | |
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第932行: | 第722行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.values_list('id', 'headline') |
− | + | <QuerySet [(1, 'First entry'), ...]> | |
− | + | >>> from django.db.models.functions import Lower | |
− | + | >>> Entry.objects.values_list('id', Lower('headline')) | |
− | + | <QuerySet [(1, 'first entry'), ...]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果只传入单个字段,还可以传入 <code>flat</code> 参数。 如果 <code>True</code>,这将意味着返回的结果是单个值,而不是一元组。 一个例子应该能让区别更清楚: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第949行: | 第737行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.values_list('id').order_by('id') |
− | + | <QuerySet[(1,), (2,), (3,), ...]> | |
− | + | >>> Entry.objects.values_list('id', flat=True).order_by('id') | |
− | + | <QuerySet [1, 2, 3, ...]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有多个字段时传入<code>flat</code>是错误的。 | |
− | + | 您可以通过 <code>named=True</code> 来获得作为 <code>namedtuple()</code> 的结果: | |
− | <code>namedtuple()</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第967行: | 第754行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.values_list('id', 'headline', named=True) |
− | + | <QuerySet [Row(id=1, headline='First entry'), ...]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 使用命名元组可能会使使用结果更易读,但代价是将结果转化为命名元组时要付出很小的性能代价。 | |
− | |||
− | + | 如果您没有将任何值传递给 <code>values_list()</code>,它将按照声明的顺序返回模型中的所有字段。 | |
− | |||
− | + | 一个常见的需求是获取某个模型实例的特定字段值。 为此,请使用 <code>values_list()</code> 后跟 <code>get()</code> 调用: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第986行: | 第770行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.values_list('headline', flat=True).get(pk=1) |
− | 'First entry'</ | + | 'First entry'</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <code>values()</code> | + | <code>values()</code> 和 <code>values_list()</code> 都是针对特定用例的优化:在没有创建模型实例的开销的情况下检索数据子集。 当处理多对多和其他多值关系(例如反向外键的一对多关系)时,这个比喻就失效了,因为“一行一个对象”假设不成立。 |
− | |||
− | |||
− | |||
− | |||
− | + | 例如,请注意跨 [[../fields#django.db.models|ManyToManyField]] 查询时的行为: | |
− | [[../fields#django.db.models| | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,005行: | 第784行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Author.objects.values_list('name', 'entry__headline') |
− | + | <QuerySet [('Noam Chomsky', 'Impressions of Gaza'), | |
('George Orwell', 'Why Socialists Do Not Believe in Fun'), | ('George Orwell', 'Why Socialists Do Not Believe in Fun'), | ||
('George Orwell', 'In Defence of English Cooking'), | ('George Orwell', 'In Defence of English Cooking'), | ||
− | ('Don Quixote', None)] | + | ('Don Quixote', None)]></syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有多个条目的作者多次出现,没有任何条目的作者的条目标题为 <code>None</code>。 | |
− | |||
− | + | 同样,当查询反向外键时,对于没有任何作者的条目,会出现 <code>None</code>: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,024行: | 第801行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.values_list('authors') |
− | + | <QuerySet [('Noam Chomsky',), ('George Orwell',), (None,)]></syntaxhighlight> | |
</div> | </div> | ||
第1,034行: | 第811行: | ||
<div id="dates" class="section"> | <div id="dates" class="section"> | ||
− | ==== | + | ==== dates() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">dates</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">field</span></span>'', ''<span class="n"><span class="pre">kind</span></span>'', ''<span class="n"><span class="pre">order</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">'ASC'</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回一个 <code>QuerySet</code>,其计算结果为 <code>datetime.date</code> 对象列表,表示 <code>QuerySet</code> 内容中特定类型的所有可用日期。 | |
− | |||
− | |||
− | <code>field</code> | + | <code>field</code> 应该是您模型的 <code>DateField</code> 的名称。 <code>kind</code> 应为 <code>"year"</code>、<code>"month"</code>、<code>"week"</code> 或 <code>"day"</code>。 结果列表中的每个 <code>datetime.date</code> 对象都被“截断”为给定的 <code>type</code>。 |
− | <code>kind</code> | ||
− | |||
− | |||
− | * <code>"year"</code> | + | * <code>"year"</code> 返回字段的所有不同年份值的列表。 |
− | * <code>"month"</code> | + | * <code>"month"</code> 返回字段的所有不同年/月值的列表。 |
− | * <code>"week"</code> | + | * <code>"week"</code> 返回该字段所有不同年/周值的列表。 所有日期都将是星期一。 |
− | * <code>"day"</code> | + | * <code>"day"</code> 返回字段的所有不同年/月/日值的列表。 |
− | <code>order</code> | + | <code>order</code>,默认为 <code>'ASC'</code>,应该是 <code>'ASC'</code> 或 <code>'DESC'</code>。 这指定了如何对结果进行排序。 |
− | <code>'DESC'</code> | ||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,062行: | 第833行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.dates('pub_date', 'year') |
[datetime.date(2005, 1, 1)] | [datetime.date(2005, 1, 1)] | ||
− | + | >>> Entry.objects.dates('pub_date', 'month') | |
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)] | [datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)] | ||
− | + | >>> Entry.objects.dates('pub_date', 'week') | |
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)] | [datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)] | ||
− | + | >>> Entry.objects.dates('pub_date', 'day') | |
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)] | [datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)] | ||
− | + | >>> Entry.objects.dates('pub_date', 'day', order='DESC') | |
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)] | [datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)] | ||
− | + | >>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day') | |
− | [datetime.date(2005, 3, 20)]</ | + | [datetime.date(2005, 3, 20)]</syntaxhighlight> |
</div> | </div> | ||
第1,080行: | 第851行: | ||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | + | <span class="versionmodified changed"> 2.1 版变更: </span> 增加了“周”支持。 | |
第1,088行: | 第859行: | ||
<div id="datetimes" class="section"> | <div id="datetimes" class="section"> | ||
− | ==== | + | ==== datetimes() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">datetimes</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">field_name</span></span>'', ''<span class="n"><span class="pre">kind</span></span>'', ''<span class="n"><span class="pre">order</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">'ASC'</span></span>'', ''<span class="n"><span class="pre">tzinfo</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回一个 <code>QuerySet</code>,其计算结果为 <code>datetime.datetime</code> 对象列表,表示 <code>QuerySet</code> 内容中特定类型的所有可用日期。 | |
− | |||
− | |||
− | <code>field_name</code> | + | <code>field_name</code> 应该是您模型的 <code>DateTimeField</code> 的名称。 |
− | <code>kind</code> | + | <code>kind</code> 应为 <code>"year"</code>、<code>"month"</code>、<code>"week"</code>、<code>"day"</code>、<code>"hour"</code>、<code>"minute"</code> , 或 <code>"second"</code>。 结果列表中的每个 <code>datetime.datetime</code> 对象都被“截断”为给定的 <code>type</code>。 |
− | <code>"hour"</code> | ||
− | |||
− | <code>order</code> | + | <code>order</code>,默认为 <code>'ASC'</code>,应该是 <code>'ASC'</code> 或 <code>'DESC'</code>。 这指定了如何对结果进行排序。 |
− | <code>'DESC'</code> | ||
− | <code>tzinfo</code> | + | <code>tzinfo</code> 定义日期时间在截断之前转换到的时区。 实际上,根据使用的时区,给定的日期时间具有不同的表示形式。 此参数必须是 <code>datetime.tzinfo</code> 对象。 如果是 <code>None</code>,Django 使用 [[../../../topics/i18n/timezones#default-current-time-zone|当前时区]] 。 [[#id4|:setting:`USE_TZ`]] 为 <code>False</code> 时无效。 |
− | |||
− | |||
− | |||
− | <code>False</code> | ||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | + | <span class="versionmodified changed"> 2.1 版变更: </span> 增加了“周”支持。 | |
第1,122行: | 第884行: | ||
注解 | 注解 | ||
− | + | 此函数直接在数据库中执行时区转换。 因此,您的数据库必须能够解释 <code>tzinfo.tzname(None)</code> 的值。 这转化为以下要求: | |
− | |||
− | <code>tzinfo.tzname(None)</code> | ||
− | * | + | * SQLite:没有要求。 使用 [http://pytz.sourceforge.net/ pytz](安装 Django 时安装)在 Python 中执行转换。 |
− | * | + | * PostgreSQL:无要求(参见 [https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-TIMEZONES 时区] )。 |
− | * | + | * Oracle:无要求(请参阅 [https://docs.oracle.com/en/database/oracle/oracle-database/18/nlspg/datetime-data-types-and-time-zone-support.html#GUID-805AB986-DE12-4FEA-AF56-5AABCD2132DF 选择时区文件] )。 |
− | * | + | * MySQL:使用 [https://dev.mysql.com/doc/refman/en/mysql-tzinfo-to-sql.html mysql_tzinfo_to_sql] 加载时区表。 |
第1,137行: | 第897行: | ||
<div id="none" class="section"> | <div id="none" class="section"> | ||
− | ==== | + | ==== none() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">none</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
: | : | ||
− | + | 调用 none() 将创建一个永远不会返回任何对象的查询集,并且在访问结果时不会执行任何查询。 qs.none() 查询集是 <code>EmptyQuerySet</code> 的一个实例。 | |
− | |||
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,152行: | 第910行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.none() |
− | + | <QuerySet []> | |
− | + | >>> from django.db.models.query import EmptyQuerySet | |
− | + | >>> isinstance(Entry.objects.none(), EmptyQuerySet) | |
− | True</ | + | True</syntaxhighlight> |
</div> | </div> | ||
第1,165行: | 第923行: | ||
<div id="all" class="section"> | <div id="all" class="section"> | ||
− | ==== | + | ==== all() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">all</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
: | : | ||
− | + | 返回当前 <code>QuerySet</code>(或 <code>QuerySet</code> 子类)的 ''副本'' 。 这在您可能希望传入模型管理器或 <code>QuerySet</code> 并对结果进行进一步过滤的情况下非常有用。 在任一对象上调用 <code>all()</code> 之后,您肯定会有一个 <code>QuerySet</code> 可以使用。 | |
− | |||
− | |||
− | <code>all()</code> | ||
− | + | 当 <code>QuerySet</code> 被 [[#when-querysets-are-evaluated|评估]] 时,它通常会缓存其结果。 如果自评估 <code>QuerySet</code> 后数据库中的数据可能已更改,您可以通过对先前评估的 <code>QuerySet</code> 调用 <code>all()</code> 来获取同一查询的更新结果。 | |
− | |||
− | |||
− | |||
第1,184行: | 第936行: | ||
<div id="union" class="section"> | <div id="union" class="section"> | ||
− | ==== | + | ==== union() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">union</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">other_qs</span></span>'', ''<span class="n"><span class="pre">all</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 使用 SQL 的 <code>UNION</code> 运算符组合两个或多个 <code>QuerySet</code> 的结果。 例如: | |
− | <code>QuerySet</code> | ||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,196行: | 第947行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> qs1.union(qs2, qs3)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 默认情况下,<code>UNION</code> 运算符仅选择不同的值。 要允许重复值,请使用 <code>all=True</code> 参数。 | |
− | |||
− | <code>union()</code> | + | <code>union()</code>、<code>intersection()</code> 和 <code>difference()</code> 返回第一个 <code>QuerySet</code> 类型的模型实例,即使参数是其他的 <code>QuerySet</code>楷模。 只要 <code>SELECT</code> 列表在所有 <code>QuerySet</code> 中都相同(至少是类型,名称无关紧要,只要类型的顺序相同)就可以传递不同的模型。 在这种情况下,您必须使用应用到结果 <code>QuerySet</code> 的 <code>QuerySet</code> 方法中第一个 <code>QuerySet</code> 的列名。 例如: |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,216行: | 第960行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> qs1 = Author.objects.values_list('name') |
− | + | >>> qs2 = Entry.objects.values_list('headline') | |
− | + | >>> qs1.union(qs2).order_by('name')</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此外,只有 <code>LIMIT</code>、<code>OFFSET</code>、<code>COUNT(*)</code>、<code>ORDER BY</code>,并指定列(即 切片,[[#django.db.models.query.QuerySet.count|count()]]、[[#django.db.models.query.QuerySet.order_by|order_by()]] 和 [[#django.db.models.query.QuerySet.values|values()]]/[[#django.db.models.query.QuerySet.values_list|values_list()]]) 允许在结果 [X123X ]。 此外,数据库对组合查询中允许的操作进行了限制。 例如,大多数数据库不允许在组合查询中使用 <code>LIMIT</code> 或 <code>OFFSET</code>。 | |
− | |||
− | [[#django.db.models.query.QuerySet.values| | ||
− | |||
− | |||
− | <code>LIMIT</code> | ||
第1,234行: | 第973行: | ||
<div id="intersection" class="section"> | <div id="intersection" class="section"> | ||
− | ==== | + | ==== intersection() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">intersection</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">other_qs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 使用 SQL 的 <code>INTERSECT</code> 运算符返回两个或多个 <code>QuerySet</code> 的共享元素。 例如: | |
− | <code>QuerySet</code> | ||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,246行: | 第984行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> qs1.intersection(qs2, qs3)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有关一些限制,请参阅 [[#django.db.models.query.QuerySet.union|union()]]。 | |
第1,257行: | 第995行: | ||
<div id="difference" class="section"> | <div id="difference" class="section"> | ||
− | ==== | + | ==== difference() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">difference</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">other_qs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 使用 SQL 的 <code>EXCEPT</code> 运算符仅保留 <code>QuerySet</code> 中存在的元素,而不保留其他一些 <code>QuerySet</code> 中的元素。 例如: | |
− | <code>QuerySet</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,269行: | 第1,006行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> qs1.difference(qs2, qs3)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有关一些限制,请参阅 [[#django.db.models.query.QuerySet.union|union()]]。 | |
第1,280行: | 第1,017行: | ||
<div id="select-related" class="section"> | <div id="select-related" class="section"> | ||
− | ==== | + | ==== select_related() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">select_related</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">fields</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回一个 <code>QuerySet</code>,它将“遵循”外键关系,在执行查询时选择其他相关对象数据。 这是一个性能助推器,它导致一个更复杂的查询,但意味着以后使用外键关系不需要数据库查询。 | |
− | |||
− | |||
− | |||
− | + | 以下示例说明了普通查找和 <code>select_related()</code> 查找之间的区别。 这是标准查找: | |
− | <code>select_related()</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,297行: | 第1,030行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Hits the database. |
e = Entry.objects.get(id=5) | e = Entry.objects.get(id=5) | ||
# Hits the database again to get the related Blog object. | # Hits the database again to get the related Blog object. | ||
− | b = e.blog</ | + | b = e.blog</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这是 <code>select_related</code> 查找: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,312行: | 第1,045行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Hits the database. |
e = Entry.objects.select_related('blog').get(id=5) | e = Entry.objects.select_related('blog').get(id=5) | ||
# Doesn't hit the database, because e.blog has been prepopulated | # Doesn't hit the database, because e.blog has been prepopulated | ||
# in the previous query. | # in the previous query. | ||
− | b = e.blog</ | + | b = e.blog</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以将 <code>select_related()</code> 与任何对象查询集一起使用: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,328行: | 第1,061行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.utils import timezone |
# Find all the blogs with entries scheduled to be published in the future. | # Find all the blogs with entries scheduled to be published in the future. | ||
第1,336行: | 第1,069行: | ||
# Without select_related(), this would make a database query for each | # Without select_related(), this would make a database query for each | ||
# loop iteration in order to fetch the related blog for each entry. | # loop iteration in order to fetch the related blog for each entry. | ||
− | blogs.add(e.blog)</ | + | blogs.add(e.blog)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>filter()</code> 和 <code>select_related()</code> 链接的顺序并不重要。 这些查询集是等效的: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,348行: | 第1,080行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog') |
− | Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())</ | + | Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以按照与查询类似的方式跟踪外键。 如果您有以下型号: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,361行: | 第1,092行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.db import models |
class City(models.Model): | class City(models.Model): | ||
第1,378行: | 第1,109行: | ||
class Book(models.Model): | class Book(models.Model): | ||
# ... | # ... | ||
− | author = models.ForeignKey(Person, on_delete=models.CASCADE)</ | + | author = models.ForeignKey(Person, on_delete=models.CASCADE)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | ... | + | ...然后调用 <code>Book.objects.select_related('author__hometown').get(id=4)</code> 将缓存相关的 <code>Person</code> ''和'' 相关的 <code>City</code>: |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,390行: | 第1,120行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Hits the database with joins to the author and hometown tables. |
b = Book.objects.select_related('author__hometown').get(id=4) | b = Book.objects.select_related('author__hometown').get(id=4) | ||
p = b.author # Doesn't hit the database. | p = b.author # Doesn't hit the database. | ||
第1,398行: | 第1,128行: | ||
b = Book.objects.get(id=4) # Hits the database. | b = Book.objects.get(id=4) # Hits the database. | ||
p = b.author # Hits the database. | p = b.author # Hits the database. | ||
− | c = p.hometown # Hits the database.</ | + | c = p.hometown # Hits the database.</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以在传递给 <code>select_related()</code> 的字段列表中引用任何 [[../fields#django.db.models|ForeignKey]] 或 [[../fields#django.db.models|OneToOneField]] 关系。 | |
− | [[../fields#django.db.models| | ||
− | |||
− | + | 还可以参考传递给 <code>select_related</code> 的字段列表中的 [[../fields#django.db.models|OneToOneField]] 的反向——也就是说,可以遍历一个 [[../fields#django.db.models|OneToOneField]] 回到对象上定义了哪个字段。 不要指定字段名称,而是使用 [[../fields#django.db.models.ForeignKey|related_name]] 作为相关对象上的字段。 | |
− | [[../fields#django.db.models| | ||
− | |||
− | [[../fields#django.db.models| | ||
− | |||
− | + | 在某些情况下,您可能希望使用大量相关对象调用 <code>select_related()</code>,或者您不知道所有关系。 在这些情况下,可以不带参数调用 <code>select_related()</code>。 这将遵循它可以找到的所有非空外键 - 必须指定可为空的外键。 在大多数情况下不建议这样做,因为它可能会使基础查询更复杂,并返回比实际需要更多的数据。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 如果需要清除 <code>QuerySet</code> 上过去调用 <code>select_related</code> 添加的相关字段列表,可以将 <code>None</code> 作为参数传递: | |
− | <code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,427行: | 第1,145行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> without_relations = queryset.select_related(None)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 链接 <code>select_related</code> 调用的工作方式与其他方法类似 - 即 <code>select_related('foo', 'bar')</code> 等效于 <code>select_related('foo').select_related('bar')</code>。 | |
− | |||
− | <code>select_related('foo').select_related('bar')</code> | ||
第1,440行: | 第1,156行: | ||
<div id="prefetch-related" class="section"> | <div id="prefetch-related" class="section"> | ||
− | ==== | + | ==== prefetch_related() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">prefetch_related</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">lookups</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回一个 <code>QuerySet</code>,它将自动为每个指定的查找批量检索相关对象。 | |
− | |||
− | + | 这与 <code>select_related</code> 的目的相似,都旨在阻止因访问相关对象而导致的数据库查询泛滥,但策略却大不相同。 | |
− | |||
− | |||
− | <code>select_related</code> | + | <code>select_related</code> 通过创建 SQL 连接并在 <code>SELECT</code> 语句中包含相关对象的字段来工作。 为此,<code>select_related</code> 在同一个数据库查询中获取相关对象。 但是,为了避免因跨“多”关系加入而导致的更大的结果集,<code>select_related</code> 仅限于单值关系 - 外键和一对一。 |
− | |||
− | |||
− | |||
− | <code>select_related</code> | ||
− | |||
− | <code>prefetch_related</code> | + | 另一方面,<code>prefetch_related</code> 对每个关系进行单独的查找,并在 Python 中进行“连接”。 除了<code>select_related</code>。 它还支持 [X32X]GenericRelation 和 [[../../contrib/contenttypes#django.contrib.contenttypes.fields|GenericForeignKey]] 的预取,但是,它必须限制为一组同构的结果。 例如,仅当查询限制为一个 <code>ContentType</code> 时,才支持预取由 <code>GenericForeignKey</code> 引用的对象。 |
− | |||
− | |||
− | <code>select_related</code> | ||
− | |||
− | [ | ||
− | [[../../contrib/contenttypes#django.contrib.contenttypes.fields| | ||
− | |||
− | |||
− | |||
− | + | 例如,假设您有以下模型: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,476行: | 第1,175行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.db import models |
class Topping(models.Model): | class Topping(models.Model): | ||
第1,486行: | 第1,185行: | ||
def __str__(self): | def __str__(self): | ||
− | return | + | return "%s (%s)" % ( |
self.name, | self.name, | ||
− | + | ", ".join(topping.name for topping in self.toppings.all()), | |
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 并运行: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,500行: | 第1,199行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Pizza.objects.all() |
− | [ | + | ["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 问题在于,每次 <code>Pizza.__str__()</code> 请求 <code>self.toppings.all()</code> 时,它都必须查询数据库,因此 <code>Pizza.objects.all()</code> 将对 every[ X165X] 比萨中的项目 <code>QuerySet</code>。 | |
− | <code>self.toppings.all()</code> | ||
− | <code>Pizza.objects.all()</code> | ||
− | |||
− | + | 我们可以使用 <code>prefetch_related</code> 减少到两个查询: | |
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,517行: | 第1,213行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Pizza.objects.all().prefetch_related('toppings')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这意味着每个 <code>Pizza</code> 都有一个 <code>self.toppings.all()</code>; 现在,每次调用 <code>self.toppings.all()</code> 时,不必去数据库查找项目,它会在预取的 <code>QuerySet</code> 缓存中找到它们,该缓存已填充在单个查询中。 | |
− | <code>self.toppings.all()</code> | ||
− | |||
− | |||
− | + | 也就是说,所有相关的浇头都将在单个查询中被获取,并用于使 <code>QuerySets</code> 具有相关结果的预填充缓存; 这些 <code>QuerySets</code> 然后用于 <code>self.toppings.all()</code> 调用。 | |
− | |||
− | |||
− | + | <code>prefetch_related()</code> 中的附加查询在 <code>QuerySet</code> 开始评估并执行主查询后执行。 | |
− | <code>QuerySet</code> | ||
− | + | 如果您有可迭代的模型实例,您可以使用 [[#django.db.models.prefetch_related_objects|prefetch_related_objects()]] 函数预取这些实例上的相关属性。 | |
− | |||
− | |||
− | + | 请注意,主 <code>QuerySet</code> 的结果缓存和所有指定的相关对象将被完全加载到内存中。 这改变了 <code>QuerySets</code> 的典型行为,它通常会尽量避免在需要之前将所有对象加载到内存中,即使在数据库中执行了查询之后也是如此。 | |
− | |||
− | |||
− | |||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
第1,548行: | 第1,232行: | ||
注解 | 注解 | ||
− | + | 请记住,与 <code>QuerySets</code> 一样,任何暗示不同数据库查询的后续链接方法都将忽略先前缓存的结果,并使用新的数据库查询检索数据。 因此,如果您编写以下内容: | |
− | |||
− | |||
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,557行: | 第1,238行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> pizzas = Pizza.objects.prefetch_related('toppings') |
− | + | >>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | ... | + | ...那么<code>pizza.toppings.all()</code> 已被预取的事实对您没有帮助。 <code>prefetch_related('toppings')</code> 暗示 <code>pizza.toppings.all()</code>,但 <code>pizza.toppings.filter()</code> 是一个新的不同的查询。 预取缓存在这里无济于事; 事实上,它会损害性能,因为您已经完成了一个您没有使用过的数据库查询。 所以请谨慎使用此功能! |
− | |||
− | <code>pizza.toppings.all()</code> | ||
− | |||
− | |||
− | |||
− | + | 此外,如果您调用数据库更改方法 [[../relations#django.db.models.fields.related.RelatedManager|add()]]、[[../relations#django.db.models.fields.related.RelatedManager|remove()]]、[[../relations#django.db.models.fields.related.RelatedManager|clear()]] 或 [[../relations#django.db.models.fields.related.RelatedManager|set()]],在 [[../relations#django.db.models.fields.related|相关管理器]] 上,该关系的任何预取缓存都将被清除。 | |
− | [[../relations#django.db.models.fields.related.RelatedManager| | ||
− | [[../relations#django.db.models.fields.related.RelatedManager| | ||
− | [[../relations#django.db.models.fields.related.RelatedManager| | ||
− | [[../relations#django.db.models.fields.related.RelatedManager| | ||
− | [[../relations#django.db.models.fields.related| | ||
− | |||
</div> | </div> | ||
− | + | 您也可以使用普通的 join 语法来做相关字段的相关字段。 假设我们在上面的例子中有一个额外的模型: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,587行: | 第1,256行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">class Restaurant(models.Model): |
pizzas = models.ManyToManyField(Pizza, related_name='restaurants') | pizzas = models.ManyToManyField(Pizza, related_name='restaurants') | ||
− | best_pizza = models.ForeignKey(Pizza, related_name='championed_by', on_delete=models.CASCADE)</ | + | best_pizza = models.ForeignKey(Pizza, related_name='championed_by', on_delete=models.CASCADE)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 以下都是合法的: | |
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,600行: | 第1,269行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Restaurant.objects.prefetch_related('pizzas__toppings')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这将预取属于餐厅的所有比萨饼,以及属于这些比萨饼的所有配料。 这将导致总共 3 个数据库查询 - 一个用于餐厅,一个用于比萨饼,一个用于浇头。 | |
− | |||
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,613行: | 第1,280行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Restaurant.objects.prefetch_related('best_pizza__toppings')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这将为每个餐厅获取最好的比萨饼和所有最好的比萨饼的配料。 这将在 3 个数据库查询中完成 - 一个用于餐厅,一个用于“最好的比萨饼”,另一个用于浇头。 | |
− | |||
− | |||
− | + | 当然,也可以使用 <code>select_related</code> 来获取 <code>best_pizza</code> 关系,以将查询计数减少到 2: | |
− | <code> | ||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,629行: | 第1,293行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 由于预取是在主查询(包括 <code>select_related</code> 所需的连接)之后执行的,因此能够检测到 <code>best_pizza</code> 对象已经被提取,它会跳过再次提取它们. | |
− | |||
− | |||
− | + | 链接 <code>prefetch_related</code> 调用将累积预取的查找。 要清除任何 <code>prefetch_related</code> 行为,请将 <code>None</code> 作为参数传递: | |
− | |||
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,646行: | 第1,306行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> non_prefetched = qs.prefetch_related(None)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 使用 <code>prefetch_related</code> 时要注意的一个区别是查询创建的对象可以在它们相关的不同对象之间共享,即 单个 Python 模型实例可以出现在返回的对象树中的多个点。 这通常会发生在外键关系中。 通常这种行为不会有问题,而且实际上会节省内存和 CPU 时间。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 虽然 <code>prefetch_related</code> 支持预取 <code>GenericForeignKey</code> 关系,但查询的数量将取决于数据。 由于 <code>GenericForeignKey</code> 可以引用多个表中的数据,因此需要对每个引用的表进行一次查询,而不是对所有项目进行一次查询。 如果尚未提取相关行,则可能会对 <code>ContentType</code> 表进行其他查询。 | |
− | |||
− | <code>GenericForeignKey</code> | ||
− | |||
− | |||
− | |||
− | <code>prefetch_related</code> | + | <code>prefetch_related</code> 在大多数情况下将使用使用“IN”运算符的 SQL 查询来实现。 这意味着对于大的 <code>QuerySet</code> 可能会生成一个大的“IN”子句,这取决于数据库,在解析或执行 SQL 查询时可能会出现其自身的性能问题。 始终针对您的用例进行分析! |
− | |||
− | |||
− | |||
− | |||
− | + | 请注意,如果您使用 <code>iterator()</code> 运行查询,<code>prefetch_related()</code> 调用将被忽略,因为这两个优化一起使用没有意义。 | |
− | |||
− | + | 您可以使用 [[#django.db.models.Prefetch|Prefetch]] 对象进一步控制预取操作。 | |
− | |||
− | + | 最简单的形式 <code>Prefetch</code> 相当于传统的基于字符串的查找: | |
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,684行: | 第1,327行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import Prefetch |
− | + | >>> Restaurant.objects.prefetch_related(Prefetch('pizzas__toppings'))</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以使用可选的 <code>queryset</code> 参数提供自定义查询集。 这可用于更改查询集的默认顺序: | |
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,697行: | 第1,339行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Restaurant.objects.prefetch_related( |
− | ... Prefetch('pizzas__toppings', queryset=Toppings.objects.order_by('name')))</ | + | ... Prefetch('pizzas__toppings', queryset=Toppings.objects.order_by('name')))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 或者在适用时调用 [[#django.db.models.query.QuerySet.select_related|select_related()]] 以进一步减少查询数量: | |
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,710行: | 第1,351行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Pizza.objects.prefetch_related( |
− | ... Prefetch('restaurants', queryset=Restaurant.objects.select_related('best_pizza')))</ | + | ... Prefetch('restaurants', queryset=Restaurant.objects.select_related('best_pizza')))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您还可以使用可选的 <code>to_attr</code> 参数将预取结果分配给自定义属性。 结果将直接存储在列表中。 | |
− | <code>to_attr</code> | ||
− | + | 这允许使用不同的 <code>QuerySet</code> 多次预取相同的关系; 例如: | |
− | <code>QuerySet</code> | ||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,726行: | 第1,365行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> vegetarian_pizzas = Pizza.objects.filter(vegetarian=True) |
− | + | >>> Restaurant.objects.prefetch_related( | |
... Prefetch('pizzas', to_attr='menu'), | ... Prefetch('pizzas', to_attr='menu'), | ||
− | ... Prefetch('pizzas', queryset=vegetarian_pizzas, to_attr='vegetarian_menu'))</ | + | ... Prefetch('pizzas', queryset=vegetarian_pizzas, to_attr='vegetarian_menu'))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 使用自定义 <code>to_attr</code> 创建的查找仍然可以像往常一样被其他查找遍历: | |
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,741行: | 第1,379行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> vegetarian_pizzas = Pizza.objects.filter(vegetarian=True) |
− | + | >>> Restaurant.objects.prefetch_related( | |
... Prefetch('pizzas', queryset=vegetarian_pizzas, to_attr='vegetarian_menu'), | ... Prefetch('pizzas', queryset=vegetarian_pizzas, to_attr='vegetarian_menu'), | ||
− | ... 'vegetarian_menu__toppings')</ | + | ... 'vegetarian_menu__toppings')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在过滤预取结果时建议使用 <code>to_attr</code>,因为它比将过滤结果存储在相关管理器的缓存中更不模糊: | |
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,756行: | 第1,393行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> queryset = Pizza.objects.filter(vegetarian=True) |
− | + | >>> | |
− | + | >>> # Recommended: | |
− | + | >>> restaurants = Restaurant.objects.prefetch_related( | |
... Prefetch('pizzas', queryset=queryset, to_attr='vegetarian_pizzas')) | ... Prefetch('pizzas', queryset=queryset, to_attr='vegetarian_pizzas')) | ||
− | + | >>> vegetarian_pizzas = restaurants[0].vegetarian_pizzas | |
− | + | >>> | |
− | + | >>> # Not recommended: | |
− | + | >>> restaurants = Restaurant.objects.prefetch_related( | |
... Prefetch('pizzas', queryset=queryset)) | ... Prefetch('pizzas', queryset=queryset)) | ||
− | + | >>> vegetarian_pizzas = restaurants[0].pizzas.all()</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 自定义预取也适用于单个相关关系,如前向 <code>ForeignKey</code> 或 <code>OneToOneField</code>。 通常,您需要将 [[#django.db.models.query.QuerySet.select_related|select_related()]] 用于这些关系,但在许多情况下,使用自定义 <code>QuerySet</code> 进行预取很有用: | |
− | |||
− | [[#django.db.models.query.QuerySet.select_related| | ||
− | |||
<ul> | <ul> | ||
− | <li><p> | + | <li><p>您想使用 <code>QuerySet</code> 对相关模型执行进一步预取。</p></li> |
− | + | <li><p>你想只预取相关对象的一个子集。</p></li> | |
− | <li><p> | + | <li><p>您想使用性能优化技术,如 [[#django.db.models.query.QuerySet.defer|延迟字段]] :</p> |
− | <li><p> | ||
− | [[#django.db.models.query.QuerySet.defer| | ||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> queryset = Pizza.objects.only('name') |
− | + | >>> | |
− | + | >>> restaurants = Restaurant.objects.prefetch_related( | |
− | ... Prefetch('best_pizza', queryset=queryset))</ | + | ... Prefetch('best_pizza', queryset=queryset))</syntaxhighlight> |
</div> | </div> | ||
第1,799行: | 第1,431行: | ||
注解 | 注解 | ||
− | + | 查询的顺序很重要。 | |
− | + | 下面举例说明: | |
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,807行: | 第1,439行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> prefetch_related('pizzas__toppings', 'pizzas')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 即使它是无序的,这也有效,因为 <code>'pizzas__toppings'</code> 已经包含所有需要的信息,因此第二个参数 <code>'pizzas'</code> 实际上是多余的。 | |
− | |||
− | <code>'pizzas'</code> | ||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,820行: | 第1,450行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> prefetch_related('pizzas__toppings', Prefetch('pizzas', queryset=Pizza.objects.all()))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 由于尝试重新定义先前看到的查找的查询集,这将引发 <code>ValueError</code>。 请注意,作为 <code>'pizzas__toppings'</code> 查找的一部分,创建了一个隐式查询集来遍历 <code>'pizzas'</code>。 | |
− | |||
− | |||
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第1,834行: | 第1,461行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> prefetch_related('pizza_list__toppings', Prefetch('pizzas', to_attr='pizza_list'))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这将触发 <code>AttributeError</code>,因为在处理 <code>'pizza_list__toppings'</code> 时 <code>'pizza_list'</code> 还不存在。 | |
− | |||
− | + | 这种考虑不限于使用 <code>Prefetch</code> 对象。 一些高级技术可能要求以特定顺序执行查找以避免创建额外的查询; 因此,建议始终谨慎地对 <code>prefetch_related</code> 参数进行排序。 | |
− | |||
− | |||
− | |||
第1,853行: | 第1,476行: | ||
<div id="extra" class="section"> | <div id="extra" class="section"> | ||
− | ==== | + | ==== extra() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">extra</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">select</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">where</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">params</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">tables</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">order_by</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">select_params</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 有时,Django 查询语法本身无法轻松表达复杂的 <code>WHERE</code> 子句。 对于这些边缘情况,Django 提供了 <code>extra()</code> <code>QuerySet</code> 修饰符——一个钩子,用于将特定子句注入由 <code>QuerySet</code> 生成的 SQL。 | |
− | <code>WHERE</code> | ||
− | <code>QuerySet</code> | ||
− | |||
<div class="admonition-use-this-method-as-a-last-resort admonition"> | <div class="admonition-use-this-method-as-a-last-resort admonition"> | ||
− | + | 在万不得已的情况下使用这种方法 | |
− | + | 这是一个旧的 API,我们的目标是在未来的某个时候弃用。 仅当您无法使用其他查询集方法表达查询时才使用它。 如果您确实需要使用它,请使用 [https://code.djangoproject.com/query?status=assigned&status=new&keywords=~QuerySet.extra QuerySet.extra 关键字] 与您的用例一起 [https://code.djangoproject.com/newticket 提交票证] (请先检查现有票证列表),以便我们可以增强QuerySet API 允许删除 <code>extra()</code>。 我们不再改进或修复此方法的错误。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 例如,<code>extra()</code> 的这种用法: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,881行: | 第1,495行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> qs.extra( |
− | ... select={'val': | + | ... select={'val': "select col from sometable where othercol = %s"}, |
... select_params=(someparam,), | ... select_params=(someparam,), | ||
− | ... )</ | + | ... )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 相当于: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,895行: | 第1,509行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 使用 [[../expressions#django.db.models.expressions|RawSQL]] 的主要好处是您可以根据需要设置 <code>output_field</code>。 主要的缺点是,如果您在原始 SQL 中引用查询集的某个表别名,那么 Django 可能会更改该别名(例如,当查询集用作另一个查询中的子查询时)。 | |
− | |||
− | |||
− | |||
− | |||
第1,912行: | 第1,522行: | ||
警告 | 警告 | ||
− | + | 使用 <code>extra()</code> 时应该非常小心。 每次使用它时,都应该使用 <code>params</code> 对用户可以控制的任何参数进行转义,以防止 SQL 注入攻击。 | |
− | |||
− | <code>params</code> | ||
− | + | 您也不得在 SQL 字符串中引用占位符。 由于 <code>%s</code> 周围的引号,此示例容易受到 SQL 注入攻击: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第1,923行: | 第1,530行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">"select col from sometable where othercol = '%s'" # unsafe!</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以阅读有关 Django [[../../../topics/security#sql-injection-protection|SQL 注入保护]] 工作原理的更多信息。 | |
</div> | </div> | ||
− | + | 根据定义,这些额外的查找可能无法移植到不同的数据库引擎(因为您明确地编写 SQL 代码)并且违反了 DRY 原则,因此您应该尽可能避免它们。 | |
− | |||
− | |||
− | + | 指定 <code>params</code>、<code>select</code>、<code>where</code> 或 <code>tables</code> 中的一个或多个。 不需要任何参数,但您应该至少使用其中之一。 | |
− | |||
<ul> | <ul> | ||
<li><p><code>select</code></p> | <li><p><code>select</code></p> | ||
− | <p> | + | <p><code>select</code> 参数允许您在 <code>SELECT</code> 子句中放置额外的字段。 它应该是一个将属性名称映射到 SQL 子句以用于计算该属性的字典。</p> |
− | + | <p>例子:</p> | |
− | |||
− | <p> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>因此,每个 <code>Entry</code> 对象都会有一个额外的属性 <code>is_recent</code>,这是一个布尔值,表示条目的 <code>pub_date</code> 是否大于 Jan。 1, 2006.</p> |
− | <code>is_recent</code> | + | <p>Django 将给定的 SQL 片段直接插入到 <code>SELECT</code> 语句中,因此上述示例的结果 SQL 将类似于:</p> |
− | |||
− | <p>Django | ||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent |
− | FROM blog_entry;</ | + | FROM blog_entry;</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>下一个例子更高级; 它执行一个子查询,为每个结果 <code>Blog</code> 对象提供一个 <code>entry_count</code> 属性,关联 <code>Entry</code> 对象的整数计数:</p> |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.extra( |
select={ | select={ | ||
'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id' | 'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id' | ||
}, | }, | ||
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>在这种特殊情况下,我们利用查询已经在其 <code>FROM</code> 子句中包含 <code>blog_blog</code> 表的事实。</p> |
− | + | <p>上述示例的结果 SQL 将是:</p> | |
− | <p> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count |
− | FROM blog_blog;</ | + | FROM blog_blog;</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>请注意,在 Django 的 <code>select</code> 子句中,大多数数据库引擎所需的围绕子查询的括号不是必需的。 另请注意,某些数据库后端(例如某些 MySQL 版本)不支持子查询。</p> |
− | + | <p>在极少数情况下,您可能希望将参数传递给 <code>extra(select=...)</code> 中的 SQL 片段。 为此,请使用 <code>select_params</code> 参数。 由于 <code>select_params</code> 是一个序列,而 <code>select</code> 属性是一个字典,因此需要小心谨慎,以便参数与额外的选择片段正确匹配。 在这种情况下,您应该对 <code>select</code> 值使用 <code>collections.OrderedDict</code>,而不仅仅是普通的 Python 字典。</p> | |
− | + | <p>这将起作用,例如:</p> | |
− | |||
− | <p> | ||
− | |||
− | <code>select_params</code> | ||
− | |||
− | |||
− | |||
− | |||
− | <p> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.extra( |
select=OrderedDict([('a', '%s'), ('b', '%s')]), | select=OrderedDict([('a', '%s'), ('b', '%s')]), | ||
− | select_params=('one', 'two'))</ | + | select_params=('one', 'two'))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>如果需要在选择字符串中使用文字 <code>%s</code>,请使用序列 <code>%%s</code>。</p></li> |
− | |||
<li><p><code>where</code> / <code>tables</code></p> | <li><p><code>where</code> / <code>tables</code></p> | ||
− | <p> | + | <p>您可以使用 <code>where</code> 定义显式 SQL <code>WHERE</code> 子句——也许是为了执行非显式连接。 您可以使用 <code>tables</code> 手动向 SQL <code>FROM</code> 子句添加表。</p> |
− | + | <p><code>where</code> 和 <code>tables</code> 都采用字符串列表。 所有 <code>where</code> 参数都与任何其他搜索条件进行“与”运算。</p> | |
− | + | <p>例子:</p> | |
− | <p><code>where</code> | ||
− | |||
− | <p> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p>... | + | <p>...翻译(大致)为以下 SQL:</p> |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>如果您指定的表已在查询中使用,则在使用 <code>tables</code> 参数时要小心。 当您通过 <code>tables</code> 参数添加额外的表时,Django 假定您希望该表包含额外的时间,如果它已经包含在内。 这就产生了一个问题,因为表名将被赋予一个别名。 如果一个表在 SQL 语句中多次出现,则第二次和后续出现必须使用别名,以便数据库可以区分它们。 如果您指的是在额外 <code>where</code> 参数中添加的额外表,这将导致错误。</p> |
− | + | <p>通常,您只会添加尚未出现在查询中的额外表。 但是,如果确实发生了上述情况,则有几种解决方案。 首先,看看您是否可以不包含额外的表并使用查询中已有的表。 如果这是不可能的,请将您的 <code>extra()</code> 调用放在查询集构造的前面,以便您的表是该表的第一次使用。 最后,如果所有其他方法都失败了,请查看生成的查询并重写您的 <code>where</code> 添加以使用为额外表提供的别名。 每次以相同的方式构造查询集时,别名都将相同,因此您可以依赖别名不会更改。</p></li> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | <p> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | <code>where</code> | ||
− | |||
− | |||
<li><p><code>order_by</code></p> | <li><p><code>order_by</code></p> | ||
− | <p> | + | <p>如果您需要使用通过 <code>extra()</code> 包含的一些新字段或表对结果查询集进行排序,请使用 <code>order_by</code> 参数到 <code>extra()</code> 并传入一系列字符串。 这些字符串应该是模型字段(如查询集上的正常 [[#django.db.models.query.QuerySet.order_by|order_by()]] 方法),形式为 <code>table_name.column_name</code> 或您在 [ 中指定的列的别名X181X] 参数设置为 <code>extra()</code>。</p> |
− | + | <p>例如:</p> | |
− | |||
− | |||
− | [[#django.db.models.query.QuerySet.order_by| | ||
− | <code>table_name.column_name</code> | ||
− | |||
− | <p> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"}) |
− | q = q.extra(order_by = ['-is_recent'])</ | + | q = q.extra(order_by = ['-is_recent'])</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>这会将 <code>is_recent</code> 为真的所有项目排序到结果集的前面(<code>True</code> 按降序排列在 <code>False</code> 之前)。</p> |
− | + | <p>顺便说一下,这表明您可以多次调用 <code>extra()</code> 并且它会按照您的预期运行(每次添加新约束)。</p></li> | |
− | |||
− | <p> | ||
− | |||
<li><p><code>params</code></p> | <li><p><code>params</code></p> | ||
− | <p> | + | <p>上述 <code>where</code> 参数可以使用标准 Python 数据库字符串占位符 — <code>'%s'</code> 来指示数据库引擎应自动引用的参数。 <code>params</code> 参数是要替换的任何额外参数的列表。</p> |
− | + | <p>例子:</p> | |
− | |||
− | |||
− | <p> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.extra(where=['headline=%s'], params=['Lennon'])</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>始终使用 <code>params</code> 而不是直接将值嵌入到 <code>where</code> 中,因为 <code>params</code> 将确保根据您的特定后端正确引用值。 例如,引号将被正确转义。</p> |
− | <code>where</code> | + | <p>坏的:</p> |
− | |||
− | |||
− | <p> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.extra(where=["headline='Lennon'"])</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>好的:</p> |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.extra(where=['headline=%s'], params=['Lennon'])</syntaxhighlight> |
</div> | </div> | ||
第2,137行: | 第1,687行: | ||
警告 | 警告 | ||
− | + | 如果您在 MySQL 上执行查询,请注意 MySQL 的静默类型强制在混合类型时可能会导致意外结果。 如果查询字符串类型的列,但使用的是整数值,MySQL 会在执行比较之前将表中所有值的类型强制为整数。 例如,如果您的表包含值 <code>'abc'</code>、<code>'def'</code> 并且您查询 <code>WHERE mycolumn=0</code>,则两行都将匹配。 为防止出现这种情况,请在查询中使用该值之前执行正确的类型转换。 | |
第2,145行: | 第1,695行: | ||
<div id="defer" class="section"> | <div id="defer" class="section"> | ||
− | ==== | + | ==== defer() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">defer</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">fields</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 在某些复杂的数据建模情况下,您的模型可能包含大量字段,其中一些字段可能包含大量数据(例如,文本字段),或者需要进行昂贵的处理才能将它们转换为 Python 对象。 如果您在最初获取数据时不知道是否需要这些特定字段的情况下使用查询集的结果,您可以告诉 Django 不要从数据库中检索它们。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 这是通过将字段的名称传递给 <code>defer()</code> 来完成的: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,163行: | 第1,708行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.defer("headline", "body")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 具有延迟字段的查询集仍将返回模型实例。 如果您访问该字段(一次一个,而不是一次访问所有延迟字段),将从数据库中检索每个延迟字段。 | |
− | |||
− | |||
− | + | 您可以多次呼叫 <code>defer()</code>。 每次调用都会向延迟集合添加新字段: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,179行: | 第1,721行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Defers both the body and headline fields. |
− | Entry.objects.defer( | + | Entry.objects.defer("body").filter(rating=5).defer("headline")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 将字段添加到延迟集中的顺序无关紧要。 使用已被延迟的字段名称调用 <code>defer()</code> 是无害的(该字段仍将被延迟)。 | |
− | |||
− | |||
− | + | 您可以通过使用标准双下划线符号分隔相关字段来延迟加载相关模型中的字段(如果相关模型通过 [[#django.db.models.query.QuerySet.select_related|select_related()]] 加载): | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,197行: | 第1,735行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.select_related().defer("entry__headline", "entry__body")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果要清除延迟字段集,请将 <code>None</code> 作为参数传递给 <code>defer()</code>: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,209行: | 第1,746行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Load all fields immediately. |
− | my_queryset.defer(None)</ | + | my_queryset.defer(None)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 模型中的某些字段不会被延迟,即使您要求它们。 你永远不能推迟主键的加载。 如果您使用 [[#django.db.models.query.QuerySet.select_related|select_related()]] 检索相关模型,则不应推迟从主模型连接到相关模型的字段的加载,否则会导致错误。 | |
− | |||
− | [[#django.db.models.query.QuerySet.select_related| | ||
− | |||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
第2,225行: | 第1,758行: | ||
注解 | 注解 | ||
− | + | <code>defer()</code> 方法(及其表亲 [[#django.db.models.query.QuerySet.only|only()]],如下)仅适用于高级用例。 当您仔细分析查询并准确了解 '''' 您需要什么信息并测量返回您需要的字段与模型的完整字段集之间的差异时,它们提供了一种优化。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 即使您认为您处于高级用例情况, '''仅在您无法在查询集加载时确定是否需要额外字段时才使用 defer()''' 。 如果您经常加载和使用数据的特定子集,您可以做出的最佳选择是规范化模型并将未加载的数据放入单独的模型(和数据库表)中。 如果列 ''must'' 出于某种原因保留在一个表中,请使用 <code>Meta.managed = False</code> 创建一个模型(请参阅 [[../options#django.db.models.Options|managed 属性]] 文档)只包含您通常需要的字段加载并使用您可能会调用 <code>defer()</code> 的地方。 这使您的代码对读者更加明确,速度稍快,并且在 Python 进程中消耗的内存更少。 | |
− | defer() | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | [[../options#django.db.models.Options| | ||
− | |||
− | |||
− | |||
− | |||
− | + | 例如,这两个模型都使用相同的底层数据库表: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,250行: | 第1,768行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">class CommonlyUsedModel(models.Model): |
f1 = models.CharField(max_length=10) | f1 = models.CharField(max_length=10) | ||
第2,266行: | 第1,784行: | ||
# Two equivalent QuerySets: | # Two equivalent QuerySets: | ||
CommonlyUsedModel.objects.all() | CommonlyUsedModel.objects.all() | ||
− | ManagedModel.objects.all().defer('f2')</ | + | ManagedModel.objects.all().defer('f2')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果很多字段需要在非托管模型中重复,最好的办法是创建一个共享字段的抽象模型,然后让非托管模型和托管模型从抽象模型中继承。 | |
− | |||
− | |||
第2,281行: | 第1,797行: | ||
注解 | 注解 | ||
− | + | 当为具有延迟字段的实例调用 [[../instances#django.db.models.Model|save()]] 时,只会保存加载的字段。 有关更多详细信息,请参阅 [[../instances#django.db.models.Model|save()]]。 | |
− | |||
− | [[../instances#django.db.models.Model| | ||
第2,291行: | 第1,805行: | ||
<div id="only" class="section"> | <div id="only" class="section"> | ||
− | ==== | + | ==== only() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">only</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">fields</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | <code>only()</code> 方法或多或少与 [[#django.db.models.query.QuerySet.defer|defer()]] 相反。 您可以使用在检索模型时应延迟 ''而非'' 的字段调用它。 如果您的模型几乎所有字段都需要延迟,则使用 <code>only()</code> 指定补充字段集可以简化代码。 | |
− | |||
− | |||
− | <code>only()</code> | ||
− | |||
− | + | 假设您有一个包含字段 <code>name</code>、<code>age</code> 和 <code>biography</code> 的模型。 以下两个查询集在延迟字段方面是相同的: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,309行: | 第1,818行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Person.objects.defer("age", "biography") |
− | Person.objects.only( | + | Person.objects.only("name")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 每当您调用 <code>only()</code> 时,它会 ''替换'' 立即加载的字段集。 该方法的名称是助记符:'''only''' 那些字段会立即加载; 其余的被推迟。 因此,对 <code>only()</code> 的连续调用导致只考虑最终字段: | |
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,324行: | 第1,830行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># This will defer all fields except the headline. |
− | Entry.objects.only( | + | Entry.objects.only("body", "rating").only("headline")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 由于 <code>defer()</code> 以增量方式起作用(将字段添加到延迟列表中),您可以组合对 <code>only()</code> 和 <code>defer()</code> 的调用,并且事情将按逻辑进行: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,338行: | 第1,842行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Final result is that everything except "headline" is deferred. |
− | Entry.objects.only( | + | Entry.objects.only("headline", "body").defer("body") |
# Final result loads headline and body immediately (only() replaces any | # Final result loads headline and body immediately (only() replaces any | ||
# existing set of fields). | # existing set of fields). | ||
− | Entry.objects.defer( | + | Entry.objects.defer("body").only("headline", "body")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | [[#django.db.models.query.QuerySet.defer|defer()]] 文档注释中的所有注意事项也适用于 <code>only()</code>。 谨慎使用它,并且只有在用尽你的其他选择之后。 | |
− | <code>only()</code> | ||
− | |||
− | + | 使用 [[#django.db.models.query.QuerySet.only|only()]] 并省略使用 [[#django.db.models.query.QuerySet.select_related|select_related()]] 请求的字段也是一个错误。 | |
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
第2,359行: | 第1,860行: | ||
注解 | 注解 | ||
− | + | 当为具有延迟字段的实例调用 [[../instances#django.db.models.Model|save()]] 时,只会保存加载的字段。 有关更多详细信息,请参阅 [[../instances#django.db.models.Model|save()]]。 | |
− | |||
− | [[../instances#django.db.models.Model| | ||
第2,369行: | 第1,868行: | ||
<div id="using" class="section"> | <div id="using" class="section"> | ||
− | ==== | + | ==== using() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">using</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">alias</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 如果您使用多个数据库,此方法用于控制 <code>QuerySet</code> 将针对哪个数据库进行评估。 此方法采用的唯一参数是数据库的别名,如 [[#id6|:setting:`DATABASES`]] 中所定义。 | |
− | |||
− | |||
− | [[ | ||
− | + | 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,385行: | 第1,881行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># queries the database with the 'default' alias. |
− | + | >>> Entry.objects.all() | |
# queries the database with the 'backup' alias | # queries the database with the 'backup' alias | ||
− | + | >>> Entry.objects.using('backup')</syntaxhighlight> | |
</div> | </div> | ||
第2,398行: | 第1,894行: | ||
<div id="select-for-update" class="section"> | <div id="select-for-update" class="section"> | ||
− | ==== | + | ==== select_for_update() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">select_for_update</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">nowait</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">skip_locked</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">of</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">()</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回将锁定行直到事务结束的查询集,在支持的数据库上生成 <code>SELECT ... FOR UPDATE</code> SQL 语句。 | |
− | |||
− | + | 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,412行: | 第1,907行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.db import transaction |
entries = Entry.objects.select_for_update().filter(author=request.user) | entries = Entry.objects.select_for_update().filter(author=request.user) | ||
with transaction.atomic(): | with transaction.atomic(): | ||
for entry in entries: | for entry in entries: | ||
− | ...</ | + | ...</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 当查询集被评估时(在本例中为 <code>for entry in entries</code>),所有匹配的条目将被锁定,直到事务块结束,这意味着将阻止其他事务更改或获取对它们的锁。 | |
− | |||
− | |||
− | |||
− | + | 通常,如果另一个事务已经获得了对选定行之一的锁定,则查询将阻塞,直到锁定被释放。 如果这不是您想要的行为,请调用 <code>select_for_update(nowait=True)</code>。 这将使调用非阻塞。 如果另一个事务已经获取了冲突锁,则在评估查询集时将引发 [[../../exceptions#django.db|DatabaseError]]。 您也可以使用 <code>select_for_update(skip_locked=True)</code> 来忽略锁定的行。 <code>nowait</code> 和 <code>skip_locked</code> 是互斥的,在启用这两个选项的情况下尝试调用 <code>select_for_update()</code> 将导致 <code>ValueError</code>。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | <code>select_for_update(skip_locked=True)</code> | ||
− | <code>skip_locked</code> | ||
− | <code>select_for_update()</code> | ||
− | <code>ValueError</code> | ||
− | + | 默认情况下,<code>select_for_update()</code> 锁定查询选择的所有行。 例如,除了查询集模型的行之外,[[#django.db.models.query.QuerySet.select_related|select_related()]] 中指定的相关对象的行也被锁定。 如果不需要,请使用与 [[#django.db.models.query.QuerySet.select_related|select_related()]] 相同的字段语法在 <code>select_for_update(of=(...))</code> 中指定要锁定的相关对象。 使用值 <code>'self'</code> 来引用查询集的模型。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="admonition-lock-parents-models-in-select-for-update-of admonition"> | <div class="admonition-lock-parents-models-in-select-for-update-of admonition"> | ||
− | + | 在 <code>select_for_update(of=(...))</code> 中锁定父母模型 | |
− | + | 如果要在使用 [[../../../topics/db/models#multi-table-inheritance|多表继承]] 时锁定父模型,则必须在 <code>of</code> 参数中指定父链接字段(默认为 <code><parent_model_name>_ptr</code>)。 例如: | |
− | <code><parent_model_name>_ptr</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,456行: | 第1,933行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Restaurant.objects.select_for_update(of=('self', 'place_ptr'))</syntaxhighlight> |
</div> | </div> | ||
第2,463行: | 第1,940行: | ||
</div> | </div> | ||
− | + | 您不能在可空关系上使用 <code>select_for_update()</code>: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,469行: | 第1,946行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Person.objects.select_related('hometown').select_for_update() |
Traceback (most recent call last): | Traceback (most recent call last): | ||
... | ... | ||
− | django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullable side of an outer join</ | + | django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullable side of an outer join</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 为了避免这种限制,如果您不关心它们,您可以排除空对象: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,484行: | 第1,960行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Person.objects.select_related('hometown').select_for_update().exclude(hometown=None) |
− | + | <QuerySet [<Person: ...)>, ...]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 目前,<code>postgresql</code>、<code>oracle</code> 和 <code>mysql</code> 数据库后端支持 <code>select_for_update()</code>。 但是,MySQL 不支持 [X35X] 参数,并且仅在 MySQL 8.0.1+ 上支持 <code>nowait</code> 和 <code>skip_locked</code> 参数。 | |
− | |||
− | |||
− | |||
− | + | 使用不支持这些选项的数据库后端(例如 MySQL)将 <code>nowait=True</code>、<code>skip_locked=True</code> 或 <code>of</code> 传递给 <code>select_for_update()</code>,会引发 NotSupportedError[ X160X]。 这可以防止代码意外阻塞。 | |
− | <code>select_for_update()</code> | ||
− | |||
− | |||
− | + | 在支持 <code>SELECT ... FOR UPDATE</code> 的后端在自动提交模式下使用 <code>select_for_update()</code> 评估查询集是一个 [[../../exceptions#django.db.transaction|TransactionManagementError]] 错误,因为在这种情况下行未锁定。 如果允许,这将促进数据损坏,并且很容易由调用预期在事务之外的事务中运行的代码引起。 | |
− | |||
− | [[../../exceptions#django.db.transaction| | ||
− | |||
− | |||
− | |||
− | + | 在不支持 <code>SELECT ... FOR UPDATE</code> 的后端(例如 SQLite)上使用 <code>select_for_update()</code> 将不起作用。 <code>SELECT ... FOR UPDATE</code> 不会添加到查询中,如果在自动提交模式下使用 <code>select_for_update()</code>,则不会引发错误。 | |
− | <code>SELECT ... FOR UPDATE</code> ( | ||
− | <code>SELECT ... FOR UPDATE</code> | ||
− | |||
<div class="admonition warning"> | <div class="admonition warning"> | ||
第2,516行: | 第1,978行: | ||
警告 | 警告 | ||
− | + | 尽管 <code>select_for_update()</code> 在自动提交模式下通常会失败,但由于 [[../../../topics/testing/tools#django.test|TestCase]] 自动将每个测试包装在一个事务中,即使在 [[../../../topics/db/transactions#django.db.transaction|之外调用 <code>TestCase</code> 中的 <code>select_for_update()</code> atomic()]] 块将(可能出乎意料地)通过而不引发 <code>TransactionManagementError</code>。 要正确测试 <code>select_for_update()</code>,您应该使用 [[../../../topics/testing/tools#django.test|TransactionTestCase]]。 | |
− | [[../../../topics/testing/tools#django.test| | ||
− | |||
− | |||
− | |||
− | <code>select_for_update()</code> | ||
− | [[../../../topics/testing/tools#django.test| | ||
第2,528行: | 第1,984行: | ||
<div class="admonition-certain-expressions-may-not-be-supported admonition"> | <div class="admonition-certain-expressions-may-not-be-supported admonition"> | ||
− | + | 可能不支持某些表达方式 | |
− | PostgreSQL | + | PostgreSQL 不支持 <code>select_for_update()</code> 和 [[../expressions#django.db.models.expressions|Window]] 表达式。 |
− | [[../expressions#django.db.models.expressions| | ||
第2,539行: | 第1,994行: | ||
<div id="raw" class="section"> | <div id="raw" class="section"> | ||
− | ==== | + | ==== raw() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">raw</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">raw_query</span></span>'', ''<span class="n"><span class="pre">params</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">translations</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 获取原始 SQL 查询,执行它,并返回一个 <code>django.db.models.query.RawQuerySet</code> 实例。 这个 <code>RawQuerySet</code> 实例可以像普通的 <code>QuerySet</code> 一样迭代以提供对象实例。 | |
− | <code>django.db.models.query.RawQuerySet</code> | ||
− | |||
− | + | 有关详细信息,请参阅 [[../../../topics/db/sql|执行原始 SQL 查询]] 。 | |
<div class="admonition warning"> | <div class="admonition warning"> | ||
第2,554行: | 第2,007行: | ||
警告 | 警告 | ||
− | <code>raw()</code> | + | <code>raw()</code> 总是触发一个新的查询并且不考虑之前的过滤。 因此,通常应该从 <code>Manager</code> 或新的 <code>QuerySet</code> 实例调用它。 |
− | |||
− | |||
第2,566行: | 第2,017行: | ||
<div id="operators-that-return-new-querysets" class="section"> | <div id="operators-that-return-new-querysets" class="section"> | ||
− | === | + | === 返回新 QuerySet 的运算符 === |
− | + | 组合的查询集必须使用相同的模型。 | |
<div id="and" class="section"> | <div id="and" class="section"> | ||
− | ==== | + | ==== 与 (&) ==== |
− | + | 使用 SQL <code>AND</code> 运算符组合两个 <code>QuerySet</code>。 | |
− | + | 以下是等效的: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,582行: | 第2,033行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Model.objects.filter(x=1) & Model.objects.filter(y=2) |
Model.objects.filter(x=1, y=2) | Model.objects.filter(x=1, y=2) | ||
from django.db.models import Q | from django.db.models import Q | ||
− | Model.objects.filter(Q(x=1) & | + | Model.objects.filter(Q(x=1) & Q(y=2))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-sql notranslate"> | <div class="highlight-sql notranslate"> | ||
第2,596行: | 第2,047行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="sql">SELECT ... WHERE x=1 AND y=2</syntaxhighlight> |
</div> | </div> | ||
第2,605行: | 第2,056行: | ||
<div id="or" class="section"> | <div id="or" class="section"> | ||
− | ==== | + | ==== 或 (|) ==== |
− | + | 使用 SQL <code>OR</code> 运算符组合两个 <code>QuerySet</code>。 | |
− | + | 以下是等效的: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,615行: | 第2,066行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Model.objects.filter(x=1) | Model.objects.filter(y=2) |
from django.db.models import Q | from django.db.models import Q | ||
− | Model.objects.filter(Q(x=1) | Q(y=2))</ | + | Model.objects.filter(Q(x=1) | Q(y=2))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-sql notranslate"> | <div class="highlight-sql notranslate"> | ||
第2,628行: | 第2,079行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="sql">SELECT ... WHERE x=1 OR y=2</syntaxhighlight> |
</div> | </div> | ||
第2,639行: | 第2,090行: | ||
<div id="methods-that-do-not-return-querysets" class="section"> | <div id="methods-that-do-not-return-querysets" class="section"> | ||
− | === | + | === 不返回 QuerySets 的方法 === |
− | + | 以下 <code>QuerySet</code> 方法评估 <code>QuerySet</code> 并返回 ''以外的'' 和 <code>QuerySet</code>。 | |
− | |||
− | + | 这些方法不使用缓存(参见 [[../../../topics/db/queries#caching-and-querysets|Caching 和 QuerySets]])。 相反,他们每次被调用时都会查询数据库。 | |
− | |||
<div id="get" class="section"> | <div id="get" class="section"> | ||
− | ==== | + | ==== get() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">get</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回与给定查找参数匹配的对象,该对象应采用 [[#id10|字段查找]] 中描述的格式。 | |
− | |||
− | <code>get()</code> | + | <code>get()</code> 如果找到多个对象,则引发 [[../../exceptions#django.core.exceptions|MultipleObjectsReturned]]。 [[../../exceptions#django.core.exceptions|MultipleObjectsReturned]] 异常是模型类的一个属性。 |
− | |||
− | [[../../exceptions#django.core.exceptions| | ||
− | |||
− | <code>get()</code> | + | <code>get()</code> 如果未找到给定参数的对象,则会引发 [[../instances#django.db.models.Model|DoesNotExist]] 异常。 这个异常是模型类的一个属性。 例子: |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,670行: | 第2,113行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.get(id='foo') # raises Entry.DoesNotExist</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | [[../instances#django.db.models.Model|DoesNotExist]] 异常继承自 [[../../exceptions#django.core.exceptions|django.core.exceptions.ObjectDoesNotExist]],因此您可以针对多个 [[../instances#django.db.models.Model|DoesNotExist]] 异常。 例子: | |
− | [[../../exceptions#django.core.exceptions| | ||
− | [[../instances#django.db.models.Model| | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,683行: | 第2,124行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.core.exceptions import ObjectDoesNotExist |
try: | try: | ||
e = Entry.objects.get(id=3) | e = Entry.objects.get(id=3) | ||
b = Blog.objects.get(id=1) | b = Blog.objects.get(id=1) | ||
except ObjectDoesNotExist: | except ObjectDoesNotExist: | ||
− | print( | + | print("Either the entry or blog doesn't exist.")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您希望查询集返回一行,则可以使用不带任何参数的 <code>get()</code> 来返回该行的对象: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,700行: | 第2,140行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">entry = Entry.objects.filter(...).exclude(...).get()</syntaxhighlight> |
</div> | </div> | ||
第2,709行: | 第2,149行: | ||
<div id="create" class="section"> | <div id="create" class="section"> | ||
− | ==== | + | ==== create() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">create</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 一种创建对象并将其全部保存在一个步骤中的便捷方法。 因此: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,720行: | 第2,160行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">p = Person.objects.create(first_name="Bruce", last_name="Springsteen")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 和: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,731行: | 第2,171行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">p = Person(first_name="Bruce", last_name="Springsteen") |
− | p.save(force_insert=True)</ | + | p.save(force_insert=True)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 是等效的。 | |
− | + | [[../instances#ref-models-force-insert|force_insert]] 参数在别处有说明,但这意味着总是会创建一个新对象。 通常你不需要担心这个。 但是,如果您的模型包含您设置的手动主键值,并且该值已存在于数据库中,则调用 <code>create()</code> 将失败并显示 [[../../exceptions#django.db|IntegrityError]],因为主键必须是唯一的. 如果您使用手动主键,请准备好处理异常。 | |
− | |||
− | |||
− | |||
− | |||
− | [[../../exceptions#django.db| | ||
− | |||
第2,751行: | 第2,185行: | ||
<div id="get-or-create" class="section"> | <div id="get-or-create" class="section"> | ||
− | ==== | + | ==== get_or_create() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">get_or_create</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">defaults</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 一种使用给定 <code>kwargs</code> 查找对象的便捷方法(如果您的模型具有所有字段的默认值,则可能为空),如有必要,创建一个。 | |
− | |||
− | + | 返回 <code>(object, created)</code> 的元组,其中 <code>object</code> 是检索或创建的对象,<code>created</code> 是指定是否创建新对象的布尔值。 | |
− | |||
− | |||
− | + | 这是为了防止在并行发出请求时创建重复的对象,并作为样板代码的快捷方式。 例如: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,770行: | 第2,200行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">try: |
obj = Person.objects.get(first_name='John', last_name='Lennon') | obj = Person.objects.get(first_name='John', last_name='Lennon') | ||
except Person.DoesNotExist: | except Person.DoesNotExist: | ||
obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9)) | obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9)) | ||
− | obj.save()</ | + | obj.save()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这里,对于并发请求,可能会多次尝试保存具有相同参数的 <code>Person</code>。 为了避免这种竞争条件,上面的例子可以使用 <code>get_or_create()</code> 重写,如下所示: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,787行: | 第2,215行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">obj, created = Person.objects.get_or_create( |
first_name='John', | first_name='John', | ||
last_name='Lennon', | last_name='Lennon', | ||
defaults={'birthday': date(1940, 10, 9)}, | defaults={'birthday': date(1940, 10, 9)}, | ||
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 任何传递给 <code>get_or_create()</code> 的关键字参数 — ''除外'' 一个称为 <code>defaults</code> 的可选参数 — 将在 [[#django.db.models.query.QuerySet.get|get()]] 调用中使用。 如果找到对象,<code>get_or_create()</code> 返回该对象的元组和 <code>False</code>。 | |
− | |||
− | |||
<div class="admonition warning"> | <div class="admonition warning"> | ||
第2,804行: | 第2,230行: | ||
警告 | 警告 | ||
− | + | 此方法是原子的,假设数据库强制关键字参数的唯一性(请参阅 [[../fields#django.db.models.Field|unique]] 或 [[../options#django.db.models.Options|unique_together]])。 如果关键字参数中使用的字段没有唯一性约束,则对此方法的并发调用可能会导致插入具有相同参数的多行。 | |
− | |||
− | [[../options#django.db.models.Options| | ||
− | |||
− | |||
− | |||
</div> | </div> | ||
− | + | 您可以通过将 <code>get_or_create()</code> 与 <code>filter()</code> 链接起来并使用 [[#django.db.models.Q|Q 对象]] 来为检索到的对象指定更复杂的条件。 例如,检索 Robert 或 Bob Marley(如果存在),否则创建后者: | |
− | <code>get_or_create()</code> | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,821行: | 第2,240行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from django.db.models import Q |
obj, created = Person.objects.filter( | obj, created = Person.objects.filter( | ||
Q(first_name='Bob') | Q(first_name='Robert'), | Q(first_name='Bob') | Q(first_name='Robert'), | ||
− | ).get_or_create(last_name='Marley', defaults={'first_name': 'Bob'})</ | + | ).get_or_create(last_name='Marley', defaults={'first_name': 'Bob'})</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果找到多个对象,<code>get_or_create()</code> 会引发 [[../../exceptions#django.core.exceptions|MultipleObjectsReturned]]。 如果''没有''找到一个对象,<code>get_or_create()</code>将实例化并保存一个新对象,返回一个新对象和<code>True</code>的元组。 新对象将根据以下算法大致创建: | |
− | [[../../exceptions#django.core.exceptions| | ||
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,840行: | 第2,255行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">params = {k: v for k, v in kwargs.items() if '__' not in k} |
params.update({k: v() if callable(v) else v for k, v in defaults.items()}) | params.update({k: v() if callable(v) else v for k, v in defaults.items()}) | ||
obj = self.model(**params) | obj = self.model(**params) | ||
− | obj.save()</ | + | obj.save()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在英语中,这意味着以任何不包含双下划线的非 <code>'defaults'</code> 关键字参数开头(这表示不精确的查找)。 然后添加 <code>defaults</code> 的内容,必要时覆盖任何键,并将结果用作模型类的关键字参数。 如果 <code>defaults</code> 中有任何可调用对象,请评估它们。 如上所述,这是所用算法的简化,但它包含所有相关细节。 内部实现具有比这更多的错误检查并处理一些额外的边缘条件; 如果您有兴趣,请阅读代码。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 如果您有一个名为 <code>defaults</code> 的字段并希望将其用作 <code>get_or_create()</code> 中的精确查找,只需使用 <code>'defaults__exact'</code>,如下所示: | |
− | <code>get_or_create()</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,864行: | 第2,271行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 当您使用手动指定的主键时,<code>get_or_create()</code> 方法具有与 [[#django.db.models.query.QuerySet.create|create()]] 类似的错误行为。 如果需要创建对象并且密钥已经存在于数据库中,则会引发 [[../../exceptions#django.db|IntegrityError]]。 | |
− | |||
− | |||
− | [[../../exceptions#django.db| | ||
− | + | 最后,谈谈在 Django 视图中使用 <code>get_or_create()</code>。 请确保仅在 <code>POST</code> 请求中使用它,除非您有充分的理由不这样做。 <code>GET</code> 请求不应对数据产生任何影响。 相反,只要对页面的请求对您的数据有副作用,就使用 <code>POST</code>。 有关更多信息,请参阅 HTTP 规范中的 <span id="index-0" class="target"></span>[https://tools.ietf.org/html/rfc7231.html#section-4.2.1 安全方法]。 | |
− | |||
− | <code>GET</code> | ||
− | |||
− | <span id="index-0" class="target"></span>[https://tools.ietf.org/html/rfc7231.html#section-4.2.1 | ||
<div class="admonition warning"> | <div class="admonition warning"> | ||
第2,884行: | 第2,284行: | ||
警告 | 警告 | ||
− | + | 您可以使用 <code>get_or_create()</code> 到 [[../fields#django.db.models|ManyToManyField]] 属性和反向关系。 在这种情况下,您将限制该关系上下文内的查询。 如果您不始终如一地使用它,这可能会导致您出现一些完整性问题。 | |
− | |||
− | |||
− | |||
− | + | 有以下型号: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,895行: | 第2,292行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">class Chapter(models.Model): |
title = models.CharField(max_length=255, unique=True) | title = models.CharField(max_length=255, unique=True) | ||
class Book(models.Model): | class Book(models.Model): | ||
title = models.CharField(max_length=256) | title = models.CharField(max_length=256) | ||
− | chapters = models.ManyToManyField(Chapter)</ | + | chapters = models.ManyToManyField(Chapter)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您可以通过 Book 的章节字段使用 <code>get_or_create()</code>,但它只能在该书的上下文中获取: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,912行: | 第2,308行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> book = Book.objects.create(title="Ulysses") |
− | + | >>> book.chapters.get_or_create(title="Telemachus") | |
− | ( | + | (<Chapter: Telemachus>, True) |
− | + | >>> book.chapters.get_or_create(title="Telemachus") | |
− | ( | + | (<Chapter: Telemachus>, False) |
− | + | >>> Chapter.objects.create(title="Chapter 1") | |
− | + | <Chapter: Chapter 1> | |
− | + | >>> book.chapters.get_or_create(title="Chapter 1") | |
− | # Raises IntegrityError</ | + | # Raises IntegrityError</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 之所以发生这种情况,是因为它试图通过“尤利西斯”一书获取或创建“第 1 章”,但它无法执行任何操作:该关系无法获取该章节,因为它与该书无关,但是它也不能创建它,因为 <code>title</code> 字段应该是唯一的。 | |
− | |||
− | |||
− | |||
第2,936行: | 第2,329行: | ||
<div id="update-or-create" class="section"> | <div id="update-or-create" class="section"> | ||
− | ==== | + | ==== update_or_create() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">update_or_create</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">defaults</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 一种使用给定 <code>kwargs</code> 更新对象的便捷方法,必要时创建一个新对象。 <code>defaults</code> 是用于更新对象的(字段,值)对的字典。 <code>defaults</code> 中的值可以是可调用的。 | |
− | |||
− | |||
− | + | 返回 <code>(object, created)</code> 元组,其中 <code>object</code> 是创建或更新的对象,<code>created</code> 是一个布尔值,指定是否创建了新对象。 | |
− | |||
− | |||
− | + | <code>update_or_create</code> 方法尝试根据给定的 <code>kwargs</code> 从数据库中获取一个对象。 如果找到匹配项,它会更新 <code>defaults</code> 字典中传递的字段。 | |
− | |||
− | <code>defaults</code> | ||
− | + | 这意味着作为样板代码的快捷方式。 例如: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,959行: | 第2,346行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">defaults = {'first_name': 'Bob'} |
try: | try: | ||
obj = Person.objects.get(first_name='John', last_name='Lennon') | obj = Person.objects.get(first_name='John', last_name='Lennon') | ||
第2,969行: | 第2,356行: | ||
new_values.update(defaults) | new_values.update(defaults) | ||
obj = Person(**new_values) | obj = Person(**new_values) | ||
− | obj.save()</ | + | obj.save()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 随着模型中字段数量的增加,这种模式变得非常笨拙。 上面的例子可以使用 <code>update_or_create()</code> 重写,如下所示: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第2,981行: | 第2,367行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">obj, created = Person.objects.update_or_create( |
first_name='John', last_name='Lennon', | first_name='John', last_name='Lennon', | ||
defaults={'first_name': 'Bob'}, | defaults={'first_name': 'Bob'}, | ||
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有关如何解析 <code>kwargs</code> 中传递的名称的详细说明,请参阅 [[#django.db.models.query.QuerySet.get_or_create|get_or_create()]]。 | |
− | [[#django.db.models.query.QuerySet.get_or_create| | ||
− | + | 如上文 [[#django.db.models.query.QuerySet.get_or_create|get_or_create()]] 中所述,此方法容易出现竞争条件,如果未在数据库级别强制执行唯一性,则可能导致同时插入多行。 | |
− | |||
− | |||
− | + | 像 [[#django.db.models.query.QuerySet.get_or_create|get_or_create()]] 和 [[#django.db.models.query.QuerySet.create|create()]],如果您使用手动指定的主键并且需要创建一个对象但该键已经存在于数据库中,则 [[../../exceptions#django.db|引发 IntegrityError]]。 | |
− | |||
− | |||
第3,004行: | 第2,385行: | ||
<div id="bulk-create" class="section"> | <div id="bulk-create" class="section"> | ||
− | ==== | + | ==== bulk_create() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">bulk_create</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">objs</span></span>'', ''<span class="n"><span class="pre">batch_size</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">ignore_conflicts</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 此方法以高效的方式将提供的对象列表插入到数据库中(通常只有 1 个查询,无论有多少个对象): | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,017行: | 第2,396行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.bulk_create([ |
... Entry(headline='This is a test'), | ... Entry(headline='This is a test'), | ||
... Entry(headline='This is only a test'), | ... Entry(headline='This is only a test'), | ||
− | ... ])</ | + | ... ])</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 不过这有一些注意事项: | |
<ul> | <ul> | ||
− | <li><p> | + | <li><p>不会调用模型的<code>save()</code>方法,也不会发送<code>pre_save</code>和<code>post_save</code>信号。</p></li> |
− | <code>post_save</code> | + | <li><p>在多表继承的情况下,它不能与子模型一起工作。</p></li> |
− | <li><p> | + | <li><p>如果模型的主键是 [[../fields#django.db.models|AutoField]],它不会像 <code>save()</code> 那样检索和设置主键属性,除非数据库后端支持它(当前是 PostgreSQL)。</p></li> |
− | <li><p> | + | <li><p>对于多对多的关系,它是行不通的。</p></li> |
− | + | <li><p>它将 <code>objs</code> 转换为一个列表,如果它是一个生成器,它会完全评估 <code>objs</code>。 强制转换允许检查所有对象,以便可以首先插入具有手动设置的主键的任何对象。 如果您想批量插入对象而不一次评估整个生成器,只要对象没有任何手动设置的主键,您就可以使用此技术:</p> | |
− | |||
− | <li><p> | ||
− | <li><p> | ||
− | |||
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">from itertools import islice |
batch_size = 100 | batch_size = 100 | ||
第3,052行: | 第2,424行: | ||
if not batch: | if not batch: | ||
break | break | ||
− | Entry.objects.bulk_create(batch, batch_size)</ | + | Entry.objects.bulk_create(batch, batch_size)</syntaxhighlight> |
</div> | </div> | ||
第3,058行: | 第2,430行: | ||
</div></li></ul> | </div></li></ul> | ||
− | + | <code>batch_size</code> 参数控制在单个查询中创建的对象数量。 默认是批量创建所有对象,SQLite 除外,默认情况下每个查询最多使用 999 个变量。 | |
− | |||
− | |||
− | + | 在支持它的数据库上(除了 PostgreSQL < 9.5 和 Oracle),设置<code>ignore_conflicts</code>参数为<code>True</code>告诉数据库忽略插入失败约束(例如重复唯一值)的任何行的失败。 启用此参数将禁用在每个模型实例上设置主键(如果数据库通常支持它)。 | |
− | |||
− | |||
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | + | <span class="versionmodified changed"> 2.2 版更改: </span> 添加了 <code>ignore_conflicts</code> 参数。 | |
第3,078行: | 第2,444行: | ||
<div id="bulk-update" class="section"> | <div id="bulk-update" class="section"> | ||
− | ==== | + | ==== bulk_update() ==== |
<div class="versionadded"> | <div class="versionadded"> | ||
+ | <span class="versionmodified added">2.2 版中的新功能。</span> | ||
</div> | </div> | ||
− | ; < | + | ; <span class="sig-name descname"><span class="pre">bulk_update</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">objs</span></span>'', ''<span class="n"><span class="pre">fields</span></span>'', ''<span class="n"><span class="pre">batch_size</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 此方法有效地更新提供的模型实例上的给定字段,通常使用一个查询: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,095行: | 第2,461行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> objs = [ |
... Entry.objects.create(headline='Entry 1'), | ... Entry.objects.create(headline='Entry 1'), | ||
... Entry.objects.create(headline='Entry 2'), | ... Entry.objects.create(headline='Entry 2'), | ||
... ] | ... ] | ||
− | + | >>> objs[0].headline = 'This is entry 1' | |
− | + | >>> objs[1].headline = 'This is entry 2' | |
− | + | >>> Entry.objects.bulk_update(objs, ['headline'])</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | [[#django.db.models.query.QuerySet.update| | + | [[#django.db.models.query.QuerySet.update|QuerySet.update()]] 用于保存更改,因此这比遍历模型列表并在每个模型上调用 <code>save()</code> 更有效,但它有一些警告: |
− | |||
− | |||
− | * | + | * 您无法更新模型的主键。 |
− | * | + | * 不会调用每个模型的 <code>save()</code> 方法,并且不会发送 [[../../signals#django.db.models.signals|pre_save]] 和 [[../../signals#django.db.models.signals|post_save]] 信号。 |
− | * | + | * 如果更新大量行中的大量列,则生成的 SQL 可能非常大。 通过指定合适的 <code>batch_size</code> 来避免这种情况。 |
− | * | + | * 更新定义在多表继承祖先上的字段将给每个祖先带来额外的查询。 |
− | * | + | * 如果 <code>objs</code> 包含重复项,则仅更新第一个。 |
− | + | <code>batch_size</code> 参数控制在单个查询中保存的对象数量。 默认是批量更新所有对象,SQLite 和 Oracle 除外,它们对查询中使用的变量数量有限制。 | |
− | |||
− | |||
第3,124行: | 第2,486行: | ||
<div id="count" class="section"> | <div id="count" class="section"> | ||
− | ==== | + | ==== count() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">count</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
: | : | ||
− | + | 返回一个整数,表示数据库中与 <code>QuerySet</code> 匹配的对象数。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,138行: | 第2,499行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Returns the total number of entries in the database. |
Entry.objects.count() | Entry.objects.count() | ||
# Returns the number of entries whose headline contains 'Lennon' | # Returns the number of entries whose headline contains 'Lennon' | ||
− | Entry.objects.filter(headline__contains='Lennon').count()</ | + | Entry.objects.filter(headline__contains='Lennon').count()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>count()</code> 调用在幕后执行 <code>SELECT COUNT(*)</code>,因此您应该始终使用 <code>count()</code>,而不是将所有记录加载到 Python 对象中并调用 <code>len()</code>结果(除非您无论如何都需要将对象加载到内存中,在这种情况下 <code>len()</code> 会更快)。 | |
− | |||
− | |||
− | |||
− | + | 请注意,如果您想要 <code>QuerySet</code> 中的项目数并且还从中检索模型实例(例如,通过对其进行迭代),则使用 <code>len(queryset)</code> 可能更有效t 会导致像 <code>count()</code> 这样的额外数据库查询。 | |
− | |||
− | |||
− | |||
第3,161行: | 第2,516行: | ||
<div id="in-bulk" class="section"> | <div id="in-bulk" class="section"> | ||
− | ==== | + | ==== in_bulk() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">in_bulk</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">id_list</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">field_name</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">'pk'</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 获取字段值列表 (<code>id_list</code>) 和这些值的 <code>field_name</code>,并返回一个字典,将每个值映射到具有给定字段值的对象实例。 如果未提供 <code>id_list</code>,则返回查询集中的所有对象。 <code>field_name</code> 必须是唯一字段,默认为主键。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,178行: | 第2,529行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Blog.objects.in_bulk([1]) |
− | {1: | + | {1: <Blog: Beatles Blog>} |
− | + | >>> Blog.objects.in_bulk([1, 2]) | |
− | {1: | + | {1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>} |
− | + | >>> Blog.objects.in_bulk([]) | |
{} | {} | ||
− | + | >>> Blog.objects.in_bulk() | |
− | {1: | + | {1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>} |
− | + | >>> Blog.objects.in_bulk(['beatles_blog'], field_name='slug') | |
− | {'beatles_blog': | + | {'beatles_blog': <Blog: Beatles Blog>}</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果你传递 <code>in_bulk()</code> 一个空列表,你会得到一个空字典。 | |
第3,198行: | 第2,549行: | ||
<div id="iterator" class="section"> | <div id="iterator" class="section"> | ||
− | ==== | + | ==== iterator() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">iterator</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">chunk_size</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">2000</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 评估 <code>QuerySet</code>(通过执行查询)并返回一个迭代器(参见 <span id="index-1" class="target"></span>[https://www.python.org/dev/peps/pep-0234 PEP 234])。 <code>QuerySet</code> 通常在内部缓存其结果,以便重复评估不会导致额外的查询。 相比之下, <code>iterator()</code> 会直接读取结果,不会在 <code>QuerySet</code> 级别做任何缓存(在内部,默认迭代器调用 <code>iterator()</code> 并缓存返回值)。 对于返回大量只需要访问一次的对象的 <code>QuerySet</code>,这可以带来更好的性能并显着减少内存。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 请注意,在已经计算过的 <code>QuerySet</code> 上使用 <code>iterator()</code> 将强制它再次计算,重复查询。 | |
− | |||
− | + | 此外,使用 <code>iterator()</code> 会导致之前的 <code>prefetch_related()</code> 调用被忽略,因为这两个优化一起使用没有意义。 | |
− | |||
− | + | 根据数据库后端,查询结果将被一次性加载或使用服务器端的游标从数据库中流转。 | |
− | |||
<div id="with-server-side-cursors" class="section"> | <div id="with-server-side-cursors" class="section"> | ||
− | ===== | + | ===== 使用服务器端游标 ===== |
− | Oracle | + | Oracle 和 [[../../databases#postgresql-server-side-cursors|PostgreSQL]] 使用服务器端游标从数据库中流式传输结果,而无需将整个结果集加载到内存中。 |
− | |||
− | |||
− | + | Oracle 数据库驱动程序总是使用服务器端的游标。 | |
− | + | 对于服务器端游标,<code>chunk_size</code> 参数指定要在数据库驱动程序级别缓存的结果数。 获取更大的块会减少数据库驱动程序和数据库之间的往返次数,但会消耗内存。 | |
− | |||
− | |||
− | |||
− | + | 在 PostgreSQL 上,服务器端游标只会在以下情况下使用 [[#id8|:设置:`DISABLE_SERVER_SIDE_CURSORS `]] 设置是<code>False</code> . 如果您使用以事务池模式配置的连接池,请阅读 [[../../databases#transaction-pooling-server-side-cursors|事务池和服务器端游标]] 。 禁用服务器端游标时,行为与不支持服务器端游标的数据库相同。 | |
− | [[ | ||
− | |||
− | |||
− | |||
− | |||
第3,247行: | 第2,578行: | ||
<div id="without-server-side-cursors" class="section"> | <div id="without-server-side-cursors" class="section"> | ||
− | ===== | + | ===== 没有服务器端游标 ===== |
− | MySQL | + | MySQL 不支持流式结果,因此 Python 数据库驱动程序将整个结果集加载到内存中。 然后,数据库适配器使用 <span id="index-2" class="target"></span>[https://www.python.org/dev/peps/pep-0249 PEP 249] 中定义的 <code>fetchmany()</code> 方法将结果集转换为 Python 行对象。 |
− | |||
− | |||
− | |||
− | SQLite | + | SQLite 可以使用 <code>fetchmany()</code> 批量获取结果,但由于 SQLite 不提供连接内查询之间的隔离,因此在写入被迭代的表时要小心。 有关详细信息,请参阅使用 QuerySet.iterator() [[../../databases#sqlite-isolation|时的]] 隔离。 |
− | |||
− | |||
− | |||
− | + | <code>chunk_size</code> 参数控制 Django 从数据库驱动程序中检索的批次大小。 较大的批处理减少了与数据库驱动程序通信的开销,但代价是内存消耗略有增加。 | |
− | |||
− | |||
− | + | <code>chunk_size</code> 的默认值,2000,来自 psycopg 邮件列表 [https://www.postgresql.org/message-id/4D2F2C71.8080805%40dndg.it 上的] 计算: | |
− | |||
<blockquote><div> | <blockquote><div> | ||
− | + | 假设行数为 10-20 列,文字数据和数字数据混合,2000 要取不到 100KB 的数据,这似乎是一个很好的折中方案,在传输的行数和提前退出循环时丢弃的数据之间。 | |
− | |||
− | |||
− | |||
第3,277行: | 第2,596行: | ||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | + | <span class="versionmodified changed"> 2.2 版更改:</span> 添加了对 SQLite 上的结果流的支持。 | |
第3,287行: | 第2,606行: | ||
<div id="latest" class="section"> | <div id="latest" class="section"> | ||
− | ==== | + | ==== latest() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">latest</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">fields</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 根据给定的字段,返回表中最新的对象。 | |
− | + | 本示例根据 <code>pub_date</code> 字段返回表中最新的 <code>Entry</code>: | |
− | <code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,301行: | 第2,619行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.latest('pub_date')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您还可以根据多个字段选择最新的。 例如,当两个条目具有相同的 <code>pub_date</code> 时,选择 <code>Entry</code> 与最早的 <code>expire_date</code>: | |
− | |||
− | <code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,314行: | 第2,630行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.latest('pub_date', '-expire_date')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>'-expire_date'</code>中的负号表示按''降序''对<code>expire_date</code>进行排序。 由于<code>latest()</code>得到最后的结果,所以选择了<code>expire_date</code>最早的<code>Entry</code>。 | |
− | |||
− | |||
− | + | 如果您模型的 [[../../../topics/db/models#meta-options|Meta]] 指定 [[../options#django.db.models.Options|get_latest_by]],则您可以省略 <code>earliest()</code> 或 <code>latest()</code> 的任何参数。 默认情况下将使用 [[../options#django.db.models.Options|get_latest_by]] 中指定的字段。 | |
− | [[../options#django.db.models.Options| | ||
− | <code>earliest()</code> | ||
− | [[../options#django.db.models.Options| | ||
− | + | 像 [[#django.db.models.query.QuerySet.get|get()]]、<code>earliest()</code> 和 <code>latest()</code> 引发 [[../instances#django.db.models.Model|DoesNotExist]] 如果没有具有给定参数的对象。 | |
− | [[../instances#django.db.models.Model| | ||
− | |||
− | + | 请注意,<code>earliest()</code> 和 <code>latest()</code> 的存在纯粹是为了方便和可读性。 | |
− | |||
<div class="admonition-earliest-and-latest-may-return-instances-with-null-dates admonition"> | <div class="admonition-earliest-and-latest-may-return-instances-with-null-dates admonition"> | ||
− | <code>earliest()</code> | + | <code>earliest()</code> 和 <code>latest()</code> 可能返回具有空日期的实例。 |
− | + | 由于排序委托给数据库,如果您使用不同的数据库,则允许空值的字段的结果可能会以不同的方式排序。 例如,PostgreSQL 和 MySQL 对空值进行排序,就好像它们高于非空值一样,而 SQLite 则相反。 | |
− | |||
− | |||
− | |||
− | + | 您可能希望过滤掉空值: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,350行: | 第2,655行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__isnull=False).latest('pub_date')</syntaxhighlight> |
</div> | </div> | ||
第3,361行: | 第2,666行: | ||
<div id="earliest" class="section"> | <div id="earliest" class="section"> | ||
− | ==== | + | ==== earliest() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">earliest</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">fields</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 除了方向改变外,其他方式与 [[#django.db.models.query.QuerySet.latest|latest()]] 类似。 | |
− | |||
第3,373行: | 第2,677行: | ||
<div id="first" class="section"> | <div id="first" class="section"> | ||
− | ==== | + | ==== first() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">first</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
: | : | ||
− | + | 返回查询集匹配的第一个对象,如果没有匹配的对象,则返回 <code>None</code>。 如果 <code>QuerySet</code> 没有定义排序,那么查询集会自动按主键排序。 这可能会影响聚合结果,如 [[../../../topics/db/aggregation#aggregation-ordering-interaction|与默认排序或 order_by()]] 的交互中所述。 | |
− | |||
− | |||
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,389行: | 第2,690行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">p = Article.objects.order_by('title', 'pub_date').first()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 注意 <code>first()</code> 是一个方便的方法,下面的代码示例相当于上面的例子: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,401行: | 第2,701行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">try: |
p = Article.objects.order_by('title', 'pub_date')[0] | p = Article.objects.order_by('title', 'pub_date')[0] | ||
except IndexError: | except IndexError: | ||
− | p = None</ | + | p = None</syntaxhighlight> |
</div> | </div> | ||
第3,413行: | 第2,713行: | ||
<div id="last" class="section"> | <div id="last" class="section"> | ||
− | ==== | + | ==== last() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">last</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
: | : | ||
− | + | 像 [[#django.db.models.query.QuerySet.first|first()]] 一样工作,但返回查询集中的最后一个对象。 | |
第3,424行: | 第2,724行: | ||
<div id="aggregate" class="section"> | <div id="aggregate" class="section"> | ||
− | ==== | + | ==== aggregate() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">aggregate</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回在 <code>QuerySet</code> 上计算的聚合值(平均值、总和等)的字典。 <code>aggregate()</code> 的每个参数指定一个值,该值将包含在返回的字典中。 | |
− | |||
− | |||
− | + | Django 提供的聚合函数在下面的 [[#id41|Aggregation Functions]] 中描述。 由于聚合也是 [[../expressions|查询表达式]] ,您可以将聚合与其他聚合或值组合以创建复杂的聚合。 | |
− | [[# | ||
− | |||
− | |||
− | + | 使用关键字参数指定的聚合将使用关键字作为注释的名称。 匿名参数将根据聚合函数的名称和正在聚合的模型字段为它们生成一个名称。 复杂聚合不能使用匿名参数,必须将关键字参数指定为别名。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 例如,当您处理博客条目时,您可能想知道贡献了博客条目的作者数量: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,451行: | 第2,741行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import Count |
− | + | >>> q = Blog.objects.aggregate(Count('entry')) | |
− | {'entry__count': 16}</ | + | {'entry__count': 16}</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 通过使用关键字参数指定聚合函数,您可以控制返回的聚合值的名称: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,465行: | 第2,754行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> q = Blog.objects.aggregate(number_of_entries=Count('entry')) |
− | {'number_of_entries': 16}</ | + | {'number_of_entries': 16}</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有关聚合的深入讨论,请参阅 [[../../../topics/db/aggregation|有关聚合的主题指南]] 。 | |
− | |||
第3,478行: | 第2,766行: | ||
<div id="exists" class="section"> | <div id="exists" class="section"> | ||
− | ==== | + | ==== exists() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">exists</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
: | : | ||
− | + | 如果 [[#django.db.models.query.QuerySet|QuerySet]] 包含任何结果,则返回 <code>True</code>,否则返回 <code>False</code>。 这试图以最简单和最快的方式执行查询,但它 ''确实'' 执行与普通 [[#django.db.models.query.QuerySet|QuerySet]] 查询几乎相同的查询。 | |
− | |||
− | |||
− | [[#django.db.models.query.QuerySet| | ||
− | [[#django.db.models.query.QuerySet.exists| | + | [[#django.db.models.query.QuerySet.exists|exists()]] 可用于与 [[#django.db.models.query.QuerySet|QuerySet]] 中的对象成员资格和 [[#django.db.models.query.QuerySet|QuerySet]] 中任何对象的存在相关的搜索,特别是在大 [[#django.db.models.query.QuerySet|查询集]] 。 |
− | |||
− | |||
− | + | 查找模型是否具有唯一字段的最有效方法(例如 <code>primary_key</code>) 是 [[#django.db.models.query.QuerySet|QuerySet]] 的成员是: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,499行: | 第2,781行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">entry = Entry.objects.get(pk=123) |
if some_queryset.filter(pk=entry.pk).exists(): | if some_queryset.filter(pk=entry.pk).exists(): | ||
− | print( | + | print("Entry contained in queryset")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这将比以下需要评估和迭代整个查询集的更快: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,513行: | 第2,794行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">if entry in some_queryset: |
− | print( | + | print("Entry contained in QuerySet")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 并查找查询集是否包含任何项目: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,525行: | 第2,806行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">if some_queryset.exists(): |
− | print( | + | print("There is at least one object in some_queryset")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这将比: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,537行: | 第2,818行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">if some_queryset: |
− | print( | + | print("There is at least one object in some_queryset")</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | ……但不是很大(因此需要一个大的查询集来提高效率)。 | |
− | |||
− | + | 此外,如果 <code>some_queryset</code> 尚未被评估,但您知道它会在某个时刻被评估,那么使用 <code>some_queryset.exists()</code> 将完成更多的整体工作(一个存在检查的查询加上一个额外的一到稍后检索结果),而不是简单地使用 <code>bool(some_queryset)</code>,后者检索结果,然后检查是否有任何返回。 | |
− | |||
− | |||
− | |||
− | |||
第3,556行: | 第2,832行: | ||
<div id="update" class="section"> | <div id="update" class="section"> | ||
− | ==== | + | ==== update() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">update</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 对指定的字段执行 SQL 更新查询,并返回匹配的行数(如果有些行已经有了新的值,则可能不等于更新的行数)。 | |
− | |||
− | |||
− | + | 例如,要关闭 2010 年发布的所有博客条目的评论,您可以执行以下操作: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,572行: | 第2,845行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (这假设您的 <code>Entry</code> 模型具有字段 <code>pub_date</code> 和 <code>comments_on</code>。) | |
− | + | 您可以更新多个字段 - 数量没有限制。 例如,这里我们更新 <code>comments_on</code> 和 <code>headline</code> 字段: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,586行: | 第2,858行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>update()</code> 方法是即时应用的,更新的 [[#django.db.models.query.QuerySet|QuerySet]] 唯一的限制是它只能更新模型主表中的列,不能更新相关模型。 你不能这样做,例如: | |
− | [[#django.db.models.query.QuerySet| | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,599行: | 第2,869行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.update(blog__name='foo') # Won't work!</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 不过,仍然可以根据相关字段进行过滤: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,610行: | 第2,880行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(blog__id=1).update(comments_on=True)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您不能在已获取切片或无法再过滤的 [[#django.db.models.query.QuerySet|QuerySet]] 上调用 <code>update()</code>。 | |
− | |||
− | + | <code>update()</code> 方法返回受影响的行数: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,624行: | 第2,893行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Entry.objects.filter(id=64).update(comments_on=True) |
1 | 1 | ||
− | + | >>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True) | |
0 | 0 | ||
− | + | >>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False) | |
− | 132</ | + | 132</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果你只是更新一条记录并且不需要对模型对象做任何事情,最有效的方法是调用 <code>update()</code>,而不是将模型对象加载到内存中。 例如,不要这样做: | |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,644行: | 第2,911行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">e = Entry.objects.get(id=10) |
e.comments_on = False | e.comments_on = False | ||
− | e.save()</ | + | e.save()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | …做这个: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,657行: | 第2,924行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(id=10).update(comments_on=False)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 使用 <code>update()</code> 还可以防止竞争条件,即在加载对象和调用 <code>save()</code> 之间的短时间内,数据库中的某些内容可能会发生变化。 | |
− | |||
− | |||
− | + | 最后,意识到 <code>update()</code> 在 SQL 级别进行更新,因此不会在您的模型上调用任何 <code>save()</code> 方法,也不会发出 [[../../signals#django.db.models.signals|pre_save]] 或 [ X169X]post_save 信号(这是调用 [[../instances#django.db.models.Model|Model.save()]] 的结果)。 如果要更新具有自定义 [[../instances#django.db.models.Model|save()]] 方法的模型的一堆记录,请遍历它们并调用 [[../instances#django.db.models.Model|save()]],如下所示: | |
− | |||
− | [[../../signals#django.db.models.signals| | ||
− | [ | ||
− | |||
− | |||
− | [[../instances#django.db.models.Model| | ||
− | [[../instances#django.db.models.Model| | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,679行: | 第2,937行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">for e in Entry.objects.filter(pub_date__year=2010): |
e.comments_on = False | e.comments_on = False | ||
− | e.save()</ | + | e.save()</syntaxhighlight> |
</div> | </div> | ||
第3,690行: | 第2,948行: | ||
<div id="delete" class="section"> | <div id="delete" class="section"> | ||
− | ==== | + | ==== delete() ==== |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">delete</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
: | : | ||
− | + | 对 [[#django.db.models.query.QuerySet|QuerySet]] 中的所有行执行 SQL 删除查询,并返回删除的对象数和包含每种对象类型删除数的字典。 | |
− | |||
− | |||
− | + | <code>delete()</code> 立即应用。 您不能在已获取切片或无法再过滤的 [[#django.db.models.query.QuerySet|QuerySet]] 上调用 <code>delete()</code>。 | |
− | [[#django.db.models.query.QuerySet|<code> | ||
− | |||
− | + | 例如,要删除特定博客中的所有条目: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,709行: | 第2,963行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> b = Blog.objects.get(pk=1) |
# Delete all the entries belonging to this Blog. | # Delete all the entries belonging to this Blog. | ||
− | + | >>> Entry.objects.filter(blog=b).delete() | |
− | (4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})</ | + | (4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 默认情况下,Django 的 [[../fields#django.db.models|ForeignKey]] 模拟 SQL 约束 <code>ON DELETE CASCADE</code> — 换句话说,任何外键指向要删除的对象的对象都将被删除。 例如: | |
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,727行: | 第2,978行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> blogs = Blog.objects.all() |
# This will delete all Blogs and all of their Entry objects. | # This will delete all Blogs and all of their Entry objects. | ||
− | + | >>> blogs.delete() | |
− | (5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})</ | + | (5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此级联行为可通过 [[../fields#django.db.models|ForeignKey]] 的 [[../fields#django.db.models.ForeignKey|on_delete]] 参数进行自定义。 | |
− | + | <code>delete()</code> 方法执行批量删除,并且不会在您的模型上调用任何 <code>delete()</code> 方法。 但是,它确实为所有已删除的对象(包括级联删除)发出 [[../../signals#django.db.models.signals|pre_delete]] 和 [[../../signals#django.db.models.signals|post_delete]] 信号。 | |
− | |||
− | [[../../signals#django.db.models.signals| | ||
− | [[../../signals#django.db.models.signals| | ||
− | |||
− | Django | + | Django 需要将对象提取到内存中以发送信号和处理级联。 但是,如果没有级联和信号,那么 Django 可能会采用快速路径并删除对象而不提取到内存中。 对于大型删除,这会导致显着减少内存使用。 执行查询的数量也可以减少。 |
− | |||
− | |||
− | |||
− | |||
− | + | 设置为 [[../fields#django.db.models.ForeignKey|on_delete]] <code>DO_NOTHING</code> 的外键不会阻止在删除时采用快速路径。 | |
− | <code>DO_NOTHING</code> | ||
− | + | 需要注意的是,对象删除中产生的查询是一个实现细节,可能会发生变化。 | |
− | |||
第3,760行: | 第3,001行: | ||
<div id="as-manager" class="section"> | <div id="as-manager" class="section"> | ||
− | ==== | + | ==== as_manager() ==== |
− | ; ''classmethod'' < | + | ; ''<span class="pre">classmethod</span>'' <span class="sig-name descname"><span class="pre">as_manager</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
: | : | ||
− | + | 返回 [[../../../topics/db/managers#django.db.models|Manager]] 实例和 <code>QuerySet</code> 方法副本的类方法。 有关更多详细信息,请参阅 [[../../../topics/db/managers#create-manager-with-queryset-methods|使用 QuerySet 方法创建管理器]] 。 | |
− | |||
− | [[../../../topics/db/managers#create-manager-with-queryset-methods| | ||
第3,773行: | 第3,012行: | ||
<div id="explain" class="section"> | <div id="explain" class="section"> | ||
− | ==== | + | ==== explain() ==== |
<div class="versionadded"> | <div class="versionadded"> | ||
+ | <span class="versionmodified added">2.1 版中的新功能。</span> | ||
</div> | </div> | ||
− | ; < | + | ; <span class="sig-name descname"><span class="pre">explain</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">format</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">options</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 返回 <code>QuerySet</code> 的执行计划的字符串,它详细说明了数据库将如何执行查询,包括将使用的任何索引或连接。 了解这些细节可能会帮助您提高慢查询的性能。 | |
− | |||
− | |||
− | |||
− | + | 例如,当使用 PostgreSQL 时: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,794行: | 第3,031行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> print(Blog.objects.filter(title='My Blog').explain()) |
Seq Scan on blog (cost=0.00..35.50 rows=10 width=12) | Seq Scan on blog (cost=0.00..35.50 rows=10 width=12) | ||
− | Filter: (title = 'My Blog'::bpchar)</ | + | Filter: (title = 'My Blog'::bpchar)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 不同数据库之间的输出有很大的不同。 | |
− | <code>explain()</code> | + | <code>explain()</code> 被除 Oracle 之外的所有内置数据库后端支持,因为那里的实现并不简单。 |
− | |||
− | + | <code>format</code> 参数改变了数据库默认的输出格式,通常是基于文本的。 PostgreSQL 支持 <code>'TEXT'</code>、<code>'JSON'</code>、<code>'YAML'</code> 和 <code>'XML'</code>。 MySQL 支持 <code>'TEXT'</code>(也称为 <code>'TRADITIONAL'</code>)和 <code>'JSON'</code>。 | |
− | |||
− | <code>'XML'</code> | ||
− | <code>'JSON'</code> | ||
− | + | 某些数据库接受可以返回有关查询的更多信息的标志。 将这些标志作为关键字参数传递。 例如,当使用 PostgreSQL 时: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,818行: | 第3,050行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> print(Blog.objects.filter(title='My Blog').explain(verbose=True)) |
Seq Scan on public.blog (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1) | Seq Scan on public.blog (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1) | ||
Output: id, title | Output: id, title | ||
Filter: (blog.title = 'My Blog'::bpchar) | Filter: (blog.title = 'My Blog'::bpchar) | ||
Planning time: 0.064 ms | Planning time: 0.064 ms | ||
− | Execution time: 0.058 ms</ | + | Execution time: 0.058 ms</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在某些数据库上,标志可能会导致执行查询,这可能会对您的数据库产生不利影响。 例如,如果有触发器或函数被调用,PostgreSQL 的 <code>ANALYZE</code> 标志可能会导致数据更改,即使对于 <code>SELECT</code> 查询也是如此。 | |
− | |||
− | |||
− | |||
第3,839行: | 第3,068行: | ||
<div id="field-lookups" class="section"> | <div id="field-lookups" class="section"> | ||
− | <span id=" | + | <span id="id10"></span> |
− | === | + | === Field 查找 === |
− | + | 字段查找是您指定 SQL <code>WHERE</code> 子句内容的方式。 它们被指定为 <code>QuerySet</code> 方法 [[#django.db.models.query.QuerySet.filter|filter()]]、[[#django.db.models.query.QuerySet.exclude|exclude()]] 和 [[#django.db.models.query.QuerySet.get|get()]] 的关键字参数。 | |
− | |||
− | [[#django.db.models.query.QuerySet.exclude| | ||
− | + | 有关介绍,请参阅 [[../../../topics/db/queries#field-lookups-intro|模型和数据库查询文档]] 。 | |
− | Django | + | 下面列出了 Django 的内置查找。 也可以为模型字段编写 [[../../../howto/custom-lookups|自定义查找]] 。 |
− | [[../../../howto/custom-lookups| | ||
− | + | 为了方便,当没有提供查找类型时(如 <code>Entry.objects.get(id=14)</code>),查找类型假定为 [[#id11|:lookup:`exact`]]。 | |
− | <code>Entry.objects.get(id=14)</code> | ||
<div id="exact" class="section"> | <div id="exact" class="section"> | ||
− | + | ==== exact ==== | |
− | ==== | ||
− | + | 完全符合。 如果为比较提供的值是 <code>None</code>,它将被解释为 SQL <code>NULL</code>(有关更多详细信息,请参阅 [[#id13|:lookup:`isnull`]])。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,868行: | 第3,091行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.get(id__exact=14) |
− | Entry.objects.get(id__exact=None)</ | + | Entry.objects.get(id__exact=None)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等效项: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,880行: | 第3,103行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE id = 14; |
− | SELECT ... WHERE id IS NULL;</ | + | SELECT ... WHERE id IS NULL;</syntaxhighlight> |
</div> | </div> | ||
第3,888行: | 第3,111行: | ||
<div class="admonition-mysql-comparisons admonition"> | <div class="admonition-mysql-comparisons admonition"> | ||
− | MySQL | + | MySQL 比较 |
− | + | 在 MySQL 中,数据库表的“排序规则”设置确定 <code>exact</code> 比较是否区分大小写。 这是一个数据库设置, ''不是'' 一个 Django 设置。 可以将 MySQL 表配置为使用区分大小写的比较,但需要进行一些权衡。 有关这方面的更多信息,请参阅 [[../../databases|数据库]] 文档中的 [[../../databases#mysql-collation|整理部分]] 。 | |
− | <code>exact</code> | ||
− | |||
− | |||
− | |||
− | |||
第3,903行: | 第3,121行: | ||
<div id="iexact" class="section"> | <div id="iexact" class="section"> | ||
− | + | ==== iexact ==== | |
− | ==== | ||
− | + | 不区分大小写的精确匹配。 如果为比较提供的值是 <code>None</code>,它将被解释为 SQL <code>NULL</code>(有关更多详细信息,请参阅 [[#id15|:lookup:`isnull`]])。 | |
− | |||
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,916行: | 第3,131行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Blog.objects.get(name__iexact='beatles blog') |
− | Blog.objects.get(name__iexact=None)</ | + | Blog.objects.get(name__iexact=None)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等效项: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,928行: | 第3,143行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE name ILIKE 'beatles blog'; |
− | SELECT ... WHERE name IS NULL;</ | + | SELECT ... WHERE name IS NULL;</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意,第一个查询将匹配 <code>'Beatles Blog'</code>、<code>'beatles blog'</code>、<code>'BeAtLes BLoG'</code> 等。 | |
− | <code>'BeAtLes BLoG'</code> | ||
<div class="admonition-sqlite-users admonition"> | <div class="admonition-sqlite-users admonition"> | ||
− | SQLite | + | SQLite 用户 |
− | + | 使用 SQLite 后端和非 ASCII 字符串时,请记住有关字符串比较的 [[../../databases#sqlite-string-matching|数据库注释]] 。 SQLite 不会对非 ASCII 字符串进行不区分大小写的匹配。 | |
− | [[../../databases#sqlite-string-matching| | ||
− | SQLite | ||
第3,951行: | 第3,163行: | ||
<div id="contains" class="section"> | <div id="contains" class="section"> | ||
− | + | ==== contains ==== | |
− | ==== | ||
− | + | 区分大小写的包含测试。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,962行: | 第3,173行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.get(headline__contains='Lennon')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第3,973行: | 第3,184行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE headline LIKE '%Lennon%';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意,这将匹配标题 <code>'Lennon honored today'</code> 但不匹配 <code>'lennon honored today'</code>。 | |
<div class="admonition-sqlite-users admonition"> | <div class="admonition-sqlite-users admonition"> | ||
− | SQLite | + | SQLite 用户 |
− | SQLite | + | SQLite 不支持区分大小写的 <code>LIKE</code> 语句; <code>contains</code> 的作用类似于 SQLite 的 <code>icontains</code>。 有关更多信息,请参阅 [[../../databases#sqlite-string-matching|数据库注释]] 。 |
− | |||
第3,993行: | 第3,203行: | ||
<div id="icontains" class="section"> | <div id="icontains" class="section"> | ||
− | + | ==== icontains ==== | |
− | ==== | ||
− | + | 不区分大小写的包含测试。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,004行: | 第3,213行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.get(headline__icontains='Lennon')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,015行: | 第3,224行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE headline ILIKE '%Lennon%';</syntaxhighlight> |
</div> | </div> | ||
第4,022行: | 第3,231行: | ||
<div class="admonition-sqlite-users admonition"> | <div class="admonition-sqlite-users admonition"> | ||
− | SQLite | + | SQLite 用户 |
− | + | 使用 SQLite 后端和非 ASCII 字符串时,请记住有关字符串比较的 [[../../databases#sqlite-string-matching|数据库注释]] 。 | |
− | [[../../databases#sqlite-string-matching| | ||
第4,033行: | 第3,241行: | ||
<div id="in" class="section"> | <div id="in" class="section"> | ||
− | + | ==== in ==== | |
− | ==== | ||
− | + | 在给定的迭代中; 通常是列表、元组或查询集。 这不是一个常见的用例,但接受字符串(可迭代)。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,045行: | 第3,251行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(id__in=[1, 3, 4]) |
− | Entry.objects.filter(headline__in='abc')</ | + | Entry.objects.filter(headline__in='abc')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等效项: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,057行: | 第3,263行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE id IN (1, 3, 4); |
− | SELECT ... WHERE headline IN ('a', 'b', 'c');</ | + | SELECT ... WHERE headline IN ('a', 'b', 'c');</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 您还可以使用查询集来动态评估值列表,而不是提供文字值列表: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,070行: | 第3,275行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">inner_qs = Blog.objects.filter(name__contains='Cheddar') |
− | entries = Entry.objects.filter(blog__in=inner_qs)</ | + | entries = Entry.objects.filter(blog__in=inner_qs)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此查询集将作为子选择语句进行评估: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,082行: | 第3,287行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您将 <code>values()</code> 或 <code>values_list()</code> 产生的 <code>QuerySet</code> 作为值传递给 <code>__in</code> 查找,则需要确保只提取一个字段结果。 例如,这将起作用(过滤博客名称): | |
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,096行: | 第3,298行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">inner_qs = Blog.objects.filter(name__contains='Ch').values('name') |
− | entries = Entry.objects.filter(blog__name__in=inner_qs)</ | + | entries = Entry.objects.filter(blog__name__in=inner_qs)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此示例将引发异常,因为内部查询试图提取两个字段值,其中只需要一个: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,109行: | 第3,310行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python"># Bad code! Will raise a TypeError. |
inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id') | inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id') | ||
− | entries = Entry.objects.filter(blog__name__in=inner_qs)</ | + | entries = Entry.objects.filter(blog__name__in=inner_qs)</syntaxhighlight> |
</div> | </div> | ||
第4,118行: | 第3,319行: | ||
<div id="nested-queries-performance" class="admonition-performance-considerations admonition"> | <div id="nested-queries-performance" class="admonition-performance-considerations admonition"> | ||
− | + | 性能注意事项 | |
− | + | 谨慎使用嵌套查询并了解您的数据库服务器的性能特征(如果有疑问,请进行基准测试!)。 一些数据库后端,尤其是 MySQL,不能很好地优化嵌套查询。 在这些情况下,提取值列表然后将其传递到第二个查询中会更有效。 也就是说,执行两个查询而不是一个: | |
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,131行: | 第3,327行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">values = Blog.objects.filter( |
name__contains='Cheddar').values_list('pk', flat=True) | name__contains='Cheddar').values_list('pk', flat=True) | ||
− | entries = Entry.objects.filter(blog__in=list(values))</ | + | entries = Entry.objects.filter(blog__in=list(values))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意博客 <code>QuerySet</code> 周围的 <code>list()</code> 调用以强制执行第一个查询。 没有它,将执行嵌套查询,因为 [[../../../topics/db/queries#querysets-are-lazy|QuerySets 是惰性的]] 。 | |
− | |||
− | [[../../../topics/db/queries#querysets-are-lazy| | ||
第4,148行: | 第3,342行: | ||
<div id="gt" class="section"> | <div id="gt" class="section"> | ||
− | + | ==== gt ==== | |
− | ==== | ||
− | + | 大于。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,159行: | 第3,352行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(id__gt=4)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,170行: | 第3,363行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE id > 4;</syntaxhighlight> |
</div> | </div> | ||
第4,179行: | 第3,372行: | ||
<div id="gte" class="section"> | <div id="gte" class="section"> | ||
− | + | ==== gte ==== | |
− | ==== | ||
− | + | 大于等于。 | |
第4,188行: | 第3,380行: | ||
<div id="lt" class="section"> | <div id="lt" class="section"> | ||
− | + | ==== lt ==== | |
− | ==== | ||
− | + | 小于。 | |
第4,197行: | 第3,388行: | ||
<div id="lte" class="section"> | <div id="lte" class="section"> | ||
− | + | ==== lte ==== | |
− | ==== | ||
− | + | 小于等于 | |
第4,206行: | 第3,396行: | ||
<div id="startswith" class="section"> | <div id="startswith" class="section"> | ||
− | + | ==== startswith ==== | |
− | ==== | ||
− | + | 区分大小写的开头为。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,217行: | 第3,406行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(headline__startswith='Lennon')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,228行: | 第3,417行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE headline LIKE 'Lennon%';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQLite | + | SQLite 不支持区分大小写的 <code>LIKE</code> 语句; <code>startswith</code> 的作用类似于 SQLite 的 <code>istartswith</code>。 |
− | |||
第4,240行: | 第3,428行: | ||
<div id="istartswith" class="section"> | <div id="istartswith" class="section"> | ||
− | + | ==== istartswith ==== | |
− | ==== | ||
− | + | 不区分大小写的开头为。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,251行: | 第3,438行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(headline__istartswith='Lennon')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,262行: | 第3,449行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE headline ILIKE 'Lennon%';</syntaxhighlight> |
</div> | </div> | ||
第4,269行: | 第3,456行: | ||
<div class="admonition-sqlite-users admonition"> | <div class="admonition-sqlite-users admonition"> | ||
− | SQLite | + | SQLite 用户 |
− | + | 使用 SQLite 后端和非 ASCII 字符串时,请记住有关字符串比较的 [[../../databases#sqlite-string-matching|数据库注释]] 。 | |
− | [[../../databases#sqlite-string-matching| | ||
第4,280行: | 第3,466行: | ||
<div id="endswith" class="section"> | <div id="endswith" class="section"> | ||
− | + | ==== endswith ==== | |
− | ==== | ||
− | + | 区分大小写的结尾为。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,291行: | 第3,476行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(headline__endswith='Lennon')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,302行: | 第3,487行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE headline LIKE '%Lennon';</syntaxhighlight> |
</div> | </div> | ||
第4,309行: | 第3,494行: | ||
<div class="admonition-sqlite-users admonition"> | <div class="admonition-sqlite-users admonition"> | ||
− | SQLite | + | SQLite 用户 |
− | SQLite | + | SQLite 不支持区分大小写的 <code>LIKE</code> 语句; <code>endswith</code> 的作用类似于 SQLite 的 <code>iendswith</code>。 有关更多信息,请参阅 [[../../databases#sqlite-string-matching|数据库注释]] 文档。 |
− | |||
第4,320行: | 第3,504行: | ||
<div id="iendswith" class="section"> | <div id="iendswith" class="section"> | ||
− | + | ==== iendswith ==== | |
− | ==== | ||
− | + | 不区分大小写的结尾为。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,331行: | 第3,514行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(headline__iendswith='Lennon')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,342行: | 第3,525行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE headline ILIKE '%Lennon'</syntaxhighlight> |
</div> | </div> | ||
第4,349行: | 第3,532行: | ||
<div class="admonition-sqlite-users admonition"> | <div class="admonition-sqlite-users admonition"> | ||
− | SQLite | + | SQLite 用户 |
− | + | 使用 SQLite 后端和非 ASCII 字符串时,请记住有关字符串比较的 [[../../databases#sqlite-string-matching|数据库注释]] 。 | |
− | [[../../databases#sqlite-string-matching| | ||
第4,360行: | 第3,542行: | ||
<div id="range" class="section"> | <div id="range" class="section"> | ||
− | + | ==== range ==== | |
− | ==== | ||
− | + | 范围测试(含)。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,371行: | 第3,552行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">import datetime |
start_date = datetime.date(2005, 1, 1) | start_date = datetime.date(2005, 1, 1) | ||
end_date = datetime.date(2005, 3, 31) | end_date = datetime.date(2005, 3, 31) | ||
− | Entry.objects.filter(pub_date__range=(start_date, end_date))</ | + | Entry.objects.filter(pub_date__range=(start_date, end_date))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,385行: | 第3,566行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在 SQL 中可以使用 <code>BETWEEN</code> 的任何地方都可以使用 <code>range</code> — 用于日期、数字甚至字符。 | |
− | |||
<div class="admonition warning"> | <div class="admonition warning"> | ||
第4,397行: | 第3,577行: | ||
警告 | 警告 | ||
− | + | 用日期过滤 <code>DateTimeField</code> 将不包括最后一天的项目,因为边界被解释为“给定日期的上午 0 点”。 如果 <code>pub_date</code> 是一个 <code>DateTimeField</code>,上面的表达式会变成这个 SQL: | |
− | |||
− | <code>pub_date</code> | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,406行: | 第3,583行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 一般来说,您不能混合日期和日期时间。 | |
第4,419行: | 第3,596行: | ||
<div id="date" class="section"> | <div id="date" class="section"> | ||
− | + | ==== date ==== | |
− | ==== | ||
− | + | 对于日期时间字段,将值转换为日期。 允许链接额外的字段查找。 获取日期值。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,431行: | 第3,606行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) |
− | Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))</ | + | Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。 | |
− | |||
− | + | 当 [[#id17|:setting:`USE_TZ`]] 为 <code>True</code> 时,字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,448行: | 第3,620行: | ||
<div id="year" class="section"> | <div id="year" class="section"> | ||
− | + | ==== year ==== | |
− | ==== | ||
− | + | 对于日期和日期时间字段,精确的年份匹配。 允许链接额外的字段查找。 取整数年。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,460行: | 第3,630行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__year=2005) |
− | Entry.objects.filter(pub_date__year__gte=2005)</ | + | Entry.objects.filter(pub_date__year__gte=2005)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,472行: | 第3,642行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31'; |
− | SELECT ... WHERE pub_date | + | SELECT ... WHERE pub_date >= '2005-01-01';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (确切的 SQL 语法因每个数据库引擎而异)。 | |
− | + | 当 [[#id19|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,488行: | 第3,656行: | ||
<div id="iso-year" class="section"> | <div id="iso-year" class="section"> | ||
− | + | ==== iso_year ==== | |
− | ==== | ||
<div class="versionadded"> | <div class="versionadded"> | ||
+ | <span class="versionmodified added">2.2 版中的新功能。</span> | ||
</div> | </div> | ||
− | + | 对于日期和日期时间字段,精确的 ISO 8601 周编号年份匹配。 允许链接额外的字段查找。 取整数年。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,505行: | 第3,672行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__iso_year=2005) |
− | Entry.objects.filter(pub_date__iso_year__gte=2005)</ | + | Entry.objects.filter(pub_date__iso_year__gte=2005)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (确切的 SQL 语法因每个数据库引擎而异)。 | |
− | + | 当 [[#id21|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,521行: | 第3,686行: | ||
<div id="month" class="section"> | <div id="month" class="section"> | ||
− | + | ==== month ==== | |
− | ==== | ||
− | + | 对于日期和日期时间字段,精确的月份匹配。 允许链接额外的字段查找。 取整数 1(一月)到 12(十二月)。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,533行: | 第3,696行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__month=12) |
− | Entry.objects.filter(pub_date__month__gte=6)</ | + | Entry.objects.filter(pub_date__month__gte=6)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,545行: | 第3,708行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12'; |
− | SELECT ... WHERE EXTRACT('month' FROM pub_date) | + | SELECT ... WHERE EXTRACT('month' FROM pub_date) >= '6';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (确切的 SQL 语法因每个数据库引擎而异)。 | |
− | + | 当 [[#id23|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,561行: | 第3,722行: | ||
<div id="day" class="section"> | <div id="day" class="section"> | ||
− | + | ==== day ==== | |
− | ==== | ||
− | + | 对于日期和日期时间字段,精确的日期匹配。 允许链接额外的字段查找。 需要整数天。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,573行: | 第3,732行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__day=3) |
− | Entry.objects.filter(pub_date__day__gte=3)</ | + | Entry.objects.filter(pub_date__day__gte=3)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,585行: | 第3,744行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE EXTRACT('day' FROM pub_date) = '3'; |
− | SELECT ... WHERE EXTRACT('day' FROM pub_date) | + | SELECT ... WHERE EXTRACT('day' FROM pub_date) >= '3';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (确切的 SQL 语法因每个数据库引擎而异)。 | |
− | + | 请注意,这将匹配任何带有 pub_date 的月份第三天的记录,如 1 月 3 日,7 月 3 日等。 | |
− | |||
− | + | 当 [[#id25|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,604行: | 第3,760行: | ||
<div id="week" class="section"> | <div id="week" class="section"> | ||
− | + | ==== week ==== | |
− | ==== | ||
− | + | 对于日期和日期时间字段,根据 [https://en.wikipedia.org/wiki/ISO-8601 ISO-8601] 返回周数(1-52 或 53),即周从星期一开始,第一周包含年度的第一个星期四。 | |
− | |||
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,617行: | 第3,770行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__week=52) |
− | Entry.objects.filter(pub_date__week__gte=32, pub_date__week__lte=38)</ | + | Entry.objects.filter(pub_date__week__gte=32, pub_date__week__lte=38)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。 | |
− | |||
− | + | 当 [[#id27|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,634行: | 第3,784行: | ||
<div id="week-day" class="section"> | <div id="week-day" class="section"> | ||
− | + | ==== week_day ==== | |
− | ==== | ||
− | + | 对于日期和日期时间字段,“星期几”匹配。 允许链接额外的字段查找。 | |
− | |||
− | + | 从 1(星期日)到 7(星期六)取一个整数值,代表一周的一天。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,649行: | 第3,796行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__week_day=2) |
− | Entry.objects.filter(pub_date__week_day__gte=2)</ | + | Entry.objects.filter(pub_date__week_day__gte=2)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。 | |
− | |||
− | + | 请注意,这将匹配任何具有 <code>pub_date</code> 位于星期一(一周的第 2 天)的记录,而不管它发生在哪个月份或年份。 工作日被索引,第 1 天是星期日,第 7 天是星期六。 | |
− | 2 | ||
− | |||
− | + | 当 [[#id29|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,670行: | 第3,812行: | ||
<div id="quarter" class="section"> | <div id="quarter" class="section"> | ||
− | + | ==== quarter ==== | |
− | ==== | ||
− | + | 对于日期和日期时间字段,“一年中的一个季度”匹配。 允许链接额外的字段查找。 取一个 1 到 4 之间的整数值,表示一年中的一个季度。 | |
− | |||
− | |||
− | + | 检索第二季度(4 月 1 日至 6 月 30 日)条目的示例: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,683行: | 第3,822行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__quarter=2)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。 | |
− | |||
− | + | 当 [[#id31|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,699行: | 第3,835行: | ||
<div id="time" class="section"> | <div id="time" class="section"> | ||
− | + | ==== time ==== | |
− | ==== | ||
− | + | 对于日期时间字段,将值转换为时间。 允许链接额外的字段查找。 采用 <code>datetime.time</code> 值。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,711行: | 第3,845行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__time=datetime.time(14, 30)) |
− | Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))</ | + | Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。 | |
− | |||
− | + | 当 [[#id33|:setting:`USE_TZ`]] 为 <code>True</code> 时,字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,728行: | 第3,859行: | ||
<div id="hour" class="section"> | <div id="hour" class="section"> | ||
− | + | ==== hour ==== | |
− | ==== | ||
− | + | 对于日期时间和时间字段,精确的小时匹配。 允许链接额外的字段查找。 取 0 到 23 之间的整数。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,740行: | 第3,869行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Event.objects.filter(timestamp__hour=23) |
Event.objects.filter(time__hour=5) | Event.objects.filter(time__hour=5) | ||
− | Event.objects.filter(timestamp__hour__gte=12)</ | + | Event.objects.filter(timestamp__hour__gte=12)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,753行: | 第3,882行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23'; |
SELECT ... WHERE EXTRACT('hour' FROM time) = '5'; | SELECT ... WHERE EXTRACT('hour' FROM time) = '5'; | ||
− | SELECT ... WHERE EXTRACT('hour' FROM timestamp) | + | SELECT ... WHERE EXTRACT('hour' FROM timestamp) >= '12';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (确切的 SQL 语法因每个数据库引擎而异)。 | |
− | + | 当 [[#id35|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,770行: | 第3,897行: | ||
<div id="minute" class="section"> | <div id="minute" class="section"> | ||
− | + | ==== minute ==== | |
− | ==== | ||
− | + | 对于日期时间和时间字段,精确的分钟匹配。 允许链接额外的字段查找。 取 0 到 59 之间的整数。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,782行: | 第3,907行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Event.objects.filter(timestamp__minute=29) |
Event.objects.filter(time__minute=46) | Event.objects.filter(time__minute=46) | ||
− | Event.objects.filter(timestamp__minute__gte=29)</ | + | Event.objects.filter(timestamp__minute__gte=29)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,795行: | 第3,920行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE EXTRACT('minute' FROM timestamp) = '29'; |
SELECT ... WHERE EXTRACT('minute' FROM time) = '46'; | SELECT ... WHERE EXTRACT('minute' FROM time) = '46'; | ||
− | SELECT ... WHERE EXTRACT('minute' FROM timestamp) | + | SELECT ... WHERE EXTRACT('minute' FROM timestamp) >= '29';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (确切的 SQL 语法因每个数据库引擎而异)。 | |
− | + | 当 [[#id37|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,812行: | 第3,935行: | ||
<div id="second" class="section"> | <div id="second" class="section"> | ||
− | + | ==== second ==== | |
− | ==== | ||
− | + | 对于日期时间和时间字段,精确的第二个匹配。 允许链接额外的字段查找。 取 0 到 59 之间的整数。 | |
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,824行: | 第3,945行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Event.objects.filter(timestamp__second=31) |
Event.objects.filter(time__second=2) | Event.objects.filter(time__second=2) | ||
− | Event.objects.filter(timestamp__second__gte=31)</ | + | Event.objects.filter(timestamp__second__gte=31)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,837行: | 第3,958行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE EXTRACT('second' FROM timestamp) = '31'; |
SELECT ... WHERE EXTRACT('second' FROM time) = '2'; | SELECT ... WHERE EXTRACT('second' FROM time) = '2'; | ||
− | SELECT ... WHERE EXTRACT('second' FROM timestamp) | + | SELECT ... WHERE EXTRACT('second' FROM timestamp) >= '31';</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | (确切的 SQL 语法因每个数据库引擎而异)。 | |
− | + | 当 [[#id39|:setting:`USE_TZ`]] 为 <code>True</code> 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 [[#database-time-zone-definitions|中的]] 时区定义。 | |
− | |||
− | |||
第4,854行: | 第3,973行: | ||
<div id="isnull" class="section"> | <div id="isnull" class="section"> | ||
− | + | ==== isnull ==== | |
− | ==== | ||
− | + | 采用 <code>True</code> 或 <code>False</code>,分别对应于 <code>IS NULL</code> 和 <code>IS NOT NULL</code> 的 SQL 查询。 | |
− | <code>IS NULL</code> | ||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,866行: | 第3,983行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.filter(pub_date__isnull=True)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等价于: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,877行: | 第3,994行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE pub_date IS NULL;</syntaxhighlight> |
</div> | </div> | ||
第4,886行: | 第4,003行: | ||
<div id="regex" class="section"> | <div id="regex" class="section"> | ||
− | + | ==== regex ==== | |
− | ==== | ||
− | + | 区分大小写的正则表达式匹配。 | |
− | + | 正则表达式语法是正在使用的数据库后端的语法。 对于没有内置正则表达式支持的 SQLite,此功能由(Python)用户定义的 REGEXP 函数提供,因此正则表达式语法是 Python 的 <code>re</code> 模块的语法。 | |
− | |||
− | |||
− | |||
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,902行: | 第4,015行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.get(title__regex=r'^(An?|The) +')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等效项: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,913行: | 第4,026行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL |
SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle | SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle | ||
第4,919行: | 第4,032行: | ||
SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL | SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL | ||
− | SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite</ | + | SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 建议使用原始字符串(例如,<code>r'foo'</code> 而不是 <code>'foo'</code>)来传递正则表达式语法。 | |
− | |||
第4,931行: | 第4,043行: | ||
<div id="iregex" class="section"> | <div id="iregex" class="section"> | ||
− | + | ==== iregex ==== | |
− | ==== | ||
− | + | 不区分大小写的正则表达式匹配。 | |
− | + | 例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,942行: | 第4,053行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">Entry.objects.get(title__iregex=r'^(an?|the) +')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | SQL | + | SQL 等效项: |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第4,953行: | 第4,064行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL |
SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle | SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle | ||
第4,959行: | 第4,070行: | ||
SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL | SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL | ||
− | SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite</ | + | SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite</syntaxhighlight> |
</div> | </div> | ||
第4,970行: | 第4,081行: | ||
<div id="aggregation-functions" class="section"> | <div id="aggregation-functions" class="section"> | ||
− | <span id=" | + | <span id="id41"></span> |
− | === | + | === 聚合函数 === |
− | Django | + | Django 在 <code>django.db.models</code> 模块中提供了以下聚合函数。 有关如何使用这些聚合函数的详细信息,请参阅 [[../../../topics/db/aggregation|聚合主题指南]] 。 请参阅 [[../expressions#django.db.models|Aggregate]] 文档以了解如何创建聚合。 |
− | <code>django.db.models</code> | ||
− | |||
− | |||
<div class="admonition warning"> | <div class="admonition warning"> | ||
第4,982行: | 第4,090行: | ||
警告 | 警告 | ||
− | SQLite | + | SQLite 无法处理开箱即用的日期/时间字段的聚合。 这是因为 SQLite 中没有原生日期/时间字段,而 Django 目前使用文本字段模拟这些功能。 尝试在 SQLite 中的日期/时间字段上使用聚合将引发 <code>NotImplementedError</code>。 |
− | |||
− | |||
− | |||
− | <code>NotImplementedError</code> | ||
第4,992行: | 第4,096行: | ||
<div class="admonition-note admonition"> | <div class="admonition-note admonition"> | ||
− | + | 注解 | |
− | + | 与空 <code>QuerySet</code> 一起使用时,聚合函数返回 <code>None</code>。 例如,如果 <code>QuerySet</code> 不包含任何条目,则 <code>Sum</code> 聚合函数返回 <code>None</code> 而不是 <code>0</code>。 一个例外是 <code>Count</code>,如果 <code>QuerySet</code> 为空,它会返回 <code>0</code>。 | |
− | <code> | ||
− | |||
− | <code>Count</code> | ||
</div> | </div> | ||
− | + | 所有聚合体都有以下共同的参数: | |
<div id="expressions" class="section"> | <div id="expressions" class="section"> | ||
− | ==== | + | ==== expressions ==== |
− | + | 引用模型字段的字符串,或 [[../expressions|查询表达式]] 。 | |
第5,013行: | 第4,114行: | ||
<div id="output-field" class="section"> | <div id="output-field" class="section"> | ||
− | ==== | + | ==== output_field ==== |
− | + | 一个可选参数,表示返回值的 [[../fields|模型字段]] | |
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
第5,022行: | 第4,122行: | ||
注解 | 注解 | ||
− | + | 在组合多个字段类型时,如果所有字段类型相同,Django 只能确定 <code>output_field</code>。 否则,您必须自己提供 <code>output_field</code>。 | |
− | <code>output_field</code> | ||
− | |||
第5,032行: | 第4,130行: | ||
<div id="aggregate-filter" class="section"> | <div id="aggregate-filter" class="section"> | ||
− | <span id=" | + | <span id="id42"></span> |
− | ==== | + | ==== filter ==== |
− | + | 一个可选的 [[#django.db.models.Q|Q 对象]] ,用于过滤聚合的行。 | |
− | |||
− | + | 有关示例用法,请参阅 [[../conditional-expressions#conditional-aggregation|条件聚合]] 和 [[../../../topics/db/aggregation#filtering-on-annotations|注释过滤]] 。 | |
− | |||
</div> | </div> | ||
− | <div id=" | + | <div id="id43" class="section"> |
− | ==== | + | ==== **extra ==== |
− | + | 关键字参数,可以为聚合生成的 SQL 提供额外的上下文。 | |
− | |||
第5,054行: | 第4,149行: | ||
<div id="avg" class="section"> | <div id="avg" class="section"> | ||
− | ==== | + | ==== Avg ==== |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">Avg</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">expression</span></span>'', ''<span class="n"><span class="pre">output_field</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">filter</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">extra</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 返回给定表达式的平均值,除非您指定不同的 <code>output_field</code>,否则该平均值必须是数字。 |
− | ;* | + | ;* 默认别名:<code><field>__avg</code> |
− | ;* | + | ;* 返回类型:<code>float</code> 如果输入是 <code>int</code>,否则与输入字段相同,或者 <code>output_field</code> 如果提供 |
</div> | </div> | ||
− | <div id=" | + | <div id="id44" class="section"> |
− | ==== | + | ==== Count ==== |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">Count</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">expression</span></span>'', ''<span class="n"><span class="pre">distinct</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">filter</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">extra</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>返回通过提供的表达式关联的对象数量。</p> |
− | |||
<ul> | <ul> | ||
− | <li><p> | + | <li><p>默认别名:<code><field>__count</code></p></li> |
− | <li><p> | + | <li><p>返回类型:<code>int</code></p></li></ul> |
<p>包含一个可选参数:</p> | <p>包含一个可选参数:</p> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">distinct</span></span></dt> |
− | <dd><p> | + | <dd><p>如果为 <code>distinct=True</code>,则计数将仅包括唯一实例。 这是 <code>COUNT(DISTINCT <field>)</code> 的 SQL 等价物。 默认值为 <code>False</code>。</p></dd></dl> |
− | |||
− | |||
</dd></dl> | </dd></dl> | ||
第5,087行: | 第4,179行: | ||
<div id="max" class="section"> | <div id="max" class="section"> | ||
− | ==== | + | ==== Max ==== |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">Max</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">expression</span></span>'', ''<span class="n"><span class="pre">output_field</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">filter</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">extra</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 返回给定表达式的最大值。 |
− | ;* | + | ;* 默认别名:<code><field>__max</code> |
− | ;* | + | ;* 返回类型:与输入字段相同,或 <code>output_field</code>(如果提供) |
第5,098行: | 第4,190行: | ||
<div id="min" class="section"> | <div id="min" class="section"> | ||
− | ==== | + | ==== Min ==== |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">Min</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">expression</span></span>'', ''<span class="n"><span class="pre">output_field</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">filter</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">extra</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 返回给定表达式的最小值。 |
− | ;* | + | ;* 默认别名:<code><field>__min</code> |
− | ;* | + | ;* 返回类型:与输入字段相同,或 <code>output_field</code>(如果提供) |
第5,109行: | 第4,201行: | ||
<div id="stddev" class="section"> | <div id="stddev" class="section"> | ||
− | ==== | + | ==== StdDev ==== |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">StdDev</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">expression</span></span>'', ''<span class="n"><span class="pre">output_field</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">sample</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">filter</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">extra</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>返回给定表达式中数据的标准差。</p> |
<ul> | <ul> | ||
− | <li><p> | + | <li><p>默认别名:<code><field>__stddev</code></p></li> |
− | <li><p> | + | <li><p>返回类型:<code>float</code> 如果输入是 <code>int</code>,否则与输入字段相同,或者 <code>output_field</code> 如果提供</p></li></ul> |
− | |||
<p>包含一个可选参数:</p> | <p>包含一个可选参数:</p> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">sample</span></span></dt> |
− | <dd><p> | + | <dd><p>默认情况下,<code>StdDev</code> 返回总体标准差。 但是,如果<code>sample=True</code>,则返回值将是样本标准差。</p></dd></dl> |
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p>SQLite | + | <p><span class="versionmodified changed">在 2.2 版更改:添加了 </span>SQLite 支持。</p> |
</div></dd></dl> | </div></dd></dl> | ||
第5,135行: | 第4,225行: | ||
<div id="sum" class="section"> | <div id="sum" class="section"> | ||
− | ==== | + | ==== Sum ==== |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">Sum</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">expression</span></span>'', ''<span class="n"><span class="pre">output_field</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">filter</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">extra</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 计算给定表达式的所有值的总和。 |
− | ;* | + | ;* 默认别名:<code><field>__sum</code> |
− | ;* | + | ;* 返回类型:与输入字段相同,或 <code>output_field</code>(如果提供) |
第5,146行: | 第4,236行: | ||
<div id="variance" class="section"> | <div id="variance" class="section"> | ||
− | ==== | + | ==== Variance ==== |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">Variance</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">expression</span></span>'', ''<span class="n"><span class="pre">output_field</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">sample</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">filter</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">extra</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>返回给定表达式中数据的方差。</p> |
<ul> | <ul> | ||
− | <li><p> | + | <li><p>默认别名:<code><field>__variance</code></p></li> |
− | <li><p> | + | <li><p>返回类型:<code>float</code> 如果输入是 <code>int</code>,否则与输入字段相同,或者 <code>output_field</code> 如果提供</p></li></ul> |
− | |||
<p>包含一个可选参数:</p> | <p>包含一个可选参数:</p> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">sample</span></span></dt> |
− | <dd><p> | + | <dd><p>默认情况下,<code>Variance</code> 返回总体方差。 但是,如果<code>sample=True</code>,返回值将是样本方差。</p></dd></dl> |
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p>SQLite | + | <p><span class="versionmodified changed">在 2.2 版更改:添加了 </span>SQLite 支持。</p> |
</div></dd></dl> | </div></dd></dl> | ||
第5,176行: | 第4,264行: | ||
<div id="query-related-tools" class="section"> | <div id="query-related-tools" class="section"> | ||
− | == | + | == 查询相关工具 == |
− | + | 本节提供了与查询相关的工具的参考资料,其他地方没有记载。 | |
− | |||
<div id="q-objects" class="section"> | <div id="q-objects" class="section"> | ||
− | === | + | === Q() 物体 === |
− | ; ''class | + | ; ''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">Q</span></span> |
: | : | ||
− | + | <code>Q()</code> 对象,如 [[../expressions#django.db.models|F]] 对象,将 SQL 表达式封装在 Python 对象中,可用于数据库相关操作。 | |
− | SQL | ||
− | |||
− | + | 通常,<code>Q() objects</code> 可以定义和重用条件。 这允许使用 <code>|</code> (<code>OR</code>) 和 <code>&</code> (<code>AND</code>) 运算符构建复杂数据库查询 [[../../../topics/db/queries#complex-lookups-with-q|的]] ; 特别是,不能在 <code>QuerySets</code> 中使用 <code>OR</code>。 | |
− | |||
− | |||
第5,200行: | 第4,283行: | ||
<div id="prefetch-objects" class="section"> | <div id="prefetch-objects" class="section"> | ||
− | === | + | === Prefetch() 物体 === |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">Prefetch</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">lookup</span></span>'', ''<span class="n"><span class="pre">queryset</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">to_attr</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | <code>Prefetch()</code> 对象可用于控制 [[#django.db.models.query.QuerySet.prefetch_related|prefetch_related()]] 的操作。 | |
− | [[#django.db.models.query.QuerySet.prefetch_related| | ||
− | + | <code>lookup</code> 参数描述了要遵循的关系,其工作方式与传递给 [[#django.db.models.query.QuerySet.prefetch_related|prefetch_related()]] 的基于字符串的查找相同。 例如: | |
− | |||
− | [[#django.db.models.query.QuerySet.prefetch_related| | ||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第5,216行: | 第4,296行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import Prefetch |
− | + | >>> Question.objects.prefetch_related(Prefetch('choice_set')).get().choice_set.all() | |
− | + | <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> | |
# This will only execute two queries regardless of the number of Question | # This will only execute two queries regardless of the number of Question | ||
# and Choice objects. | # and Choice objects. | ||
− | + | >>> Question.objects.prefetch_related(Prefetch('choice_set')).all() | |
− | + | <QuerySet [<Question: What's up?>]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>queryset</code> 参数为给定的查找提供一个基本的 <code>QuerySet</code>。 这对于进一步过滤预取操作或从预取关系调用 [[#django.db.models.query.QuerySet.select_related|select_related()]] 很有用,从而进一步减少查询次数: | |
− | |||
− | [[#django.db.models.query.QuerySet.select_related| | ||
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第5,236行: | 第4,313行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> voted_choices = Choice.objects.filter(votes__gt=0) |
− | + | >>> voted_choices | |
− | + | <QuerySet [<Choice: The sky>]> | |
− | + | >>> prefetch = Prefetch('choice_set', queryset=voted_choices) | |
− | + | >>> Question.objects.prefetch_related(prefetch).get().choice_set.all() | |
− | + | <QuerySet [<Choice: The sky>]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>to_attr</code> 参数将预取操作的结果设置为自定义属性: | |
− | |||
<div class="doctest highlight-default notranslate"> | <div class="doctest highlight-default notranslate"> | ||
第5,253行: | 第4,329行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> prefetch = Prefetch('choice_set', queryset=voted_choices, to_attr='voted_choices') |
− | + | >>> Question.objects.prefetch_related(prefetch).get().voted_choices | |
− | [ | + | [<Choice: The sky>] |
− | + | >>> Question.objects.prefetch_related(prefetch).get().choice_set.all() | |
− | + | <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]></syntaxhighlight> | |
</div> | </div> | ||
第5,266行: | 第4,342行: | ||
注解 | 注解 | ||
− | + | 使用 <code>to_attr</code> 时,预取结果存储在列表中。 与将缓存结果存储在 <code>QuerySet</code> 实例中的传统 <code>prefetch_related</code> 调用相比,这可以显着提高速度。 | |
− | |||
− | <code> | ||
− | <code> | ||
第5,277行: | 第4,350行: | ||
<div id="prefetch-related-objects" class="section"> | <div id="prefetch-related-objects" class="section"> | ||
− | === | + | === prefetch_related_objects() === |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">prefetch_related_objects</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">model_instances</span></span>'', ''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">related_lookups</span></span>''<span class="sig-paren">)</span> |
: | : | ||
− | + | 在可迭代的模型实例上预取给定的查找。 这在接收模型实例列表而不是 <code>QuerySet</code> 的代码中很有用; 例如,从缓存中获取模型或手动实例化它们时。 | |
− | |||
− | |||
− | + | 传递模型实例的可迭代对象(必须都属于同一类)和要预取的查找或 [[#django.db.models.Prefetch|Prefetch]] 对象。 例如: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第5,293行: | 第4,363行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import prefetch_related_objects |
− | + | >>> restaurants = fetch_top_restaurants_from_cache() # A list of Restaurants | |
− | + | >>> prefetch_related_objects(restaurants, 'pizzas__toppings')</syntaxhighlight> | |
</div> | </div> | ||
第5,304行: | 第4,374行: | ||
<div id="filteredrelation-objects" class="section"> | <div id="filteredrelation-objects" class="section"> | ||
− | === | + | === FilteredRelation() 物体 === |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">FilteredRelation</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">relation_name</span></span>'', ''<span class="o"><span class="pre">*</span></span>'', ''<span class="n"><span class="pre">condition</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">Q()</span></span>''<span class="sig-paren">)</span></dt> |
<dd><dl> | <dd><dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">relation_name</span></span></dt> |
− | <dd><p> | + | <dd><p>您要过滤关系的字段的名称。</p></dd></dl> |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">condition</span></span></dt> |
− | <dd><p> | + | <dd><p>一个 [[#django.db.models.Q|Q]] 对象来控制过滤。</p></dd></dl> |
</dd></dl> | </dd></dl> | ||
− | <code>FilteredRelation</code> | + | <code>FilteredRelation</code> 与 [[#django.db.models.query.QuerySet.annotate|annotate()]] 一起使用,以在执行 <code>JOIN</code> 时创建 <code>ON</code> 子句。 它不作用于默认关系,而是作用于注释名称(下面示例中的 <code>pizzas_vegetarian</code>)。 |
− | <code> | ||
− | |||
− | |||
− | + | 例如,要查找名称中包含 <code>'mozzarella'</code> 的素食比萨饼的餐厅: | |
− | <code>'mozzarella'</code> | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第5,329行: | 第4,395行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> from django.db.models import FilteredRelation, Q |
− | + | >>> Restaurant.objects.annotate( | |
... pizzas_vegetarian=FilteredRelation( | ... pizzas_vegetarian=FilteredRelation( | ||
... 'pizzas', condition=Q(pizzas__vegetarian=True), | ... 'pizzas', condition=Q(pizzas__vegetarian=True), | ||
... ), | ... ), | ||
− | ... ).filter(pizzas_vegetarian__name__icontains='mozzarella')</ | + | ... ).filter(pizzas_vegetarian__name__icontains='mozzarella')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果有大量比萨饼,则此查询集的性能优于: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第5,345行: | 第4,411行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Restaurant.objects.filter( |
... pizzas__vegetarian=True, | ... pizzas__vegetarian=True, | ||
... pizzas__name__icontains='mozzarella', | ... pizzas__name__icontains='mozzarella', | ||
− | ... )</ | + | ... )</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 因为第一个查询集的 <code>WHERE</code> 子句中的过滤只会对素食比萨进行操作。 | |
− | |||
− | <code>FilteredRelation</code> | + | <code>FilteredRelation</code> 不支持: |
<ul> | <ul> | ||
− | <li><p> | + | <li><p>跨越关系字段的条件。 例如:</p> |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> Restaurant.objects.annotate( |
... pizzas_with_toppings_startswith_n=FilteredRelation( | ... pizzas_with_toppings_startswith_n=FilteredRelation( | ||
... 'pizzas__toppings', | ... 'pizzas__toppings', | ||
第5,372行: | 第4,437行: | ||
Traceback (most recent call last): | Traceback (most recent call last): | ||
... | ... | ||
− | ValueError: FilteredRelation's condition doesn't support nested relations (got 'pizzas__toppings__name__startswith').</ | + | ValueError: FilteredRelation's condition doesn't support nested relations (got 'pizzas__toppings__name__startswith').</syntaxhighlight> |
</div> | </div> | ||
</div></li> | </div></li> | ||
− | <li><p>[[#django.db.models.query.QuerySet.only| | + | <li><p>[[#django.db.models.query.QuerySet.only|QuerySet.only()]] 和 [[#django.db.models.query.QuerySet.prefetch_related|prefetch_related()]]。</p></li> |
− | <li><p> | + | <li><p>从父模型继承的 [[../../contrib/contenttypes#django.contrib.contenttypes.fields|GenericForeignKey]]。</p></li></ul> |
− | |||
第5,385行: | 第4,449行: | ||
</div> | </div> | ||
+ | |||
+ | </div> | ||
+ | <div class="clearer"> | ||
+ | |||
+ | |||
</div> | </div> | ||
− | [[Category:Django 2.2.x | + | [[Category:Django 2.2.x 文档]] |
2021年10月31日 (日) 04:05的最新版本
QuerySet API 参考
本文档描述了 QuerySet
API 的详细信息。 它建立在 模型 和 数据库查询 指南中提供的材料之上,因此您可能希望在阅读本文之前先阅读并理解这些文档。
在本参考文献中,我们将使用 数据库查询指南 中介绍的 示例网络日志模型 。
当评估 QuerySets 时
在内部,QuerySet
可以被构造、过滤、切片,并且通常在不实际访问数据库的情况下传递。 在您执行某些操作来评估查询集之前,实际上不会发生数据库活动。
您可以通过以下方式评估 QuerySet
:
Iteration.
QuerySet
是可迭代的,它会在您第一次对其进行迭代时执行其数据库查询。 例如,这将打印数据库中所有条目的标题:注意:如果您只想确定是否存在至少一个结果,请不要使用此选项。 使用 exists() 效率更高。
切片。 如 Limiting QuerySets 中所述,可以使用 Python 的数组切片语法对
QuerySet
进行切片。 切片一个未赋值的QuerySet
通常会返回另一个未赋值的QuerySet
,但如果使用切片语法的“step”参数,Django 将执行数据库查询,并返回一个列表。 对已求值的QuerySet
进行切片也会返回一个列表。另请注意,即使切片未计算的
QuerySet
返回另一个未计算的QuerySet
,也不允许进一步修改(例如,添加更多过滤器或修改排序),因为这不能很好地转换为 SQL它也不会有明确的含义。Pickling/Caching. pickling QuerySets 时所涉及的详细信息,请参阅以下部分。 就本节而言,重要的是从数据库中读取结果。
repr(). 当您调用
repr()
时,会评估QuerySet
。 这是为了方便 Python 交互式解释器,因此您可以在交互式使用 API 时立即看到结果。len(). 当您调用
len()
时,会评估QuerySet
。 正如您所料,这将返回结果列表的长度。注意:如果您只需要确定集合中的记录数(而不需要实际对象),使用 SQL 的
SELECT COUNT(*)
在数据库级别处理计数会更有效。 正是出于这个原因,Django 提供了一个 count() 方法。list(). 通过调用
list()
强制评估QuerySet
。 例如:bool(). 在布尔上下文中测试
QuerySet
,例如使用bool()
、or
、and
或if
语句,将导致执行查询。 如果至少有一个结果,则QuerySet
为True
,否则为False
。 例如:注意:如果您只想确定是否至少存在一个结果(并且不需要实际对象),则使用 exists() 更有效。
酸洗 QuerySets
如果你 pickle
a QuerySet
,这将强制所有结果在酸洗之前加载到内存中。 酸洗通常用作缓存的前兆,当重新加载缓存的查询集时,您希望结果已经存在并可供使用(从数据库中读取可能需要一些时间,违背了缓存的目的)。 这意味着当你 unpickle 一个 QuerySet
时,它包含它被pickle 时的结果,而不是当前在数据库中的结果。
如果您只想在稍后从数据库中腌制必要的信息以重新创建 QuerySet
,请腌制 QuerySet
的 query
属性。 然后,您可以使用如下代码重新创建原始 QuerySet
(不加载任何结果):
query
属性是一个不透明的对象。 它代表查询构造的内部结构,而不是公共 API 的一部分。 但是,按照此处所述对属性的内容进行pickle 和unpickle 是安全的(并且完全受支持)。
QuerySet API
这是 QuerySet
的正式声明:
- class QuerySet(model=None, query=None, using=None, hints=None)
通常,当您与
QuerySet
交互时,您会通过 链接过滤器 来使用它。 为了完成这项工作,大多数QuerySet
方法返回新的查询集。 本节稍后将详细介绍这些方法。QuerySet
类有两个可用于自省的公共属性:- ordered
True
如果订购了QuerySet
— 即 在模型上有一个 order_by() 子句或默认排序。False
否则。
- db
如果现在执行这个查询,将使用的数据库。
注解
QuerySet 的
query
参数存在以便专门的查询子类可以重建内部查询状态。 该参数的值是该查询状态的不透明表示,不是公共 API 的一部分。 简单地说:如果你需要问,你不需要使用它。
返回新 QuerySet 的方法
Django 提供了一系列 QuerySet
细化方法,可以修改 QuerySet
返回的结果类型或其 SQL 查询的执行方式。
filter()
- filter(**kwargs)
返回包含与给定查找参数匹配的对象的新 QuerySet
。
查找参数 (**kwargs
) 应采用以下 字段查找 中描述的格式。 多个参数通过底层 SQL 语句中的 AND
连接。
如果您需要执行更复杂的查询(例如,使用 OR
语句的查询),您可以使用 Q 对象 。
exclude()
- exclude(**kwargs)
返回一个新的 QuerySet
,其中包含 与 不匹配给定查找参数的对象。
查找参数 (**kwargs
) 应采用以下 字段查找 中描述的格式。 多个参数通过底层 SQL 语句中的 AND
连接起来,整个内容包含在一个 NOT()
中。
此示例排除 pub_date
晚于 2005-1-3 且 headline
为“Hello”的所有条目:
在 SQL 术语中,计算结果为:
此示例排除 pub_date
晚于 2005-1-3 或标题为“您好”的所有条目:
在 SQL 术语中,计算结果为:
请注意,第二个例子的限制性更强。
如果您需要执行更复杂的查询(例如,使用 OR
语句的查询),您可以使用 Q 对象 。
annotate()
- annotate(*args, **kwargs)
使用提供的 查询表达式 列表注释 QuerySet
中的每个对象。 表达式可以是一个简单的值、对模型(或任何相关模型)上的字段的引用,或者是对与模型中的对象相关的对象进行计算的聚合表达式(平均值、总和等)。 QuerySet
。
annotate()
的每个参数都是一个注释,将添加到返回的 QuerySet
中的每个对象。
Django 提供的聚合函数在下面的 Aggregation Functions 中描述。
使用关键字参数指定的注释将使用关键字作为注释的别名。 匿名参数将根据聚合函数的名称和正在聚合的模型字段为它们生成一个别名。 只有引用单个字段的聚合表达式才能是匿名参数。 其他所有内容都必须是关键字参数。
例如,如果您正在操作一个博客列表,您可能想要确定每个博客中已经创建了多少条目:
Blog
模型本身并没有定义 entry__count
属性,但是通过使用关键字参数指定聚合函数,您可以控制注解的名称:
有关聚合的深入讨论,请参阅 有关聚合的主题指南 。
order_by()
- order_by(*fields)
默认情况下,QuerySet
返回的结果按模型的 Meta
中的 ordering
选项给出的排序元组排序。 您可以使用 order_by
方法在每个 QuerySet
的基础上覆盖它。
例子:
上面的结果将按 pub_date
降序排列,然后按 headline
升序排列。 "-pub_date"
前面的负号表示降序。 升序是隐含的。 要随机排序,请使用 "?"
,如下所示:
注意:order_by('?')
查询可能既昂贵又缓慢,具体取决于您使用的数据库后端。
要按不同模型中的字段排序,请使用与跨模型关系查询时相同的语法。 即,字段名称,后跟双下划线 (__
),后跟新模型中的字段名称,依此类推,适用于您想要加入的模型。 例如:
如果您尝试按与另一个模型相关的字段排序,Django 将使用相关模型的默认排序,或者如果未指定 Meta.ordering,则按相关模型的主键排序。 例如,由于 Blog
模型没有指定默认排序:
……等同于:
如果 Blog
有 ordering = ['name']
,那么第一个查询集将等同于:
您还可以通过在表达式上调用 asc() 或 desc() 来按 查询表达式 排序:
asc() 和 desc() 具有控制空值排序方式的参数(nulls_first
和 nulls_last
)。
如果您还使用 distinct(),则在按相关模型中的字段排序时要小心。 有关相关模型排序如何改变预期结果的说明,请参阅 distinct() 中的注释。
注解
允许指定多值字段以对结果进行排序(例如,ManyToManyField 字段,或 ForeignKey 字段的反向关系)。
考虑这个案例:
在这里,每个 Event
可能有多个排序数据; 每个带有多个 children
的 Event
将多次返回到 order_by()
创建的新 QuerySet
中。 换句话说,在 QuerySet
上使用 order_by()
可能会返回比您开始时更多的项目 - 这可能既不符合预期也没有用。
因此,在使用多值字段对结果进行排序时要小心。 如果您可以确定您订购的每件商品只有一个订购数据,那么这种方法应该不会出现问题。 如果没有,请确保结果符合您的预期。
无法指定排序是否应区分大小写。 关于区分大小写,Django 会对结果进行排序,但您的数据库后端通常会对其进行排序。
您可以使用 Lower 将字段转换为小写,这将实现大小写一致的排序:
如果您不希望对查询应用任何排序,甚至不希望应用默认排序,请调用不带参数的 order_by()。
您可以通过检查 QuerySet.ordered 属性来判断查询是否已排序,如果 QuerySet
已以任何方式排序,则该属性将为 True
。
每个 order_by()
调用将清除任何先前的排序。 例如,此查询将按 pub_date
而不是 headline
排序:
警告
订购不是免费的操作。 您添加到排序中的每个字段都会对您的数据库产生成本。 您添加的每个外键也将隐式包含其所有默认排序。
如果查询未指定排序,则以未指定的顺序从数据库返回结果。 只有在按唯一标识结果中每个对象的一组字段进行排序时,才能保证特定的排序。 例如,如果 name
字段不是唯一的,按它排序并不能保证具有相同名称的对象总是以相同的顺序出现。
reverse()
- reverse()
使用 reverse()
方法反转查询集元素的返回顺序。 再次调用 reverse()
会将排序恢复到正常方向。
要检索查询集中的“最后”五个项目,您可以这样做:
请注意,这与在 Python 中从序列末尾进行切片并不完全相同。 上面的例子将首先返回最后一项,然后是倒数第二项,依此类推。 如果我们有一个 Python 序列并查看 seq[-5:]
,我们将首先看到倒数第五项。 Django 不支持这种访问模式(从末尾切片),因为在 SQL 中不可能有效地做到这一点。
另外,请注意 reverse()
通常只应在具有定义排序的 QuerySet
上调用(例如,当查询定义默认排序的模型时,或使用 order_by( ))。 如果没有为给定的 QuerySet
定义这样的排序,则对其调用 reverse()
没有实际影响(排序在调用 reverse()
之前未定义,之后将保持未定义) .
distinct()
- distinct(*fields)
返回在其 SQL 查询中使用 SELECT DISTINCT
的新 QuerySet
。 这消除了查询结果中的重复行。
默认情况下,QuerySet
不会消除重复行。 在实践中,这很少成为问题,因为像 Blog.objects.all()
这样的简单查询不会引入重复结果行的可能性。 但是,如果您的查询跨越多个表,则在评估 QuerySet
时可能会得到重复的结果。 那时你会使用 distinct()
。
注解
order_by() 调用中使用的任何字段都包含在 SQL SELECT
列中。 当与 distinct()
结合使用时,有时会导致意外结果。 如果您按相关模型中的字段排序,这些字段将被添加到选定的列中,否则它们可能会使重复的行看起来不同。 由于额外的列不会出现在返回的结果中(它们仅用于支持排序),因此有时看起来返回的是非不同的结果。
同样,如果您使用 values() 查询来限制选择的列,则任何 order_by()(或默认模型排序)中使用的列仍将涉及并可能影响唯一性的结果。
这里的寓意是,如果您使用 distinct()
,请注意按相关型号排序。 同样,当一起使用 distinct()
和 values() 时,按不在 values() 调用中的字段排序时要小心。
仅在 PostgreSQL 上,您可以传递位置参数 (*fields
) 以指定 DISTINCT
应适用的字段名称。 这将转换为 SELECT DISTINCT ON
SQL 查询。 这就是不同之处。 对于正常的 distinct()
调用,数据库在确定哪些行不同时比较每行中的 each 字段。 对于具有指定字段名称的 distinct()
调用,数据库将仅比较指定的字段名称。
注解
指定字段名称时,必须在QuerySet
中提供order_by()
,order_by()
中的字段必须以distinct()
,顺序相同。
例如,SELECT DISTINCT ON (a)
为您提供 a
列中每个值的第一行。 如果您不指定顺序,您将得到一些任意行。
示例(第一个之后的仅适用于 PostgreSQL):
注解
请记住, order_by() 使用已定义的任何默认相关模型排序。 您可能必须按关系 _id
或引用字段显式排序,以确保 DISTINCT ON
表达式与 ORDER BY
子句开头的表达式匹配。 例如,如果 Blog
模型通过 name
定义了一个 ordering:
……不起作用,因为查询将按 blog__name
排序,从而与 DISTINCT ON
表达式不匹配。 您必须按关系 _id 字段(在本例中为 blog_id
)或引用的字段(blog__pk
)明确排序,以确保两个表达式匹配。
values()
- values(*fields, **expressions)
返回一个 QuerySet
返回字典,而不是模型实例。
其中每一个字典都代表一个对象,键与模型对象的属性名相对应。
本示例将 values()
的字典与普通模型对象进行比较:
values()
方法采用可选的位置参数 *fields
,它指定 SELECT
应限制为的字段名称。 如果您指定字段,则每个字典将仅包含您指定的字段的字段键/值。 如果不指定字段,则每个字典将包含数据库表中每个字段的键和值。
例子:
values()
方法还接受可选的关键字参数,**expressions
,这些参数被传递给 annotate():
您可以在排序中使用内置和 自定义查找 。 例如:
2.1 版更改: 添加了对查找的支持。
values()
子句中的聚合应用在同一 values()
子句中的其他参数之前。 如果您需要按另一个值分组,请将其添加到较早的 values()
子句中。 例如:
有几个微妙的地方值得一提:
如果您有一个名为
foo
的字段,它是一个 ForeignKey,默认的values()
调用将返回一个名为foo_id
的字典键,因为这是名称存储实际值的隐藏模型属性(foo
属性指的是相关模型)。 当你调用values()
并传入字段名称时,你可以传入foo
或foo_id
并且你会得到同样的东西(字典键将匹配字段您传入的名称)。例如:
将
values()
与 distinct() 一起使用时,请注意排序会影响结果。 有关详细信息,请参阅 distinct() 中的注释。如果在 extra() 调用后使用
values()
子句,则 extra() 中由select
参数定义的任何字段必须明确包含在values()
调用中。 在values()
调用之后进行的任何 extra() 调用都将忽略其额外的选定字段。在
values()
之后调用 only() 和 defer() 没有意义,因此这样做会引发NotImplementedError
。组合转换和聚合需要使用两个 annotate() 调用,明确地或作为 values() 的关键字参数。 如上,如果转换已在相关字段类型上注册,则第一个 annotate() 可以省略,因此以下示例是等效的:
当您知道只需要少量可用字段中的值并且不需要模型实例对象的功能时,它很有用。 仅选择需要使用的字段会更有效率。
最后,注意可以调用filter()
、order_by()
等。 在 values()
调用之后,这意味着这两个调用是相同的:
制作 Django 的人更喜欢将所有影响 SQL 的方法放在第一位,然后(可选)任何影响输出的方法(例如 values()
),但这并不重要。 这是你真正炫耀个人主义的机会。
您还可以通过 OneToOneField
、ForeignKey
和 ManyToManyField
属性引用具有反向关系的相关模型上的字段:
警告
因为 ManyToManyField 属性和反向关系可以有多个相关行,包括这些可以对结果集的大小产生乘数效应。 如果您在 values()
查询中包含多个这样的字段,这将特别明显,在这种情况下,将返回所有可能的组合。
values_list()
- values_list(*fields, flat=False, named=False)
这类似于 values()
不同之处在于它在迭代时返回元组而不是返回字典。 每个元组包含来自传递到 values_list()
调用的相应字段或表达式的值——所以第一项是第一个字段,依此类推。 例如:
如果只传入单个字段,还可以传入 flat
参数。 如果 True
,这将意味着返回的结果是单个值,而不是一元组。 一个例子应该能让区别更清楚:
有多个字段时传入flat
是错误的。
您可以通过 named=True
来获得作为 namedtuple()
的结果:
使用命名元组可能会使使用结果更易读,但代价是将结果转化为命名元组时要付出很小的性能代价。
如果您没有将任何值传递给 values_list()
,它将按照声明的顺序返回模型中的所有字段。
一个常见的需求是获取某个模型实例的特定字段值。 为此,请使用 values_list()
后跟 get()
调用:
values()
和 values_list()
都是针对特定用例的优化:在没有创建模型实例的开销的情况下检索数据子集。 当处理多对多和其他多值关系(例如反向外键的一对多关系)时,这个比喻就失效了,因为“一行一个对象”假设不成立。
例如,请注意跨 ManyToManyField 查询时的行为:
有多个条目的作者多次出现,没有任何条目的作者的条目标题为 None
。
同样,当查询反向外键时,对于没有任何作者的条目,会出现 None
:
dates()
- dates(field, kind, order='ASC')
返回一个 QuerySet
,其计算结果为 datetime.date
对象列表,表示 QuerySet
内容中特定类型的所有可用日期。
field
应该是您模型的 DateField
的名称。 kind
应为 "year"
、"month"
、"week"
或 "day"
。 结果列表中的每个 datetime.date
对象都被“截断”为给定的 type
。
"year"
返回字段的所有不同年份值的列表。"month"
返回字段的所有不同年/月值的列表。"week"
返回该字段所有不同年/周值的列表。 所有日期都将是星期一。"day"
返回字段的所有不同年/月/日值的列表。
order
,默认为 'ASC'
,应该是 'ASC'
或 'DESC'
。 这指定了如何对结果进行排序。
例子:
2.1 版变更: 增加了“周”支持。
datetimes()
- datetimes(field_name, kind, order='ASC', tzinfo=None)
返回一个 QuerySet
,其计算结果为 datetime.datetime
对象列表,表示 QuerySet
内容中特定类型的所有可用日期。
field_name
应该是您模型的 DateTimeField
的名称。
kind
应为 "year"
、"month"
、"week"
、"day"
、"hour"
、"minute"
, 或 "second"
。 结果列表中的每个 datetime.datetime
对象都被“截断”为给定的 type
。
order
,默认为 'ASC'
,应该是 'ASC'
或 'DESC'
。 这指定了如何对结果进行排序。
tzinfo
定义日期时间在截断之前转换到的时区。 实际上,根据使用的时区,给定的日期时间具有不同的表示形式。 此参数必须是 datetime.tzinfo
对象。 如果是 None
,Django 使用 当前时区 。 :setting:`USE_TZ` 为 False
时无效。
2.1 版变更: 增加了“周”支持。
注解
此函数直接在数据库中执行时区转换。 因此,您的数据库必须能够解释 tzinfo.tzname(None)
的值。 这转化为以下要求:
- SQLite:没有要求。 使用 pytz(安装 Django 时安装)在 Python 中执行转换。
- PostgreSQL:无要求(参见 时区 )。
- Oracle:无要求(请参阅 选择时区文件 )。
- MySQL:使用 mysql_tzinfo_to_sql 加载时区表。
none()
- none()
调用 none() 将创建一个永远不会返回任何对象的查询集,并且在访问结果时不会执行任何查询。 qs.none() 查询集是 EmptyQuerySet
的一个实例。
例子:
all()
- all()
返回当前 QuerySet
(或 QuerySet
子类)的 副本 。 这在您可能希望传入模型管理器或 QuerySet
并对结果进行进一步过滤的情况下非常有用。 在任一对象上调用 all()
之后,您肯定会有一个 QuerySet
可以使用。
当 QuerySet
被 评估 时,它通常会缓存其结果。 如果自评估 QuerySet
后数据库中的数据可能已更改,您可以通过对先前评估的 QuerySet
调用 all()
来获取同一查询的更新结果。
union()
- union(*other_qs, all=False)
使用 SQL 的 UNION
运算符组合两个或多个 QuerySet
的结果。 例如:
默认情况下,UNION
运算符仅选择不同的值。 要允许重复值,请使用 all=True
参数。
union()
、intersection()
和 difference()
返回第一个 QuerySet
类型的模型实例,即使参数是其他的 QuerySet
楷模。 只要 SELECT
列表在所有 QuerySet
中都相同(至少是类型,名称无关紧要,只要类型的顺序相同)就可以传递不同的模型。 在这种情况下,您必须使用应用到结果 QuerySet
的 QuerySet
方法中第一个 QuerySet
的列名。 例如:
此外,只有 LIMIT
、OFFSET
、COUNT(*)
、ORDER BY
,并指定列(即 切片,count()、order_by() 和 values()/values_list()) 允许在结果 [X123X ]。 此外,数据库对组合查询中允许的操作进行了限制。 例如,大多数数据库不允许在组合查询中使用 LIMIT
或 OFFSET
。
intersection()
- intersection(*other_qs)
使用 SQL 的 INTERSECT
运算符返回两个或多个 QuerySet
的共享元素。 例如:
有关一些限制,请参阅 union()。
difference()
- difference(*other_qs)
使用 SQL 的 EXCEPT
运算符仅保留 QuerySet
中存在的元素,而不保留其他一些 QuerySet
中的元素。 例如:
有关一些限制,请参阅 union()。
extra()
- extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
有时,Django 查询语法本身无法轻松表达复杂的 WHERE
子句。 对于这些边缘情况,Django 提供了 extra()
QuerySet
修饰符——一个钩子,用于将特定子句注入由 QuerySet
生成的 SQL。
在万不得已的情况下使用这种方法
这是一个旧的 API,我们的目标是在未来的某个时候弃用。 仅当您无法使用其他查询集方法表达查询时才使用它。 如果您确实需要使用它,请使用 QuerySet.extra 关键字 与您的用例一起 提交票证 (请先检查现有票证列表),以便我们可以增强QuerySet API 允许删除 extra()
。 我们不再改进或修复此方法的错误。
例如,extra()
的这种用法:
相当于:
使用 RawSQL 的主要好处是您可以根据需要设置 output_field
。 主要的缺点是,如果您在原始 SQL 中引用查询集的某个表别名,那么 Django 可能会更改该别名(例如,当查询集用作另一个查询中的子查询时)。
警告
使用 extra()
时应该非常小心。 每次使用它时,都应该使用 params
对用户可以控制的任何参数进行转义,以防止 SQL 注入攻击。
您也不得在 SQL 字符串中引用占位符。 由于 %s
周围的引号,此示例容易受到 SQL 注入攻击:
您可以阅读有关 Django SQL 注入保护 工作原理的更多信息。
根据定义,这些额外的查找可能无法移植到不同的数据库引擎(因为您明确地编写 SQL 代码)并且违反了 DRY 原则,因此您应该尽可能避免它们。
指定 params
、select
、where
或 tables
中的一个或多个。 不需要任何参数,但您应该至少使用其中之一。
select
select
参数允许您在SELECT
子句中放置额外的字段。 它应该是一个将属性名称映射到 SQL 子句以用于计算该属性的字典。例子:
因此,每个
Entry
对象都会有一个额外的属性is_recent
,这是一个布尔值,表示条目的pub_date
是否大于 Jan。 1, 2006.Django 将给定的 SQL 片段直接插入到
SELECT
语句中,因此上述示例的结果 SQL 将类似于:下一个例子更高级; 它执行一个子查询,为每个结果
Blog
对象提供一个entry_count
属性,关联Entry
对象的整数计数:在这种特殊情况下,我们利用查询已经在其
FROM
子句中包含blog_blog
表的事实。上述示例的结果 SQL 将是:
请注意,在 Django 的
select
子句中,大多数数据库引擎所需的围绕子查询的括号不是必需的。 另请注意,某些数据库后端(例如某些 MySQL 版本)不支持子查询。在极少数情况下,您可能希望将参数传递给
extra(select=...)
中的 SQL 片段。 为此,请使用select_params
参数。 由于select_params
是一个序列,而select
属性是一个字典,因此需要小心谨慎,以便参数与额外的选择片段正确匹配。 在这种情况下,您应该对select
值使用collections.OrderedDict
,而不仅仅是普通的 Python 字典。这将起作用,例如:
如果需要在选择字符串中使用文字
%s
,请使用序列%%s
。where
/tables
您可以使用
where
定义显式 SQLWHERE
子句——也许是为了执行非显式连接。 您可以使用tables
手动向 SQLFROM
子句添加表。where
和tables
都采用字符串列表。 所有where
参数都与任何其他搜索条件进行“与”运算。例子:
...翻译(大致)为以下 SQL:
如果您指定的表已在查询中使用,则在使用
tables
参数时要小心。 当您通过tables
参数添加额外的表时,Django 假定您希望该表包含额外的时间,如果它已经包含在内。 这就产生了一个问题,因为表名将被赋予一个别名。 如果一个表在 SQL 语句中多次出现,则第二次和后续出现必须使用别名,以便数据库可以区分它们。 如果您指的是在额外where
参数中添加的额外表,这将导致错误。通常,您只会添加尚未出现在查询中的额外表。 但是,如果确实发生了上述情况,则有几种解决方案。 首先,看看您是否可以不包含额外的表并使用查询中已有的表。 如果这是不可能的,请将您的
extra()
调用放在查询集构造的前面,以便您的表是该表的第一次使用。 最后,如果所有其他方法都失败了,请查看生成的查询并重写您的where
添加以使用为额外表提供的别名。 每次以相同的方式构造查询集时,别名都将相同,因此您可以依赖别名不会更改。order_by
如果您需要使用通过
extra()
包含的一些新字段或表对结果查询集进行排序,请使用order_by
参数到extra()
并传入一系列字符串。 这些字符串应该是模型字段(如查询集上的正常 order_by() 方法),形式为table_name.column_name
或您在 [ 中指定的列的别名X181X] 参数设置为extra()
。例如:
这会将
is_recent
为真的所有项目排序到结果集的前面(True
按降序排列在False
之前)。顺便说一下,这表明您可以多次调用
extra()
并且它会按照您的预期运行(每次添加新约束)。params
上述
where
参数可以使用标准 Python 数据库字符串占位符 —'%s'
来指示数据库引擎应自动引用的参数。params
参数是要替换的任何额外参数的列表。例子:
始终使用
params
而不是直接将值嵌入到where
中,因为params
将确保根据您的特定后端正确引用值。 例如,引号将被正确转义。坏的:
好的:
警告
如果您在 MySQL 上执行查询,请注意 MySQL 的静默类型强制在混合类型时可能会导致意外结果。 如果查询字符串类型的列,但使用的是整数值,MySQL 会在执行比较之前将表中所有值的类型强制为整数。 例如,如果您的表包含值 'abc'
、'def'
并且您查询 WHERE mycolumn=0
,则两行都将匹配。 为防止出现这种情况,请在查询中使用该值之前执行正确的类型转换。
defer()
- defer(*fields)
在某些复杂的数据建模情况下,您的模型可能包含大量字段,其中一些字段可能包含大量数据(例如,文本字段),或者需要进行昂贵的处理才能将它们转换为 Python 对象。 如果您在最初获取数据时不知道是否需要这些特定字段的情况下使用查询集的结果,您可以告诉 Django 不要从数据库中检索它们。
这是通过将字段的名称传递给 defer()
来完成的:
具有延迟字段的查询集仍将返回模型实例。 如果您访问该字段(一次一个,而不是一次访问所有延迟字段),将从数据库中检索每个延迟字段。
您可以多次呼叫 defer()
。 每次调用都会向延迟集合添加新字段:
将字段添加到延迟集中的顺序无关紧要。 使用已被延迟的字段名称调用 defer()
是无害的(该字段仍将被延迟)。
您可以通过使用标准双下划线符号分隔相关字段来延迟加载相关模型中的字段(如果相关模型通过 select_related() 加载):
如果要清除延迟字段集,请将 None
作为参数传递给 defer()
:
模型中的某些字段不会被延迟,即使您要求它们。 你永远不能推迟主键的加载。 如果您使用 select_related() 检索相关模型,则不应推迟从主模型连接到相关模型的字段的加载,否则会导致错误。
注解
defer()
方法(及其表亲 only(),如下)仅适用于高级用例。 当您仔细分析查询并准确了解 ' 您需要什么信息并测量返回您需要的字段与模型的完整字段集之间的差异时,它们提供了一种优化。
即使您认为您处于高级用例情况, 仅在您无法在查询集加载时确定是否需要额外字段时才使用 defer() 。 如果您经常加载和使用数据的特定子集,您可以做出的最佳选择是规范化模型并将未加载的数据放入单独的模型(和数据库表)中。 如果列 must 出于某种原因保留在一个表中,请使用 Meta.managed = False
创建一个模型(请参阅 managed 属性 文档)只包含您通常需要的字段加载并使用您可能会调用 defer()
的地方。 这使您的代码对读者更加明确,速度稍快,并且在 Python 进程中消耗的内存更少。
例如,这两个模型都使用相同的底层数据库表:
如果很多字段需要在非托管模型中重复,最好的办法是创建一个共享字段的抽象模型,然后让非托管模型和托管模型从抽象模型中继承。
only()
- only(*fields)
only()
方法或多或少与 defer() 相反。 您可以使用在检索模型时应延迟 而非 的字段调用它。 如果您的模型几乎所有字段都需要延迟,则使用 only()
指定补充字段集可以简化代码。
假设您有一个包含字段 name
、age
和 biography
的模型。 以下两个查询集在延迟字段方面是相同的:
每当您调用 only()
时,它会 替换 立即加载的字段集。 该方法的名称是助记符:only 那些字段会立即加载; 其余的被推迟。 因此,对 only()
的连续调用导致只考虑最终字段:
由于 defer()
以增量方式起作用(将字段添加到延迟列表中),您可以组合对 only()
和 defer()
的调用,并且事情将按逻辑进行:
defer() 文档注释中的所有注意事项也适用于 only()
。 谨慎使用它,并且只有在用尽你的其他选择之后。
使用 only() 并省略使用 select_related() 请求的字段也是一个错误。
using()
- using(alias)
如果您使用多个数据库,此方法用于控制 QuerySet
将针对哪个数据库进行评估。 此方法采用的唯一参数是数据库的别名,如 :setting:`DATABASES` 中所定义。
例如:
select_for_update()
- select_for_update(nowait=False, skip_locked=False, of=())
返回将锁定行直到事务结束的查询集,在支持的数据库上生成 SELECT ... FOR UPDATE
SQL 语句。
例如:
当查询集被评估时(在本例中为 for entry in entries
),所有匹配的条目将被锁定,直到事务块结束,这意味着将阻止其他事务更改或获取对它们的锁。
通常,如果另一个事务已经获得了对选定行之一的锁定,则查询将阻塞,直到锁定被释放。 如果这不是您想要的行为,请调用 select_for_update(nowait=True)
。 这将使调用非阻塞。 如果另一个事务已经获取了冲突锁,则在评估查询集时将引发 DatabaseError。 您也可以使用 select_for_update(skip_locked=True)
来忽略锁定的行。 nowait
和 skip_locked
是互斥的,在启用这两个选项的情况下尝试调用 select_for_update()
将导致 ValueError
。
默认情况下,select_for_update()
锁定查询选择的所有行。 例如,除了查询集模型的行之外,select_related() 中指定的相关对象的行也被锁定。 如果不需要,请使用与 select_related() 相同的字段语法在 select_for_update(of=(...))
中指定要锁定的相关对象。 使用值 'self'
来引用查询集的模型。
在 select_for_update(of=(...))
中锁定父母模型
如果要在使用 多表继承 时锁定父模型,则必须在 of
参数中指定父链接字段(默认为 <parent_model_name>_ptr
)。 例如:
您不能在可空关系上使用 select_for_update()
:
为了避免这种限制,如果您不关心它们,您可以排除空对象:
目前,postgresql
、oracle
和 mysql
数据库后端支持 select_for_update()
。 但是,MySQL 不支持 [X35X] 参数,并且仅在 MySQL 8.0.1+ 上支持 nowait
和 skip_locked
参数。
使用不支持这些选项的数据库后端(例如 MySQL)将 nowait=True
、skip_locked=True
或 of
传递给 select_for_update()
,会引发 NotSupportedError[ X160X]。 这可以防止代码意外阻塞。
在支持 SELECT ... FOR UPDATE
的后端在自动提交模式下使用 select_for_update()
评估查询集是一个 TransactionManagementError 错误,因为在这种情况下行未锁定。 如果允许,这将促进数据损坏,并且很容易由调用预期在事务之外的事务中运行的代码引起。
在不支持 SELECT ... FOR UPDATE
的后端(例如 SQLite)上使用 select_for_update()
将不起作用。 SELECT ... FOR UPDATE
不会添加到查询中,如果在自动提交模式下使用 select_for_update()
,则不会引发错误。
警告
尽管 select_for_update()
在自动提交模式下通常会失败,但由于 TestCase 自动将每个测试包装在一个事务中,即使在 之外调用 TestCase
中的 select_for_update()
atomic() 块将(可能出乎意料地)通过而不引发 TransactionManagementError
。 要正确测试 select_for_update()
,您应该使用 TransactionTestCase。
raw()
- raw(raw_query, params=None, translations=None)
获取原始 SQL 查询,执行它,并返回一个 django.db.models.query.RawQuerySet
实例。 这个 RawQuerySet
实例可以像普通的 QuerySet
一样迭代以提供对象实例。
有关详细信息,请参阅 执行原始 SQL 查询 。
警告
raw()
总是触发一个新的查询并且不考虑之前的过滤。 因此,通常应该从 Manager
或新的 QuerySet
实例调用它。
返回新 QuerySet 的运算符
组合的查询集必须使用相同的模型。
与 (&)
使用 SQL AND
运算符组合两个 QuerySet
。
以下是等效的:
SQL 等价于:
或 (|)
使用 SQL OR
运算符组合两个 QuerySet
。
以下是等效的:
SQL 等价于:
不返回 QuerySets 的方法
以下 QuerySet
方法评估 QuerySet
并返回 以外的 和 QuerySet
。
这些方法不使用缓存(参见 Caching 和 QuerySets)。 相反,他们每次被调用时都会查询数据库。
get()
- get(**kwargs)
返回与给定查找参数匹配的对象,该对象应采用 字段查找 中描述的格式。
get()
如果找到多个对象,则引发 MultipleObjectsReturned。 MultipleObjectsReturned 异常是模型类的一个属性。
get()
如果未找到给定参数的对象,则会引发 DoesNotExist 异常。 这个异常是模型类的一个属性。 例子:
DoesNotExist 异常继承自 django.core.exceptions.ObjectDoesNotExist,因此您可以针对多个 DoesNotExist 异常。 例子:
如果您希望查询集返回一行,则可以使用不带任何参数的 get()
来返回该行的对象:
create()
- create(**kwargs)
一种创建对象并将其全部保存在一个步骤中的便捷方法。 因此:
和:
是等效的。
force_insert 参数在别处有说明,但这意味着总是会创建一个新对象。 通常你不需要担心这个。 但是,如果您的模型包含您设置的手动主键值,并且该值已存在于数据库中,则调用 create()
将失败并显示 IntegrityError,因为主键必须是唯一的. 如果您使用手动主键,请准备好处理异常。
get_or_create()
- get_or_create(defaults=None, **kwargs)
一种使用给定 kwargs
查找对象的便捷方法(如果您的模型具有所有字段的默认值,则可能为空),如有必要,创建一个。
返回 (object, created)
的元组,其中 object
是检索或创建的对象,created
是指定是否创建新对象的布尔值。
这是为了防止在并行发出请求时创建重复的对象,并作为样板代码的快捷方式。 例如:
这里,对于并发请求,可能会多次尝试保存具有相同参数的 Person
。 为了避免这种竞争条件,上面的例子可以使用 get_or_create()
重写,如下所示:
任何传递给 get_or_create()
的关键字参数 — 除外 一个称为 defaults
的可选参数 — 将在 get() 调用中使用。 如果找到对象,get_or_create()
返回该对象的元组和 False
。
警告
此方法是原子的,假设数据库强制关键字参数的唯一性(请参阅 unique 或 unique_together)。 如果关键字参数中使用的字段没有唯一性约束,则对此方法的并发调用可能会导致插入具有相同参数的多行。
您可以通过将 get_or_create()
与 filter()
链接起来并使用 Q 对象 来为检索到的对象指定更复杂的条件。 例如,检索 Robert 或 Bob Marley(如果存在),否则创建后者:
如果找到多个对象,get_or_create()
会引发 MultipleObjectsReturned。 如果没有找到一个对象,get_or_create()
将实例化并保存一个新对象,返回一个新对象和True
的元组。 新对象将根据以下算法大致创建:
在英语中,这意味着以任何不包含双下划线的非 'defaults'
关键字参数开头(这表示不精确的查找)。 然后添加 defaults
的内容,必要时覆盖任何键,并将结果用作模型类的关键字参数。 如果 defaults
中有任何可调用对象,请评估它们。 如上所述,这是所用算法的简化,但它包含所有相关细节。 内部实现具有比这更多的错误检查并处理一些额外的边缘条件; 如果您有兴趣,请阅读代码。
如果您有一个名为 defaults
的字段并希望将其用作 get_or_create()
中的精确查找,只需使用 'defaults__exact'
,如下所示:
当您使用手动指定的主键时,get_or_create()
方法具有与 create() 类似的错误行为。 如果需要创建对象并且密钥已经存在于数据库中,则会引发 IntegrityError。
最后,谈谈在 Django 视图中使用 get_or_create()
。 请确保仅在 POST
请求中使用它,除非您有充分的理由不这样做。 GET
请求不应对数据产生任何影响。 相反,只要对页面的请求对您的数据有副作用,就使用 POST
。 有关更多信息,请参阅 HTTP 规范中的 安全方法。
警告
您可以使用 get_or_create()
到 ManyToManyField 属性和反向关系。 在这种情况下,您将限制该关系上下文内的查询。 如果您不始终如一地使用它,这可能会导致您出现一些完整性问题。
有以下型号:
您可以通过 Book 的章节字段使用 get_or_create()
,但它只能在该书的上下文中获取:
之所以发生这种情况,是因为它试图通过“尤利西斯”一书获取或创建“第 1 章”,但它无法执行任何操作:该关系无法获取该章节,因为它与该书无关,但是它也不能创建它,因为 title
字段应该是唯一的。
update_or_create()
- update_or_create(defaults=None, **kwargs)
一种使用给定 kwargs
更新对象的便捷方法,必要时创建一个新对象。 defaults
是用于更新对象的(字段,值)对的字典。 defaults
中的值可以是可调用的。
返回 (object, created)
元组,其中 object
是创建或更新的对象,created
是一个布尔值,指定是否创建了新对象。
update_or_create
方法尝试根据给定的 kwargs
从数据库中获取一个对象。 如果找到匹配项,它会更新 defaults
字典中传递的字段。
这意味着作为样板代码的快捷方式。 例如:
随着模型中字段数量的增加,这种模式变得非常笨拙。 上面的例子可以使用 update_or_create()
重写,如下所示:
有关如何解析 kwargs
中传递的名称的详细说明,请参阅 get_or_create()。
如上文 get_or_create() 中所述,此方法容易出现竞争条件,如果未在数据库级别强制执行唯一性,则可能导致同时插入多行。
像 get_or_create() 和 create(),如果您使用手动指定的主键并且需要创建一个对象但该键已经存在于数据库中,则 引发 IntegrityError。
bulk_create()
- bulk_create(objs, batch_size=None, ignore_conflicts=False)
此方法以高效的方式将提供的对象列表插入到数据库中(通常只有 1 个查询,无论有多少个对象):
不过这有一些注意事项:
不会调用模型的
save()
方法,也不会发送pre_save
和post_save
信号。在多表继承的情况下,它不能与子模型一起工作。
如果模型的主键是 AutoField,它不会像
save()
那样检索和设置主键属性,除非数据库后端支持它(当前是 PostgreSQL)。对于多对多的关系,它是行不通的。
它将
objs
转换为一个列表,如果它是一个生成器,它会完全评估objs
。 强制转换允许检查所有对象,以便可以首先插入具有手动设置的主键的任何对象。 如果您想批量插入对象而不一次评估整个生成器,只要对象没有任何手动设置的主键,您就可以使用此技术:
batch_size
参数控制在单个查询中创建的对象数量。 默认是批量创建所有对象,SQLite 除外,默认情况下每个查询最多使用 999 个变量。
在支持它的数据库上(除了 PostgreSQL < 9.5 和 Oracle),设置ignore_conflicts
参数为True
告诉数据库忽略插入失败约束(例如重复唯一值)的任何行的失败。 启用此参数将禁用在每个模型实例上设置主键(如果数据库通常支持它)。
2.2 版更改: 添加了 ignore_conflicts
参数。
bulk_update()
2.2 版中的新功能。
- bulk_update(objs, fields, batch_size=None)
此方法有效地更新提供的模型实例上的给定字段,通常使用一个查询:
QuerySet.update() 用于保存更改,因此这比遍历模型列表并在每个模型上调用 save()
更有效,但它有一些警告:
- 您无法更新模型的主键。
- 不会调用每个模型的
save()
方法,并且不会发送 pre_save 和 post_save 信号。 - 如果更新大量行中的大量列,则生成的 SQL 可能非常大。 通过指定合适的
batch_size
来避免这种情况。 - 更新定义在多表继承祖先上的字段将给每个祖先带来额外的查询。
- 如果
objs
包含重复项,则仅更新第一个。
batch_size
参数控制在单个查询中保存的对象数量。 默认是批量更新所有对象,SQLite 和 Oracle 除外,它们对查询中使用的变量数量有限制。
count()
- count()
返回一个整数,表示数据库中与 QuerySet
匹配的对象数。
例子:
count()
调用在幕后执行 SELECT COUNT(*)
,因此您应该始终使用 count()
,而不是将所有记录加载到 Python 对象中并调用 len()
结果(除非您无论如何都需要将对象加载到内存中,在这种情况下 len()
会更快)。
请注意,如果您想要 QuerySet
中的项目数并且还从中检索模型实例(例如,通过对其进行迭代),则使用 len(queryset)
可能更有效t 会导致像 count()
这样的额外数据库查询。
in_bulk()
- in_bulk(id_list=None, field_name='pk')
获取字段值列表 (id_list
) 和这些值的 field_name
,并返回一个字典,将每个值映射到具有给定字段值的对象实例。 如果未提供 id_list
,则返回查询集中的所有对象。 field_name
必须是唯一字段,默认为主键。
例子:
如果你传递 in_bulk()
一个空列表,你会得到一个空字典。
iterator()
- iterator(chunk_size=2000)
评估 QuerySet
(通过执行查询)并返回一个迭代器(参见 PEP 234)。 QuerySet
通常在内部缓存其结果,以便重复评估不会导致额外的查询。 相比之下, iterator()
会直接读取结果,不会在 QuerySet
级别做任何缓存(在内部,默认迭代器调用 iterator()
并缓存返回值)。 对于返回大量只需要访问一次的对象的 QuerySet
,这可以带来更好的性能并显着减少内存。
请注意,在已经计算过的 QuerySet
上使用 iterator()
将强制它再次计算,重复查询。
此外,使用 iterator()
会导致之前的 prefetch_related()
调用被忽略,因为这两个优化一起使用没有意义。
根据数据库后端,查询结果将被一次性加载或使用服务器端的游标从数据库中流转。
使用服务器端游标
Oracle 和 PostgreSQL 使用服务器端游标从数据库中流式传输结果,而无需将整个结果集加载到内存中。
Oracle 数据库驱动程序总是使用服务器端的游标。
对于服务器端游标,chunk_size
参数指定要在数据库驱动程序级别缓存的结果数。 获取更大的块会减少数据库驱动程序和数据库之间的往返次数,但会消耗内存。
在 PostgreSQL 上,服务器端游标只会在以下情况下使用 :设置:`DISABLE_SERVER_SIDE_CURSORS ` 设置是False
. 如果您使用以事务池模式配置的连接池,请阅读 事务池和服务器端游标 。 禁用服务器端游标时,行为与不支持服务器端游标的数据库相同。
没有服务器端游标
MySQL 不支持流式结果,因此 Python 数据库驱动程序将整个结果集加载到内存中。 然后,数据库适配器使用 PEP 249 中定义的 fetchmany()
方法将结果集转换为 Python 行对象。
SQLite 可以使用 fetchmany()
批量获取结果,但由于 SQLite 不提供连接内查询之间的隔离,因此在写入被迭代的表时要小心。 有关详细信息,请参阅使用 QuerySet.iterator() 时的 隔离。
chunk_size
参数控制 Django 从数据库驱动程序中检索的批次大小。 较大的批处理减少了与数据库驱动程序通信的开销,但代价是内存消耗略有增加。
chunk_size
的默认值,2000,来自 psycopg 邮件列表 上的 计算:
假设行数为 10-20 列,文字数据和数字数据混合,2000 要取不到 100KB 的数据,这似乎是一个很好的折中方案,在传输的行数和提前退出循环时丢弃的数据之间。
2.2 版更改: 添加了对 SQLite 上的结果流的支持。
latest()
- latest(*fields)
根据给定的字段,返回表中最新的对象。
本示例根据 pub_date
字段返回表中最新的 Entry
:
您还可以根据多个字段选择最新的。 例如,当两个条目具有相同的 pub_date
时,选择 Entry
与最早的 expire_date
:
'-expire_date'
中的负号表示按降序对expire_date
进行排序。 由于latest()
得到最后的结果,所以选择了expire_date
最早的Entry
。
如果您模型的 Meta 指定 get_latest_by,则您可以省略 earliest()
或 latest()
的任何参数。 默认情况下将使用 get_latest_by 中指定的字段。
像 get()、earliest()
和 latest()
引发 DoesNotExist 如果没有具有给定参数的对象。
请注意,earliest()
和 latest()
的存在纯粹是为了方便和可读性。
earliest()
和 latest()
可能返回具有空日期的实例。
由于排序委托给数据库,如果您使用不同的数据库,则允许空值的字段的结果可能会以不同的方式排序。 例如,PostgreSQL 和 MySQL 对空值进行排序,就好像它们高于非空值一样,而 SQLite 则相反。
您可能希望过滤掉空值:
first()
- first()
返回查询集匹配的第一个对象,如果没有匹配的对象,则返回 None
。 如果 QuerySet
没有定义排序,那么查询集会自动按主键排序。 这可能会影响聚合结果,如 与默认排序或 order_by() 的交互中所述。
例子:
注意 first()
是一个方便的方法,下面的代码示例相当于上面的例子:
aggregate()
- aggregate(*args, **kwargs)
返回在 QuerySet
上计算的聚合值(平均值、总和等)的字典。 aggregate()
的每个参数指定一个值,该值将包含在返回的字典中。
Django 提供的聚合函数在下面的 Aggregation Functions 中描述。 由于聚合也是 查询表达式 ,您可以将聚合与其他聚合或值组合以创建复杂的聚合。
使用关键字参数指定的聚合将使用关键字作为注释的名称。 匿名参数将根据聚合函数的名称和正在聚合的模型字段为它们生成一个名称。 复杂聚合不能使用匿名参数,必须将关键字参数指定为别名。
例如,当您处理博客条目时,您可能想知道贡献了博客条目的作者数量:
通过使用关键字参数指定聚合函数,您可以控制返回的聚合值的名称:
有关聚合的深入讨论,请参阅 有关聚合的主题指南 。
exists()
- exists()
如果 QuerySet 包含任何结果,则返回 True
,否则返回 False
。 这试图以最简单和最快的方式执行查询,但它 确实 执行与普通 QuerySet 查询几乎相同的查询。
exists() 可用于与 QuerySet 中的对象成员资格和 QuerySet 中任何对象的存在相关的搜索,特别是在大 查询集 。
查找模型是否具有唯一字段的最有效方法(例如 primary_key
) 是 QuerySet 的成员是:
这将比以下需要评估和迭代整个查询集的更快:
并查找查询集是否包含任何项目:
这将比:
……但不是很大(因此需要一个大的查询集来提高效率)。
此外,如果 some_queryset
尚未被评估,但您知道它会在某个时刻被评估,那么使用 some_queryset.exists()
将完成更多的整体工作(一个存在检查的查询加上一个额外的一到稍后检索结果),而不是简单地使用 bool(some_queryset)
,后者检索结果,然后检查是否有任何返回。
update()
- update(**kwargs)
对指定的字段执行 SQL 更新查询,并返回匹配的行数(如果有些行已经有了新的值,则可能不等于更新的行数)。
例如,要关闭 2010 年发布的所有博客条目的评论,您可以执行以下操作:
(这假设您的 Entry
模型具有字段 pub_date
和 comments_on
。)
您可以更新多个字段 - 数量没有限制。 例如,这里我们更新 comments_on
和 headline
字段:
update()
方法是即时应用的,更新的 QuerySet 唯一的限制是它只能更新模型主表中的列,不能更新相关模型。 你不能这样做,例如:
不过,仍然可以根据相关字段进行过滤:
您不能在已获取切片或无法再过滤的 QuerySet 上调用 update()
。
update()
方法返回受影响的行数:
如果你只是更新一条记录并且不需要对模型对象做任何事情,最有效的方法是调用 update()
,而不是将模型对象加载到内存中。 例如,不要这样做:
…做这个:
使用 update()
还可以防止竞争条件,即在加载对象和调用 save()
之间的短时间内,数据库中的某些内容可能会发生变化。
最后,意识到 update()
在 SQL 级别进行更新,因此不会在您的模型上调用任何 save()
方法,也不会发出 pre_save 或 [ X169X]post_save 信号(这是调用 Model.save() 的结果)。 如果要更新具有自定义 save() 方法的模型的一堆记录,请遍历它们并调用 save(),如下所示:
delete()
- delete()
对 QuerySet 中的所有行执行 SQL 删除查询,并返回删除的对象数和包含每种对象类型删除数的字典。
delete()
立即应用。 您不能在已获取切片或无法再过滤的 QuerySet 上调用 delete()
。
例如,要删除特定博客中的所有条目:
默认情况下,Django 的 ForeignKey 模拟 SQL 约束 ON DELETE CASCADE
— 换句话说,任何外键指向要删除的对象的对象都将被删除。 例如:
此级联行为可通过 ForeignKey 的 on_delete 参数进行自定义。
delete()
方法执行批量删除,并且不会在您的模型上调用任何 delete()
方法。 但是,它确实为所有已删除的对象(包括级联删除)发出 pre_delete 和 post_delete 信号。
Django 需要将对象提取到内存中以发送信号和处理级联。 但是,如果没有级联和信号,那么 Django 可能会采用快速路径并删除对象而不提取到内存中。 对于大型删除,这会导致显着减少内存使用。 执行查询的数量也可以减少。
设置为 on_delete DO_NOTHING
的外键不会阻止在删除时采用快速路径。
需要注意的是,对象删除中产生的查询是一个实现细节,可能会发生变化。
as_manager()
- classmethod as_manager()
返回 Manager 实例和 QuerySet
方法副本的类方法。 有关更多详细信息,请参阅 使用 QuerySet 方法创建管理器 。
explain()
2.1 版中的新功能。
- explain(format=None, **options)
返回 QuerySet
的执行计划的字符串,它详细说明了数据库将如何执行查询,包括将使用的任何索引或连接。 了解这些细节可能会帮助您提高慢查询的性能。
例如,当使用 PostgreSQL 时:
不同数据库之间的输出有很大的不同。
explain()
被除 Oracle 之外的所有内置数据库后端支持,因为那里的实现并不简单。
format
参数改变了数据库默认的输出格式,通常是基于文本的。 PostgreSQL 支持 'TEXT'
、'JSON'
、'YAML'
和 'XML'
。 MySQL 支持 'TEXT'
(也称为 'TRADITIONAL'
)和 'JSON'
。
某些数据库接受可以返回有关查询的更多信息的标志。 将这些标志作为关键字参数传递。 例如,当使用 PostgreSQL 时:
在某些数据库上,标志可能会导致执行查询,这可能会对您的数据库产生不利影响。 例如,如果有触发器或函数被调用,PostgreSQL 的 ANALYZE
标志可能会导致数据更改,即使对于 SELECT
查询也是如此。
Field 查找
字段查找是您指定 SQL WHERE
子句内容的方式。 它们被指定为 QuerySet
方法 filter()、exclude() 和 get() 的关键字参数。
有关介绍,请参阅 模型和数据库查询文档 。
下面列出了 Django 的内置查找。 也可以为模型字段编写 自定义查找 。
为了方便,当没有提供查找类型时(如 Entry.objects.get(id=14)
),查找类型假定为 :lookup:`exact`。
iexact
不区分大小写的精确匹配。 如果为比较提供的值是 None
,它将被解释为 SQL NULL
(有关更多详细信息,请参阅 :lookup:`isnull`)。
例子:
SQL 等效项:
请注意,第一个查询将匹配 'Beatles Blog'
、'beatles blog'
、'BeAtLes BLoG'
等。
contains
区分大小写的包含测试。
例子:
SQL 等价于:
请注意,这将匹配标题 'Lennon honored today'
但不匹配 'lennon honored today'
。
in
在给定的迭代中; 通常是列表、元组或查询集。 这不是一个常见的用例,但接受字符串(可迭代)。
例子:
SQL 等效项:
您还可以使用查询集来动态评估值列表,而不是提供文字值列表:
此查询集将作为子选择语句进行评估:
如果您将 values()
或 values_list()
产生的 QuerySet
作为值传递给 __in
查找,则需要确保只提取一个字段结果。 例如,这将起作用(过滤博客名称):
此示例将引发异常,因为内部查询试图提取两个字段值,其中只需要一个:
性能注意事项
谨慎使用嵌套查询并了解您的数据库服务器的性能特征(如果有疑问,请进行基准测试!)。 一些数据库后端,尤其是 MySQL,不能很好地优化嵌套查询。 在这些情况下,提取值列表然后将其传递到第二个查询中会更有效。 也就是说,执行两个查询而不是一个:
请注意博客 QuerySet
周围的 list()
调用以强制执行第一个查询。 没有它,将执行嵌套查询,因为 QuerySets 是惰性的 。
gt
大于。
例子:
SQL 等价于:
gte
大于等于。
lt
小于。
lte
小于等于
startswith
区分大小写的开头为。
例子:
SQL 等价于:
SQLite 不支持区分大小写的 LIKE
语句; startswith
的作用类似于 SQLite 的 istartswith
。
endswith
区分大小写的结尾为。
例子:
SQL 等价于:
range
范围测试(含)。
例子:
SQL 等价于:
在 SQL 中可以使用 BETWEEN
的任何地方都可以使用 range
— 用于日期、数字甚至字符。
警告
用日期过滤 DateTimeField
将不包括最后一天的项目,因为边界被解释为“给定日期的上午 0 点”。 如果 pub_date
是一个 DateTimeField
,上面的表达式会变成这个 SQL:
一般来说,您不能混合日期和日期时间。
date
对于日期时间字段,将值转换为日期。 允许链接额外的字段查找。 获取日期值。
例子:
(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。
当 :setting:`USE_TZ` 为 True
时,字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
year
对于日期和日期时间字段,精确的年份匹配。 允许链接额外的字段查找。 取整数年。
例子:
SQL 等价于:
(确切的 SQL 语法因每个数据库引擎而异)。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
iso_year
2.2 版中的新功能。
对于日期和日期时间字段,精确的 ISO 8601 周编号年份匹配。 允许链接额外的字段查找。 取整数年。
例子:
(确切的 SQL 语法因每个数据库引擎而异)。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
month
对于日期和日期时间字段,精确的月份匹配。 允许链接额外的字段查找。 取整数 1(一月)到 12(十二月)。
例子:
SQL 等价于:
(确切的 SQL 语法因每个数据库引擎而异)。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
day
对于日期和日期时间字段,精确的日期匹配。 允许链接额外的字段查找。 需要整数天。
例子:
SQL 等价于:
(确切的 SQL 语法因每个数据库引擎而异)。
请注意,这将匹配任何带有 pub_date 的月份第三天的记录,如 1 月 3 日,7 月 3 日等。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
week
对于日期和日期时间字段,根据 ISO-8601 返回周数(1-52 或 53),即周从星期一开始,第一周包含年度的第一个星期四。
例子:
(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
week_day
对于日期和日期时间字段,“星期几”匹配。 允许链接额外的字段查找。
从 1(星期日)到 7(星期六)取一个整数值,代表一周的一天。
例子:
(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。
请注意,这将匹配任何具有 pub_date
位于星期一(一周的第 2 天)的记录,而不管它发生在哪个月份或年份。 工作日被索引,第 1 天是星期日,第 7 天是星期六。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
quarter
对于日期和日期时间字段,“一年中的一个季度”匹配。 允许链接额外的字段查找。 取一个 1 到 4 之间的整数值,表示一年中的一个季度。
检索第二季度(4 月 1 日至 6 月 30 日)条目的示例:
(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
time
对于日期时间字段,将值转换为时间。 允许链接额外的字段查找。 采用 datetime.time
值。
例子:
(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。
当 :setting:`USE_TZ` 为 True
时,字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
hour
对于日期时间和时间字段,精确的小时匹配。 允许链接额外的字段查找。 取 0 到 23 之间的整数。
例子:
SQL 等价于:
(确切的 SQL 语法因每个数据库引擎而异)。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
minute
对于日期时间和时间字段,精确的分钟匹配。 允许链接额外的字段查找。 取 0 到 59 之间的整数。
例子:
SQL 等价于:
(确切的 SQL 语法因每个数据库引擎而异)。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
second
对于日期时间和时间字段,精确的第二个匹配。 允许链接额外的字段查找。 取 0 到 59 之间的整数。
例子:
SQL 等价于:
(确切的 SQL 语法因每个数据库引擎而异)。
当 :setting:`USE_TZ` 为 True
时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。
isnull
采用 True
或 False
,分别对应于 IS NULL
和 IS NOT NULL
的 SQL 查询。
例子:
SQL 等价于:
regex
区分大小写的正则表达式匹配。
正则表达式语法是正在使用的数据库后端的语法。 对于没有内置正则表达式支持的 SQLite,此功能由(Python)用户定义的 REGEXP 函数提供,因此正则表达式语法是 Python 的 re
模块的语法。
例子:
SQL 等效项:
建议使用原始字符串(例如,r'foo'
而不是 'foo'
)来传递正则表达式语法。
iregex
不区分大小写的正则表达式匹配。
例子:
SQL 等效项:
聚合函数
Django 在 django.db.models
模块中提供了以下聚合函数。 有关如何使用这些聚合函数的详细信息,请参阅 聚合主题指南 。 请参阅 Aggregate 文档以了解如何创建聚合。
警告
SQLite 无法处理开箱即用的日期/时间字段的聚合。 这是因为 SQLite 中没有原生日期/时间字段,而 Django 目前使用文本字段模拟这些功能。 尝试在 SQLite 中的日期/时间字段上使用聚合将引发 NotImplementedError
。
注解
与空 QuerySet
一起使用时,聚合函数返回 None
。 例如,如果 QuerySet
不包含任何条目,则 Sum
聚合函数返回 None
而不是 0
。 一个例外是 Count
,如果 QuerySet
为空,它会返回 0
。
所有聚合体都有以下共同的参数:
output_field
一个可选参数,表示返回值的 模型字段
注解
在组合多个字段类型时,如果所有字段类型相同,Django 只能确定 output_field
。 否则,您必须自己提供 output_field
。
**extra
关键字参数,可以为聚合生成的 SQL 提供额外的上下文。
Avg
- class Avg(expression, output_field=None, filter=None, **extra)
- 返回给定表达式的平均值,除非您指定不同的
output_field
,否则该平均值必须是数字。
- 默认别名:
<field>__avg
- 返回类型:
float
如果输入是int
,否则与输入字段相同,或者output_field
如果提供
- 默认别名:
Count
- class Count(expression, distinct=False, filter=None, **extra)
返回通过提供的表达式关联的对象数量。
默认别名:
<field>__count
返回类型:
int
包含一个可选参数:
- distinct
如果为
distinct=True
,则计数将仅包括唯一实例。 这是COUNT(DISTINCT <field>)
的 SQL 等价物。 默认值为False
。
Max
- class Max(expression, output_field=None, filter=None, **extra)
- 返回给定表达式的最大值。
- 默认别名:
<field>__max
- 返回类型:与输入字段相同,或
output_field
(如果提供)
- 默认别名:
Min
- class Min(expression, output_field=None, filter=None, **extra)
- 返回给定表达式的最小值。
- 默认别名:
<field>__min
- 返回类型:与输入字段相同,或
output_field
(如果提供)
- 默认别名:
StdDev
- class StdDev(expression, output_field=None, sample=False, filter=None, **extra)
返回给定表达式中数据的标准差。
默认别名:
<field>__stddev
返回类型:
float
如果输入是int
,否则与输入字段相同,或者output_field
如果提供
包含一个可选参数:
- sample
默认情况下,
StdDev
返回总体标准差。 但是,如果sample=True
,则返回值将是样本标准差。
在 2.2 版更改:添加了 SQLite 支持。
Sum
- class Sum(expression, output_field=None, filter=None, **extra)
- 计算给定表达式的所有值的总和。
- 默认别名:
<field>__sum
- 返回类型:与输入字段相同,或
output_field
(如果提供)
- 默认别名:
Variance
- class Variance(expression, output_field=None, sample=False, filter=None, **extra)
返回给定表达式中数据的方差。
默认别名:
<field>__variance
返回类型:
float
如果输入是int
,否则与输入字段相同,或者output_field
如果提供
包含一个可选参数:
- sample
默认情况下,
Variance
返回总体方差。 但是,如果sample=True
,返回值将是样本方差。
在 2.2 版更改:添加了 SQLite 支持。