查询表达式

查询表达式描述了可用作更新,创建,过滤,排序,注释或聚合的一部分的值或计算。 有许多内置的表达式(下面有说明)可以用来帮助您编写查询。 表达式可以组合,或者在某些情况下嵌套,形成更复杂的计算。

支持的算术

Django支持使用Python常量,变量,甚至其他表达式对查询表达式进行加,减,乘,除,模运算和强大的运算符。

一些例子

from django.db.models import F, Count, 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()
Company.objects.order_by(Length('name').asc())
Company.objects.order_by(Length('name').desc())

内置表达式

注意

这些表达式在django.db.models.expressionsdjango.db.models.aggregates中定义,但为了方便起见,它们通常从django.db.models

F()表达式

F[source]

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

Although reporter.stories_filed = F('stories_filed') + 1 looks like a normal Python assignment of value to an instance attribute, in fact it’s an SQL construct describing an operation on the database.

当Django遇到一个F()的实例时,它将覆盖标准的Python运算符来创建一个封装的SQL表达式;在这种情况下,指示数据库增加由reporter.stories_filed表示的数据库字段。

Whatever value is or was on reporter.stories_filed, Python never gets to know about it - it is dealt with entirely by the database. All Python does, through Django’s F() class, is create the SQL syntax to refer to the field and describe the operation.

要访问以此方式保存的新值,必须重新加载对象:

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

As well as being used in operations on single instances as above, F() can be used on QuerySets of object instances, with update(). 这减少了我们上面使用的两个查询 - get()save() - 只有一个:

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

我们也可以使用update()来增加多个对象的字段值 - 这可能比从数据库中将它们全部引入Python快得多,循环遍历它们,递增每个字段的值,并将每一个保存回数据库:

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

F() therefore can offer performance advantages by:

  • 得到数据库,而不是Python,做工作
  • 减少一些操作所需的查询次数

使用F() 来避免竞争条件

Another useful benefit of F() is that having the database - rather than Python - update a field’s value avoids a race condition.

如果两个Python线程在上面的第一个示例中执行代码,则一个线程可以在另一个线程从数据库中检索到字段值之后检索,增加和保存字段值。 第二个线程保存的值将基于原始值;第一个线程的工作将会丢失。

如果数据库负责更新字段,则该过程更加健壮:当save()update()被执行,而不是基于它的值,当实例被检索。

F() assignments persist after Model.save()

F() objects assigned to model fields persist after saving the model instance and will be applied on each 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

在过滤器中使用F()

F() is also very useful in QuerySet filters, where they make it possible to filter a set of objects against criteria based on their field values, rather than on Python values.

这在using F() expressions in queries

使用注释F()

F() can be used to create dynamic fields on your models by combining different fields with arithmetic:

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

如果你组合的字段是不同的类型,你需要告诉Django将返回什么样的字段。 由于F()不直接支持output_field,所以您需要用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

Func()表达式

Func() expressions are the base type of all expressions that involve database functions like COALESCE and LOWER, or aggregates like SUM. 他们可以直接使用:

from django.db.models import Func, F

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

或者可以用来构建数据库功能库:

class Lower(Func):
    function = 'LOWER'

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

但是这两种情况都会导致一个查询集,在这个查询集中,每个模型都有一个额外的属性field_lower

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

有关内置数据库函数的列表,请参阅Database Functions

Func API如下:

Func*表达式**额外[source]
功能 T0> ¶ T1>

描述将要生成的函数的类属性。 具体来说,function将被插入template中的function占位符。 默认为None

模板 T0> ¶ T1>

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

If you’re constructing SQL like strftime('%W', 'date') and need a literal % character in the query, quadruple it (%%%%) in the template attribute because the string is interpolated twice: once during the template interpolation in as_sql() and once in the SQL interpolation with the query parameters in the database cursor.

arg_joiner T0> ¶ T1>

表示用于连接expressions列表的字符的类属性。 默认为', '

元数 T0> ¶ T1>

表示函数接受的参数个数的类属性。 If this attribute is set and the function is called with a different number of expressions, TypeError will be raised. 默认为None

as_sql(compiler, connection, function=None, template=None, arg_joiner=None, **extra_context)[source]

生成数据库函数的SQL。

as_vendor()方法应该使用functiontemplatearg_joiner和任何其他**extra_context参数来根据需要自定义SQL。 例如:

Django的/ DB /模型/ functions.py
class ConcatPair(Func):
    ...
    function = 'CONCAT'
    ...

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

To avoid a SQL injection vulnerability, extra_context must not contain untrusted user input as these values are interpolated into the SQL string rather than passed as query parameters, where the database driver would escape them.

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

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

