查询表达式和条件表达式

查询表达式

查询表达式可以作为过滤,分组,注解或者是聚合的一个值或者是计算。有很多内置表达式可以帮助你完成自己的查询。表达式可以组合,甚至是嵌套,来完成更加复杂的计算

内置表达式

说明

这些表达式定义在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(valueoutput_field=None)[源代码]

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

你很少需要直接使用 Value()。当你写表达式 F('field') + 1 时,Django隐含地将 1 包装在 Value() 中,允许在更复杂的表达式中使用简单的值。当您要将字符串传递到表达式时,您将需要使用 Value()。大多数表达式将字符串参数解释为字段的名称,如 Lower('name')

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

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

 

条件表达式

条件表达式允许你在过滤器,注解,聚合和更新操作使用if..else..逻辑。

条件表达式为表中的每一行计算一系列的条件,并返回匹配到的结果表达式。

When

class When(condition=Nonethen=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: Client object>
Client.objects.create(name='james Smith', account_type=Client.GOLD, registered_on=date.today() - timedelta(days=5))
#    <Client: Client object>
Client.objects.create(name='Jack Black', account_type=Client.PLATINUM, registered_on=date.today() - timedelta(days=10*365))
#    <Client: Client object>
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参数,会使用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')
<QuerySet [('Jack Black', 'P')]>

高级查询

条件表达式可以用于注解、聚合、查找和更新。它们也可以和其它表达式混合和嵌套。这可以让你构造更强大的条件查询。

条件更新

假设我们要为客户更改 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')
<QuerySet [('jane doe', 'G'), ('james Smith', 'R'), ('Jack Black', 'P')]>

条件聚合

弄清楚每个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}

 

转载于:https://my.oschina.net/acutesun/blog/1518577

    原文作者:chimianli4037
    原文地址: https://blog.csdn.net/chimianli4037/article/details/100856956
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