Django小技巧03: 优化数据库查询

翻译整理自: simpleisbetterthancomplex.com

本文介绍一个非常简单的技巧, 能够帮助你在使用 Django ORM 时优化数据库查询.

需要注意的是, Django QuerySets 是惰性查询的, 如果使用得当非常适用。

例如, 我们有一个叫做Invoice模型,并执行以下代码:

Python

invoices = Invoice.objects.all()
unpaid_invoices = invoices.filter(status='UNPAID')

此时, Django ORM 还没有触及到数据库,也就是说没有执行操作。当我们调用这个 queryset(unpaid_invoices) 才会真正的执行到数据库查询。通常情况下, 当我们去遍历这个 Queryset 就会发生这种情况, 即 queryset 开始执行。如下面代码所示:

Django/Jinja

<table>
  <tbody>
  {% for invoice in unpaid_invoices %}
    <tr>
      <td>{{ invoice.id }}</td>
      <td>{{ invoice.description }}</td>
      <td>{{ invoice.status }}</td>
    </tr>
  {% endfor %}
  </tbody>
</table>

上面的代码, 看起来没有什么问题。只会执行一个数据库查询。 但是当您的模型有关系数据字段时, 比如ForeignKey, OneToOneFieldManyToManyField. 上面的查询就会发生变化了。

假设Invoice模型有一个vendor字段是个ForeignKey:

Python

class Invoice(models.Model):
    description = models.CharField(max_length=255)
    status = models.CharField(max_length=10)
    vendor = models.ForeignKey(Vendor)

现在和上面的模板中一样去迭代这个 queyset, 但这次显示了供应商名称,Django ORM将对unpaid_invoices数据集每一条记录执行一次额外的查询.

Django/Jinja

<table>
  <tbody>
  {% for invoice in unpaid_invoices %}
    <tr>
      <td>{{ invoice.id }}</td>
      <td>{{ invoice.description }}</td>
      <td>{{ invoice.status }}</td>
      <td>{{ invoice.vendor.name }}</td>
    </tr>
  {% endfor %}
  </tbody>
</table>

如果unpaid_invoices数据集有100条记录, 那么将会有101条查询生成。检索invoices所有对象的一条查询, 和每个invoice供应商的一次查询, 共计101条。

当然, 可以使用select_related方法, 来减轻这种不期望的影响,以便在单次数据查询中,检索所有必要的信息。

所以,不要像上面那样过滤未付款的发票,可以这样做:

Python

invoices = Invoice.objects.all()
unpaid_invoices = invoices.select_related('vendor').filter(status='UNPAID')

这样, Django ORM 将会在同一查询中为每个发票检索供应商数据.因此这种情况不需要额外的查询,这样可以为您的应用程序出色的性能提升。

推荐一个可以跟踪数据库查询的调试工具Django Debug Toolbar

阅读更多关于Django QuerySet API的文档. Django Documentation

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android源码框架分析

Android模拟器识别技术

Android模拟器常常被用来刷单,如何准确的识别模拟器成为App开发中的一个重要模块,目前也有专门的公司提供相应的SDK供开发者识别模拟器。 目前流行的And...

50540
来自专栏吉浦迅科技

新手,想用Nisight调试CUDA代码,但断点无效怎么破?

新手,刚接触CUDA编程,搭好了环境,想用nsight来调试,在vs里面,在核函数里面设置了断点,用CUDA Debugging,但断点就是不生效,电脑左下角会...

52050
来自专栏FreeBuf

无文件Powershell恶意程序使用DNS作为隐蔽信道

思科Talos安全团队最近发现一款Powershell恶意程序,用DNS进行双向通信。 前言 DNS是企业网络中最常用的Internet应用层协议。DNS提供域...

31090
来自专栏Python、Flask、Django

关于Git克隆项目时密码输入错误导致一系列坑的解决方法

23540
来自专栏FreeBuf

「无文件」攻击方式渗透实验

前几天看了一个文章《全球上百家银行和金融机构感染了一种“无文件”恶意程序,几乎无法检测》,觉得powershell很是神奇,自己希望亲手实验一下,以最大程度还原...

48880
来自专栏kevin-blog

学会如何让你在网络上变的相对匿名

因为最近在弄渗透测试,就担心那天被查水表了,就想起怎么让自己的变的相对匿名呢,这时我想到了tor,“Tor(The Onion Router)是第二代洋葱...

39310
来自专栏小白课代表

Autodesk Revit 2019安装教程

Revit是Autodesk公司一套系列软件的名称。Revit系列软件是专为建筑信息模型(BIM)构建的,可帮助建筑设计师设计、建造和维护质量更好、能效更高的建...

24130
来自专栏FreeBuf

CVE-2017-8759完美复现(另附加hta+powershell弹框闪烁解决方案)

CVE-2017-8759 是前几天出的 0 DAY ,搜了下,国内几乎没有人复现,这个洞总体来说,危害很大,而且比CVE-2017-0199 更难防御。 漏...

317100
来自专栏Jerry的SAP技术分享

如何查找S4 Fiori UI上某个字段对应的后台存储表的名称

如果是SAPGUI里的事务码,比如MM01,对于开发者来说这个任务非常容易完成。

22760
来自专栏晨星先生的自留地

实战系列之你真的会mysql注入么?

22150

扫码关注云+社区

领取腾讯云代金券