本文首发于公众号:Hunter后端

原文链接:Django笔记二十三之条件表达式搜索、更新等操作

这一篇笔记将介绍条件表达式,就是如何在 model 的使用中根据不同的条件筛选数据返回。

这个操作类似于数据库中 if elif else 的逻辑。

以下是本篇笔记的目录:

  1. model 和数据准备
  2. When 和 Case 操作新增字段返回
  3. 条件搜索
  4. 条件更新
  5. 条件聚合

1、model 和数据准备

这篇笔记我们用到的 model 是 Client,放在 blog/models.py 下

以下是 Client 的 model 定义:

  1. class Client(models.Model):
  2. REGULAR = 'R'
  3. GOLD = 'G'
  4. PLATINUM = 'P'
  5. ACCOUNT_TYPE_CHOICES = [
  6. (REGULAR, 'Regular'),
  7. (GOLD, 'Gold'),
  8. (PLATINUM, 'Platinum'),
  9. ]
  10. name = models.CharField(max_length=50)
  11. registered_on = models.DateField()
  12. account_type = models.CharField(
  13. max_length=1,
  14. choices=ACCOUNT_TYPE_CHOICES,
  15. default=REGULAR,
  16. )

其中 choices 的操作在前面字段类型中都有介绍到,这里不再赘述。

然后 migrate 相关操作这里不多说了,接下来插入一些数据,在 shell 中执行:

  1. from blog.models import Client
  2. Client.objects.create(name="client_1", registered_on="2020-01-01", account_type="R")
  3. Client.objects.create(name="client_2", registered_on="2021-07-12", account_type="G")
  4. Client.objects.create(name="client_3", registered_on="2022-09-20", account_type="P")
  5. Client.objects.create(name="client_4", registered_on="2022-12-07", account_type="P")

接下来介绍我们操作的知识点。

2、When 和 Case 操作新增字段返回

我们使用的条件表达式使用的 When 和 Case 的函数,这个其实就对应于 SQL 里的 CASE 和 WHEN 函数。

我们先来说一下需求,我们在获取 Client 数据的时候,想要知道这条数据 registered_on 日期字段所在的季节,比如 1月就是 Spring,7月就是 Autumn。

怎么处理呢?

很简单,先获取 Client 数据,然后根据 registered_on 字段判断月份,比如在 1,2,3 之间就是 Spring。

这种方法是可行的,但是如果我们有另一个需求,比如说想要筛选出所有季节为 Spring 的数据呢

(这个例子其实不太恰当,因为这种操作,我们可以直接通过 filter(registered_on__month__in=[1,2,3])来筛选,但是这里我们强行要求使用filter(季节='Spring')的这种形式来操作)

那么这时候就可以用上我们的 When Case 的用法了。

在下面的操作中,我们通过判断 registered_on 字段的月份区间来得到一个新的字段:

  1. from django.db.models import Case, Value, When
  2. from blog.models import Client
  3. results = Client.objects.annotate(
  4. season=
  5. Case(
  6. When(registered_on__month__in=[1,2,3], then=Value("Spring")),
  7. When(registered_on__month__in=[4,5,6], then=Value("Summer")),
  8. When(registered_on__month__in=[7,8,9], then=Value("Autumn")),
  9. When(registered_on__month__in=[10,11,12], then=Value("Winter")),
  10. default=Value("Spring")
  11. )
  12. )

在上面的代码中,我们通过 annotate() 来新建一个 season 字段,这个字段的值是根据 registered_on 的月份所在区间来为 season 赋值。

Case() 函数内包含了四种 When 的可能性,然后会有一个 default 默认值

在每一个 When() 函数里,前一个是个表达式,可以是这种形式,也可以是 Q() 操作的语句,then= 表示如果满足前面的表达式,那么值的内容将会是后面的值。

在值的定义里,我们这里用到了 Value() 函数,Value() 表示其值是一个字符串。

获取字段值

如果该字段取值是获取某个字段的内容,比如 Client 里的 name 字段,就不需要 Value() 函数来操作,可以直接使用:

  1. When(registered_on__month__in=[1,2,3], then="name")

或者通过 F() 函数来取字段值:

  1. from django.db.models import F
  2. When(registered_on__month__in=[1,2,3], then=F("name"))

在不需要对字段内容进行操作的情况下,上面两条命令的作用是一样的

3、条件搜索

还是前面的需求,我们需要对 Client 的数据进行数据筛选,筛选出 season 为 Spring 的数据,可以在上面的操作中接着 filter():

  1. results = Client.objects.annotate(
  2. season=
  3. Case(
  4. When(registered_on__month__in=[1,2,3], then=Value("Spring")),
  5. When(registered_on__month__in=[4,5,6], then=Value("Summer")),
  6. When(registered_on__month__in=[7,8,9], then=Value("Autumn")),
  7. When(registered_on__month__in=[10,11,12], then=Value("Winter")),
  8. default=Value("Spring")
  9. )
  10. ).filter(season="Spring")