**extra kwargs是key=value对,可以插入到template属性中。 To avoid a SQL injection vulnerability, extra must not contain untrusted user input as these values are interpolated into the SQL string rather than passed as query parameters, where the database driver would escape them.

可以使用functiontemplatearg_joiner关键字替换相同名称的属性,而无需定义自己的类。 output_field can be used to define the expected return type.

Aggregate()表达式

聚合表达式是Func() expression的一个特例,它通知查询:GROUP BY是必须的。 所有aggregate 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如下:

Aggregate(expression, output_field=None, filter=None, **extra)[source]
模板 T0> ¶ T1>

作为格式字符串的类属性,描述为此聚合生成的SQL。 默认为'%(function)s( %(expressions)s )'

功能 T0> ¶ T1>

描述将生成的聚合函数的类属性。 具体来说,function将被插入template中的function占位符。 默认为None

window_compatible T0> ¶ T1>
Django 2.0新增功能

默认为True,因为大多数集合函数可以用作Window中的源表达式。

expression参数可以是模型或其他表达式上字段的名称。 它将被转换为一个字符串,并用作template中的expressions占位符。

output_field参数需要一个模型字段实例,比如IntegerField()或者BooleanField(),Django在从数据库。 通常,在实例化模型字段时不需要参数,因为任何与数据验证有关的参数(max_lengthmax_digits等)都不会在表达式的输出值上强制执行。

请注意,仅当Django无法确定结果应为的字段类型时,才需要output_field 混合字段类型的复杂表达式应该定义所需的output_field 例如,将IntegerField()FloatField()加在一起可能会定义output_field=FloatField()

filter参数需要用于过滤聚合的行的Q object 有关示例,请参阅Conditional aggregationFiltering on annotations

**extra kwargs是key=value对,可以插入到template属性中。

在Django 2.0中更改:

添加了filter参数。

创建您自己的聚合函数

创建自己的聚合非常简单。 您至少需要定义function,但是您也可以完全自定义生成的SQL。 这里有一个简单的例子:

from django.db.models import Aggregate

class Count(Aggregate):
    # supports COUNT(distinct field)
    function = 'COUNT'
    template = '%(function)s(%(distinct)s%(expressions)s)'

    def __init__(self, expression, distinct=False, **extra):
        super().__init__(
            expression,
            distinct='DISTINCT ' if distinct else '',
            output_field=IntegerField(),
            **extra
        )

Value()表达式

output_field =无[source]

一个Value()对象表示表达式中最小的可能的组件:一个简单的值。 当需要在表达式中表示整数,布尔值或字符串的值时,可以将该值包装在Value()中。

您很少需要直接使用Value() When you write the expression F('field') + 1, Django implicitly wraps the 1 in a Value(), allowing simple values to be used in more complex expressions. 如果要将字符串传递给表达式,则需要使用Value() 大多数表达式将字符串参数解释为字段的名称,如Lower('name')

value参数描述了要包含在表达式中的值,例如1TrueNone Django知道如何将这些Python值转换为相应的数据库类型。

output_field参数应该是一个模型字段实例,比如IntegerField()BooleanField(),Django将在检索后加载该值从数据库。 通常,在实例化模型字段时不需要参数,因为任何与数据验证有关的参数(max_lengthmax_digits等)都不会在表达式的输出值上强制执行。

ExpressionWrapper() expressions

ExpressionWrapper表达式output_field[source]

ExpressionWrapper simply surrounds another expression and provides access to properties, such as output_field, that may not be available on other expressions. ExpressionWrapper is necessary when using arithmetic on F() expressions with different types as described in Using F() with annotations.

条件表达式

条件表达式允许您在查询中使用if ... elif ... else逻辑。 Django本地支持SQL CASE表达式。 有关更多详细信息,请参阅Conditional Expressions

Subquery() expressions

子查询querysetoutput_field =无[source]
Django 1.11新增功能

您可以使用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看起来像:

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"

注意

本节中的示例旨在演示如何强制Django执行子查询。 在某些情况下,编写一个能够更清晰或更高效地执行相同任务的等效查询集也许是可能的。

引用来自外部查询集的列

OuterRef(field)[source]
Django 1.11新增功能

Subquery中的查询集需要引用来自外部查询的字段时,请使用OuterRef 它的行为就像一个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()来仅返回一个列:帖子的主键。

将子查询限制为一行

为了防止子查询返回多行,使用查询集的片段([:1]):

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

在这种情况下,子查询只能返回单个行:最近创建的注释的电子邮件地址。

