Django模型查詢與性能調優:告別N+1問題

文章目錄

    • 一、查詢基礎
      • QuerySet 詳解
      • 一對多關聯查詢
      • 多對多關聯查詢
    • 二、N+1查詢問題
      • 問題分析
      • 檢測方法
      • 解決方案
    • 三、高級查詢優化
      • values()
      • values_list()
      • values()和values_list()對比
      • Q() 對象復雜查詢
      • 查看生成的 SQL
    • 四、項目實戰
      • 場景
      • 實戰


一、查詢基礎

QuerySet 詳解

Django 中通過模型類的 Manager 構建 QuerySet 來檢索數據庫對象,其核心特性包括:

  • 代表數據庫中對象的集合
  • 可通過過濾器縮小查詢范圍
  • 具有惰性執行特性(僅在需要結果時才執行 SQL)

常用過濾器

  • all():返回所有對象
  • filter(**kwargs):返回滿足條件的對象
  • exclude(** kwargs):返回不滿足條件的對象
  • get(**kwargs):返回單個匹配對象(無匹配或多匹配會拋異常)
  • 切片
# 切片操作示例:返回前5個對象(LIMIT 5)
Book.objects.all()[:5]

一對多關聯查詢

假設一個作者可以寫多本書,但每本書只能屬于一個作者。

from django.db import modelsclass Author(models.Model):first_name = models.CharField(max_length=100)last_name = models.CharField(max_length=100)def __str__(self):return f"{self.first_name} {self.last_name}"class Book(models.Model):title = models.CharField(max_length=100)publication_date = models.DateField()# 外鍵關聯Author,級聯刪除,反向查詢名為booksauthor = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')def __str__(self):return self.title

正向查詢(通過外鍵屬性訪問)

b = Book.objects.get(id=2)
b.author  # 獲取關聯的Blog對象,查詢數據庫
b.author = some_body  # 設置關聯對象
b.save()  # 保存更改

使用 select_related() 預加載關聯對象,避免額外查詢

b = Book.objects.select_related().get(id=2)
print(b.author)  # 已預加載到緩存,使用緩存,不查詢數據庫

反向查詢(通過關聯管理器)

# 未定義related_name, 默認Manager名稱為:<模型名稱小寫>_set
a = Author.objects.get(id=1)
a.book_set.all()  # 返回所有關聯的Book# 定義了related_name='books'
a.books.all()  # 更直觀的訪問方式

關聯對象操作方法如下。所有 “反向” 操作對數據庫都是立刻生效,保存到數據庫。

  • add(obj1, obj2):添加關聯對象
  • create(**kwargs):創建并關聯新對象
  • remove(obj1, obj2):移除關聯對象
  • clear():清空所有關聯
  • set(objs):替換關聯集合
a = Author.objects.get(id=1)
a.books.set([b1, b2]) #  b1 和 b2 都是 Book 實例

多對多關聯查詢

假設一個作者可以寫多本書,一本書也可以有多個作者。

from django.db import modelsclass Author(models.Model):name = models.CharField(max_length=100)email = models.EmailField()def __str__(self):return self.nameclass Book(models.Model):title = models.CharField(max_length=200)publication_date = models.DateField()# 多對多關聯Authorauthors = models.ManyToManyField(Author, related_name='books')def __str__(self):return self.title

正向與反向查詢示例

# 正向查詢
b = Book.objects.get(id=3)
b.authors.all() # 獲取所有關聯的Author
b.authors.count()
b.authors.filter(name__contains="張三")# 反向查詢
a = Author.objects.get(id=5)
a.book_set.all()  # 獲取所有關聯的Book

多對多關聯中,add()、set() 和 remove() 可直接使用主鍵

a = Author.objects.get(id=5)
a.book_set.set([b1, b2])
# 等價于
a.book_set.set([b1.pk, b2.pk])

二、N+1查詢問題

問題分析

N+1 查詢是常見的性能問題,表現為主查詢后執行 N 次額外查詢。例如:

books = Book.objects.all()
for book in books:print(book.author.first_name)

以上代碼會產生 1 次查詢獲取所有 Book,加上 N 次查詢獲取對應的 Author(N 為 Book 數量),共 N+1 次查詢。

