Django中select_related查询优化的具体实现

 更新时间:2025年12月24日 10:25:06   作者:言之。  
本文介绍了Django ORM中的N+1查询问题及其解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

目标

  • 理解 N+1 查询问题及其性能影响
  • 掌握 select_related 的作用和使用方法
  • 能够分析 Django ORM 生成的 SQL 语句
  • 在实际开发中正确应用查询优化技巧

一、问题引入:N+1 查询问题

1.1 数据模型定义

# models.py
class Author(models.Model):
    name = models.CharField(max_length=100)
    country = models.CharField(max_length=50)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    publish_date = models.DateField()

1.2 存在性能问题的代码

# views.py (存在N+1查询问题)
books = Book.objects.all()
for book in books:
    print(f"书名:{book.title}, 作者:{book.author.name}")

1.3 对应的 SQL 查询

-- 第一次查询:获取所有书籍
SELECT 
    "library_book"."id",
    "library_book"."title", 
    "library_book"."author_id",
    "library_book"."publish_date"
FROM "library_book";

-- 后续 N 次查询:为每本书单独查询作者
SELECT "library_author"."id", "library_author"."name", "library_author"."country"
FROM "library_author" WHERE "library_author"."id" = 1;

SELECT "library_author"."id", "library_author"."name", "library_author"."country"
FROM "library_author" WHERE "library_author"."id" = 2;

-- ... 依此类推,产生 N+1 次查询

问题分析:如果有 100 本书,将产生 101 次数据库查询,严重影响性能。

二、解决方案:select_related

2.1select_related的基本用法

# views.py (优化后的代码)
books = Book.objects.select_related('author').all()
for book in books:
    print(f"书名:{book.title}, 作者:{book.author.name}")

2.2 优化后的 SQL

SELECT 
    "library_book"."id",
    "library_book"."title",
    "library_book"."author_id", 
    "library_book"."publish_date",
    -- 通过 JOIN 一次性获取作者信息
    "library_author"."id",
    "library_author"."name",
    "library_author"."country"
FROM "library_book"
INNER JOIN "library_author" 
    ON ("library_book"."author_id" = "library_author"."id");

优化效果:无论有多少本书,都只执行 1 次 数据库查询。

三、select_related的深入使用

3.1 深度关联查询

扩展数据模型:

class Country(models.Model):
    name = models.CharField(max_length=50)
    code = models.CharField(max_length=3)

class Author(models.Model):
    name = models.CharField(max_length=100)
    country = models.ForeignKey(Country, on_delete=models.CASCADE)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    publish_date = models.DateField()

深度关联查询:

# 一次性获取 Book -> Author -> Country 的所有数据
books = Book.objects.select_related('author__country').all()
for book in books:
    print(f"{book.title} - {book.author.name} - {book.author.country.name}")

对应的 SQL:

SELECT 
    "library_book"."id",
    "library_book"."title",
    "library_book"."author_id",
    "library_book"."publish_date",
    -- 作者表字段
    "library_author"."id",
    "library_author"."name", 
    "library_author"."country_id",
    -- 国家表字段(通过第二次 JOIN)
    "library_country"."id",
    "library_country"."name",
    "library_country"."code"
FROM "library_book"
INNER JOIN "library_author" 
    ON ("library_book"."author_id" = "library_author"."id")
INNER JOIN "library_country" 
    ON ("library_author"."country_id" = "library_country"."id");

四、实践技巧与注意事项

4.1 查看生成的 SQL 语句

方法1:使用 query 属性

books = Book.objects.select_related('author')
print(books.query)

方法2:配置 Django SQL 日志
settings.py 中添加:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },
}

4.2 使用原则

  1. 适用场景

    • 一对一关系(OneToOneField)
    • 多对一关系(ForeignKey)
    • 需要立即访问关联对象字段时
  2. 不适用场景

    • 多对多关系(ManyToManyField)
    • 反向的多对一关系
    • 对于这些情况,应该使用 prefetch_related
  3. 注意事项

    • 不要过度使用深度关联
    • 考虑查询返回的数据量
    • 只在确实需要访问关联对象时使用

