“Django/docs/2.2.x/ref/models/conditional-expressions”的版本间差异

来自菜鸟教程
Django/docs/2.2.x/ref/models/conditional-expressions
跳转至:导航、​搜索
(autoload)
 
(Page commit)
 
第1行: 第1行:
 +
{{DISPLAYTITLE:条件表达式 — Django 文档}}
 
<div id="conditional-expressions" class="section">
 
<div id="conditional-expressions" class="section">
  
= Conditional Expressions =
+
= 条件表达式 =
  
Conditional expressions let you use <code>if</code> ... <code>elif</code> ...
+
条件表达式允许您在过滤器、注释、聚合和更新中使用 <code>if</code> ... <code>elif</code> ... <code>else</code> 逻辑。 条件表达式为表的每一行计算一系列条件并返回匹配的结果表达式。 条件表达式也可以像其他 [[../expressions|表达式]] 一样组合和嵌套。
<code>else</code> logic within filters, annotations, aggregations, and updates. A
 
conditional expression evaluates a series of conditions for each row of a
 
table and returns the matching result expression. Conditional expressions can
 
also be combined and nested like other [[../expressions|<span class="doc">expressions</span>]].
 
  
 
<div id="the-conditional-expression-classes" class="section">
 
<div id="the-conditional-expression-classes" class="section">
  
== The conditional expression classes ==
+
== 条件表达式类 ==
  
We'll be using the following model in the subsequent examples:
+
我们将在后续示例中使用以下模型:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第19行: 第16行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from django.db import models
+
<syntaxhighlight lang="python">from django.db import models
  
 
class Client(models.Model):
 
class Client(models.Model):
第36行: 第33行:
 
         choices=ACCOUNT_TYPE_CHOICES,
 
         choices=ACCOUNT_TYPE_CHOICES,
 
         default=REGULAR,
 
         default=REGULAR,
     )</pre>
+
     )</syntaxhighlight>
  
 
</div>
 
</div>
第43行: 第40行:
 
<div id="when" class="section">
 
<div id="when" class="section">
  
=== <code>When</code> ===
+
=== When ===
  