根据条件进行filter

对于 Client 这个 model,我们想实现这样的搜索条件:

如果 account_type 的值为 Client.GOLD,则 registered_on 字段搜索一个月以前的数据

如果 account_type 的值为 Client.PLATINUM,则 registered_on 字段搜索一年前的数据

对于这个需求,在之前我们怎么做?

使用 Q() 语法来连接:

  1. from blog.models import Client
  2. from datetime import date, timedelta
  3. from django.db.models import Q
  4. a_month_ago = date.today() - timedelta(days=30)
  5. a_year_ago = date.today() - timedelta(days=365)
  6. condition = (Q(account_type=Client.GOLD) & Q(registered_on__lte=a_month_ago))| \
  7. (Q(account_type= Client.PLATINUM) & Q(registered_on__lte= a_year_ago))
  8. Client.objects.filter(condition)

在这里,如果用到我们的 Case 和 When 的函数也是可以的:

  1. Client.objects.filter(
  2. registered_on__lte=Case(
  3. When(account_type=Client.GOLD, then=a_month_ago),
  4. When(account_type=Client.PLATINUM, then=a_year_ago)
  5. )
  6. )

一个例子

之前我在工作中遇到这样一种需求,假设有一个 TestModel,有一个 field_1 的字段,他的值被有 A, B, C 三种或者还有其他的值,但其他的值我们不关心

现在需要将数据按照 B,C,A 的顺序返回结果,那么这里用到 Case 和 When 的处理方法就可以,我们可以通过条件得出一个新的字段 priority,然后 order_by("priority") 即可

处理如下:

  1. TestModel.objects.annotate(
  2. priority=Case(
  3. When(field_1="B", then=1),
  4. When(field_1="C", then=2),
  5. When(field_1="A", then=3),
  6. default=4
  7. )
  8. ).order_by("priority")

4、条件更新

除了前面对数据进行条件的筛选,我们还可以根据条件来对数据进行更新

假设现在需求是对 registered_on 字段的年份进行条件更新:

年份为 2020年的 account_type 字段内容变为 Client.PLATINUM

年份为 2021年的 account_type 字段内容变为 Client.REGULAR

那么相应的代码应该如下:

  1. Client.objects.update(
  2. account_type=Case(
  3. When(registered_on__year=2020, then=Value(Client.PLATINUM)),
  4. When(registered_on__year=2021, then=Value(Client.REGULAR)),
  5. default=Value(Client.GOLD)
  6. )
  7. )

需要注意的是,在上面的代码中我们没有针对数据进行 filter() 操作,所以作用的是全表数据,其他非 2020 和 2021 年份的数据也会被更新,如果仅希望操作 2020 和 2021 年的数据,可以加上 filter() 的条件限制:

  1. Client.objects.filter(registered_on__year__in=[2020, 2021]).update(
  2. account_type=Case(
  3. When(registered_on__year=2020, then=Value(Client.PLATINUM)),
  4. When(registered_on__year=2021, then=Value(Client.REGULAR)),
  5. default=Value(Client.GOLD)
  6. )
  7. )

5、条件聚合

我们现在需要对数据根据条件进行聚合操作,比如 Client 这个 model,我们对其按照 account_type 分组,获取各自的总数。

代码如下:

  1. from django.db.models import Count, Q
  2. Client.objects.aggregate(
  3. regular=Count('pk', filter=(Q(account_type=Client.REGULAR))),
  4. gold=Count('pk', filter=Q(account_type=Client.GOLD)),
  5. platinum=Count('pk', filter=Q(account_type=Client.PLATINUM)),
  6. )

返回的结果为:

  1. {'regular': 1, 'gold': 0, 'platinum': 3}

这个操作对应于 MySQL 中的语句如下:

  1. select count(CASE WHEN account_type='R' THEN id ELSE null end) as regular,
  2. count(CASE WHEN account_type='G' THEN id ELSE null end) as gold,
  3. count(CASE WHEN account_type='P' THEN id ELSE null end) as platinum
  4. FROM blog_client;

我们也可以根据另一种方式来获取各自的总数数据,但是返回的结构是不一样的:

  1. Client.objects.values("account_type").annotate(count=Count("account_type"))

返回的结果形式为:

  1. <QuerySet [{'account_type': 'P', 'count': 3}, {'account_type': 'R', 'count': 1}]>

以上就是本篇笔记关于条件表达式的全部内容,在接下来几篇笔记中将会介绍 model 的数据库函数,大致的内容会是比较和转换函数、日期函数、数据公式、文本函数等。