檢測方法

  • Django Debug Toolbar:直觀顯示請求中的 SQL 查詢
  • 日志記錄:配置日志記錄 SQL 語句
  • 性能分析工具:如 Django Silk 分析查詢性能

解決方案

方法 1:使用 select_related

適用于一對多(正向)和一對一關系,通過 SQL JOIN 預加載關聯對象

  • 語法:select_related('related_field')related_field 是模型中定義的 ForeignKeyOneToOneField 字段
books = Book.objects.select_related('author').all()
for book in books:print(book.author.first_name) # 無額外查詢 

可結合 only() 選擇需要的字段

books = Book.objects.select_related('author').only('title', 'author__name')

支持多級關聯

# 加載書籍、作者及作者家鄉信息
books = Book.objects.select_related('author__hometown').all()
for book in books:print(book.author.hometown.name)  # 無額外查詢

方法 2:使用 prefetch_related

適用于多對多和反向關系,通過批量查詢后在 Python 中關聯。適用場景:

  • 多對多關系(ManyToManyField)
  • 反向一對多關系
  • 反向一對一關系
books = Book.objects.prefetch_related('authors').all()
for book in books:print(book.authors.all())  # 無額外查詢

參考資料:Django 數據庫訪問優化

三、高級查詢優化

values()

返回字典形式的查詢集(返回一個 ValuesQuerySet 對象,其中每個元素是一個字典),適合提取特定字段

books = Book.objects.values('title', 'author')
for book in books:print(book) # 輸出示例
{'title': 'Book1', 'author': 'Author1'}
{'title': 'Book2', 'author': 'Author2'}

values_list()

返回元組形式的查詢集(返回一個 ValuesListQuerySet 對象,其中每個元素是一個元組),內存占用更低

books = Book.objects.values_list('title', 'author')
for book in books:print(book)### 輸出示例
('Book1', 'Author1')
('Book2', 'Author2')

使用 flat=True 獲取單一字段值列表。如果有多個字段時,傳入 flat 會報錯。

titles = Book.objects.values_list('title', flat=True)
# <QuerySet ['紅樓夢', '西游記', ...]>

使用 named=True ,結果返回 namedtuple()

books_info = Book.objects.values_list("id", "title", named=True)
# <QuerySet [Row(id=1, title='紅樓夢'), ...]>

values()和values_list()對比

對比維度values()values_list()
返回值類型返回一個包含字典的查詢集,字典的鍵為字段名,值為字段對應的數據返回一個包含元組的查詢集,元組中的元素依次對應指定字段的值
內存占用相對較高,因為字典需要存儲鍵值對信息通常更節省內存,元組是更輕量的數據結構,無需存儲字段名
使用場景適合需要通過字段名訪問字段值的場景,例如需要明確知道每個值對應的字段時適合僅需要獲取字段值的場景,例如只需批量獲取某個或某幾個字段的具體數據時

Q() 對象復雜查詢

Q() 對象用于構建復雜查詢條件,支持邏輯運算

  • &:邏輯與(AND)
  • |:邏輯或(OR)
  • ~:邏輯非(NOT)
from django.db.models import Q# 標題含Python或作者為John的書籍
books = Book.objects.filter(Q(title__icontains="Python") | Q(author="John")
)# 復雜組合條件
books = Book.objects.filter((Q(title__icontains="Python") | Q(title__icontains="Django")) &~Q(author="John")
)

查看生成的 SQL

調試時可查看 QuerySet 生成的 SQL

queryset = Book.objects.filter(author="John")
print(queryset.query)  # 輸出對應的SQL語句

四、項目實戰

場景

Django+Vue 后臺管理系統中,一般需要支持不同的數據權限

  • 僅本人數據權限
  • 本部門及以下數據權限
  • 本部門數據權限
  • 指定部門數據權限
  • 全部數據權限

在這里插入圖片描述

數據權限與功能權限(基于RBAC實現)的區別

  • 功能權限:控制 “能做什么”(如新增、刪除按鈕的顯示和執行)
  • 數據權限:控制 “能看到什么數據”(如銷售經理只能查看自己團隊的數據)

實戰

使用Q() 對象構建復雜查詢,實現靈活的數據權限計算

在這里插入圖片描述

