Django中select_related和prefetch_related的用法与区别详解

2023-11-21 08:59

本文主要是介绍Django中select_related和prefetch_related的用法与区别详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

0. 本文借助django-debug-toolbar来展现效果

django-debug-toolbar的安装

1. 介绍

select_related:

将会根据外键关系(注意: 仅限单对单和单对多关系),在执行查询语句的时候通过创建一条包含SQL inner join操作的SELECT语句来一次性获得主对象及相关对象的信息

prefetch_related

对于多对多字段,你不能使用select_related方法,这样做是为了避免对多对多字段执行JOIN操作从而造成最后的表非常大。

Django提供了prefect_related方法来解决这个问题。

prefect_related可用于多对多关系字段,也可用于反向外键关系(related_name)。

相同点:

都作用于queryset对象上面

注意点:

  • 对与单对单或单对多外键ForeignKey字段,使用select_related方法
  • 对于多对多字段和反向外键关系,使用prefetch_related方法
  • 两种方法均支持双下划线指定需要查询的关联对象的字段名
  • 使用Prefetch方法可以给prefetch_related方法额外添加额外条件和属性。

2. 使用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

from django.db import models

  

class UserInfo(models.Model):

    username = models.CharField(verbose_name='用户名', max_length=225)

  

    def __str__(self):

        return self.username

  

class Tag(models.Model):

    name = models.CharField(verbose_name='标签名称', max_length=225)

  

    def __str__(self):

        return self.name

  

class Article(models.Model):

    title = models.CharField(verbose_name='标题', max_length=225)

    content = models.CharField(verbose_name='内容', max_length=225)

    # 外键

    username = models.ForeignKey(verbose_name='用户', to='UserInfo', on_delete=models.DO_NOTHING)

    tag = models.ManyToManyField(verbose_name='标签', to='Tag')

  

    def __str__(self):

        return self.title

2.1 原生的查询

2.1.1 代码

1

2

3

4

5

def article_list(request):

    if request.method == 'GET':

        # select_related---->queryset

        article_queryset = models.Article.objects.all()

        return render(request, 't2.html', context={'article_queryset': article_queryset})

2.1.2 图示

2.1.3 查询解释 

1.从图示我们可以看出来,一共进行13次查询,且有10次重复的!!!

原因是:当我们第一次查询时,返回的值,只有文章对象,对于标签以及用户,并没有查询,当前端界面需要这两个时,每循环一次,就会去数据库查询一次

2.为了避免重复查询,django提供select_related和prefetch_related方法来提升数据库查询效率,类似于SQL的JOIN方法。

3.效果就是当第一次查询时,进行连表,一次性把所有数据全部查询到

2.2 使用select_related

2.2.2 代码

1

2

3

4

5

6

7

8

9

10

from django.shortcuts import render

  

from blog import models

  

  

def article_list(request):

    if request.method == 'GET':

        # select_related---->queryset

        article_queryset = models.Article.objects.all().select_related('tag', 'username')

        return render(request, 't2.html', context={'article_queryset': article_queryset})

2.2.3 图示

 2.2.4 解释

可以看到现在只有三次查询,耗时大大减少

 2.2.5 其他常用用法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

# 获取id=1的文章对象同时,获取其相关username信息

Article.objects.select_related('username').get(id=1)

  

# 获取id=1的文章对象同时,获取其相关作者名字信息

Article.objects.select_related('username__username').get(id=1)

  

# 获取id=1的文章对象同时,获取其相关tag和相关作者名字信息。下面方法等同。

# 方式一:

Article.objects.select_related('tag', 'username__username').get(id=1)

# 方式二:

Article.objects.select_related('tag').select_related('username__username').get(id=1)

  

# 使用select_related()可返回所有相关主键信息。all()非必需。

Article.objects.all().select_related()

  

# 获取Article信息同时获取username信息。filter方法和selected_related方法顺序不重要。

# 方式一:

Article.objects.filter(tag__gt=3).select_related('username')

# 方式二:

Article.objects.select_related('username').filter(tag__gt=3)

2.3. 使用prefetch_related方法

对于多对多字段,你不能使用select_related方法,这样做是为了避免对多对多字段执行JOIN操作从而造成最后的表非常大。

2.3.1 常用的案例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

articles = Article.objects.all().select_related('category').prefecth_related('tags')

  

# 文章列表及每篇文章的tags对象名字信息

Article.objects.all().prefetch_related('tags__name')

  

# 获取id=13的文章对象同时,获取其相关tags信息

Article.objects.prefetch_related('tags').get(id=13)

  

# 获取文章列表及每篇文章相关的名字以P开头的tags对象信息

Article.objects.all().prefetch_related(

    Prefetch('tags', queryset=Tag.objects.filter(name__startswith="P"))

)

  

# 文章列表及每篇文章的名字以P开头的tags对象信息, 放在article_p_tag列表

Article.objects.all().prefetch_related(

    Prefetch('tags', queryset=Tag.objects.filter(name__startswith="P")),

to_attr='article_p_tag'

这篇关于Django中select_related和prefetch_related的用法与区别详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/401300

相关文章

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

Django开发时如何避免频繁发送短信验证码(python图文代码)

《Django开发时如何避免频繁发送短信验证码(python图文代码)》Django开发时,为防止频繁发送验证码,后端需用Redis限制请求频率,结合管道技术提升效率,通过生产者消费者模式解耦业务逻辑... 目录避免频繁发送 验证码1. www.chinasem.cn避免频繁发送 验证码逻辑分析2. 避免频繁

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

C语言中%zu的用法解读

《C语言中%zu的用法解读》size_t是无符号整数类型,用于表示对象大小或内存操作结果,%zu是C99标准中专为size_t设计的printf占位符,避免因类型不匹配导致错误,使用%u或%d可能引发... 目录size_t 类型与 %zu 占位符%zu 的用途替代占位符的风险兼容性说明其他相关占位符验证示

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

Python标准库之数据压缩和存档的应用详解

《Python标准库之数据压缩和存档的应用详解》在数据处理与存储领域,压缩和存档是提升效率的关键技术,Python标准库提供了一套完整的工具链,下面小编就来和大家简单介绍一下吧... 目录一、核心模块架构与设计哲学二、关键模块深度解析1.tarfile:专业级归档工具2.zipfile:跨平台归档首选3.

全面解析Golang 中的 Gorilla CORS 中间件正确用法

《全面解析Golang中的GorillaCORS中间件正确用法》Golang中使用gorilla/mux路由器配合rs/cors中间件库可以优雅地解决这个问题,然而,很多人刚开始使用时会遇到配... 目录如何让 golang 中的 Gorilla CORS 中间件正确工作一、基础依赖二、错误用法(很多人一开

idea的终端(Terminal)cmd的命令换成linux的命令详解

《idea的终端(Terminal)cmd的命令换成linux的命令详解》本文介绍IDEA配置Git的步骤:安装Git、修改终端设置并重启IDEA,强调顺序,作为个人经验分享,希望提供参考并支持脚本之... 目录一编程、设置前二、前置条件三、android设置四、设置后总结一、php设置前二、前置条件

Java Stream流之GroupBy的用法及应用场景

《JavaStream流之GroupBy的用法及应用场景》本教程将详细介绍如何在Java中使用Stream流的groupby方法,包括基本用法和一些常见的实际应用场景,感兴趣的朋友一起看看吧... 目录Java Stream流之GroupBy的用法1. 前言2. 基础概念什么是 GroupBy?Stream

python中列表应用和扩展性实用详解

《python中列表应用和扩展性实用详解》文章介绍了Python列表的核心特性:有序数据集合,用[]定义,元素类型可不同,支持迭代、循环、切片,可执行增删改查、排序、推导式及嵌套操作,是常用的数据处理... 目录1、列表定义2、格式3、列表是可迭代对象4、列表的常见操作总结1、列表定义是处理一组有序项目的