五、总结对比

5.1 性能对比表

查询方式查询次数适用场景SQL 类型
普通查询N+1 次不确定是否需要关联对象简单查询 + 多次关联查询
select_related1 次确定需要访问关联对象的所有字段JOIN 查询

5.2 核心要点

  1. 解决问题:N+1 查询问题
  2. 实现原理:使用 SQL JOIN 语句一次性加载关联数据
  3. 使用语法Model.objects.select_related('foreign_key_field')
  4. 深度关联:使用双下划线 'field__related_field'
  5. 记忆口诀:“往前拿,用 select_related

5.3 最佳实践

# 好的实践:明确知道需要作者信息
books = Book.objects.select_related('author').filter(publish_date__year=2023)
for book in books:
    # 这里会频繁访问 author 的字段
    display_info = f"{book.title} by {book.author.name}"

# 不好的实践:不确定是否需要关联对象
books = Book.objects.select_related('author').filter(publish_date__year=2023)
for book in books:
    # 如果这里根本不访问 book.author,就浪费了 JOIN 的性能
    print(book.title)

到此这篇关于Django中select_related查询优化的具体实现的文章就介绍到这了,更多相关Django select_related查询优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python实现简单flappy bird

    python实现简单flappy bird

    这篇文章主要为大家详细介绍了python实现简单flappy bird小游戏,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • 深入理解Python内置函数eval的使用

    深入理解Python内置函数eval的使用

    在Python中,eval函数是一个内置函数,用于将字符串解析并执行为Python表达式,本文将详细介绍eval函数的使用方法和注意事项,需要的可以参考一下
    2023-06-06
  • Python Sweetviz轻松实现探索性数据分析

    Python Sweetviz轻松实现探索性数据分析

    Sweetviz是一个开放源代码Python库,可生成精美的高密度可视化文件,以单行代码启动EDA(探索性数据分析)。输出是一个完全独立的HTML应用程序,该系统围绕快速可视化目标值和比较数据集而构建。其目标是帮助快速分析目标特征,训练与测试数据以及其他此类数据表征任务
    2021-11-11
  • 详解Django中类视图使用装饰器的方式

    详解Django中类视图使用装饰器的方式

    这篇文章主要介绍了详解Django中类视图使用装饰器的方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • Tensorflow中的dropout的使用方法

    Tensorflow中的dropout的使用方法

    这篇文章主要介绍了Tensorflow中的dropout的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Python3时间转换之时间戳转换为指定格式的日期方法详解

    Python3时间转换之时间戳转换为指定格式的日期方法详解

    这篇文章主要介绍了Python3时间转换之时间戳转换为指定格式的日期,需要的朋友可以参考下
    2021-04-04
  • Python自然语言处理 NLTK 库用法入门教程【经典】

    Python自然语言处理 NLTK 库用法入门教程【经典】

    这篇文章主要介绍了Python自然语言处理 NLTK 库用法,结合实例形式详细分析了NLTK库的功能、安装、引用以及使用NLTK库进行文本分析的各种常用操作技巧,需要的朋友可以参考下
    2018-06-06
  • 详解python 发送邮件实例代码

    详解python 发送邮件实例代码

    本篇文章主要介绍了python 发送邮件实例代码,详细的介绍了各种方式发送邮件,包括文件形式的邮件、HTML形式的邮件、带图片的HTML邮件等,有兴趣的可以了解一下。
    2016-12-12
  • python matplotlib坐标轴设置的方法

    python matplotlib坐标轴设置的方法

    本篇文章主要介绍了python matplotlib坐标轴设置的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • 使用Python转换Markdown文件为Word文档

    使用Python转换Markdown文件为Word文档

    Markdown格式的文章转换成Word文档能够帮助作者制作更复杂的文档,同时确保内容的一致性和美观性,本文将使用Python实现Markdown转Word,感兴趣的可以了解下
    2024-11-11

最新评论