跳轉到

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 提供更好的互動式體驗,包括語法高亮、自動補全等功能。

uv add ipython --group=dev

進入 Django Shell

uv run manage.py 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:

from blog.models import Article, Author, Tag

自動匯入

從 Django 5.0 開始 shell 會自動匯入他找到的 Model 所以可以省略這個步驟。

建立資料(Create)

方法 1:create()

Article.objects.create(
    title="我的第一篇文章",
    content="這是文章內容",
)

方法 2:save()

article = Article(title="第二篇文章", content="內容")
article.save()

查詢資料(Read)

取得所有資料

Article.objects.all()

取得單一資料(找不到會拋出錯誤)

Article.objects.get(id=1)

取得單一資料(找不到回傳 None)

Article.objects.filter(id=1).first()  # 第一筆
Article.objects.filter(id=1).last()  # 最後一筆

篩選資料

Article.objects.filter(title="我的第一篇文章")

排除資料

Article.objects.exclude(title="草稿")

排序

降冪

Article.objects.order_by("-created_at")

升冪

Article.objects.order_by("created_at")

多欄位排序

Article.objects.order_by("-created_at", "title")  # (1)!
  1. 先按 created_at 降冪,再按 title 升冪

隨機排序

Article.objects.order_by("?")

亂數排序效能

order_by("?") 在大型資料表上可能會很慢,因為資料庫需要為每筆資料生成隨機數。

切片(Slicing)

QuerySet 支援 Python 的切片語法:

Article.objects.all()[:5]  # (1)!

Article.objects.all()[5:10]  # (2)!
  1. 前 5 筆
  2. 第 6 到第 10 筆

切片不支援負數索引

Django QuerySet 不支援負數索引,如 Article.objects.all()[-1] 會拋出錯誤。

如果要取最後一筆,請使用 last()

Article.objects.last()

計數

Article.objects.count()

查詢條件

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()

article = Article.objects.get(id=1)
article.title = "新標題"
article.save()

方法 2:update()(批量更新)

Article.objects.filter(id=1).update(title="新標題")

刪除資料(Delete)

刪除單一資料

article = Article.objects.get(id=1)
article.delete()

批量刪除

Article.objects.filter(title__contains="草稿").delete()

進階查詢

鏈式查詢

QuerySet 支援鏈式呼叫:

Article.objects \
    .filter(title__contains="Django") \
    .exclude(content__isnull=True) \
    .order_by("-created_at")[:10]

多條件篩選

多個參數(AND)

Article.objects.filter(  # (1)!
    title__contains="Django",
    content__contains="ORM",
)
  1. title 包含 "Django" 且 content 包含 "ORM"

鏈式 filter(AND)

Article.objects.filter(title__contains="Django").filter(content__contains="ORM")

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)
  1. OR 條件:title 包含 "Django" 或 content 包含 "Python"
  2. AND 條件
  3. NOT 條件
  4. 複雜組合:(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)!
  1. {'total': 100}
  2. {'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 的文件