點擊查看完整代碼


您正在閱讀的是《Django從入門到實戰》專欄!關注不迷路~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917321.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917321.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917321.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PyTorch 中 Tensor 統計學函數及相關概念

文章目錄PyTorch 中 Tensor 統計學函數及相關概念一、引言二、基礎統計學函數&#xff08;一&#xff09;torch.mean()——均值計算&#xff08;二&#xff09;torch.sum()——總和計算&#xff08;三&#xff09;torch.prod()——元素積計算&#xff08;四&#xff09;torch.m…

淺拷貝與深拷貝的區別

淺拷貝和深拷貝是兩種不同的對象復制方式&#xff0c;主要區別在于它們如何處理對象內部的引用類型字段。淺拷貝 (Shallow Copy)特點&#xff1a;只復制對象本身&#xff08;基本類型字段&#xff09;和對象中的引用&#xff08;地址&#xff09;不復制引用指向的實際對象原始對…

腳本統計MongoDB集合表數據量

腳本&#xff1a; #!/bin/bashipxxx.xx.xx.xx portxxxx dbxxxdb #user #passwmongo -host ${ip}:${port} <<EOF 2>/dev/null|grep -vE version|not match|session|compressors||Warning|delivers|upcoming|installation|https|switched|bye >collec use ${db}; sho…

圖漾AGV行業常用相機使用文檔

文章目錄1.圖漾相機設置IP1.1 前期準備2.FM851-E2相機2.1 FM851-E2適用場景2.2 FM851-E2 IO線和數據線定義2.2.1 IO接口定義2.2.2 數據接口線2.2.3 相機正面安裝方向2.2.4 相機IO指示燈2.3 FM851-E2/FM855-E2-7相機RGB顏色異常【解決措施1】&#xff1a;【解決措施2】&#xff…

電力系統分析學習筆記(二)- 標幺值計算與變壓器建模

電力系統分析學習筆記&#xff08;二&#xff09;- 標幺值計算與變壓器建模 1. 電力系統參數計算的基本原理 1.1 基本級的概念與選擇 基本級定義&#xff1a; 在多電壓等級的電力系統中&#xff0c;需要將所有參數歸算到同一個電壓等級這個統一的電壓等級稱為基本級 基本級選擇…

防火墻相關技術內容

防火墻的狀態檢測和會話技術一、防火墻的檢測機制早期包過濾防火墻采用逐包檢測機制&#xff0c;對每個報文獨立檢測其源地址、目的地址、端口等信息&#xff0c;根據預設規則決定轉發或丟棄。安全隱患&#xff1a;僅基于單包信息判斷&#xff0c;無法識別連接狀態。例如&#…

在 Mac 上用 Vagrant 安裝 K8s

文章目錄&#x1f4cb; 1. 環境準備1.1 系統要求1.2 軟件清單&#x1f680; 2. 安裝步驟2.1 安裝Parallels Desktop2.2 配置網絡代理&#xff08;可選&#xff09;2.3 安裝Homebrew2,4 準備項目目錄2.5 安裝Vagrant及插件2.6 配置Python環境2.6.1 安裝Python管理工具2.6.2 配置…

【AI學習】RadioDiff:代碼學習

之前學習了RadioDiff這篇論文&#xff0c;最近在復刻相關代碼。 這段代碼實現了一個基于潛在擴散模型&#xff08;Latent Diffusion Model, LDM&#xff09;的訓練框架。借助DeepSeek總體學習一下&#xff1a; 1. 整體結構 代碼主要分為以下幾個部分&#xff1a; 參數解析和…

【專題十七】多源 BFS

&#x1f4dd;前言說明&#xff1a; 本專欄主要記錄本人的基礎算法學習以及LeetCode刷題記錄&#xff0c;按專題劃分每題主要記錄&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代碼&#xff1b;&#xff08;2&#xff09;優質解法 優質代碼&#xff1b;&#xff…

京東零售在智能供應鏈領域的前沿探索與技術實踐

近日&#xff0c;“智匯運河 智算未來”2025人工智能創新創業大會在杭州召開。香港工程科學院院士、香港大學副校長、研究生院院長、講座教授、京東零售供應鏈首席科學家申作軍教授與供應鏈算法團隊技術總監戚永志博士受邀出席并擔任《AI智慧物流與供應鏈分享會》聯席主席&…

