本篇笔记目录如下:

  1. select_related
  2. prefetch_related

在介绍 select_related 和 prefetch_related 这两个函数前,我们先来看一个例子。

对于,Entry 和 Blog 这两个 model,前面介绍过,Blog 是 Entry 的外键,如下:

class Blog(models.Model):name = models.CharField(max_length=100)tagline = models.TextField()class Entry(models.Model):blog = models.ForeignKey(Blog, on_delete=models.CASCADE)headline = models.CharField(max_length=255)body_text = models.TextField()pub_date = models.DateField()mod_date = models.DateField()authors = models.ManyToManyField(Author)number_of_comments = models.IntegerField()number_of_pingbacks = models.IntegerField()rating = models.IntegerField()

比如我们需要获取 Entry 的前十条数据,然后打印出关联的 Blog 的 name 字段信息。

我们一般会如此操作:

for entry in Entry.objects.all()[:10]if entry.blog:print(entry.blog.name)else:print("没有关联 blog 数据")

但是这样会有一个问题,那就是,这个 for 循环的操作会查询数据十一次,一次查询 Entry 数据,十次是查询每个 entry_obj 关联的 blog 数据。

这个设计对于系统来说是不合理的,想一想如果我们查询的数据是一千条,一万条,无论是系统接口的等待时间,还是数据库的访问压力,都是不可接受的。

因此我们可以引入 外键 和 ManyToManyTo 的一种能够减少数据库的访问次数的方式:select_related,prefetch_related。

1、select_related

当我们在使用的时候,如果有需要获取的外键数据,比如 Entry 关联的 Blog 数据,则可以将其字段名作为参数传入,这样在获取数据的时候就可以一次性将所有关联的 Blog 数据也取出来,而不用单独再去查询一遍数据库。

如下,批量操作

for entry in Entry.objects.select_related("blog").all():print(e.blog)  # 这个操作不会额外再去查询数据库

当然也适用于单条数据

e = Entry.objects.get(id=5).select_related("blog")

为了验证 select_related() 确实会只查询一遍数据库,有两种方法:
一种是在数据库层面打印出来所有查询的 SQL语句,
另一种可以从侧面表示,那就是在系统层面打印出我们的查询条件转化的 SQL 语句。

比如:

Entry.objects.select_related("blog").all().query.__str__()

可以看到会输出一个 关联了 Blog 表的 inner join 的 SQL 语句。

SELECT `blog_entry`.`id`, `blog_entry`.`blog_id`, `blog_entry`.`headline`, `blog_entry`.`body_text`, `blog_entry`.`pub_date`, `blog_entry`.`mod_date`, `blog_entry`.`number_of_comments`, `blog_entry`.`number_of_pingbacks`, `blog_entry`.`rating`, `blog_blog`.`id`, `blog_blog`.`name`, `blog_blog`.`tagline` FROM `blog_entry` INNER JOIN `blog_blog` ON (`blog_entry`.`blog_id` = `blog_blog`.`id`)

链式获取外键数据

比如下面的 model:

class City(models.Model):passclass Person(models.Model):hometown = models.ForeignKey(City, on_delete=models.SET_NULL, blank=True, null=True)class Book(models.Model):author = models.ForeignKey(Person, on_delete=models.CASCADE)

我们可以通过以下语句来将 Book 关联的 Person,以及该条 Person 数据关联的 City 数据一起查询出来:

book = Book.objects.select_related("author__hometown").get(id=4)
person = book.author
city = person.hometown

因为我们在第一步查询的时候,通过双下划线将两个外键字段连接在一起取了出来,所以在第二步和第三步取 Person 数据和 City 数据的时候,就不需要再次查询数据库了。

同时获取多个外键关联字段

如果一个 model 有两个外键字段 foo 和 bar,那么下面的两种写法都将这两个外键字段关联取出:

select_related("foo", "bar")
select_related("foo").select_related("bar")

需要注意的是,这个链式的操作和 order_by() 的结果是不一样的哦,前面提到的 order_by() 的链式操作会导致后面的覆盖前面的,但是取外键数据的时候会同时取出。

注意: select_related() 仅作用于 ForeignKey 和 OneToOne,如果是 ManyToMany 字段,则需要用到下面的 prefetch_related() 函数。

2、prefetch_related()

prefetch_related() 和 select_related() 作用类似,都是通过减少查询的次数,来实现查询优化。

但 prefetch_related() 是针对 ManyToMany 的操作。

举个例子:

from django.db import modelsclass Topping(models.Model):name = models.CharField(max_length=30)class Pizza(models.Model):name = models.CharField(max_length=50)toppings = models.ManyToManyField(Topping)def __str__(self):return "%s (%s)" % (self.name,", ".join(topping.name for topping in self.toppings.all()),)

当我们执行:

Pizza.objects.all()

的时候,因为每一条 Pizza 数据实例化的时候,都会调用 str() 函数,而这个函数会再次去请求一遍数据库,所以多条 Pizza 数据会导致查询多次数据库。

因为我们可以使用 prefetch_related() 函数来达到减少查询的目的:

Pizza.objects.prefetch_related('toppings').all()

这样的话,对数据库的查询会减少到两次,一次是查询出所有的 Pizza 数据,一次是根据所有的 pizza_id 找到所有关联的 topping 数据。

如果有兴趣,可以比对下面两条语句在 shell 中执行的时候,MySQL 服务器接收到的 SQL 查询语句:

Pizza.objects.all()Pizza.objects.prefetch_related('toppings').all()

