Django Models 基礎:定義你的第一個資料模型 | Django 教學

2026/05/27 2026/05/21
Django Models 基礎:定義你的第一個資料模型 | Django 教學

DjangoMTV 架構中,Model 扮演著資料層的核心角色。透過 ORM(Object-Relational Mapping)機制,你可以用 Python 類別定義資料模型,完全不需要手寫 SQL。本篇將以部落格文章 Post 為範例,帶你從零定義第一個 Django Model,學會基本欄位類型、Meta 設定、migrations 流程,以及如何在 Django ShellAdmin 後台操作資料。

什麼是 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_nowauto_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):將 Post Model 註冊到 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、使用 CharFieldTextFieldIntegerFieldBooleanFieldDateTimeField 等基本欄位類型、透過 Meta 類別設定排序與顯示名稱、定義 __str__ 方法改善可讀性。在資料庫同步方面,掌握了 makemigrationsmigrate 的兩步驟流程。最後透過 Django Shell 實際操作了資料的建立、查詢、篩選與刪除,並將 Model 註冊到 Admin 後台進行視覺化管理。下一篇我們將深入探討 Model 之間的關聯(Relationships),包含一對多、一對一、多對多等常見關聯類型。

BenZ Software Developer

熱愛技術的軟體開發者,在這裡分享程式開發經驗與學習筆記。