查询表达式
查询表达式可以作为过滤,分组,注解或者是聚合的一个值或者是计算。有很多内置表达式可以帮助你完成自己的查询。表达式可以组合,甚至是嵌套,来完成更加复杂的计算
内置表达式
说明
这些表达式定义在django.db.models.expressions 和django.db.models.aggregates中, 但为了方便,通常可以直接从django.db.models导入.
F() 表达式
class F
一个 F()对象代表了一个model的字段值或注释列。使用它就可以直接参考model的field和执行数据库操作而不用再把它们(model field)查询出来放到python内存中
使用update()方法批量地增加多个对象的字段值。这比先从数据库查询后,通过循环一个个增加,并一个个保存要快的很多。
Reporter.objects.all().update(stories_filed=F('stories_filed') + 1)
F() 表达式效率上的优点在于:
- 直接通过数据库操作而不是python
- 减少数据库查询次数
使用 F() 避免竞态条件
使用 F() 的另一个好处是通过数据库而不是Python来更新字段值以避免竞态条件.
如果两个Python线程执行上面第一个例子中的代码,一个线程可能在另一个线程刚从数据库中获取完字段值后获取、增加、保存该字段值。第二个线程保存的值将会基于原始字段值;第一个线程的工作将会丢失。
如果让数据库对更新字段负责,这个过程将变得更稳健:它将只会在 save() 或update()执行时根据数据库中该字段值来更新字段,而不是基于实例之前获取的字段值。
在过滤器中使用 F()
F() 在查询集过滤器中使用,使得条件通过字段值而不是python值过滤
Reporter.objects.filter(pub_date=F('mod_date') )
使用F()和注释
通过将不同字段与算术集合在模型创建动态字段。
company = Company.objects.annotate( chairs_needed=F('num_employees') - F('num_chairs'))
如果组合字段是不同类型,需要告诉Django返回类型字段。
from django.db.models import DateTimeField, ExpressionWrapper, F Ticket.objects.annotate( expires=ExpressionWrapper( F('active_at') + F('duration'), output_field=DateTimeField()))
Func()表达式
Func() 表达式是所有表达式的基础类型,包括数据库函数如 COALESCE 和 LOWER, 或者 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(F('field')))
但是这两种情况都将导致查询集,其中每个模型用从以下SQL大致生成的额外属性field_lower注释:
SELECT ... LOWER("app_label"."field") as "field_lower"
Func API如下:
class Func(*expressions, **extra)
function
描述将生成的函数的类属性。具体来说,函数将被插入为模板中的函数占位符。默认为无。
template
类属性,作为格式字符串,描述为此函数生成的SQL。默认为'%(function)s(%(expressions)s)'。
arg_joiner
类属性,表示用于连接表达式列表的字符。默认为', '。
*expressions参数是函数将要应用与表达式的位置参数列表。表达式将转换为字符串,与arg_joiner连接在一起,然后作为expressions占位符插入template。
位置参数可以是表达式或Python值。字符串假定为列引用,并且将包装在F()表达式中,而其他值将包裹在Value()表达式中。
**extra kwargs是可以插入到template属性中的key=value对。请注意,关键字function和template可用于分别替换function和template属性,而无需定义自己的类。output_field可用于定义预期的返回类型。
更多详细链接
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
等)相关的任何参数不会对表达式的输出值强制执行。
条件表达式
条件表达式允许你在过滤器,注解,聚合和更新操作使用if..else..逻辑。
条件表达式为表中的每一行计算一系列的条件,并返回匹配到的结果表达式。
When
class When(condition=None, then=None, **lookups)[source]¶
When()对象用于封装条件和它的结果,为了在条件表达式中使用。使用When()对象和使用filter() 方法类似。条件可以使用字段查找 或者 Q 来指定。结果通过使用then关键字来提供
Case
class Case(*cases, **extra)[source]
Case()表达式就像是Python中的if ... elif ...else语句。每个提供的When()中的condition 按照顺序计算,直到得到一个真值。返回匹配When() 对象的result表达式。
创建对象:
from datetime import date, timedelta
from django.db.models import CharField, Case, Value
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))
#
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')
Case() 接受任意数量的When()对象作为独立的参数。其它选项使用关键字参数提供。如果没有条件为TRUE,表达式会返回提供的default关键字参数。如果没有提供default参数,会使用Value(None)
记住条件按照顺序来计算。这就像Python中的if ... elif ...else语句一样。
Case() 也在filter() 子句中工作,如找到一个多月前注册的黄金客户和一年多前注册的白金客户
ama = date.today() - timedelta(days=30)
aya = date.today() - timedelta(days=365)
Client.objects.filter(registered_on__lte=Case(When(account_type=Client.GOLD, then=ama), When(account_type=Client.PLATINUM, then=aya))).values_list('name', 'account_type')
高级查询
条件表达式可以用于注解、聚合、查找和更新。它们也可以和其它表达式混合和嵌套。这可以让你构造更强大的条件查询。
条件更新
假设我们要为客户更改 account_type
以符合其注册日期。我们可以使用条件表达式和 update()
方法来做到这一点:
ama = date.today() - timedelta(days=30)
aya = date.today() - timedelta(days=365)
Client.objects.update(account_type=Case(When(registered_on__lte=aya, then=Value(Client.PLATINUM)), When(registered_on__lte=ama, then=Value(Client.GOLD)), default=Value(Client.REGULAR)))
3
Client.objects.values_list('name', 'account_type')
条件聚合
弄清楚每个account_type 有多少个客户端(regular,gold,Platinum各自有多少个):
在增加点数据
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())
Client.objects.aggregate(
regular=Sum(Case(When(account_type=Client.REGULAR, then=1), output_field=models.IntegerField())),
gold=Sum(Case(When(account_type=Client.GOLD, then=1), output_field=models.IntegerField())),
platinum=Sum(Case(When(account_type=Client.PLATINUM, then=1), output_field=models.IntegerField())),
)
{'platinum': 3, 'gold': 1, 'regular': 2}