Django Model 與 Migrations¶
開始之前¶
任務目標
在這個章節中,我們會完成:
- 建立 blog App
- 建立 Django Model
- 了解 migrations 機制
- 學習建立關聯表
建立 blog App¶
在開始使用 Model 之前,我們先建立一個新的 App 來練習資料庫相關功能。
建立 App¶
使用 Django 管理指令建立 blog app:
這會在專案中建立一個 blog 資料夾,包含以下檔案:
註冊 App¶
在 core/settings.py 的 INSTALLED_APPS 中加入 blog:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"practices",
"blog", # (1)!
]
- 新增 blog app
現在 Django 就能辨識這個新的 App 了。
Django Model¶
在 Django 中,我們使用 Model 來定義資料表的結構。Model 是一個 Python class,Django 會自動將它轉換成資料庫的表格。
建立第一個 Model¶
在 blog/models.py 中建立一個 Article Model:
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
__str__ 方法
__str__ 是 Python 的特殊方法,用來定義物件的「字串表示」。
當你在 Django Shell 或 Admin 中查看物件時,會顯示這個方法的回傳值:
# 沒有定義 __str__
>>> Article.objects.first()
<Article: Article object (1)>
# 有定義 __str__
>>> Article.objects.first()
<Article: 我的第一篇文章>
建議每個 Model 都定義 __str__,讓物件更容易識別。
常用的欄位類型¶
| 欄位類型 | 說明 | 範例 |
|---|---|---|
CharField |
短文字(需指定 max_length) | 標題、名稱 |
TextField |
長文字 | 文章內容 |
IntegerField |
整數 | 數量、年齡 |
FloatField |
浮點數 | 價格 |
BooleanField |
布林值 | 是否發布 |
DateField |
日期 | 生日 |
DateTimeField |
日期時間 | 建立時間 |
EmailField |
電子郵件 | |
URLField |
URL | 網址 |
FileField |
檔案 | 上傳檔案 |
ImageField |
圖片 | 上傳圖片 |
完整欄位類型列表請參考 Django Model field reference。
常用的欄位選項¶
| 選項 | 說明 | 範例 |
|---|---|---|
max_length |
最大長度 | CharField(max_length=200) |
default |
預設值 | BooleanField(default=False) |
null |
資料庫允許 NULL | CharField(null=True) |
blank |
表單允許空白 | CharField(blank=True) |
unique |
唯一值 | EmailField(unique=True) |
auto_now_add |
建立時自動設定 | DateTimeField(auto_now_add=True) |
auto_now |
更新時自動設定 | DateTimeField(auto_now=True) |
完整欄位選項列表請參考 Django Field options。
Migrations 機制¶
Django 使用 migrations 來管理資料庫結構的變更。
什麼是 Migrations?¶
flowchart LR
A[Model<br/>Python Class] -->|makemigrations| B[Migration File<br/>變更記錄]
B -->|migrate| C[Database<br/>資料庫]
- Model:Python 程式碼中的資料結構定義
- Migration File:記錄 Model 變更的檔案
- Database:實際的資料庫表格
步驟 1:建立 Migration¶
當你修改了 Model 後,執行:
這會在 blog/migrations/ 資料夾中建立一個 migration 檔案,記錄你的變更。
輸出類似:
步驟 2:套用 Migration¶
執行 migration 來更新資料庫:
輸出類似:
Operations to perform:
Apply all migrations: admin, auth, contenttypes, blog, practices, sessions
Running migrations:
Applying blog.0001_initial... OK
範例:新增欄位¶
假設我們想在 Article Model 中新增一個 is_published 欄位:
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_published = models.BooleanField(default=False)
def __str__(self):
return self.title
執行 makemigrations:
輸出:
Migrations for 'blog':
blog/migrations/0002_article_is_published.py
+ Add field is_published to article
Django 會自動偵測到 Model 的變更,並建立一個新的 migration 檔案。
執行 migrate 套用變更:
輸出:
Operations to perform:
Apply all migrations: admin, auth, contenttypes, blog, practices, sessions
Running migrations:
Applying blog.0002_article_is_published... OK
新增欄位時的預設值
當你對已有資料的表格新增欄位時,Django 會問你如何處理現有資料:
- 提供預設值:使用
default=...參數 - 允許 NULL:使用
null=True參數 - 互動式輸入:如果沒有設定,Django 會在
makemigrations時詢問你
建議在新增欄位時都設定 default 或 null=True,避免 migration 時出錯。
範例:建立關聯表¶
讓我們新增 Author 和 Tag Model,來練習資料庫關聯:
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
bio = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_published = models.BooleanField(default=False)
author = models.ForeignKey( # (1)!
Author,
on_delete=models.CASCADE,
related_name="articles",
null=True,
blank=True,
)
tags = models.ManyToManyField( # (2)!
Tag,
related_name="articles",
blank=True,
)
def __str__(self):
return self.title
- 一對多關聯(一個作者可以有多篇文章)
- 多對多關聯(一篇文章可以有多個標籤)
不需要手動建立中間表
在前一章的資料庫關聯中,我們提到多對多關聯需要一個中間表(Junction Table)來連接兩個表。
但在 Django 中使用 ManyToManyField 時,Django 會自動建立這個中間表,你不需要手動定義它。
Django 會在背後建立一個名為 {app}_{model1}_{field_name} 的表(例如:blog_article_tags),包含兩個外鍵分別指向 Article 和 Tag。
如果需要在中間表中儲存額外的欄位(例如:加入時間),可以使用 through 參數自訂中間表,請參考 Extra fields on many-to-many relationships。
ForeignKey 參數說明¶
| 參數 | 說明 |
|---|---|
on_delete |
當關聯的物件被刪除時的行為 |
related_name |
反向查詢的名稱(從 Author 查 Article)如果不寫預設會是 {model_name}_set |
null=True |
允許資料庫中為 NULL |
blank=True |
允許表單中為空白 |
on_delete 選項¶
| 選項 | 說明 |
|---|---|
CASCADE |
一起刪除(刪除作者時,刪除所有文章) |
PROTECT |
保護(如果有文章,無法刪除作者) |
SET_NULL |
設為 NULL(需要 null=True) |
SET_DEFAULT |
設為預設值(需要 default) |
DO_NOTHING |
不做任何事(可能造成資料不一致) |
建立遷移檔並套用
常用的 Migration 指令¶
| 指令 | 說明 |
|---|---|
makemigrations |
根據 Model 變更建立 migration 檔案 |
migrate |
套用 migration 到資料庫 |
showmigrations |
顯示所有 migration 的狀態 |
sqlmigrate ${app_name} ${migration_version} |
顯示 migration 的 SQL 語句 |
不要手動修改 Migration 檔案
Migration 檔案是自動生成的,除非你非常清楚自己在做什麼,否則不要手動修改。
任務結束¶
完成!
恭喜你完成了這個章節!現在你已經:
- 建立 blog App
- 建立 Django Model
- 了解 migrations 機制
- 學習建立關聯表