MyBatisPlus之CRUD接口(IService與BaseMapper)

MyBatisPlus之CRUD接口—IService與BaseMapper一、BaseMapper與IService的關系二、BaseMapper核心方法詳解2.1 新增操作&#xff08;Insert&#xff09;2.2 查詢操作&#xff08;Select&#xff09;2.3 更新操作&#xff08;Update&#xff09;2.4 刪除操作&#xff08;Delete&…

axios請求的取消

axios請求的取消解決&#xff1a;axios請求的取消解決&#xff1a;axios請求的取消 在使用 Axios 發起請求時&#xff0c;有時候你可能需要取消這些請求&#xff0c;比如當組件銷毀時或者用戶操作導致不再需要獲取之前發起的請求結果。Axios 支持通過 Cancel Token 取消請求。 …

深入理解C++中的Lazy Evaluation:延遲計算的藝術

在編程世界里&#xff0c;“最好的運算就是從未執行的運算” —— 這句話深刻揭示了性能優化的核心思路。如果一個計算過程最終不會被使用&#xff0c;那么提前執行它就是純粹的資源浪費。這種思想衍生出了 Lazy Evaluation&#xff08;緩式評估&#xff09; 技術&#xff1a;延…

php完整處理word中表單數據的方法

使用php基礎方式實現word中表單處理<?php/*** zipFile 類用于處理 .docx 文件的解壓、修改和重新打包*/ class zipFile {/** var ZipArchive ZIP 文件對象 */private $zipFile;/** var string 臨時目錄路徑 */private $tempDir;/** var string 嵌入的 Excel 文件臨時目錄路…

Node.js 操作 MongoDB

目錄 Node.js 操作 MongoDB 一、什么是 MongoDB&#xff1f; 二、MongoDB 的功能概覽 三、MongoDB 的安裝與啟動 安裝 MongoDB&#xff08;以本地安裝為例&#xff09; 啟動 MongoDB 四、Node.js 如何連接 MongoDB&#xff1f; 使用 Mongoose ODM 工具 建立連接 五、…

先學Python還是c++?

選擇先學Python還是C&#xff0c;取決于你的學習目標、應用場景和職業規劃。以下是兩者的對比分析和建議&#xff0c;幫助你做出更適合自己的選擇&#xff1a;一、核心差異對比維度PythonC學習曲線簡單易上手&#xff08;語法接近自然語言&#xff09;復雜&#xff08;需理解指…

Trae + Notion MCP:將你的Notion數據庫升級為智能對話機器人

前言 Notion作為一款功能強大的信息管理工具&#xff0c;被廣泛用于項目跟蹤、知識庫構建和數據整理。然而&#xff0c;隨著數據量的增長&#xff0c;我們常常會發現自己陷入了重復和繁瑣的操作中。比如&#xff0c;為了找到符合特定條件的幾條數據&#xff0c;需要在龐大的數…

【iOS】retain/release底層實現原理

文章目錄前言前情知識retain和release的實現原理&#xff08;MRC手動管理&#xff09;retain&#xff08;MRC手動管理&#xff09;retain源碼內聯函數rootRetain源碼相關的sidetable_tryRetain()方法retain底層工作流程總結releaserelease源碼內聯函數rootRelease源碼小結前言 …

文件同步神器-rsync命令講解

rsync 是一個強大的文件同步與傳輸工具&#xff0c;廣泛用于本地或遠程服務器之間的高效文件備份、鏡像或同步。其核心優勢是通過增量傳輸?&#xff08;僅傳輸文件差異部分&#xff09;和壓縮減少數據傳輸量&#xff0c;同時支持保留文件元數據&#xff08;如權限、時間戳、所…

Rust: 工具鏈版本更新

遇到 cargo build --release 錯誤&#xff0c;比如&#xff0c;當前 Rust 工具鏈版本&#xff08;1.78.0&#xff09;低于依賴項所需的最低版本&#xff08;部分依賴要求 ≥1.82.0&#xff09;。以下是系統化的解決方案&#xff1a; &#x1f527; 一、升級 Rust 工具鏈&#x…