(使用get()而不是片将会失败,因为直到在Subquery中使用查询集时,OuterRef才能被解析。

Exists()子查询

Exists(queryset)[source]
Django 1.11新增功能

Exists是一个使用SQL EXISTS语句的Subquery子类。 在许多情况下,它会比子查询执行得更好,因为当找到第一个匹配的行时,数据库能够停止评估子查询。

例如,要注释每个帖子是否有来自最后一天内的评论:

>>> 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看起来像:

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表达式上过滤

无法直接使用SubqueryExists进行过滤,例如:

>>> Post.objects.filter(Exists(recent_comments))
...
TypeError: 'Exists' object is not iterable

您必须首先注释查询集,然后根据该注释进行过滤,从而过滤子查询表达式:

>>> Post.objects.annotate(
...     recent_comment=Exists(recent_comments),
... ).filter(recent_comment=True)

使用Subquery表达式中的聚合

可以在Subquery中使用聚合,但是它们需要filter()values()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模型中默认的ordering(如果有的话)。 values('post')通过Post聚合评论。 Finally, annotate(...) performs the aggregation. 这些queryset方法的应用顺序非常重要。 在这种情况下,由于子查询必须限制在一个列中,因此需要values('total')

这是在Subquery内执行聚合的唯一方式,因为使用aggregate()试图评估查询集(如果存在OuterRef

原始SQL表达式

RawSQLsqlparamsoutput_field = None[source] ¶ T6>

Sometimes database expressions can’t easily express a complex WHERE clause. 在这些边缘情况下,使用RawSQL表达式。 例如:

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

这些额外的查找可能不能移植到不同的数据库引擎(因为你明确写了SQL代码)并违反了DRY原则,因此如果可能的话,应该避免使用它们。

警告

To protect against SQL injection attacks, you must escape any parameters that the user can control by using params. params is a required argument to force you to acknowledge that you’re not interpolating your SQL with user-provided data.

您也不能在SQL字符串中引用占位符。 由于%s周围的引号,此示例易受SQL注入攻击:

RawSQL("select col from sometable where othercol = '%s'")  # unsafe!

您可以阅读更多关于Django的SQL injection protection的工作原理。

窗口函数

Django 2.0新增功能

窗口函数提供了一种在分区上应用函数的方法。 与一般的聚合函数不同,它计算由group by定义的每个集合的最终结果,窗口函数对frames和分区进行操作,并计算每行的结果。

您可以在同一个查询中指定多个窗口,在Django的ORM中,这个窗口将等同于在QuerySet.annotate()调用中包含多个表达式。 ORM不使用命名窗口,而是它们是选定列的一部分。

Window表达式partition_by = Noneorder_by = Noneframe = Noneoutput_field = None[source]
过滤 T0> ¶ T1>

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

模板 T0> ¶ T1>

默认为%(表达式)s OVER (%(window)s​​)' 如果仅提供expression参数,那么window子句将为空。

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

expression参数可以是一个window function,一个aggregate function或一个在窗口子句中兼容的表达式。

partition_by参数是控制行分区的表达式列表(列名称应包含在F - 对象中)。 分区缩小哪些行用于计算结果集。

The output_field is specified either as an argument or by the expression.

order_by参数接受一系列可以调用asc()desc()的表达式。 排序控制表达式的应用顺序。 例如,如果总结分区中的行,第一个结果就是第一行的值,第二个是第一行和第二行的总和。

frame参数指定在计算中应该使用哪些其他行。 有关详细信息,请参见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()desc()中的选项可能不受支持。 根据需要查阅数据库文档。

¶ T0>

对于窗口框架,您可以选择基于范围的行序列或普通的行序列。

ValueRangestart = Noneend = None[source]
FRAME_TYPE T0> ¶ T1>

这个属性被设置为'RANGE'

PostgreSQL has limited support for ValueRange and only supports use of the standard start and end points, such as CURRENT ROW and UNBOUNDED FOLLOWING.

RowRange(start=None, end=None)[source]
FRAME_TYPE T0> ¶ T1>

该属性被设置为'ROWS'

这两个类都使用模板返回SQL:

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

框架缩小用于计算结果的行。 他们从一些起点转移到某个特定的终点。 框架可以在有和没有分区的情况下使用,但是指定窗口的顺序以确保确定性的结果通常是一个好主意。 在一个框架中,一个框架中的一个对等体是一个具有等价值的行,或者如果一个排序子句不存在,则是所有的行。

The default starting point for a frame is UNBOUNDED PRECEDING which is the first row of the partition. 终点始终显式包含在ORM生成的SQL中,并且默认是UNBOUNDED FOLLOWING 默认框架包括从分区到集合中最后一行的所有行。

startend参数的接受值是None,一个整数或零。 A negative integer for start results in N preceding, while None yields UNBOUNDED PRECEDING. 对于startend,零将返回CURRENT ROW 正整数被接受为end

CURRENT ROW包含的内容上有所不同。 当在ROWS模式中指定时,框架以当前行开始或结束。 当在RANGE模式中指定时,帧根据排序条款在第一个或最后一个同级开始或结束。 Thus, RANGE CURRENT ROW evaluates the expression for rows which have the same value specified by the ordering. 由于模板包含startend点,因此可以用下面的表达式表示:

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

如果数据库支持它,则可以根据分区中表达式的值指定开始点和结束点。 If the released field of the Movie model stores the release month of each movies, this ValueRange example annotates each movie with the average rating of a movie’s peers released between twelve months before and twelve months after the each movie.

>>> from django.db.models import Avg, ExpressionList, 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

查询表达式实现了query expression API,但是也暴露了下面列出的一些额外的方法和属性。 所有查询表达式都必须从Expression()或相关的子类继承。

当查询表达式包装另一个表达式时,它负责在包装的表达式上调用适当的方法。

Expression[source]
contains_aggregate T0> ¶ T1>

告诉Django这个表达式包含一个聚合,并且需要将一个GROUP BY子句添加到查询中。

contains_over_clause T0> ¶ T1>
Django 2.0新增功能

告诉Django这个表达式包含一个Window表达式。 例如,它用于在修改数据的查询中禁止窗口函数表达式。 默认为True

过滤 T0> ¶ T1>
Django 2.0新增功能

告诉Django这个表达式可以在QuerySet.filter()中被引用。 默认为True

window_compatible T0> ¶ T1>
Django 2.0新增功能

告诉Django这个表达式可以用作Window中的源表达式。 默认为False

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

提供在添加到查询之前对表达式进行任何预处理或验证的机会。 resolve_expression() must also be called on any nested expressions. selfcopy()应该返回任何必要的转换。

query is the backend query implementation.

allow_joins is a boolean that allows or denies the use of joins in the query.

reuse is a set of reusable joins for multi-join scenarios.

summarize is a boolean that, when True, signals that the query being computed is a terminal aggregate query.

get_source_expressions T0>()¶ T1>

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

>>> Sum(F('foo')).get_source_expressions()
[F('foo')]
set_source_expressions T0>(表达式 T1>)¶ T2>

Takes a list of expressions and stores them such that get_source_expressions() can return them.

relabeled_clone T0>( change_map T1>)¶ T2>

返回self的克隆(副本),并重新标记任何列别名。 当创建子查询时,列别名被重命名。 relabeled_clone() should also be called on any nested expressions and assigned to the clone.

change_map is a dictionary mapping old aliases to new aliases.

例:

def relabeled_clone(self, change_map):
    clone = copy.copy(self)
    clone.expression = self.expression.relabeled_clone(change_map)
    return clone
convert_valuevalue表达式连接

允许表达式将value转换为更适当的类型的钩子。

get_group_by_cols T0>()¶ T1>

负责通过此表达式返回列引用列表。 get_group_by_cols() should be called on any nested expressions. F() objects, in particular, hold a reference to a column.

ascnulls_first = Falsenulls_last = False

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

nulls_first and nulls_last define how null values are sorted.

在Django 1.11中更改:

添加了nulls_lastnulls_first参数。

descnulls_first = Falsenulls_last = False

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

nulls_first and nulls_last define how null values are sorted.

在Django 1.11中更改:

添加了nulls_firstnulls_last参数。

reverse_ordering T0>()¶ T1>

返回self,并调用order_by中的排序顺序。 As an example, an expression implementing NULLS LAST would change its value to be NULLS FIRST. 对于实现排序顺序的表达式,如OrderBy,只需要修改。 在查询集上调用reverse()时调用此方法。

编写自己的查询表达式

您可以编写自己的查询表达式类,这些类使用并可以与其他查询表达式集成。 Let’s step through an example by writing an implementation of the COALESCE SQL function, without using the built-in Func() expressions.

The COALESCE SQL function is defined as taking a list of columns or values. 它将返回第一列或不是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, 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() methods can support custom keyword arguments, allowing as_vendorname() methods to override data used to generate the SQL string. 使用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注入

Since a Func’s keyword arguments for __init__() (**extra) and as_sql() (**extra_context) are interpolated into the SQL string rather than passed as query parameters (where the database driver would escape them), they must not contain untrusted user input.

例如,如果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 a 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语法,则可以通过将新方法修补到函数的类中来添加对它的支持。

Let’s say we’re writing a backend for Microsoft’s SQL Server which uses the SQL LEN instead of LENGTH for the Length function. 我们将修补一个名为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文件中或在导入的顶层expressions.py文件(或包)中注册它们的功能从顶层__init__.py

对于希望修补其使用的后端的用户项目,此代码应该存在于AppConfig.ready()方法中。