Django Models 基礎:定義你的第一個資料模型 | Django 教學
在 Django 的 MTV 架構中,Model 扮演著資料層的核心角色。透過 ORM(Object-Relational Mapping)機制,你可以用 Python 類別定義資料模型,完全不需要手寫 SQL。本篇將以部落格文章 Post 為範例,帶你從零定義第一個 Django Model,學會基本欄位類型、Meta 設定、migrations 流程,以及如何在 Django Shell 與 Admin 後台操作資料。
什麼是 ORM?
ORM(Object-Relational Mapping,物件關聯對映)是一種將程式語言中的物件與關聯式資料庫的表格進行對映的技術。簡單來說,它讓你用 Python 程式碼來操作資料庫,而不需要直接撰寫 SQL 語句。
Django 的 ORM 有幾個核心對映關係:
| Python 概念 | 資料庫概念 | 說明 |
|---|---|---|
| 一個 Model 類別 | 一張資料表(Table) | 類別名稱對應表名 |
| 一個 Field 屬性 | 一個欄位(Column) | Field 類型決定資料類型 |
| 一個 Model 實例 | 一筆資料列(Row) | 對實例操作等同操作該筆資料 |
舉例來說,當你定義了一個 Post 類別,Django 就會在資料庫中建立一張 blog_post 表;當你建立一個 Post 物件並儲存,就等於在這張表中插入了一筆資料。
Django Model 的角色:MTV 中的 M
在 Django 的 MTV(Model-Template-View)架構中:
- Model 負責定義資料結構與資料庫互動邏輯
- Template 負責呈現畫面
- View 負責處理請求與回應的商業邏輯
Model 是整個應用的資料基礎。你在 Model 中定義了哪些資料要儲存、資料的類型與限制,其他層級都圍繞這些定義來運作。
定義第一個 Model:Post 部落格文章
延續前幾篇教學的 myblog 專案,我們在 blog 應用中建立一個 Post Model。開啟 blog/models.py 檔案:
# blog/models.py
from django.db import models
from django.utils import timezone
class Post(models.Model):
"""部落格文章模型"""
title = models.CharField(max_length=200, verbose_name="標題")
content = models.TextField(verbose_name="內文")
author = models.CharField(max_length=100, verbose_name="作者")
views = models.IntegerField(default=0, verbose_name="瀏覽次數")
is_published = models.BooleanField(default=False, verbose_name="是否發布")
created_at = models.DateTimeField(default=timezone.now, verbose_name="建立時間")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新時間")
class Meta:
ordering = ["-created_at"]
verbose_name = "文章"
verbose_name_plural = "文章列表"
db_table = "blog_post"
def __str__(self):
return self.title
這段程式碼包含了幾個重要元素,接下來逐一說明。
繼承 models.Model
class Post(models.Model):
所有的 Django Model 都必須繼承 django.db.models.Model。這個基礎類別(Base Class)提供了與資料庫互動的所有能力,包含儲存、查詢、刪除等方法。繼承之後,Django 就知道這個類別要對映到資料庫中的一張表。
基本欄位類型
Django 提供了多種 Field 類型來對應不同的資料需求。以下是 Post Model 中使用到的欄位:
| 欄位類型 | 對應 SQL 類型 | 用途說明 |
|---|---|---|
CharField(max_length=n) | VARCHAR(n) | 短文字,必須指定 max_length |
TextField() | TEXT | 長文字,無長度限制 |
IntegerField() | INTEGER | 整數 |
BooleanField() | BOOLEAN | 布林值(True / False) |
DateTimeField() | DATETIME | 日期與時間 |
每個欄位都可以接受額外的參數(Options)來控制行為:
# default:設定預設值
views = models.IntegerField(default=0)
# verbose_name:在 Admin 後台顯示的欄位名稱
title = models.CharField(max_length=200, verbose_name="標題")
# auto_now=True:每次呼叫 save() 時自動更新為當前時間
updated_at = models.DateTimeField(auto_now=True)
# auto_now_add=True:僅在建立時自動設定(之後不會再變動)
# created_at = models.DateTimeField(auto_now_add=True)
注意 auto_now 和 auto_now_add 的差異:auto_now 在每次儲存時都會更新時間,而 auto_now_add 只在第一次建立時設定。在範例中,created_at 使用了 default=timezone.now 的寫法,效果類似 auto_now_add,但允許手動覆寫。
Meta 類別選項
Meta 是 Model 內部的一個巢狀類別(Nested Class),用來設定 Model 層級的配置:
class Meta:
# 預設排序:依建立時間降冪(最新的排在前面)
ordering = ["-created_at"]
# Admin 後台顯示的名稱(單數)
verbose_name = "文章"
# Admin 後台顯示的名稱(複數)
verbose_name_plural = "文章列表"
# 自訂資料表名稱(預設為 app名稱_model名稱)
db_table = "blog_post"
ordering 中的 - 前綴代表降冪排序(Descending)。如果不加 -,則為升冪排序(Ascending)。
__str__ 方法定義
def __str__(self):
return self.title
__str__ 方法決定了 Model 實例被轉換為字串時的顯示內容。這在以下場景非常重要:
- Django Admin 後台的資料列表
- Django Shell 中查詢結果的顯示
- 除錯時使用
print()印出物件
如果沒有定義 __str__,預設會顯示 Post object (1) 這樣不易辨識的資訊。
makemigrations 與 migrate 流程
定義好 Model 之後,需要透過 遷移(Migration)機制將定義同步到資料庫。這個流程分為兩個步驟:
第一步:產生遷移檔案
python manage.py makemigrations
# 輸出:
# Migrations for 'blog':
# blog/migrations/0001_initial.py
# - Create model Post
makemigrations 會偵測 Model 的變更,並在 blog/migrations/ 目錄下產生一個遷移檔案。這個檔案記錄了需要對資料庫做哪些操作(例如建立表、新增欄位、修改欄位類型等)。
第二步:執行遷移
python manage.py migrate
# 輸出:
# Operations to perform:
# Apply all migrations: admin, auth, blog, contenttypes, sessions
# Running migrations:
# Applying blog.0001_initial... OK
migrate 會讀取所有未執行的遷移檔案,並將變更套用到資料庫中。此時資料庫中就會出現 blog_post 這張表。
如果之後修改了 Model(例如新增欄位),只需重複這兩個步驟:
# 假設新增了 summary 欄位
# blog/models.py 中加入:
# summary = models.CharField(max_length=300, blank=True, verbose_name="摘要")
python manage.py makemigrations
# 輸出:
# Migrations for 'blog':
# blog/migrations/0002_post_summary.py
# - Add field summary to post
python manage.py migrate
# 輸出:
# Applying blog.0002_post_summary... OK
在 Django Shell 中操作 Model
Django 提供了互動式的 Shell 環境,讓你可以直接用 Python 程式碼操作 Model 資料。啟動方式如下:
python manage.py shell
建立資料(Create)
from blog.models import Post
# 方法一:使用 create() 直接建立並儲存
post1 = Post.objects.create(
title="我的第一篇文章",
content="這是文章內容,歡迎來到我的部落格!",
author="Benz",
is_published=True,
)
print(post1)
# 輸出:我的第一篇文章
# 方法二:先建立實例,再手動儲存
post2 = Post(
title="Django 學習筆記",
content="今天學習了 Django Models 的基本用法。",
author="Benz",
)
post2.save()
create() 會直接將資料寫入資料庫,而第二種方式需要手動呼叫 save() 才會儲存。
查詢所有資料(all)
posts = Post.objects.all()
print(posts)
# 輸出:<QuerySet [<Post: 我的第一篇文章>, <Post: Django 學習筆記>]>
all() 回傳一個 QuerySet(查詢集),包含該 Model 的所有資料。QuerySet 具有惰性求值(Lazy Evaluation)的特性,在實際存取資料之前不會執行 SQL 查詢。
篩選資料(filter)
# 篩選已發布的文章
published_posts = Post.objects.filter(is_published=True)
print(published_posts)
# 輸出:<QuerySet [<Post: 我的第一篇文章>]>
# 篩選作者為 Benz 的文章
benz_posts = Post.objects.filter(author="Benz")
print(benz_posts.count())
# 輸出:2
# 篩選標題包含「Django」的文章(不區分大小寫)
django_posts = Post.objects.filter(title__icontains="django")
print(django_posts)
# 輸出:<QuerySet [<Post: Django 學習筆記>]>
filter() 支援多種查詢條件,使用雙底線語法(Double Underscore Lookup)來指定比較方式,例如 __icontains(不區分大小寫的包含)、__gt(大於)、__lte(小於等於)等。
取得單筆資料(get)
# 用主鍵(pk)取得特定文章
post = Post.objects.get(pk=1)
print(post.title)
# 輸出:我的第一篇文章
print(post.created_at)
# 輸出:2026-05-27 10:30:00+00:00
get() 只會回傳一筆資料。如果查詢條件匹配到零筆資料,會拋出 Post.DoesNotExist 例外(Exception);如果匹配到多筆資料,會拋出 Post.MultipleObjectsReturned 例外。因此使用 get() 時要確保查詢條件能精確匹配到唯一一筆。
更新與刪除
# 更新單筆資料
post = Post.objects.get(pk=1)
post.views = 100
post.save()
# 批次更新(不會觸發 save() 方法)
Post.objects.filter(is_published=False).update(is_published=True)
# 刪除單筆資料
post = Post.objects.get(pk=2)
post.delete()
# 輸出:(1, {'blog.Post': 1})
在 Admin 後台查看 Model 資料
Django 內建了強大的 Admin 管理後台。要讓 Post Model 出現在 Admin 中,需要在 blog/admin.py 中進行註冊(Register):
# blog/admin.py
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ["title", "author", "is_published", "created_at"]
list_filter = ["is_published", "author"]
search_fields = ["title", "content"]
上面的設定做了以下幾件事:
@admin.register(Post):將PostModel 註冊到 Admin 後台list_display:指定列表頁要顯示哪些欄位list_filter:在右側加入篩選器search_fields:啟用搜尋功能,可搜尋標題與內文
如果還沒建立管理員帳號,先執行以下指令:
python manage.py createsuperuser
接著啟動開發伺服器(Development Server),瀏覽 http://127.0.0.1:8000/admin/ 即可登入後台,查看並管理 Post 資料。
python manage.py runserver
總結
本篇介紹了 Django Models 的基礎知識。我們從 ORM 的概念出發,了解了 Model 在 MTV 架構中的角色,接著以部落格文章 Post 為範例,學習了如何繼承 models.Model 定義 Model、使用 CharField、TextField、IntegerField、BooleanField、DateTimeField 等基本欄位類型、透過 Meta 類別設定排序與顯示名稱、定義 __str__ 方法改善可讀性。在資料庫同步方面,掌握了 makemigrations 與 migrate 的兩步驟流程。最後透過 Django Shell 實際操作了資料的建立、查詢、篩選與刪除,並將 Model 註冊到 Admin 後台進行視覺化管理。下一篇我們將深入探討 Model 之間的關聯(Relationships),包含一對多、一對一、多對多等常見關聯類型。