; ''class'' <code>When</code><span class="sig-paren">(</span>''<span class="n">condition</span><span class="o">=</span><span class="default_value">None</span>'', ''<span class="n">then</span><span class="o">=</span><span class="default_value">None</span>'', ''<span class="o">**</span><span class="n">lookups</span>''<span class="sig-paren">)</span>[[../../_modules/django/db/models/expressions.html#When|<span class="viewcode-link">[源代码]</span>]]
+
; ''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">When</span></span><span class="sig-paren">(</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">None</span></span>'', ''<span class="n"><span class="pre">then</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">lookups</span></span>''<span class="sig-paren">)</span>
 
:  
 
:  
  
A <code>When()</code> object is used to encapsulate a condition and its result for use
+
<code>When()</code> 对象用于封装条件及其结果以供在条件表达式中使用。 使用 <code>When()</code> 对象类似于使用 [[../querysets#django.db.models.query.QuerySet|filter()]] 方法。 可以使用 [[../querysets#field-lookups|字段查找]] [[../querysets#django.db.models|Q]] 对象指定条件。 结果是使用 <code>then</code> 关键字提供的。
in the conditional expression. Using a <code>When()</code> object is similar to using
 
the [[../querysets#django.db.models.query.QuerySet|<code>filter()</code>]] method. The condition can
 
be specified using [[../querysets#field-lookups|<span class="std std-ref">field lookups</span>]] or
 
[[../querysets#django.db.models|<code>Q</code>]] objects. The result is provided using the <code>then</code>
 
keyword.
 
  
Some examples:
+
一些例子:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第61行: 第53行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from django.db.models import F, Q, When
+
<syntaxhighlight lang="python">>>> from django.db.models import F, Q, When
&gt;&gt;&gt; # String arguments refer to fields; the following two examples are equivalent:
+
>>> # String arguments refer to fields; the following two examples are equivalent:
&gt;&gt;&gt; When(account_type=Client.GOLD, then='name')
+
>>> When(account_type=Client.GOLD, then='name')
&gt;&gt;&gt; When(account_type=Client.GOLD, then=F('name'))
+
>>> When(account_type=Client.GOLD, then=F('name'))
&gt;&gt;&gt; # You can use field lookups in the condition
+
>>> # You can use field lookups in the condition
&gt;&gt;&gt; from datetime import date
+
>>> from datetime import date
&gt;&gt;&gt; When(registered_on__gt=date(2014, 1, 1),
+
>>> When(registered_on__gt=date(2014, 1, 1),
 
...      registered_on__lt=date(2015, 1, 1),
 
...      registered_on__lt=date(2015, 1, 1),
 
...      then='account_type')
 
...      then='account_type')
&gt;&gt;&gt; # Complex conditions can be created using Q objects
+
>>> # Complex conditions can be created using Q objects
&gt;&gt;&gt; When(Q(name__startswith=&quot;John&quot;) | Q(name__startswith=&quot;Paul&quot;),
+
>>> When(Q(name__startswith="John") | Q(name__startswith="Paul"),
...      then='name')</pre>
+
...      then='name')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Keep in mind that each of these values can be an expression.
+
请记住,每个值都可以是一个表达式。
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
Since the <code>then</code> keyword argument is reserved for the result of the
+
由于 <code>then</code> 关键字参数是为 <code>When()</code> 的结果保留的,如果 [[../instances#django.db.models|Model]] 具有名为 <code>then</code> 的字段,则存在潜在冲突。 这可以通过两种方式解决:
<code>When()</code>, there is a potential conflict if a
 
[[../instances#django.db.models|<code>Model</code>]] has a field named <code>then</code>. This can be
 
resolved in two ways:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第92行: 第81行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; When(then__exact=0, then=1)
+
<syntaxhighlight lang="python">>>> When(then__exact=0, then=1)
&gt;&gt;&gt; When(Q(then=0), then=1)</pre>
+
>>> When(Q(then=0), then=1)</syntaxhighlight>
  
 
</div>
 
</div>
第104行: 第93行:
 
<div id="case" class="section">
 
<div id="case" class="section">
  
=== <code>Case</code> ===
+
=== Case ===
  
; ''class'' <code>Case</code><span class="sig-paren">(</span>''<span class="o">*</span><span class="n">cases</span>'', ''<span class="o">**</span><span class="n">extra</span>''<span class="sig-paren">)</span>[[../../_modules/django/db/models/expressions.html#Case|<span class="viewcode-link">[源代码]</span>]]
+
; ''<span class="pre">class</span>'' <span class="sig-name descname"><span class="pre">Case</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">cases</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">extra</span></span>''<span class="sig-paren">)</span>
 
:  
 
:  
  
A <code>Case()</code> expression is like the <code>if</code> ... <code>elif</code> ...
+
<code>Case()</code> 表达式类似于 <code>Python</code> 中的 <code>if</code> ... <code>elif</code> ... <code>else</code> 语句。 提供的 <code>When()</code> 对象中的每个 <code>condition</code> 都按顺序进行评估,直到评估为真实值。 返回匹配的 <code>When()</code> 对象中的 <code>result</code> 表达式。
<code>else</code> statement in <code>Python</code>. Each <code>condition</code> in the provided
 
<code>When()</code> objects is evaluated in order, until one evaluates to a
 
truthful value. The <code>result</code> expression from the matching <code>When()</code> object
 
is returned.
 
  
A simple example:
+
一个简单的例子:
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第121行: 第106行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt;
+
<syntaxhighlight lang="python">>>>
&gt;&gt;&gt; from datetime import date, timedelta
+
>>> from datetime import date, timedelta
&gt;&gt;&gt; from django.db.models import Case, CharField, Value, When
+
>>> from django.db.models import Case, CharField, Value, When
&gt;&gt;&gt; Client.objects.create(
+
>>> Client.objects.create(
 
...    name='Jane Doe',
 
...    name='Jane Doe',
 
...    account_type=Client.REGULAR,
 
...    account_type=Client.REGULAR,
 
...    registered_on=date.today() - timedelta(days=36))
 
...    registered_on=date.today() - timedelta(days=36))
&gt;&gt;&gt; Client.objects.create(
+
>>> Client.objects.create(
 
...    name='James Smith',
 
...    name='James Smith',
 
...    account_type=Client.GOLD,
 
...    account_type=Client.GOLD,
 
...    registered_on=date.today() - timedelta(days=5))
 
...    registered_on=date.today() - timedelta(days=5))
&gt;&gt;&gt; Client.objects.create(
+
>>> Client.objects.create(
 
...    name='Jack Black',
 
...    name='Jack Black',
 
...    account_type=Client.PLATINUM,
 
...    account_type=Client.PLATINUM,
 
...    registered_on=date.today() - timedelta(days=10 * 365))
 
...    registered_on=date.today() - timedelta(days=10 * 365))
&gt;&gt;&gt; # Get the discount for each Client based on the account type
+
>>> # Get the discount for each Client based on the account type
&gt;&gt;&gt; Client.objects.annotate(
+
>>> Client.objects.annotate(
 
...    discount=Case(
 
...    discount=Case(
 
...        When(account_type=Client.GOLD, then=Value('5%')),
 
...        When(account_type=Client.GOLD, then=Value('5%')),
第145行: 第130行:
 
...    ),
 
...    ),
 
... ).values_list('name', 'discount')
 
... ).values_list('name', 'discount')
&lt;QuerySet [('Jane Doe', '0%'), ('James Smith', '5%'), ('Jack Black', '10%')]&gt;</pre>
+
<QuerySet [('Jane Doe', '0%'), ('James Smith', '5%'), ('Jack Black', '10%')]></syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<code>Case()</code> accepts any number of <code>When()</code> objects as individual arguments.
+
<code>Case()</code> 接受任意数量的 <code>When()</code> 对象作为单独的参数。 使用关键字参数提供其他选项。 如果没有任何条件计算为 <code>TRUE</code>,则返回带有 <code>default</code> 关键字参数的表达式。 如果未提供 <code>default</code> 参数,则使用 <code>None</code>
Other options are provided using keyword arguments. If none of the conditions
 
evaluate to <code>TRUE</code>, then the expression given with the <code>default</code> keyword
 
argument is returned. If a <code>default</code> argument isn't provided, <code>None</code> is
 
used.
 
  
If we wanted to change our previous query to get the discount based on how long
+
如果我们想根据 <code>Client</code> 与我们在一起的时间更改我们之前的查询以获取折扣,我们可以使用查找来实现:
the <code>Client</code> has been with us, we could do so using lookups:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第163行: 第143行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; a_month_ago = date.today() - timedelta(days=30)
+
<syntaxhighlight lang="python">>>> a_month_ago = date.today() - timedelta(days=30)
&gt;&gt;&gt; a_year_ago = date.today() - timedelta(days=365)
+
>>> a_year_ago = date.today() - timedelta(days=365)
&gt;&gt;&gt; # Get the discount for each Client based on the registration date
+
>>> # Get the discount for each Client based on the registration date
&gt;&gt;&gt; Client.objects.annotate(
+
>>> Client.objects.annotate(
 
...    discount=Case(
 
...    discount=Case(
 
...        When(registered_on__lte=a_year_ago, then=Value('10%')),
 
...        When(registered_on__lte=a_year_ago, then=Value('10%')),
第174行: 第154行:
 
...    )
 
...    )
 
... ).values_list('name', 'discount')
 
... ).values_list('name', 'discount')
&lt;QuerySet [('Jane Doe', '5%'), ('James Smith', '0%'), ('Jack Black', '10%')]&gt;</pre>
+
<QuerySet [('Jane Doe', '5%'), ('James Smith', '0%'), ('Jack Black', '10%')]></syntaxhighlight>
  
 
</div>
 
</div>
第181行: 第161行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
Remember that the conditions are evaluated in order, so in the above
+
请记住,条件是按顺序计算的,因此在上面的示例中,即使第二个条件与 Jane Doe Jack Black 匹配,我们也会得到正确的结果。 这就像 <code>Python</code> 中的 <code>if</code> ... <code>elif</code> ... <code>else</code> 语句。
example we get the correct result even though the second condition matches
 
both Jane Doe and Jack Black. This works just like an <code>if</code> ...
 
<code>elif</code> ... <code>else</code> statement in <code>Python</code>.
 
  
  
 
</div>
 
</div>
<code>Case()</code> also works in a <code>filter()</code> clause. For example, to find gold
+
<code>Case()</code> 也适用于 <code>filter()</code> 子句。 例如,要查找一个多月前注册的黄金客户和一年多前注册的白金客户:
clients that registered more than a month ago and platinum clients that
 
registered more than a year ago:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第198行: 第173行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; a_month_ago = date.today() - timedelta(days=30)
+
<syntaxhighlight lang="python">>>> a_month_ago = date.today() - timedelta(days=30)
&gt;&gt;&gt; a_year_ago = date.today() - timedelta(days=365)
+
>>> a_year_ago = date.today() - timedelta(days=365)
&gt;&gt;&gt; Client.objects.filter(
+
>>> Client.objects.filter(
 
...    registered_on__lte=Case(
 
...    registered_on__lte=Case(
 
...        When(account_type=Client.GOLD, then=a_month_ago),
 
...        When(account_type=Client.GOLD, then=a_month_ago),
第206行: 第181行:
 
...    ),
 
...    ),
 
... ).values_list('name', 'account_type')
 
... ).values_list('name', 'account_type')
&lt;QuerySet [('Jack Black', 'P')]&gt;</pre>
+
<QuerySet [('Jack Black', 'P')]></syntaxhighlight>
  
 
</div>
 
</div>
第217行: 第192行:
 
<div id="advanced-queries" class="section">
 
<div id="advanced-queries" class="section">
  
== Advanced queries ==
+
== 高级查询 ==
  
Conditional expressions can be used in annotations, aggregations, lookups, and
+
条件表达式可用于注释、聚合、查找和更新。 它们也可以与其他表达式组合和嵌套。 这允许您进行强大的条件查询。
updates. They can also be combined and nested with other expressions. This
 
allows you to make powerful conditional queries.
 
  
 
<div id="conditional-update" class="section">
 
<div id="conditional-update" class="section">
  
=== Conditional update ===
+
=== 条件更新 ===
  
Let's say we want to change the <code>account_type</code> for our clients to match
+
假设我们要为客户更改 <code>account_type</code> 以匹配他们的注册日期。 我们可以使用条件表达式和 [[../querysets#django.db.models.query.QuerySet|update()]] 方法来做到这一点:
their registration dates. We can do this using a conditional expression and the
 
[[../querysets#django.db.models.query.QuerySet|<code>update()</code>]] method:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第235行: 第206行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; a_month_ago = date.today() - timedelta(days=30)
+
<syntaxhighlight lang="python">>>> a_month_ago = date.today() - timedelta(days=30)
&gt;&gt;&gt; a_year_ago = date.today() - timedelta(days=365)
+
>>> a_year_ago = date.today() - timedelta(days=365)
&gt;&gt;&gt; # Update the account_type for each Client from the registration date
+
>>> # Update the account_type for each Client from the registration date
&gt;&gt;&gt; Client.objects.update(
+
>>> Client.objects.update(
 
...    account_type=Case(
 
...    account_type=Case(
 
...        When(registered_on__lte=a_year_ago,
 
...        When(registered_on__lte=a_year_ago,
第247行: 第218行:
 
...    ),
 
...    ),
 
... )
 
... )
&gt;&gt;&gt; Client.objects.values_list('name', 'account_type')
+
>>> Client.objects.values_list('name', 'account_type')
&lt;QuerySet [('Jane Doe', 'G'), ('James Smith', 'R'), ('Jack Black', 'P')]&gt;</pre>
+
<QuerySet [('Jane Doe', 'G'), ('James Smith', 'R'), ('Jack Black', 'P')]></syntaxhighlight>
  
 
</div>
 
</div>
第258行: 第229行:
  
 
<span id="id1"></span>
 
<span id="id1"></span>
=== Conditional aggregation ===
+
=== 条件聚合 ===
  
What if we want to find out how many clients there are for each
+
如果我们想知道每个 <code>account_type</code> 有多少个客户端怎么办? 我们可以使用 [[../querysets#aggregation-functions|聚合函数]] 的 <code>filter</code> 参数来实现:
<code>account_type</code>? We can use the <code>filter</code> argument of [[../querysets#aggregation-functions|<span class="std std-ref">aggregate
 
functions</span>]] to achieve this:
 
  
 
<div class="highlight-default notranslate">
 
<div class="highlight-default notranslate">
第268行: 第237行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; # Create some more Clients first so we can have something to count
+
<syntaxhighlight lang="python">>>> # Create some more Clients first so we can have something to count
&gt;&gt;&gt; Client.objects.create(
+
>>> Client.objects.create(
 
...    name='Jean Grey',
 
...    name='Jean Grey',
 
...    account_type=Client.REGULAR,
 
...    account_type=Client.REGULAR,
 
...    registered_on=date.today())
 
...    registered_on=date.today())
&gt;&gt;&gt; Client.objects.create(
+
>>> Client.objects.create(
 
...    name='James Bond',
 
...    name='James Bond',
 
...    account_type=Client.PLATINUM,
 
...    account_type=Client.PLATINUM,
 
...    registered_on=date.today())
 
...    registered_on=date.today())
&gt;&gt;&gt; Client.objects.create(
+
>>> Client.objects.create(
 
...    name='Jane Porter',
 
...    name='Jane Porter',
 
...    account_type=Client.PLATINUM,
 
...    account_type=Client.PLATINUM,
 
...    registered_on=date.today())
 
...    registered_on=date.today())
&gt;&gt;&gt; # Get counts for each value of account_type
+
>>> # Get counts for each value of account_type
&gt;&gt;&gt; from django.db.models import Count
+
>>> from django.db.models import Count
&gt;&gt;&gt; Client.objects.aggregate(
+
>>> Client.objects.aggregate(
 
...    regular=Count('pk', filter=Q(account_type=Client.REGULAR)),
 
...    regular=Count('pk', filter=Q(account_type=Client.REGULAR)),
 
...    gold=Count('pk', filter=Q(account_type=Client.GOLD)),
 
...    gold=Count('pk', filter=Q(account_type=Client.GOLD)),
 
...    platinum=Count('pk', filter=Q(account_type=Client.PLATINUM)),
 
...    platinum=Count('pk', filter=Q(account_type=Client.PLATINUM)),
 
... )
 
... )
{'regular': 2, 'gold': 1, 'platinum': 3}</pre>
+
{'regular': 2, 'gold': 1, 'platinum': 3}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This aggregate produces a query with the SQL 2003 <code>FILTER WHERE</code> syntax
+
此聚合在支持它的数据库上使用 SQL 2003 <code>FILTER WHERE</code> 语法生成查询:
on databases that support it:
 
  
 
<div class="highlight-sql notranslate">
 
<div class="highlight-sql notranslate">
第300行: 第268行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>SELECT count('id') FILTER (WHERE account_type=1) as regular,
+
<syntaxhighlight lang="sql">SELECT count('id') FILTER (WHERE account_type=1) as regular,
 
       count('id') FILTER (WHERE account_type=2) as gold,
 
       count('id') FILTER (WHERE account_type=2) as gold,
 
       count('id') FILTER (WHERE account_type=3) as platinum
 
       count('id') FILTER (WHERE account_type=3) as platinum
FROM clients;</pre>
+
FROM clients;</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
On other databases, this is emulated using a <code>CASE</code> statement:
+
在其他数据库上,这是使用 <code>CASE</code> 语句模拟的:
  
 
<div class="highlight-sql notranslate">
 
<div class="highlight-sql notranslate">
第314行: 第282行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>SELECT count(CASE WHEN account_type=1 THEN id ELSE null) as regular,
+
<syntaxhighlight lang="sql">SELECT count(CASE WHEN account_type=1 THEN id ELSE null) as regular,
 
       count(CASE WHEN account_type=2 THEN id ELSE null) as gold,
 
       count(CASE WHEN account_type=2 THEN id ELSE null) as gold,
 
       count(CASE WHEN account_type=3 THEN id ELSE null) as platinum
 
       count(CASE WHEN account_type=3 THEN id ELSE null) as platinum
FROM clients;</pre>
+
FROM clients;</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The two SQL statements are functionally equivalent but the more explicit
+
这两个 SQL 语句在功能上是等效的,但更明确的 <code>FILTER</code> 可能会执行得更好。
<code>FILTER</code> may perform better.
 
  
  
第331行: 第298行:
  
 
</div>
 
</div>
 +
<div class="clearer">
  
[[Category:Django 2.2.x 中文文档]]
+
 
 +
 
 +
</div>
 +
 
 +
[[Category:Django 2.2.x 文档]]

2021年10月31日 (日) 04:05的最新版本

条件表达式

条件表达式允许您在过滤器、注释、聚合和更新中使用 if ... elif ... else 逻辑。 条件表达式为表的每一行计算一系列条件并返回匹配的结果表达式。 条件表达式也可以像其他 表达式 一样组合和嵌套。

条件表达式类

我们将在后续示例中使用以下模型:

from django.db import models

class Client(models.Model):
    REGULAR = 'R'
    GOLD = 'G'
    PLATINUM = 'P'
    ACCOUNT_TYPE_CHOICES = [
        (REGULAR, 'Regular'),
        (GOLD, 'Gold'),
        (PLATINUM, 'Platinum'),
    ]
    name = models.CharField(max_length=50)
    registered_on = models.DateField()
    account_type = models.CharField(
        max_length=1,
        choices=ACCOUNT_TYPE_CHOICES,
        default=REGULAR,
    )

When

class When(condition=None, then=None, **lookups)

When() 对象用于封装条件及其结果以供在条件表达式中使用。 使用 When() 对象类似于使用 filter() 方法。 可以使用 字段查找Q 对象指定条件。 结果是使用 then 关键字提供的。

一些例子:

>>> from django.db.models import F, Q, When
>>> # String arguments refer to fields; the following two examples are equivalent:
>>> When(account_type=Client.GOLD, then='name')
>>> When(account_type=Client.GOLD, then=F('name'))
>>> # You can use field lookups in the condition
>>> from datetime import date
>>> When(registered_on__gt=date(2014, 1, 1),
...      registered_on__lt=date(2015, 1, 1),
...      then='account_type')
>>> # Complex conditions can be created using Q objects
>>> When(Q(name__startswith="John") | Q(name__startswith="Paul"),
...      then='name')

请记住,每个值都可以是一个表达式。

笔记

由于 then 关键字参数是为 When() 的结果保留的,如果 Model 具有名为 then 的字段,则存在潜在冲突。 这可以通过两种方式解决:

>>> When(then__exact=0, then=1)
>>> When(Q(then=0), then=1)

Case

class Case(*cases, **extra)

Case() 表达式类似于 Python 中的 if ... elif ... else 语句。 提供的 When() 对象中的每个 condition 都按顺序进行评估,直到评估为真实值。 返回匹配的 When() 对象中的 result 表达式。

一个简单的例子:

>>>
>>> from datetime import date, timedelta
>>> from django.db.models import Case, CharField, Value, When
>>> Client.objects.create(
...     name='Jane Doe',
...     account_type=Client.REGULAR,
...     registered_on=date.today() - timedelta(days=36))
>>> Client.objects.create(
...     name='James Smith',
...     account_type=Client.GOLD,
...     registered_on=date.today() - timedelta(days=5))
>>> Client.objects.create(
...     name='Jack Black',
...     account_type=Client.PLATINUM,
...     registered_on=date.today() - timedelta(days=10 * 365))
>>> # Get the discount for each Client based on the account type
>>> Client.objects.annotate(
...     discount=Case(
...         When(account_type=Client.GOLD, then=Value('5%')),
...         When(account_type=Client.PLATINUM, then=Value('10%')),
...         default=Value('0%'),
...         output_field=CharField(),
...     ),
... ).values_list('name', 'discount')
<QuerySet [('Jane Doe', '0%'), ('James Smith', '5%'), ('Jack Black', '10%')]>

Case() 接受任意数量的 When() 对象作为单独的参数。 使用关键字参数提供其他选项。 如果没有任何条件计算为 TRUE,则返回带有 default 关键字参数的表达式。 如果未提供 default 参数,则使用 None

如果我们想根据 Client 与我们在一起的时间更改我们之前的查询以获取折扣,我们可以使用查找来实现:

>>> a_month_ago = date.today() - timedelta(days=30)
>>> a_year_ago = date.today() - timedelta(days=365)
>>> # Get the discount for each Client based on the registration date
>>> Client.objects.annotate(
...     discount=Case(
...         When(registered_on__lte=a_year_ago, then=Value('10%')),
...         When(registered_on__lte=a_month_ago, then=Value('5%')),
...         default=Value('0%'),
...         output_field=CharField(),
...     )
... ).values_list('name', 'discount')
<QuerySet [('Jane Doe', '5%'), ('James Smith', '0%'), ('Jack Black', '10%')]>

笔记

请记住,条件是按顺序计算的,因此在上面的示例中,即使第二个条件与 Jane Doe 和 Jack Black 匹配,我们也会得到正确的结果。 这就像 Python 中的 if ... elif ... else 语句。


Case() 也适用于 filter() 子句。 例如,要查找一个多月前注册的黄金客户和一年多前注册的白金客户:

>>> a_month_ago = date.today() - timedelta(days=30)
>>> a_year_ago = date.today() - timedelta(days=365)
>>> Client.objects.filter(
...     registered_on__lte=Case(
...         When(account_type=Client.GOLD, then=a_month_ago),
...         When(account_type=Client.PLATINUM, then=a_year_ago),
...     ),
... ).values_list('name', 'account_type')
<QuerySet [('Jack Black', 'P')]>

高级查询

条件表达式可用于注释、聚合、查找和更新。 它们也可以与其他表达式组合和嵌套。 这允许您进行强大的条件查询。

条件更新

假设我们要为客户更改 account_type 以匹配他们的注册日期。 我们可以使用条件表达式和 update() 方法来做到这一点:

>>> a_month_ago = date.today() - timedelta(days=30)
>>> a_year_ago = date.today() - timedelta(days=365)
>>> # Update the account_type for each Client from the registration date
>>> Client.objects.update(
...     account_type=Case(
...         When(registered_on__lte=a_year_ago,
...              then=Value(Client.PLATINUM)),
...         When(registered_on__lte=a_month_ago,
...              then=Value(Client.GOLD)),
...         default=Value(Client.REGULAR)
...     ),
... )
>>> Client.objects.values_list('name', 'account_type')
<QuerySet [('Jane Doe', 'G'), ('James Smith', 'R'), ('Jack Black', 'P')]>

条件聚合

如果我们想知道每个 account_type 有多少个客户端怎么办? 我们可以使用 聚合函数filter 参数来实现:

>>> # Create some more Clients first so we can have something to count
>>> Client.objects.create(
...     name='Jean Grey',
...     account_type=Client.REGULAR,
...     registered_on=date.today())
>>> Client.objects.create(
...     name='James Bond',
...     account_type=Client.PLATINUM,
...     registered_on=date.today())
>>> Client.objects.create(
...     name='Jane Porter',
...     account_type=Client.PLATINUM,
...     registered_on=date.today())
>>> # Get counts for each value of account_type
>>> from django.db.models import Count
>>> Client.objects.aggregate(
...     regular=Count('pk', filter=Q(account_type=Client.REGULAR)),
...     gold=Count('pk', filter=Q(account_type=Client.GOLD)),
...     platinum=Count('pk', filter=Q(account_type=Client.PLATINUM)),
... )
{'regular': 2, 'gold': 1, 'platinum': 3}

此聚合在支持它的数据库上使用 SQL 2003 FILTER WHERE 语法生成查询:

SELECT count('id') FILTER (WHERE account_type=1) as regular,
       count('id') FILTER (WHERE account_type=2) as gold,
       count('id') FILTER (WHERE account_type=3) as platinum
FROM clients;

在其他数据库上,这是使用 CASE 语句模拟的:

SELECT count(CASE WHEN account_type=1 THEN id ELSE null) as regular,
       count(CASE WHEN account_type=2 THEN id ELSE null) as gold,
       count(CASE WHEN account_type=3 THEN id ELSE null) as platinum
FROM clients;

这两个 SQL 语句在功能上是等效的,但更明确的 FILTER 可能会执行得更好。