如果想获取更多后端相关文章,可扫码关注阅读:

Django笔记二十三之case、when操作条件表达式搜索、更新等操作的更多相关文章

  1. python3.4学习笔记(二十三) Python调用淘宝IP库获取IP归属地返回省市运营商实例代码

    python3.4学习笔记(二十三) Python调用淘宝IP库获取IP归属地返回省市运营商实例代码 淘宝IP地址库 http://ip.taobao.com/目前提供的服务包括:1. 根据用户提供的 ...

  2. django笔记二之数据库

    django笔记二之数据库 [同步数据库之前的操作] yum install MySQL-python.x86_64 -y 2)开启数据库服务并创建表 创建数据库设置 为utf8: create da ...

  3. (C/C++学习笔记) 二十三. 运行时类型识别

    二十三. 运行时类型识别 ● 定义 运行时类型识别(Run-time Type Identification, RTTI) 通过RTTI, 程序能够使用基类的指针或引用来检查(check)这些指针或引 ...

  4. 【Django笔记二】Django2.0配置模板和静态文件

    一.环境版本信息: 操作系统:windows10 Django版本:2.0.5 Python版本:3.6.4 二.创建模板 1.在my_project文件夹下新建文件夹templates用于存放模板文 ...

  5. django笔记(二)

    Model many-to-many可以通过through来定义详细信息. 表结构: property django可以用F和Q来配合查找,F可以用于同一个model不同field之间进行比较,可以对 ...

  6. Java基础学习笔记二十三 Java核心语法之反射

    类加载器 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化. 加载就是指将class文件读入内存,并为之创建一个Class对象.任 ...

  7. angular学习笔记(二十三)-$http(1)-api

    之前说到的$http.get和$http.post,都是基于$http的快捷方式.下面来说说完整的$http: $http(config) $http接受一个json格式的参数config: conf ...

  8. python3笔记二十三:正则表达式之元字符

    一:学习内容 匹配单个字符与数字:..[].^.\d.\D.\w.\W.\s.\S 匹配锚字符(边界字符):^.$.\A.\Z.\b.\B 匹配多个字符:(xyz) .x?.x*..*.x+.x{n} ...

  9. python3笔记二十三:正则表达式之其他函数

    一:学习内容 re.split函数 re.finditer函数 re.sub函数 group()分组 re.compile函数 二:字符串切割---re.split函数 需要导入包:import re ...

  10. Java笔记(二十三)……Map集合

    Map接口 Map<K,V> 该集合存储的是键值对,成对往集合里存,而且要保证键的唯一性 常用方法 添加 Vput(K key, V value) voidputAll(Map<? ...

随机推荐

  1. 20181224《网络攻防技术》Exp 8 Web综合

    20181224<网络攻防技术>Exp 8 Web综合 目录 20181224<网络攻防技术>Exp 8 Web综合 相关知识点总结 web前端 web后端 数据库编程 实验内 ...

  2. bitmap_find_next_zero_area_off函数

    备注:

  3. idea安装阿里规范审查插件

    Install from repositories Settings >> Plugins >> Browse repositories... Search plugin by ...

  4. JavaScript argument

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. fetch请求方式

    Fetch请求的方式 1:GET 请求 // 未传参数 const getData = async () => { const res = await fetch('http://www.xxx ...

  6. java xml转为json的两种方法

    java xml转为json的两种方法 <?xml version="1.0" encoding="utf-8" ?><auibinsuran ...

  7. webpack之性能优化(webpack4)

    在讲解性能优化的方案之前,我们需要了解一下webpack的整个工作流程, 方案一:减少模块解析 也就是省略了构建chunk依赖模块的这几个步骤 如果没有loader对该模块进行处理,该模块的源码就是最 ...

  8. NDVI批量处理排除值-3000

    代码如下: import arcpy from arcpy import env from arcpy.sa import * import sys reload(sys) sys.setdefaul ...

  9. 需求分析之道——需求分析要做什么(C系架构设计法,sishuok)

    需求分析之道--需求分析要做什么. 需求分析是架构师开始做架构设计的第一步,对架构师来讲非常非常的重要.因为需求分析能够告诉我们,到底我们要做什么,架构设计就是为了去完成这件事情而做的. 接下来,我们 ...

  10. IDEA中使用JDBC连接MySQL数据库报错:No appropriate protocol (protocol is disabled or cipher suites are inappropriate) 的解决方法

    在IDEA中使用JDBC连接MySQL,程序运行之后报错: 定位到第16行: 根据上面报错提示,在url参数字段最前面添加参数  useSSL=false  : 再次运行程序,成功连接到数据库!