一篇文章带你了解Django ORM操作(高端篇)
来源:Python爬虫与数据挖掘
作者:Python进阶者
前言
上次两篇基本学完的Django ORM各种操作,怎么查,各种查。感兴趣的小伙伴可以戳这两篇文章学习下,一篇文章带你了解Django ORM操作(进阶篇)、一篇文章带你了解Django ORM操作(基础篇)。
但是还是遗留了一些技能。,再来瞅瞅吧!
查询
聚合操作
聚合操作,不要被名字吓到了,通常用在 筛选完一些数据之后 ,求一下 平均值 了,什么的。
例如:求所有书的总价格和平均价格
原生sql
SELECT
SUM(price) AS "所有书总价格",
avg(price) AS "所有书平均价格"
web_book;
执行结果
ORM
price = models.Book.objects.all().aggregate(Sum("price"),Avg("price"), )
print(price)
执行结果
可以发现和上面是一样的,但是会发现 列名 是默认是字段__聚合函数名。
原生sql是可以指定显示的 列名 的,同样,ORM也可以。
代码
# 需要导入的包
from django.db.models import Avg,Sum
price = models.Book.objects.all().aggregate(所有书总价格=Sum("price"), 所有书平均价格=Avg("price"), )
print(price)
执行结果
注: price的类型直接就是dict,所以,在这是不能查看 原生sql 的。
但是上述ORM对应的原生SQL确实如上,所以那样理解就行了。
分组操作
分组操作,就是将某一列, 相同的值 进行 压缩 ,然后就可以得出 压缩值 的数量。
如果 压缩的是外键 ,还可以取出 外键的详细信息 。
示例: 查询出每个 出版社 出版的 数量 。
通过研究 表结构 发现,每出版的书,都在book表中记录,并且每本书会外键一个 出版社id 。
如果我们能对 出版社id 进行压缩,然后再求出 压缩出版社id 里面对应的数量。
啧啧,这不就出来了吗?
代码
from django.db.models import Count
ret = models.Book.objects.values("publish_id").annotate(publish_count=Count("publish_id"))
print(ret)
执行结果
原生sql
SELECT
`web_book`.`publish_id`,
COUNT(`web_book`.`publish_id`) AS `publish_count`
`web_book`
GROUP BY
`web_book`.`publish_id`;
ORM分组和原生SQL对应图
这一块,我记得当初我迷茫了一段时间,主要是不知道如何和原生SQL对应上,根据多次测试经验,对应图如下。
分组获取外键字段信息
上述确实可以通过分组实现了功能。
但是上述只能获取 出版社id ,并不能获取 出版社名啥 的,但是如何获取压缩外键字段详细信息呢?
代码
ret = models.Book.objects.values("publish_id").annotate(publish_count=Count("publish_id")).values("publish__title","publish__phone","publish_count")
print(ret)
执行结果
注: 分组(annotate)后面跟的values。
里面只能写 外键字段 的列和annotate里面的列,不能写其他。
如果分组分的不是 外键字段 ,那就不能再跟values!
分组再筛选
分组再筛选本质就是 原生sql 的group by .. having,将压缩完的数据在进行条件判断。
但是对压缩的数据进行判断只能通过having。
示例: 查询出版社出版的书大于2本的数据。
代码
ret = models.Book.objects.values("publish_id") \
.annotate(publish_count=Count("publish_id")) \
.filter(publish_count__gt=2)
print(ret)
执行结果
F查询
有时候,我们可能有这样的需求,就是 两个列 之间进行比较。
比如经典问题,一个商品,找到 收藏数 大于 销量 的商品等之类的两列进行比较的需求。
示例: 查询book表,评论数 小于 收藏数的数据。
代码
from django.db.models import F
book = models.Book.objects.filter(comment_num__lt=F("collect_num"))
print(book)
实际结果
执行结果
F对象还支持加减乘除后的比较
示例: 评论数小于 两倍收藏数 的数据。
代码
可是*,也可以是-,+,÷
from django.db.models import F
book = models.Book.objects.filter(comment_num__lt=F("collect_num")*2)
print(book)
执行结果
F对象还适用于更新
代码
models.Book.objects.all().update(price=F("price")+30)
Q查询
通常情况下,我们使用的filter(条件1,条件2,...),执行的都是and查询。
但是通常一些时候,我们需要执行or查询。
比如book表,查询title=<<大明帝国>> or title=<<安史之乱>>的。
这时候,如果使用Django ORM,就只能使用 Q查询 构建条件。
代码
from django.db.models import Q
books = models.Book.objects.filter(Q(title="<<大明帝国>>") | Q(title="<<安史之乱>>"))
print(books)
执行结果
注: |是or的意思,&是and的意思。
所以,如果将上述的|换成&,filter(条件1,条件2,...)一个意思,还是and。
Q查询之~
~相当于not。
示例 :查询title = "<<大明帝国>>" or title != "<<安史之乱>>"。
代码
from django.db.models import Q
books = models.Book.objects.filter(Q(title="<<大明帝国>>") | ~Q(title="<<安史之乱>>"))
print(books)
执行结果
Q查询和and混合查询
Q查询 和 and查询 同时出现, Q查询 必须在 其他查询之前 。
示例 :查询title = "<<大明帝国>>" or title != "<<安史之乱>>" 并且publish_id=1的。
代码
from django.db.models import Q
books = models.Book.objects.filter(Q(title="<<大明帝国>>") | ~Q(title="<<安史之乱>>"),publish_id=1)
print(books)
执行结果
动态构造Q查询
一些时候,我们可能并 不太确定有什么条件 。
可能是 动态传 的, 传过来多少 ,就 拼接多少 。
Q查询,就能做到这个,在做 动态Q查询 时,动态Q不仅 支持or ,还 支持and 。
示例 :查询 publish_id=1 或者 title模糊=大明 的书 。
代码
q = Q()
# 查询方式,or还是and
q.connector = "or" # or,and
# publish_id=1
q.children.append(("publish_id", "1"))