Django条件查询的Case,When具体使用
这个操作类似于数据库中 if elif else 的逻辑。
1、数据准备:
models.py
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, )
迁移数据:
from blog.models import Client Client.objects.create(name="client_1", registered_on="2020-01-01", account_type="R") Client.objects.create(name="client_2", registered_on="2021-07-12", account_type="G") Client.objects.create(name="client_3", registered_on="2022-09-20", account_type="P") Client.objects.create(name="client_4", registered_on="2022-12-07", account_type="P")
2、When 和 Case 操作新增字段返回
When 和 Case 的函数,这个其实就对应于 SQL 里的 CASE 和 WHEN 函数。目前有这么一个需求,想要筛选出所有季节为 Spring 的数据。
通过判断 registered_on 字段的月份区间来得到一个新的字段:
from django.db.models import Case, Value, When from blog.models import Client results = Client.objects.annotate( season= Case( When(registered_on__month__in=[1,2,3], then=Value("Spring")), When(registered_on__month__in=[4,5,6], then=Value("Summer")), When(registered_on__month__in=[7,8,9], then=Value("Autumn")), When(registered_on__month__in=[10,11,12], then=Value("Winter")), default=Value("Spring") ) )
Case() 函数内包含了四种 When 的可能性,然后会有一个 default 默认值
在每一个 When() 函数里,前一个是个表达式,可以是这种形式,也可以是 Q() 操作的语句,then= 表示如果满足前面的表达式,那么值的内容将会是后面的值。
在值的定义里,我们这里用到了 Value() 函数,Value() 表示其值是一个字符串。
获取字段值
如果该字段取值是获取某个字段的内容,比如 Client 里的 name 字段,就不需要 Value() 函数来操作,可以直接使用:
When(registered_on__month__in=[1,2,3], then="name")
或者通过 F() 函数来取字段值:
from django.db.models import F When(registered_on__month__in=[1,2,3], then=F("name"))
在不需要对字段内容进行操作的情况下,上面两条命令的作用是一样的
3、条件搜索
还是前面的需求,我们需要对 Client 的数据进行数据筛选,筛选出 season 为 Spring 的数据,可以在上面的操作中接着 filter():
results = Client.objects.annotate( season= Case( When(registered_on__month__in=[1,2,3], then=Value("Spring")), When(registered_on__month__in=[4,5,6], then=Value("Summer")), When(registered_on__month__in=[7,8,9], then=Value("Autumn")), When(registered_on__month__in=[10,11,12], then=Value("Winter")), default=Value("Spring") ) ).filter(season="Spring")
根据条件进行filter
对于 Client 这个 model,我们想实现这样的搜索条件:
要查找一个月前注册的黄金客户和一年前注册的白金客户:
对于这个需求,在之前我们怎么做? 使用 Q() 语法来连接:
from blog.models import Client from datetime import date, timedelta from django.db.models import Q a_month_ago = date.today() - timedelta(days=30) a_year_ago = date.today() - timedelta(days=365) condition = (Q(account_type=Client.GOLD) & Q(registered_on__lte=a_month_ago))| \ (Q(account_type= Client.PLATINUM) & Q(registered_on__lte= a_year_ago)) Client.objects.filter(condition)
在这里,如果用到我们的 Case 和 When 的函数也是可以的:
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) ) )
例子:
假设有一个 TestModel,有一个 field_1 的字段,现在需要将数据按照 B,C,A 的顺序返回结果,那么这里用到 Case 和 When 的处理方法就可以,我们可以通过条件得出一个新的字段 priority,然后 order_by("priority") 即可。
TestModel.objects.annotate( priority=Case( When(field_1="B", then=1), When(field_1="C", then=2), When(field_1="A", then=3), default=4 ) ).order_by("priority")
4、条件更新
除了前面对数据进行条件的筛选,我们还可以根据条件来对数据进行更新
假设现在需求是对 registered_on 字段的年份进行条件更新: 年份为 2020年的 account_type 字段内容变为 Client.PLATINUM 年份为 2021年的 account_type 字段内容变为 Client.REGULAR
那么相应的代码应该如下:
Client.objects.update( account_type=Case( When(registered_on__year=2020, then=Value(Client.PLATINUM)), When(registered_on__year=2021, then=Value(Client.REGULAR)), default=Value(Client.GOLD) ) )
需要注意的是,在上面的代码中我们没有针对数据进行 filter() 操作,所以作用的是全表数据,其他非 2020 和 2021 年份的数据也会被更新,如果仅希望操作 2020 和 2021 年的数据,可以加上 filter() 的条件限制:
Client.objects.filter(registered_on__year__in=[2020, 2021]).update( account_type=Case( When(registered_on__year=2020, then=Value(Client.PLATINUM)), When(registered_on__year=2021, then=Value(Client.REGULAR)), default=Value(Client.GOLD) ) )
参考自:https://www.cnblogs.com/zonghan/p/17044123.html