下面一种情况需要注意哦:

pizzas = Pizza.objects.prefetch_related('toppings')
[list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]

因为第二步操作里,会对 toppings 数据进行一次新的 filter 过滤操作,所以会导致每次该语句重新去查询数据库,也就是说,我们的 prefetch_related() 操作是失效的。

以上就是本篇笔记全部内容,接下来会介绍查询里的 defer 和 only 函数。

本文首发于本人微信公众号,可搜索关注:Django笔记。
如果想获取更多相关文章,可扫码关注阅读:

Django笔记十一之外键查询优化select_related和prefetch_related相关推荐

  1. Django笔记六之外键ForeignKey介绍

    这一篇笔记介绍 Django 系统 model 的外键处理,ForeignKey 以及相应的处理方法. 这是一种一对多的字段类型,表示两张表之间的关联关系. 本篇笔记的目录如下: on_delete ...

  2. Django ORM查询之外键、关系的反向引用

    关系本身就是相互的,只用在一个表中记录,而不是在有关系的两个表中都记录.所以外键.关系提供反向引用机制.当然,外键可以是多个表的外键,关系也可以与多个表有关系,所以反向引用必须显式指出关系对方表(然后 ...

  3. Django笔记二十八之数据库查询优化汇总

    这一篇笔记将从以下几个方面来介绍 Django 在查询过程中的一些优化操作,有一些是介绍如何获取 Django 查询转化的 sql 语句,有一些是理解 QuerySet 是如何获取数据的. 以下是本篇 ...

  4. Django笔记总结

    1.web框架的本质 web通信流程 web我们这里指的就是通过浏览器去访问服务端,请求页面或者数据的通信方式,属于B/S架构.就是我们常见的网站.浏览器与服务端的通信流程:浏览器客户端发送一个请求信 ...

  5. Django笔记七之ManyToMany和OneToOne介绍

    ManyToMany 是一种多对多的关系,在用途和使用方法上和外键 ForeignKey 类似. 以下是本篇笔记的目录: ManyToMany 的介绍 through 参数 through_field ...

  6. Django笔记十二之defer和only

    本篇笔记将介绍查询中的 defer 和 only 两个函数的用法,笔记目录如下: defer only 1.defer defer 的英语单词的意思是 延迟.推迟,我们可以通过将字段作为参数传入,可以 ...

  7. Django 笔记4 -- 模板

    Django 笔记4 – 模板 Django 系列笔记是笔者学习.实践使用 Django 的相关笔记,大量参考了知了课堂的<Django零基础到项目实战>教程. 参考文档: Django官 ...

  8. 家族关系查询系统程序设计算法思路_【学习笔记】数据库基础 - 查询优化

    目录 什么是数据库查询优化? 影响查询优化的因素 优化策略概述 查询优化的总体思路 语义优化 -- 内容等价性 语法优化(逻辑层优化)---语法等价性 执行优化(物理层优化) 查询优化在DBMS中的位 ...

  9. Django笔记-5-视图

    2019独角兽企业重金招聘Python工程师标准>>> Django笔记-5-视图 概述 作用 视图接受web请求, 并相应web请求; 本质 视图就是一个Python中的函数 相应 ...

  10. Django笔记:ORM模型

    Django中操作数据库的方式有两种,一种是使用ORM模型,另一种是直接执行SQL,推荐使用ORM模型的方式来管理数据库,因为当需要执行的数据库操作过多时,第二种方式产生的SQL会非常难于管理和维护, ...

最新文章

  1. linux内核 RCU机制概述
  2. php codeigniter ext,php – 私有服务器上CodeIgniter不正确的系统路径
  3. 程序员面试金典 - 面试题 16.26. 计算器(栈)
  4. gridview使用经验
  5. 关于 JQuery 的克隆
  6. Python 扩展知识:编程习惯
  7. eclipse修改jsp不生效_JSP+Servlet+JDBC+mysql实现的个人日记本系统
  8. 对天敏电视精灵I老版播放器的修改和分析
  9. Dialog去掉默认白色背景
  10. 聚合数据API用法简介
  11. VM虚拟机下如何和Windows主机共享文件夹
  12. rap格式鸿蒙,你,想要成为rap star吗?
  13. PDF文件拆分合并器PDF Merge PDF Splitter + Mac
  14. adb 通过 无线连接手机
  15. 获得淘宝app商品详情原数据API
  16. laravel中seed
  17. stm32 spi nss硬件模式配置参考程序
  18. 整车控制器软件功能检测工装
  19. Unkown host ‘raw.githubusercontent.com‘以及超时问题
  20. 2021-04-08 使用Eclipse进行Web前端开发

热门文章

  1. 日常办公,无特殊要求者怎么选择显示器?
  2. 计算机基础知识面试题集合(包含计网OSI、TCP/IP、HTTP、TCP、UDP、三次握手、四次挥手、OS进程线程、死锁,常见数据结构及排序,Linux常用命令、数据库基础等。)
  3. Android手机玩8086汇编
  4. 产品经理的素质能力模型
  5. 地坛——我的最爱 (2006-11-12 09:33:18)
  6. fiddler手机抓包问题详解
  7. 计算机网络和internet选项,详细教你电脑ie的internet选项在哪
  8. 80004005错误代码_0x80004005,详细教您解决0x80004005错误代码的方法
  9. 转载:技术大停滞——范式春梦中的地球工业文明:前言
  10. 大物实验数据处理——用C求标准误差、标准偏差、标准偏差、相对误差