乐趣区

关于django:Django笔记二十三之条件表达式搜索更新等操作

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

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

以下是本篇笔记的目录:

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

1、model 和数据筹备

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

以下是 Client 的 model 定义:

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

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

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

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 函数。

咱们先来说一下需要,咱们在获取 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 字段的月份区间来失去一个新的字段:

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

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

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,咱们想实现这样的搜寻条件:

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

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

对于这个需要,在之前咱们怎么做?
应用 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 的字段,他的值被有 A, B, C 三种或者还有其余的值,但其余的值咱们不关怀

当初须要将数据依照 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)
    )
)

5、条件聚合

咱们当初须要对数据依据条件进行聚合操作,比方 Client 这个 model,咱们对其依照 account_type 分组,获取各自的总数。

代码如下:

from django.db.models import Count, Q

Client.objects.aggregate(regular=Count('pk', filter=(Q(account_type=Client.REGULAR))),
    gold=Count('pk', filter=Q(account_type=Client.GOLD)),
    platinum=Count('pk', filter=Q(account_type=Client.PLATINUM)),
)

返回的后果为:

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

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

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

咱们也能够依据另一种形式来获取各自的总数数据,然而返回的构造是不一样的:

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

返回的后果模式为:

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

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

本文首发于自己微信公众号:Django 笔记。

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

如果想获取更多相干文章,可扫码关注浏览:

退出移动版