Django ORM¶
開始之前¶
任務目標
在這個章節中,我們會完成:
- 了解 ORM 的概念
- 使用 Django Shell
- 使用 Django ORM 操作資料
什麼是 ORM?¶
ORM(Object-Relational Mapping,物件關聯映射)是一種技術,讓你可以用物件導向的方式操作資料庫,而不需要寫 SQL。
為什麼需要 ORM?¶
傳統 SQL 寫法:
SELECT * FROM articles WHERE id = 1;
INSERT INTO articles (title, content) VALUES ('Hello', 'World');
UPDATE articles SET title = 'New Title' WHERE id = 1;
DELETE FROM articles WHERE id = 1;
Django ORM 寫法:
Article.objects.get(id=1)
Article.objects.create(title='Hello', content='World')
Article.objects.filter(id=1).update(title='New Title')
Article.objects.filter(id=1).delete()
ORM 的優點¶
| 優點 | 說明 |
|---|---|
| 資料庫無關 | 更換資料庫不需要改程式碼 |
| 安全性 | 自動防止 SQL Injection |
| 可讀性 | Python 語法比 SQL 更易讀 |
| 開發效率 | 不需要學習 SQL 也能操作資料庫 |
ORM 的缺點¶
| 缺點 | 說明 |
|---|---|
| 效能 | 需要很了解語法,才能把複雜查詢效能調教的跟原生 SQL 一樣高效 |
| 學習曲線 | 需要學習 ORM 特定的語法 |
| 抽象 | 有時還是需要了解底層 SQL |
使用 Django Shell¶
在學習 ORM 語法之前,我們先安裝一個更好用的 Shell 工具。
安裝 IPython¶
IPython 提供更好的互動式體驗,包括語法高亮、自動補全等功能。
進入 Django Shell¶
如果有安裝 IPython,Django 會自動使用它。你會看到類似這樣的提示:
7 objects imported automatically (use -v 2 for details).
Python 3.14.0 (main, Oct 10 2025, 12:54:13) [Clang 20.1.4 ]
Type 'copyright', 'credits' or 'license' for more information
IPython 9.7.0 -- An enhanced Interactive Python. Type '?' for help.
Tip: You can find how to type a LaTeX symbol by back-completing it, eg `\θ<tab>` will expand to `\theta`.
In [1]:
Django ORM 語法¶
匯入 Model¶
在 Shell 中,先匯入你的 Model:
自動匯入
從 Django 5.0 開始 shell 會自動匯入他找到的 Model 所以可以省略這個步驟。
建立資料(Create)¶
方法 1:create()¶
方法 2:save()¶
查詢資料(Read)¶
取得所有資料¶
取得單一資料(找不到會拋出錯誤)¶
取得單一資料(找不到回傳 None)¶
篩選資料¶
排除資料¶
排序¶
降冪
升冪
多欄位排序
- 先按 created_at 降冪,再按 title 升冪
隨機排序
亂數排序效能
order_by("?") 在大型資料表上可能會很慢,因為資料庫需要為每筆資料生成隨機數。
切片(Slicing)¶
QuerySet 支援 Python 的切片語法:
- 前 5 筆
- 第 6 到第 10 筆
切片不支援負數索引
Django QuerySet 不支援負數索引,如 Article.objects.all()[-1] 會拋出錯誤。
如果要取最後一筆,請使用 last():
計數¶
查詢條件¶
Django ORM 使用雙底線 __ 來表示查詢條件:
| 條件 | 說明 | 範例 |
|---|---|---|
exact |
完全相等(預設) | filter(title__exact="Hello") |
iexact |
不分大小寫相等 | filter(title__iexact="hello") |
contains |
包含 | filter(title__contains="Django") |
icontains |
不分大小寫包含 | filter(title__icontains="django") |
startswith |
開頭是 | filter(title__startswith="Hello") |
endswith |
結尾是 | filter(title__endswith="!") |
gt |
大於 | filter(id__gt=5) |
gte |
大於等於 | filter(id__gte=5) |
lt |
小於 | filter(id__lt=5) |
lte |
小於等於 | filter(id__lte=5) |
in |
在列表中 | filter(id__in=[1, 2, 3]) |
isnull |
是否為 NULL | filter(title__isnull=True) |
完整查詢條件列表請參考 Django QuerySet Field lookups。
更新資料(Update)¶
方法 1:修改後 save()¶
方法 2:update()(批量更新)¶
刪除資料(Delete)¶
刪除單一資料¶
批量刪除¶
進階查詢¶
鏈式查詢¶
QuerySet 支援鏈式呼叫:
Article.objects \
.filter(title__contains="Django") \
.exclude(content__isnull=True) \
.order_by("-created_at")[:10]
多條件篩選¶
多個參數(AND)
- title 包含 "Django" 且 content 包含 "ORM"
鏈式 filter(AND)
Q 物件¶
當需要 OR 條件或複雜的邏輯組合時,使用 Q 物件:
from django.db.models import Q
articles = Article.objects.filter( # (1)!
Q(title__contains="Django") | Q(content__contains="Python")
)
print(articles)
articles = Article.objects.filter( # (2)!
Q(title__contains="Django") & Q(content__contains="ORM")
)
print(articles)
articles = Article.objects.filter( # (3)!
~Q(title__contains="草稿")
)
print(articles)
articles = Article.objects.filter( # (4)!
(Q(title__contains="Django") & Q(content__contains="ORM")) |
Q(created_at__year=2024)
)
print(articles)
- OR 條件:title 包含 "Django" 或 content 包含 "Python"
- AND 條件
- NOT 條件
- 複雜組合:(A AND B) OR C
關聯資料操作¶
# 建立作者
author = Author.objects.create(
name="Alice",
email="[email protected]",
bio="Python 開發者",
)
# 建立標籤
tag1 = Tag.objects.create(name="Django")
tag2 = Tag.objects.create(name="Python")
# 建立文章並設定作者
article = Article.objects.create(
title="Django 入門",
content="學習 Django 基礎",
author=author,
)
# 設定標籤(ManyToMany)
article.tags.add(tag1, tag2)
# 查詢作者的所有文章(反向查詢)
author.articles.all()
# 查詢標籤的所有文章
tag1.articles.all()
# 查詢文章的作者
article.author
# 查詢文章的所有標籤
article.tags.all()
統計¶
Aggregation(聚合)¶
聚合函式用於計算整個 QuerySet 的統計值:
from django.db.models import Count, Avg, Max, Min, Sum
# 計算文章總數
result = Article.objects.aggregate(total=Count("id"))
print(result) # (1)!
# 多個聚合
result = Article.objects.aggregate(
total=Count("id"),
latest=Max("created_at"),
earliest=Min("created_at"),
)
print(result) # (2)!
{'total': 100}{'total': 100, 'latest': datetime(...), 'earliest': datetime(...)}
Annotate(分組統計)¶
annotate 用於為每筆資料添加計算欄位:
from django.db.models import Count
# 假設有 Author 和 Article 的關聯
# 為每個作者計算文章數量
authors = Author.objects.annotate(
article_count=Count("articles")
)
for author in authors:
print(f"{author.name}: {author.article_count} 篇文章")
Aggregation vs Annotate¶
| 方法 | 說明 | 回傳值 |
|---|---|---|
aggregate() |
計算整個 QuerySet 的統計值 | 字典 |
annotate() |
為每筆資料添加計算欄位 | QuerySet |
# aggregate - 所有文章的總數
Article.objects.aggregate(total=Count("id"))
# {'total': 100}
# annotate - 每個作者的文章數
Author.objects.annotate(article_count=Count("articles"))
# <QuerySet [<Author: Alice>, <Author: Bob>, ...]>
# 每個 Author 物件都有 article_count 屬性
任務結束¶
完成!
恭喜你完成了這個章節!現在你已經:
- 了解 ORM 的概念
- 使用 Django Shell
- 使用 Django ORM 操作資料
補充說明
以上介紹常用的 ORM 語法,但並非完整也有其他語法也蠻常被使用,例如:
bulk_create()/bulk_update()values()/values_list()distinct()update_or_create()/get_or_create()/select_for_update()
如果像要了解更多可以參考 QuerySet API 的文件。