---
title: "查询表达式"
version: 3.1
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/3.1/ref/models/expressions/
canonical: https://djangodocs.dev/zh-hans/3.1/ref/models/expressions/
---
# 查询表达式

查询表达式描述了一个值或一个计算，它可以作为更新、创建、过滤、排序、注解或聚合的一部分。当一个表达式输出一个布尔值时，它可以直接用于过滤器中。有许多内置的表达式（在下面的文档中）可以用来帮助你编写查询。表达式可以组合，或者在某些情况下嵌套，以形成更复杂的计算。

## 支持的算术

Django 支持负、加、减、乘、除、模数运算，以及对查询表达式的幂运算符，使用 Python 常量、变量，甚至其他表达式。

## 一些例子

```python
from django.db.models import Count, F, Value
from django.db.models.functions import Length, Upper

# Find companies that have more employees than chairs.
Company.objects.filter(num_employees__gt=F('num_chairs'))

# Find companies that have at least twice as many employees
# as chairs. Both the querysets below are equivalent.
Company.objects.filter(num_employees__gt=F('num_chairs') * 2)
Company.objects.filter(
    num_employees__gt=F('num_chairs') + F('num_chairs'))

# How many chairs are needed for each company to seat all employees?
>>> company = Company.objects.filter(
...    num_employees__gt=F('num_chairs')).annotate(
...    chairs_needed=F('num_employees') - F('num_chairs')).first()
>>> company.num_employees
120
>>> company.num_chairs
50
>>> company.chairs_needed
70

# Create a new company using expressions.
>>> company = Company.objects.create(name='Google', ticker=Upper(Value('goog')))
# Be sure to refresh it if you need to access the field.
>>> company.refresh_from_db()
>>> company.ticker
'GOOG'

# Annotate models with an aggregated value. Both forms
# below are equivalent.
Company.objects.annotate(num_products=Count('products'))
Company.objects.annotate(num_products=Count(F('products')))

# Aggregates can contain complex computations also
Company.objects.annotate(num_offerings=Count(F('products') + F('services')))

# Expressions can also be used in order_by(), either directly
Company.objects.order_by(Length('name').asc())
Company.objects.order_by(Length('name').desc())
# or using the double underscore lookup syntax.
from django.db.models import CharField
from django.db.models.functions import Length
CharField.register_lookup(Length)
Company.objects.order_by('name__length')

# Boolean expression can be used directly in filters.
from django.db.models import Exists
Company.objects.filter(
    Exists(Employee.objects.filter(company=OuterRef('pk'), salary__gt=10))
)
```

## 内置表达式

