“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"> | ||
− | = | + | = 条件表达式 = |
− | + | 条件表达式允许您在过滤器、注释、聚合和更新中使用 <code>if</code> ... <code>elif</code> ... <code>else</code> 逻辑。 条件表达式为表的每一行计算一系列条件并返回匹配的结果表达式。 条件表达式也可以像其他 [[../expressions|表达式]] 一样组合和嵌套。 | |
− | <code>else</code> | ||
− | |||
− | |||
− | |||
<div id="the-conditional-expression-classes" class="section"> | <div id="the-conditional-expression-classes" class="section"> | ||
− | == | + | == 条件表达式类 == |
− | + | 我们将在后续示例中使用以下模型: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第19行: | 第16行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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, | ||
− | )</ | + | )</syntaxhighlight> |
</div> | </div> | ||
第43行: | 第40行: | ||
<div id="when" class="section"> | <div id="when" class="section"> | ||
− | === | + | === When === |
− | ; ''class'' < | + | ; ''<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> |
: | : | ||
− | + | <code>When()</code> 对象用于封装条件及其结果以供在条件表达式中使用。 使用 <code>When()</code> 对象类似于使用 [[../querysets#django.db.models.query.QuerySet|filter()]] 方法。 可以使用 [[../querysets#field-lookups|字段查找]] 或 [[../querysets#django.db.models|Q]] 对象指定条件。 结果是使用 <code>then</code> 关键字提供的。 | |
− | |||
− | |||
− | |||
− | [[../querysets#django.db.models| | ||
− | |||
− | + | 一些例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第61行: | 第53行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> 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), | ... registered_on__lt=date(2015, 1, 1), | ||
... then='account_type') | ... then='account_type') | ||
− | + | >>> # Complex conditions can be created using Q objects | |
− | + | >>> When(Q(name__startswith="John") | Q(name__startswith="Paul"), | |
− | ... then='name')</ | + | ... then='name')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请记住,每个值都可以是一个表达式。 | |
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 由于 <code>then</code> 关键字参数是为 <code>When()</code> 的结果保留的,如果 [[../instances#django.db.models|Model]] 具有名为 <code>then</code> 的字段,则存在潜在冲突。 这可以通过两种方式解决: | |
− | <code>When()</code> | ||
− | [[../instances#django.db.models| | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第92行: | 第81行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> When(then__exact=0, then=1) |
− | + | >>> When(Q(then=0), then=1)</syntaxhighlight> | |
</div> | </div> | ||
第104行: | 第93行: | ||
<div id="case" class="section"> | <div id="case" class="section"> | ||
− | === | + | === Case === |
− | ; ''class'' < | + | ; ''<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> |
: | : | ||
− | + | <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> | ||
− | <code>When()</code> | ||
− | |||
− | |||
− | + | 一个简单的例子: | |
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第121行: | 第106行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> |
− | + | >>> from datetime import date, timedelta | |
− | + | >>> from django.db.models import Case, CharField, Value, When | |
− | + | >>> 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)) | ||
− | + | >>> 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)) | ||
− | + | >>> 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)) | ||
− | + | >>> # Get the discount for each Client based on the account type | |
− | + | >>> 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') | ||
− | + | <QuerySet [('Jane Doe', '0%'), ('James Smith', '5%'), ('Jack Black', '10%')]></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | <code>Case()</code> | + | <code>Case()</code> 接受任意数量的 <code>When()</code> 对象作为单独的参数。 使用关键字参数提供其他选项。 如果没有任何条件计算为 <code>TRUE</code>,则返回带有 <code>default</code> 关键字参数的表达式。 如果未提供 <code>default</code> 参数,则使用 <code>None</code>。 |
− | |||
− | |||
− | |||
− | |||
− | + | 如果我们想根据 <code>Client</code> 与我们在一起的时间更改我们之前的查询以获取折扣,我们可以使用查找来实现: | |
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第163行: | 第143行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> 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( | ... 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') | ||
− | + | <QuerySet [('Jane Doe', '5%'), ('James Smith', '0%'), ('Jack Black', '10%')]></syntaxhighlight> | |
</div> | </div> | ||
第181行: | 第161行: | ||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 请记住,条件是按顺序计算的,因此在上面的示例中,即使第二个条件与 Jane Doe 和 Jack Black 匹配,我们也会得到正确的结果。 这就像 <code>Python</code> 中的 <code>if</code> ... <code>elif</code> ... <code>else</code> 语句。 | |
− | |||
− | |||
− | <code> | ||
</div> | </div> | ||
− | <code>Case()</code> | + | <code>Case()</code> 也适用于 <code>filter()</code> 子句。 例如,要查找一个多月前注册的黄金客户和一年多前注册的白金客户: |
− | |||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第198行: | 第173行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> a_month_ago = date.today() - timedelta(days=30) |
− | + | >>> a_year_ago = date.today() - timedelta(days=365) | |
− | + | >>> 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') | ||
− | + | <QuerySet [('Jack Black', 'P')]></syntaxhighlight> | |
</div> | </div> | ||
第217行: | 第192行: | ||
<div id="advanced-queries" class="section"> | <div id="advanced-queries" class="section"> | ||
− | == | + | == 高级查询 == |
− | + | 条件表达式可用于注释、聚合、查找和更新。 它们也可以与其他表达式组合和嵌套。 这允许您进行强大的条件查询。 | |
− | |||
− | |||
<div id="conditional-update" class="section"> | <div id="conditional-update" class="section"> | ||
− | === | + | === 条件更新 === |
− | + | 假设我们要为客户更改 <code>account_type</code> 以匹配他们的注册日期。 我们可以使用条件表达式和 [[../querysets#django.db.models.query.QuerySet|update()]] 方法来做到这一点: | |
− | |||
− | [[../querysets#django.db.models.query.QuerySet| | ||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第235行: | 第206行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> 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( | ... account_type=Case( | ||
... When(registered_on__lte=a_year_ago, | ... When(registered_on__lte=a_year_ago, | ||
第247行: | 第218行: | ||
... ), | ... ), | ||
... ) | ... ) | ||
− | + | >>> Client.objects.values_list('name', 'account_type') | |
− | + | <QuerySet [('Jane Doe', 'G'), ('James Smith', 'R'), ('Jack Black', 'P')]></syntaxhighlight> | |
</div> | </div> | ||
第258行: | 第229行: | ||
<span id="id1"></span> | <span id="id1"></span> | ||
− | === | + | === 条件聚合 === |
− | + | 如果我们想知道每个 <code>account_type</code> 有多少个客户端怎么办? 我们可以使用 [[../querysets#aggregation-functions|聚合函数]] 的 <code>filter</code> 参数来实现: | |
− | <code>account_type</code> | ||
− | |||
<div class="highlight-default notranslate"> | <div class="highlight-default notranslate"> | ||
第268行: | 第237行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">>>> # Create some more Clients first so we can have something to count |
− | + | >>> 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()) | ||
− | + | >>> 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()) | ||
− | + | >>> 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()) | ||
− | + | >>> # 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)), | ... 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}</ | + | {'regular': 2, 'gold': 1, 'platinum': 3}</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此聚合在支持它的数据库上使用 SQL 2003 <code>FILTER WHERE</code> 语法生成查询: | |
− | |||
<div class="highlight-sql notranslate"> | <div class="highlight-sql notranslate"> | ||
第300行: | 第268行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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;</ | + | FROM clients;</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在其他数据库上,这是使用 <code>CASE</code> 语句模拟的: | |
<div class="highlight-sql notranslate"> | <div class="highlight-sql notranslate"> | ||
第314行: | 第282行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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;</ | + | FROM clients;</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这两个 SQL 语句在功能上是等效的,但更明确的 <code>FILTER</code> 可能会执行得更好。 | |
− | <code>FILTER</code> | ||
第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
可能会执行得更好。