优化ORM代码
在配置了日志或Django-Debug-Toolbar之后 , 我们可以查看一下之前将老师数据导出成Excel报表的视图函数执行情况 , 这里我们关注的是ORM框架生成的SQL查询到底是什么样子的 , 相信这里的结果会让你感到有一些意外 。执行Teacher.objects.all()之后我们可以注意到 , 在控制台看到的或者通过Django-Debug-Toolbar输出的SQL是下面这样的:
SELECT `tb_teacher`.`no`, `tb_teacher`.`name`, `tb_teacher`.`detail`, `tb_teacher`.`photo`, `tb_teacher`.`good_count`, `tb_teacher`.`bad_count`, `tb_teacher`.`sno` FROM `tb_teacher`; args=()
SELECT `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERE `tb_subject`.`no` = 101; args=(101,)
SELECT `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERE `tb_subject`.`no` = 101; args=(101,)
SELECT `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERE `tb_subject`.`no` = 101; args=(101,)
SELECT `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERE `tb_subject`.`no` = 101; args=(101,)
SELECT `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERE `tb_subject`.`no` = 103; args=(103,)
SELECT `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERE `tb_subject`.`no` = 103; args=(103,)
这里的问题通常被称为“1+N查询”(或“N+1查询”) , 原本获取老师的数据只需要一条SQL , 但是由于老师关联了学科 , 当我们查询到N条老师的数据时 , Django的ORM框架又向数据库发出了N条SQL去查询老师所属学科的信息 。每条SQL执行都会有较大的开销而且会给数据库服务器带来压力 , 如果能够在一条SQL中完成老师和学科的查询肯定是更好的做法 , 这一点也很容易做到 , 相信大家已经想到怎么做了 。是的 , 我们可以使用连接查询 , 但是在使用Django的ORM框架时如何做到这一点呢?对于多对一关联(如投票应用中的老师和学科) , 我们可以使用QuerySet的用select_related()方法来加载关联对象;而对于多对多关联(如电商网站中的订单和商品) , 我们可以使用prefetch_related()方法来加载关联对象 。
在导出老师Excel报表的视图函数中 , 我们可以按照下面的方式优化代码 。
queryset = Teacher.objects.all().select_related( subject )
事实上 , 用ECharts生成前端报表的视图函数中 , 查询老师好评和差评数据的操作也能够优化 , 因为在这个例子中 , 我们只需要获取老师的姓名、好评数和差评数这三项数据 , 但是在默认的情况生成的SQL会查询老师表的所有字段 。可以用QuerySet的only()方法来指定需要查询的属性 , 也可以用QuerySet的defer()方法来指定暂时不需要查询的属性 , 这样生成的SQL会通过投影操作来指定需要查询的列 , 从而改善查询性能 , 代码如下所示:
queryset = Teacher.objects.all().only( name ,good_count ,bad_count )
当然 , 如果要统计出每个学科的老师好评和差评的平均数 , 利用Django的ORM框架也能够做到 , 代码如下所示:
queryset = Teacher.objects.values( subject ).annotate(
good=Avg( good_count ), bad=Avg( bad_count ))
这里获得的QuerySet中的元素是字典对象 , 每个字典中有三组键值对 , 分别是代表学科编号的subject、代表好评数的good和代表差评数的bad 。如果想要获得学科的名称而不是编号 , 可以按照如下所示的方式调整代码:
推荐阅读
- 罗坑船底顶经典罗新攻略 船底顶攻略
- 3个看似简单的Python问题是什么
- Color OS12已公布升级计划,相比于前一代带来了重大革新
- 5 个IDEA 必备插件是什么
- 茅台酒价为何突然全线暴跌?
- 千元游戏机皇OPPO K9s开售:骁龙 778G、侧边指纹
- 什么是PHP数组遍历
- 有哪些写Python程序的建议
- 如何提高新上线网站的排名?——蔡江博客。