> **Note**
>
> 这些表达式在 `django.db.models.expressions` 和 `django.db.models.aggregates` 中定义，但为了方便起见，通常从 [`django.db.models`](/zh-hans/3.1/topics/db/models/#module-django.db.models) 中导入使用。

### `F()` 表达式

#### `class F`

`F()` 对象表示一个模型字段或注解列的值。它使得引用模型字段的值和使用它们执行数据库操作成为可能，而不需要将它们从数据库中拉到 Python 内存中。

取而代之的是，Django 使用 `F()` 对象来生成一个 SQL 表达式，在数据库层面描述所需的操作。

我们举个例子试试。通常情况下，我们可以这样做：

```
# Tintin filed a news story!
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed += 1
reporter.save()
```

这里，我们从数据库中提取了 `reporter.stories_filed` 的值到内存中，并使用熟悉的 Python 操作符对其进行操作，然后将对象保存回数据库。但我们也可以这样做：

```
from django.db.models import F

reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
```

虽然 `reporter.stories_filed = F('stories_filed') + 1` 看起来像一个普通的 Python 赋值给一个实例属性，但实际上它是一个描述数据库操作的 SQL 结构。

当 Django 遇到 `F()` 的实例时，它会覆盖标准的 Python 运算符来创建一个封装的 SQL 表达式；在本例中，它指示数据库递增由 `reporter.stories_filed` 表示的数据库字段。

无论 `reporter.stories_filed` 上的值是多少，Python 永远不会知道它——它完全由数据库处理。通过 Django 的 `F()` 类，Python 所做的就是创建 SQL 语法来引用这个字段并描述操作。

要访问这样保存的新值，必须重新加载对象：

```
reporter = Reporters.objects.get(pk=reporter.pk)
# Or, more succinctly:
reporter.refresh_from_db()
```

`F()` 除了用于上述对单个实例的操作外，`F()` 还可以与 `update()` 一起用于对象实例的 `QuerySets`。这就把我们上面使用的两个查询——`get()` 和 [`save()`](/zh-hans/3.1/ref/models/instances/#django.db.models.Model.save) 减少到只有一个：

```
reporter = Reporters.objects.filter(name='Tintin')
reporter.update(stories_filed=F('stories_filed') + 1)
```

我们还可以使用 [`update()`](/zh-hans/3.1/ref/models/querysets/#django.db.models.query.QuerySet.update) 来递增多个对象上的字段值——这可能比从数据库中把它们全部拉到 Python 中，在它们身上循环，递增每个对象的字段值，然后把每个对象保存回数据库要快得多：

```
Reporter.objects.all().update(stories_filed=F('stories_filed') + 1)
```

因此，`F()` 可以通过以下方式提供性能优势：

- 让数据库，而不是 Python 来完成工作
- 减少某些操作所需的查询次数

#### 使用 `F()` 避免竞争条件

`F()` 的另一个有用的好处是，让数据库——而不是 Python——更新一个字段的值，避免了 *竞争条件*。

如果两个 Python 线程执行上面第一个例子中的代码，一个线程可以在另一个线程从数据库中获取一个字段的值后，检索、递增并保存它。第二个线程保存的值将基于原始值，第一个线程的工作将丢失。

如果数据库负责更新字段，那么这个过程就比较稳健：它只会在执行 [`save()`](/zh-hans/3.1/ref/models/instances/#django.db.models.Model.save) 或 `update()` 时，根据数据库中字段的值来更新字段，而不是根据检索实例时的值来更新。

#### `F()` 赋值在 `Model.save()` 之后持续存在

`F()` 分配给模型字段的对象在保存模型实例后会持续存在，并将应用于每个 [`save()`](/zh-hans/3.1/ref/models/instances/#django.db.models.Model.save)。例如：

```
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()

reporter.name = 'Tintin Jr.'
reporter.save()
```

在这种情况下，`stories_filed` 将被更新两次。如果最初是 `1`，最终值将是 `3`。这种持久性可以通过在保存模型对象后重新加载来避免，例如，使用 [`refresh_from_db()`](/zh-hans/3.1/ref/models/instances/#django.db.models.Model.refresh_from_db)。

#### 在过滤器中使用 `F()`

`F()` 在 `QuerySet` 过滤器中也非常有用，它们可以根据对象的字段值而不是 Python 值的标准来过滤一组对象。

这在 [在查询中使用 F() 表达式](/zh-hans/3.1/topics/db/queries/#using-f-expressions-in-filters) 中有所记载。

#### 与注解一起使用 `F()`

`F()` 可用于通过将不同的字段与算术相结合，在你的模型上创建动态字段：

```
company = Company.objects.annotate(
    chairs_needed=F('num_employees') - F('num_chairs'))
```

如果你要组合的字段是不同类型的，你需要告诉 Django 将返回什么样的字段。由于 `F()` 不直接支持 `output_field`，你需要用 [`ExpressionWrapper`](#django.db.models.ExpressionWrapper) 来包装表达式：

```
from django.db.models import DateTimeField, ExpressionWrapper, F

Ticket.objects.annotate(
    expires=ExpressionWrapper(
        F('active_at') + F('duration'), output_field=DateTimeField()))
```

当引用关系字段如 `ForeignKey` 时，`F()` 返回主键值而不是模型实例：

```
>> car = Company.objects.annotate(built_by=F('manufacturer'))[0]
>> car.manufacturer
<Manufacturer: Toyota>
>> car.built_by
3
```

#### 使用 `F()` 对空值进行排序

使用 `F()` 和 [`Expression.asc()`](#django.db.models.Expression.asc) 或 `esc()` 的关键词参数 `nulls_first` 或 `nulls_last` 来控制字段的空值的排序。默认情况下，排序取决于你的数据库。

例如，在已经联系过的公司之后，对尚未联系过的公司进行排序（`last_contacted` 为空）：

```
from django.db.models import F
Company.objects.order_by(F('last_contacted').desc(nulls_last=True))
```

### `Func()` 表达式

`Func()` 表达式是所有涉及 `COALESCE` 和 `LOWER` 等数据库函数或 `SUM` 等集合的表达式的基本类型。它们可以直接使用：

```
from django.db.models import F, Func

queryset.annotate(field_lower=Func(F('field'), function='LOWER'))
```

或者可以用它们来建立一个数据库函数库：

```
class Lower(Func):
    function = 'LOWER'

queryset.annotate(field_lower=Lower('field'))
```

但这两种情况都会产生一个查询集，其中每个模型都有一个额外的属性 `field_lower` 来注释，大致由以下 SQL 产生：

```sql
SELECT
    ...
    LOWER("db_table"."field") as "field_lower"
```

参见 [数据库函数](/zh-hans/3.1/ref/models/database-functions/) 获取内置的数据库函数列表。

`Func` API 如下：

#### `class Func(*expressions, **extra)`

#### `function`

描述将要生成的函数的类属性。具体来说，`function` 将作为 `function` 占位符插在 [`template`](#django.db.models.Func.template) 中。默认值为 `None`。

#### `template`

一个类属性，作为格式字符串，描述为该函数生成的SQL。默认值为 `'%(function)s(%(expressions)s)'`。

如果你正在构造像 `strftime('%W', 'date')` 这样的 SQL，并且需要在查询中使用 `%` 字符，那么在 `template` 属性中把它四倍（`%%%%`），因为这个字符串会被插值两次：一次是在 `as_sql()` 中的模板插值中，另一次是在数据库游标中的查询参数的 SQL 插值中。

#### `arg_joiner`

一个类属性，表示用于将 `expressions` 列表连接在一起的字符。默认值为 `', '`。

#### `arity`

一个类属性，表示函数接受的参数数。如果设置了这个属性，并且用不同数量的表达式调用函数，将引发 `TypeError`。默认值为 `None`。

#### `as_sql(compiler, connection, function=None, template=None, arg_joiner=None, **extra_context)`

生成数据库函数的 SQL 片段。返回一个元组 `(sql, params)`，其中 `sql` 是 SQL 字符串，`params` 是查询参数的列表或元组。

`as_vendor()` 方法应该使用 `function`、`template`、`arg_joiner` 以及其他 `**extra_context` 参数来根据需要定制 SQL。例如：

*django/db/models/functions.py*

```python
class ConcatPair(Func):
    ...
    function = 'CONCAT'
    ...

    def as_mysql(self, compiler, connection, **extra_context):
        return super().as_sql(
            compiler, connection,
            function='CONCAT_WS',
            template="%(function)s('', %(expressions)s)",
            **extra_context
        )
```

为了避免 SQL 注入漏洞，`extra_context` [不得包含不受信任的用户输入](#avoiding-sql-injection-in-query-expressions)，因为这些值会被内插到 SQL 字符串中，而不是作为查询参数传递，数据库驱动对其进行转义。

`*expressions` 参数是一个位置表达式的列表，该函数将应用于此。表达式将被转换为字符串，用 `arg_joiner` 连接在一起，然后插入到 `template` 中作为 `expressions` 占位符。

位置参数可以是表达式或 Python 值。字符串被认为是列引用，将被包装在 `F()` 表达式中，而其他值将被包装在 `Value()` 表达式中。

`**extra` 关键字是 `key=value` 对，可以内插到 `template` 属性中。为了避免 SQL 注入漏洞，`extra` [不能包含不受信任的用户输入](#avoiding-sql-injection-in-query-expressions)，因为这些值会被内插到 SQL 字符串中，而不是作为查询参数传递，数据库驱动程序对其进行转义。

`function`、`template` 和 `arg_joiner` 关键字可以用来替换同名的属性，而不必定义自己的类。`output_field` 可以用来定义预期的返回类型。

### `Aggregate()` 表达式

聚合表达式是 [Func() 表达式](#func-expressions) 的一个特例，它通知查询需要一个 `GROUP BY` 子句。所有的 [聚合函数](/zh-hans/3.1/ref/models/querysets/#aggregation-functions)，如 `Sum()` 和 `Count()`，都继承自 `Aggregate()`。

由于 `Aggregate` 是表达式和封装表达式，你可以表示一些复杂的计算：

```
from django.db.models import Count

Company.objects.annotate(
    managers_required=(Count('num_employees') / 4) + Count('num_managers'))
```

`Aggregate` 的 API 如下：

#### `class Aggregate(*expressions, output_field=None, distinct=False, filter=None, **extra)`

#### `template`

作为格式字符串的类属性，描述为该集合生成的 SQL。默认值为 `'%(function)s(%(distinct)s%(expressions)s)'`。

#### `function`

描述将生成的集合函数的类属性。具体来说，`function` 将作为 `function` 占位符插值在 [`template`](#django.db.models.Aggregate.template) 中。默认值为 `None`。

#### `window_compatible`

默认为 `True`，因为大多数聚合函数可以作为 [`Window`](#django.db.models.expressions.Window) 中的源表达式。

#### `allow_distinct`

一个类属性，决定该聚合函数是否允许传递 `distinct` 关键字参数。如果设置为 `False` （默认），如果传递了 `distinct=True`，会引发 `TypeError`。

`expressions` 位置参数可以包括表达式或模型字段的名称。它们将被转换为字符串，并在 `template` 中作为 `expressions` 占位符使用。

`output_field` 参数需要一个模型字段实例，比如 `IntegerField()` 或 `BooleanField()`，Django 会在从数据库中获取值后将其载入其中。通常在实例化模型字段时，不需要任何参数，因为任何与数据验证有关的参数（`max_length`、`max_digits` 等）都不会在表达式的输出值上执行。

请注意，只有当 Django 无法确定结果的字段类型时，才需要使用 `output_field`。混合字段类型的复杂表达式应该定义所需的 `output_field`。例如，将一个 `IntegerField()` 和一个 `FloatField()` 加在一起，可能应该定义 `output_field=FloatField()`。

`distinct` 参数决定是否应该为 `expressions` 的每一个不同的值（或一组值，对于多个 `expressions`）调用聚合函数。该参数仅在 [`allow_distinct`](#django.db.models.Aggregate.allow_distinct) 设置为 `True` 的聚合函数中得到支持。

`filter` 参数取一个 [`Q 对象`](/zh-hans/3.1/ref/models/querysets/#django.db.models.Q)，用于过滤被聚合的行。参见 [条件聚合](/zh-hans/3.1/ref/models/conditional-expressions/#conditional-aggregation) 和 [过滤注解](/zh-hans/3.1/topics/db/aggregation/#filtering-on-annotations) 的用法示例。

`**extra` 关键字是 `key=value` 对，可以内插到 `template` 属性中。

### 创建你自己的聚合函数

你也可以创建自己的聚合函数。至少，你需要定义 `function`，但你也可以完全自定义生成的 SQL。下面是一个简单的例子：

```
from django.db.models import Aggregate

class Sum(Aggregate):
    # Supports SUM(ALL field).
    function = 'SUM'
    template = '%(function)s(%(all_values)s%(expressions)s)'
    allow_distinct = False

    def __init__(self, expression, all_values=False, **extra):
        super().__init__(
            expression,
            all_values='ALL ' if all_values else '',
            **extra
        )
```

### `Value()` 表达式

#### `class Value(value, output_field=None)`

`Value()` 对象表示一个表达式中最小的成分：一个简单的值。当你需要在一个表达式中表示一个整数、布尔值或字符串的值时，你可以将该值包裹在一个 `Value()` 中。

你很少会需要直接使用 `Value()`。当你写下表达式 `F('field') + 1` 时，Django 会隐式地将 `1` 包裹在 `Value()` 中，允许简单的值被用于更复杂的表达式中。当你想把一个字符串传递给一个表达式时，你需要使用 `Value()`。大多数表达式将字符串参数解释为字段的名称，如 `Lower('name')`。

`value` 参数描述了要包含在表达式中的值，比如 `1`、`True` 或 `None`。Django 知道如何将这些 Python 值转换为相应的数据库类型。

`output_field` 参数应该是一个模型字段的实例，比如 `IntegerField()` 或 `BooleanField()`，Django 会在从数据库中获取值后将其载入其中。通常在实例化模型字段时，不需要任何参数，因为任何与数据验证有关的参数（`max_length`、`max_digits` 等）都不会在表达式的输出值上执行。

### `ExpressionWrapper()` 表达式

#### `class ExpressionWrapper(expression, output_field)`

`ExpressionWrapper` 包裹另一个表达式，并提供对 `output_field` 等属性的访问，这些属性在其他表达式上可能无法使用。`ExpressionWrapper` 在对 `F()` 具有不同类型的表达式使用算术时是必要的，如 [与注解一起使用 F()](#using-f-with-annotations) 中所述。

### 条件表达式

条件表达式允许你在查询中使用 [`if`](https://docs.python.org/3/reference/compound_stmts.html#if) ... [`elif`](https://docs.python.org/3/reference/compound_stmts.html#elif) ... [`else`](https://docs.python.org/3/reference/compound_stmts.html#else) 逻辑。Django 原生支持 SQL `CASE` 表达式。更多细节请参见 [条件表达式](/zh-hans/3.1/ref/models/conditional-expressions/)。

### `Subquery()` 表达式

#### `class Subquery(queryset, output_field=None)`

你可以使用 `Subquery` 表达式向 `QuerySet` 添加一个显式子查询。

例如，在每篇文章中标注该文章最新评论的作者的电子邮件地址：

```
>>> from django.db.models import OuterRef, Subquery
>>> newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at')
>>> Post.objects.annotate(newest_commenter_email=Subquery(newest.values('email')[:1]))
```

在 PostgreSQL 上，SQL 看起来像：

```sql
SELECT "post"."id", (
    SELECT U0."email"
    FROM "comment" U0
    WHERE U0."post_id" = ("post"."id")
    ORDER BY U0."created_at" DESC LIMIT 1
) AS "newest_commenter_email" FROM "post"
```

> **Note**
>
> 本节中的例子是为了展示如何强制 Django 执行一个子查询。在某些情况下，可以写一个等价的查询集，更清楚或更有效地执行同样的任务。

#### 从外部查询集中引用列

#### `class OuterRef(field)`

当 `Subquery` 中的查询集需要引用外部查询中的字段时，使用 `OuterRef`。它的作用类似于 [`F`](#django.db.models.F) 表达式，只是在解析外部查询集之前不会检查它是否指向一个有效字段。

`OuterRef` 的实例可以与 `Subquery` 的嵌套实例一起使用，以引用一个不是直接父级的包含查询集的实例。例如，这个查询集需要在一对嵌套的 `Subquery` 实例中才能正确解析：

```
>>> Book.objects.filter(author=OuterRef(OuterRef('pk')))
```

#### 将子查询限制为单列

有时必须从 `Subquery` 中返回一列，例如，使用 `Subquery` 作为 `__in` 查询的目标。要返回最近一天内发表的所有文章评论：

```
>>> from datetime import timedelta
>>> from django.utils import timezone
>>> one_day_ago = timezone.now() - timedelta(days=1)
>>> posts = Post.objects.filter(published_at__gte=one_day_ago)
>>> Comment.objects.filter(post__in=Subquery(posts.values('pk')))
```

在这种情况下，子查询必须使用 [`values()`](/zh-hans/3.1/ref/models/querysets/#django.db.models.query.QuerySet.values) 只返回一列：该文章的主键。

#### 将子查询限制为单行

为了防止子查询返回多条记录，使用了查询集的一个片断（`[:1]`）：

```
>>> subquery = Subquery(newest.values('email')[:1])
>>> Post.objects.annotate(newest_commenter_email=subquery)
```

在这种情况下，子查询必须只返回一列 *和* 一行：最近创建的评论的电子邮件地址。

（使用 [`get()`](/zh-hans/3.1/ref/models/querysets/#django.db.models.query.QuerySet.get) 而不是分片会导致失败，因为 `OuterRef` 在 `Subquery` 中使用查询集之前无法解析。)

#### `Exists()` 子查询

#### `class Exists(queryset)`

`Exists` 是一个 `Subquery` 子类，它使用 SQL `EXISTS` 语句。在许多情况下，它的性能比子查询更好，因为当找到第一条匹配的记录时，数据库能够停止对子查询的执行。

例如，要对每篇文章进行注解，说明它是否有过去一天内的评论：

```
>>> from django.db.models import Exists, OuterRef
>>> from datetime import timedelta
>>> from django.utils import timezone
>>> one_day_ago = timezone.now() - timedelta(days=1)
>>> recent_comments = Comment.objects.filter(
...     post=OuterRef('pk'),
...     created_at__gte=one_day_ago,
... )
>>> Post.objects.annotate(recent_comment=Exists(recent_comments))
```

在 PostgreSQL 上，SQL 看起来像：

```sql
SELECT "post"."id", "post"."published_at", EXISTS(
    SELECT U0."id", U0."post_id", U0."email", U0."created_at"
    FROM "comment" U0
    WHERE (
        U0."created_at" >= YYYY-MM-DD HH:MM:SS AND
        U0."post_id" = ("post"."id")
    )
) AS "recent_comment" FROM "post"
```

没有必要强制 `Exists` 指向单一列，因为列会被丢弃，并返回一个布尔结果。同样，由于在 SQL `EXISTS` 子查询中，排序并不重要，只会降低性能，所以会自动删除。

可以用 `` `~Exists() `` 来查询 `NOT EXISTS`。

#### 对 `Subquery()` 或 `Exists()` 表达式进行过滤。

在 [`When`](/zh-hans/3.1/ref/models/conditional-expressions/#django.db.models.expressions.When) 表达式中，返回布尔值的 `Subquery()` 和 `Exists()` 可以作为 `condition`，或者直接过滤一个查询集：

```
>>> recent_comments = Comment.objects.filter(...)  # From above
>>> Post.objects.filter(Exists(recent_comments))
```

这将确保子查询不会被添加到 `SELECT` 列中，这可能会带来更好的性能。

> **Changed in Django 3.0**
>
> 在以前的 Django 版本中，需要先进行注解，然后根据注解进行过滤。这导致注解的值总是出现在查询结果中，往往导致查询的执行时间增加。

#### 在 `Subquery` 表达式中使用集合。

聚合可以在 `Subquery` 中使用，但需要将 [`filter()`](/zh-hans/3.1/ref/models/querysets/#django.db.models.query.QuerySet.filter)、[`values()`](/zh-hans/3.1/ref/models/querysets/#django.db.models.query.QuerySet.values) 和 [`annotate()`](/zh-hans/3.1/ref/models/querysets/#django.db.models.query.QuerySet.annotate) 进行特定的组合，才能使子查询分组正确。

假设两种模式都有 `length` 字段，要找到帖子长度大于所有合计评论总长度的帖子：

```
>>> from django.db.models import OuterRef, Subquery, Sum
>>> comments = Comment.objects.filter(post=OuterRef('pk')).order_by().values('post')
>>> total_comments = comments.annotate(total=Sum('length')).values('total')
>>> Post.objects.filter(length__gt=Subquery(total_comments))
```

初始的 `filter(...)` 将子查询限制在相关参数上。`order_by()` 删除 `Comment` 模型上的默认 `Order` （如果有的话）。`values('post')` 按 `Post` 聚合评论。最后，`annotate(...)` 执行聚合。这些查询集方法的应用顺序很重要。在这种情况下，由于子查询必须限于一列，所以需要使用 `values('total')`。

这是在 `Subquery` 内进行聚合的唯一方法，因为使用 `aggregary()` 试图执行查询集（如果有 `OuterRef`，将无法解决）。

### 原始 SQL 表达式

#### `class RawSQL(sql, params, output_field=None)`

有时，数据库表达式不容易表达一个复杂的 `WHERE` 子句。在这些边缘情况下，使用 `RawSQL` 表达式。例如：

```
>>> from django.db.models.expressions import RawSQL
>>> queryset.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))
```

这些额外的查找可能无法移植到不同的数据库引擎中（因为你是显式地编写 SQL 代码），并且违反了 DRY 原则，所以你应该尽可能地避免它们。

> **Warning**
>
> 为了防止 [SQL 注入攻击](https://en.wikipedia.org/wiki/SQL_injection) ，你必须使用 `params` 来转义任何用户可以控制的参数。`params` 是一个必要的参数，以迫使你承认你没有用用户提供的数据来插值你的 SQL。
>
> 你也不能在 SQL 字符串中引用占位符。这个例子由于在 `%s` 周围的引号而容易受到 SQL 注入：
>
> ```
> RawSQL("select col from sometable where othercol = '%s'")  # unsafe!
> ```
>
> 你可以阅读更多关于 Django 的 [SQL 注入保护](/zh-hans/3.1/topics/security/#sql-injection-protection) 的工作原理。

### 窗口函数

窗口函数提供了一种在分区上应用函数的方法。与一般的聚合函数不同，窗口函数对 [帧](#window-frames) 和分区进行操作，并计算每行的结果。

你可以在同一个查询中指定多个窗口，这在 Django ORM 中相当于在一个 [QuerySet.annotate()](/zh-hans/3.1/topics/db/aggregation/) 调用中包含多个表达式。ORM 并没有利用命名窗口，而是将其作为所选列的一部分。

#### `class Window(expression, partition_by=None, order_by=None, frame=None, output_field=None)`

#### `filterable`

默认值为 `False`。SQL 标准不允许在 `WHERE` 子句中引用窗口函数，当构建 `QuerySet` 时，Django 会引发异常。

#### `template`

默认为 `%(expression)sOVER (%(window)s)'`。如果只提供 `expression` 参数，窗口子句将是空白的。

`Window` 类是 `OVER` 子句的主要表达式。

`expression` 参数是一个 [窗口函数](/zh-hans/3.1/ref/models/database-functions/#window-functions)，一个 [聚合函数](/zh-hans/3.1/ref/models/querysets/#aggregation-functions)，或者一个与窗口子句兼容的表达式。

`partition_by` 参数是一个表达式列表（列名应包在 `F` 对象中），用于控制行的分区。分区缩小了用于计算结果集的行的范围。

`output_field` 是作为参数或通过表达式指定的。

`order_by` 参数接受一个表达式序列，你可以在这个序列上调用 [`asc()`](#django.db.models.Expression.asc) 和 `esc()`。顺序控制了应用表达式的顺序。例如，如果在一个分区的行上求和，第一个结果是第一行的值，第二个结果是第一行和第二行的和。

`frame` 参数指定在计算中应该使用哪些其他行。详见 [帧](#window-frames)。

例如，用同一制片厂在同一类型和发行年份的电影的平均评分来注解每部电影：

```
>>> from django.db.models import Avg, F, Window
>>> from django.db.models.functions import ExtractYear
>>> Movie.objects.annotate(
>>>     avg_rating=Window(
>>>         expression=Avg('rating'),
>>>         partition_by=[F('studio'), F('genre')],
>>>         order_by=ExtractYear('released').asc(),
>>>     ),
>>> )
```

这可以让你检查一部电影的评分是好是坏，与它的同行相比。

你可能希望在同一个窗口，即同一个分区和帧上应用多个表达式。例如，你可以修改前面的例子，通过在同一查询中使用三个窗口函数，也包括每部电影的组别（相同的工作室、类型和发布年份）中的最佳和最差评级。前面例子中的分区和排序被提取到一个字典中，以减少重复：

```
>>> from django.db.models import Avg, F, Max, Min, Window
>>> from django.db.models.functions import ExtractYear
>>> window = {
>>>    'partition_by': [F('studio'), F('genre')],
>>>    'order_by': ExtractYear('released').asc(),
>>> }
>>> Movie.objects.annotate(
>>>     avg_rating=Window(
>>>         expression=Avg('rating'), **window,
>>>     ),
>>>     best=Window(
>>>         expression=Max('rating'), **window,
>>>     ),
>>>     worst=Window(
>>>         expression=Min('rating'), **window,
>>>     ),
>>> )
```

在 Django 内置的数据库后端中，MySQL 8.0.2+、PostgreSQL、Oracle 都支持窗口表达式。不同的数据库对不同窗口表达式功能的支持也不同。例如， [`asc()`](#django.db.models.Expression.asc) 和 `esc()` 中的选项可能不被支持。根据需要，请查阅你的数据库的文档。

#### 帧

对于一个窗口帧，你可以选择基于范围的行序列或普通的行序列。

#### `class ValueRange(start=None, end=None)`

#### `frame_type`

该属性被设置为 `'RANGE'`。

PostgreSQL 对 `ValueRange` 的支持有限，只支持使用标准的开始和结束点，如 `CURRENT ROW` 和 `UNBOUNDED FOLLOWING`。

#### `class RowRange(start=None, end=None)`

#### `frame_type`

该属性被设置为 `'ROWS'`。

两个类都返回 SQL，模板为：

```
%(frame_type)s BETWEEN %(start)s AND %(end)s
```

帧缩小了用于计算结果的行。它们从某个起点移动到某个指定的终点。帧可以不分区使用，但通常情况下，指定窗口的排序以确保结果的确定性是个好主意。在帧中，帧中的对等值是指具有等值的行，如果不存在排序子句，则是指所有行。

一个帧的默认起点是 `UNBOUNDED PRECEDING`，即分区的第一行。终点总是显式地包含在 ORM 生成的 SQL 中，默认为 `UNBOUNDED FOLLOWING`。默认帧包括从分区到集合中最后一行的所有行。

`start` 和 `end` 参数的可接受值是 `None`、一个整数或零。`start` 的负整数会导致 `N preceding`，而 `None` 会产生 `UNBOUNDED PRECEDING`。对于 `start` 和 `end`，0 将返回 `CURRENT ROW`。`end` 接受正整数。

`CURRENT ROW` 包括的内容有区别。当在 `ROWS` 模式下指定时，帧以当前行开始或结束。当在 `RANGE` 模式下指定时，根据排序子句，帧以第一个或最后一个对等值开始或结束。因此，`RANGE CURRENT ROW` 对具有由排序指定的相同值的行执行表达式。因为模板包括 `start` 和 `end` 点，所以可以用：

```
ValueRange(start=0, end=0)
```

如果一部电影的"对等值"被描述为同一制片厂在同一年发行的同一类型的电影，那么这个 `RowRange` 的例子用一部电影的前后两部同行的平均评分来注解每部电影：

```
>>> from django.db.models import Avg, F, RowRange, Window
>>> from django.db.models.functions import ExtractYear
>>> Movie.objects.annotate(
>>>     avg_rating=Window(
>>>         expression=Avg('rating'),
>>>         partition_by=[F('studio'), F('genre')],
>>>         order_by=ExtractYear('released').asc(),
>>>         frame=RowRange(start=-2, end=2),
>>>     ),
>>> )
```

如果数据库支持，可以根据分区中的表达式的值来指定开始和结束点。如果 `Movie` 模型中的 `released` 字段存储了每部电影的发布月份，那么这个 `ValueRange` 例子就用每部电影之前 12 个月到之后 12 个月之间发布的电影同行的平均评分来注释每部电影。

```
>>> from django.db.models import Avg, F, ValueRange, Window
>>> Movie.objects.annotate(
>>>     avg_rating=Window(
>>>         expression=Avg('rating'),
>>>         partition_by=[F('studio'), F('genre')],
>>>         order_by=F('released').asc(),
>>>         frame=ValueRange(start=-12, end=12),
>>>     ),
>>> )
```

## 技术信息

下面你会发现对库作者可能有用的技术实现细节。下面的技术 API 和示例将有助于创建通用的查询表达式，可以扩展 Django 提供的内置功能。

### 表达式 API

查询表达式实现了 [查询表达式 API](/zh-hans/3.1/ref/models/lookups/#query-expression)，但也暴露了下面列出的一些额外的方法和属性。所有查询表达式必须继承于 `Expression()` 或相关子类。

当一个查询表达式包装另一个表达式时，它负责在被包装的表达式上调用相应的方法。

#### `class Expression`

#### `contains_aggregate`

告诉 Django 这个表达式包含一个集合，需要在查询中添加一个 `GROUP BY` 子句。

#### `contains_over_clause`

告诉 Django 这个表达式包含一个 [`Window`](#django.db.models.expressions.Window) 表达式。例如，它用于在修改数据的查询中不允许使用窗口函数表达式。

#### `filterable`

告诉 Django 这个表达式可以在 [`QuerySet.filter()`](/zh-hans/3.1/ref/models/querysets/#django.db.models.query.QuerySet.filter) 中引用。默认值为 `True`。

#### `window_compatible`

告诉 Django 这个表达式可以作为 [`Window`](#django.db.models.expressions.Window) 的源表达式。默认值为 `False`。

#### `resolve_expression(query=None, allow_joins=True, reuse=None, summarize=False, for_save=False)`

提供了在将表达式添加到查询之前对其进行任何预处理或验证的机会。`resolve_expression()` 也必须对任何嵌套的表达式调用。`copy()` 应该返回一个 `self` 的 `` copy()` ``，并进行必要的转换。

`query` 是后端查询的实现。

`allow_joins` 是一个允许或拒绝在查询中使用连接的布尔值。

`reuse` 是一组可重用的多连接方案的连接。

`summarize` 是一个布尔值，当 `True` 时，表示正在计算的查询是一个终端聚合查询。

`for_save` 是一个布尔值，当 `True` 时，表示正在执行的查询正在进行创建或更新。

#### `get_source_expressions()`

返回内部表达式的有序列表。例如：

```
>>> Sum(F('foo')).get_source_expressions()
[F('foo')]
```

#### `set_source_expressions(expressions)`

获取一个表达式列表，并将其存储起来，使 `get_source_expressions()` 能够返回它们。

#### `relabeled_clone(change_map)`

返回 `` self` `` 的克隆（副本），并重新标明所有列别名。当创建子查询时，列别名会被重新命名。`relabeled_clone()` 也应该对任何嵌套的表达式进行调用并分配给克隆。

`change_map` 是一个将旧别名映射到新别名的字典。

举例：

```
def relabeled_clone(self, change_map):
    clone = copy.copy(self)
    clone.expression = self.expression.relabeled_clone(change_map)
    return clone
```

#### `convert_value(value, expression, connection)`

一个钩子，允许表达式将 `` value` `` 强制转换成一个更合适的类型。

`expression` 与 `self` 相同。

#### `get_group_by_cols(alias=None)`

负责返回该表达式引用的列列表。`get_group_by_cols()` 应在任何嵌套的表达式上调用。`F()` 对象，尤其是持有列的引用。`alias` 参数将是 `None`，除非表达式已经被注解并用于分组。

> **Changed in Django 3.0**
>
> 增加了 `alias` 参数。

#### `asc(nulls_first=False, nulls_last=False)`

返回准备按升序排序的表达式。

`nulls_first` 和 `nulls_last` 定义了如何对空值进行排序。参见 [使用 F() 对空值进行排序](#using-f-to-sort-null-values) 的用法示例。

#### `desc(nulls_first=False, nulls_last=False)`

返回准备好降序排序的表达式。

`nulls_first` 和 `nulls_last` 定义了如何对空值进行排序。参见 [使用 F() 对空值进行排序](#using-f-to-sort-null-values) 的用法示例。

#### `reverse_ordering()`

返回 `self`，包括在 `order_by` 中对排序顺序进行的任何修改。例如，一个实现 `NULLS LAST` 的表达式将把它的值改为 `NULLS FIRST`。只有实现排序顺序的表达式，如 `OrderBy` 才需要修改。当 [`reverse()`](/zh-hans/3.1/ref/models/querysets/#django.db.models.query.QuerySet.reverse) 在一个查询集上被调用时，会调用这个方法。

### 编写自己的查询表达式

你可以编写你自己的查询表达式类，这些类使用并可以与其他查询表达式集成。让我们通过一个例子，在不使用内置的 [Func() 表达式](#func-expressions) 的情况下，编写一个 `COALESCE` SQL 函数的实现。

`COALESCE` SQL 函数被定义为接收一个列或值的列表，它将返回第一个不是 `NULL` 的列或值。它将返回第一个不是 `NULL` 的列或值。

我们将首先定义用于生成 SQL 的模板和一个 `__init__()` 方法来设置一些属性：

```
import copy
from django.db.models import Expression

class Coalesce(Expression):
    template = 'COALESCE( %(expressions)s )'

    def __init__(self, expressions, output_field):
      super().__init__(output_field=output_field)
      if len(expressions) < 2:
          raise ValueError('expressions must have at least 2 elements')
      for expression in expressions:
          if not hasattr(expression, 'resolve_expression'):
              raise TypeError('%r is not an Expression' % expression)
      self.expressions = expressions
```

我们对参数进行一些基本的验证，包括要求至少有 2 个列或值，并确保它们是表达式。我们在这里要求 `output_field`，这样 Django 就知道要把最终的结果分配给什么样的模型字段。

现在我们实现预处理和验证。由于此时我们没有任何自己的验证，所以我们委托给嵌套的表达式：

```
def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
    c = self.copy()
    c.is_summary = summarize
    for pos, expression in enumerate(self.expressions):
        c.expressions[pos] = expression.resolve_expression(query, allow_joins, reuse, summarize, for_save)
    return c
```

接下来，我们编写负责生成 SQL 的方法：

```
def as_sql(self, compiler, connection, template=None):
    sql_expressions, sql_params = [], []
    for expression in self.expressions:
        sql, params = compiler.compile(expression)
        sql_expressions.append(sql)
        sql_params.extend(params)
    template = template or self.template
    data = {'expressions': ','.join(sql_expressions)}
    return template % data, sql_params

def as_oracle(self, compiler, connection):
    """
    Example of vendor specific handling (Oracle in this case).
    Let's make the function name lowercase.
    """
    return self.as_sql(compiler, connection, template='coalesce( %(expressions)s )')
```

`as_sql()` 方法可以支持自定义关键字参数，允许 `as_vendorname()` 方法覆盖用于生成 SQL 字符串的数据。使用 `as_sql()` 关键字参数进行自定义最好是在 `as_vendorname()` 方法中突变 `self`，因为后者在不同的数据库后端运行时可能会导致错误。如果你的类依赖于类属性来定义数据，可以考虑在你的 `as_sql()` 方法中允许覆盖。

我们使用 `compiler.compile()` 方法为每个 `expressions` 生成 SQL，并将结果用逗号连接起来。然后在模板中填入我们的数据，并返回 SQL 和参数。

我们还定义了一个专门针对 Oracle 后端的自定义实现。如果使用 Oracle 后端，将调用 `as_oracle()` 函数，而不是 `as_sql()`。

最后，我们实现了其余的方法，使我们的查询表达式能够与其他查询表达式很好地配合：

```
def get_source_expressions(self):
    return self.expressions

def set_source_expressions(self, expressions):
    self.expressions = expressions
```

让我们来看看它如何工作：

```
>>> from django.db.models import F, Value, CharField
>>> qs = Company.objects.annotate(
...    tagline=Coalesce([
...        F('motto'),
...        F('ticker_name'),
...        F('description'),
...        Value('No Tagline')
...        ], output_field=CharField()))
>>> for c in qs:
...     print("%s: %s" % (c.name, c.tagline))
...
Google: Do No Evil
Apple: AAPL
Yahoo: Internet Company
Django Software Foundation: No Tagline
```

#### 避免 SQL 注入

由于 `Func` 的关键字参数 `__init__()` （`**extra`）和 `as_sql()` （`**extra_context`）的参数是内插到 SQL 字符串中，而不是作为查询参数传递的（数据库驱动程序会对其进行转义处理），因此它们不能包含不受信任的用户输入。

例如，如果 `substring` 是用户提供的，这个函数就容易被 SQL 注入：

```
from django.db.models import Func

class Position(Func):
    function = 'POSITION'
    template = "%(function)s('%(substring)s' in %(expressions)s)"

    def __init__(self, expression, substring):
        # substring=substring is an SQL injection vulnerability!
        super().__init__(expression, substring=substring)
```

这个函数在没有任何参数的情况下生成一个 SQL 字符串。由于 `substring` 是作为关键字参数传递给 `super().__init__()` 的，所以在将查询发送到数据库之前，它就被插入到 SQL 字符串中。

下面是更正后的改写：

```
class Position(Func):
    function = 'POSITION'
    arg_joiner = ' IN '

    def __init__(self, expression, substring):
        super().__init__(substring, expression)
```

用 `substring` 代替作为位置参数传递，它将作为数据库查询的参数传递。

### 在第三方数据库后端增加支持

如果你使用的数据库后端对某个函数使用了不同的 SQL 语法，你可以通过在函数的类上打一个新的方法来增加对它的支持。

比方说，我们正在为微软的 SQL Server 编写一个后端，它使用 SQL 的 `LEN` 而不是 `LENGTH` 来实现 [`Length`](/zh-hans/3.1/ref/models/database-functions/#django.db.models.functions.Length) 函数。我们将把一个名为 `as_sqlserver()` 的新方法移植到 `Length` 类上：

```
from django.db.models.functions import Length

def sqlserver_length(self, compiler, connection):
    return self.as_sql(compiler, connection, function='LEN')

Length.as_sqlserver = sqlserver_length
```

你也可以使用 `as_sql()` 的 `template` 参数自定义 SQL。

我们使用 `as_sqlserver()`，因为 `django.db.connection.vendor` 返回 `sqlserver` 作为后端。

第三方后端可以在后端包的顶层 `__init__.py` 文件或从顶层 `__init__.py` 导入的顶层 `expressions.py` 文件（或包）中注册它们的函数。

对于希望给自己正在使用的后端打补丁的用户项目来说，这段代码应该存在于 `AppConfig.ready()` 方法中。
