Django Views:Function-Based Views 函數視圖入門 | Django 教學

2026/06/01 2026/05/27
Django Views:Function-Based Views 函數視圖入門 | Django 教學

在 Django 的 MTV(Model-Template-View)架構中, View 扮演著承上啟下的核心角色——它接收使用者的 HTTP 請求(Request),執行商業邏輯,最後回傳 HTTP 回應(Response)。本篇教學將聚焦於 Function-Based Views(FBV,函數視圖),這是 Django 中最直觀、最容易上手的 View 撰寫方式。我們將從 HttpRequestHttpResponse 物件開始,逐步學會 render()redirect()JsonResponse 等常用工具,最後完成一個完整的 CRUD 範例。

View 在 MTV 架構中的角色

在前幾篇教學中,我們認識了 Django 的 MTV 架構:Model 負責資料層、Template 負責呈現層、View 負責邏輯層。View 就像是整個應用的「交通指揮中心」,它的工作流程如下:

  1. 接收來自使用者的 HttpRequest 物件(包含請求方法、參數、表單資料等)
  2. 執行對應的商業邏輯(查詢資料庫、處理表單、計算結果等)
  3. 回傳一個 HttpResponse 物件(HTML 頁面、JSON 資料、重導向等)
┌──────────┐     HttpRequest      ┌──────────┐     查詢/寫入     ┌──────────┐
│          │ ──────────────────→  │          │ ──────────────→  │          │
│  使用者   │                      │   View   │                  │  Model   │
│          │ ←──────────────────  │          │ ←──────────────  │          │
└──────────┘     HttpResponse     └──────────┘     資料結果      └──────────┘
                                       │
                                       │ 渲染模板
                                       ↓
                                 ┌──────────┐
                                 │ Template │
                                 └──────────┘

Django 的 View 最小定義非常簡潔——只要是一個接收 request 參數並回傳 HttpResponse(或其子類別)的 Python 函式,就是一個合法的 FBV。


HttpRequest 物件

當使用者發送請求到 Django 應用時,Django 會自動將原始的 HTTP 請求封裝成一個 HttpRequest 物件,並作為第一個參數傳入 View 函式。這個物件包含了請求的所有資訊。

常用屬性一覽

def example_view(request):
    # HTTP 請求方法:'GET'、'POST'、'PUT'、'DELETE' 等
    print(request.method)

    # GET 參數(QueryDict 物件),例如 ?page=2&sort=name
    print(request.GET)
    print(request.GET.get('page', '1'))  # 取得 page 參數,預設值為 '1'

    # POST 表單資料(QueryDict 物件)
    print(request.POST)
    print(request.POST.get('username', ''))

    # 請求的原始內容(bytes),常用於處理 JSON 請求
    print(request.body)

    # 當前登入的使用者(需啟用 AuthenticationMiddleware)
    print(request.user)

    # Session 物件(dict-like),用於儲存跨請求的資料
    print(request.session)

    # 請求路徑
    print(request.path)           # 例如 '/articles/1/'
    print(request.get_full_path())  # 例如 '/articles/1/?page=2'

request.GETrequest.POST 都是 QueryDict 物件,它是 Python 字典(dict)的子類別,支援同一個 key 對應多個值(例如多選的 checkbox)。使用 .get() 方法取值時可以指定預設值,避免 key 不存在時拋出例外(Exception)。


HttpResponse 物件

View 函式必須回傳一個 HttpResponse 物件(或其子類別)。HttpResponse 代表伺服器要回傳給瀏覽器的回應內容。

from django.http import HttpResponse

def hello_view(request):
    # 最基本的回應:回傳純文字
    response = HttpResponse("Hello, Django!")
    print(response.status_code)  # 200(預設狀態碼)
    print(response.content)      # b'Hello, Django!'
    return response

你也可以自訂狀態碼(Status Code)和回應標頭(Headers):

from django.http import HttpResponse

def custom_response(request):
    response = HttpResponse("找不到頁面", status=404)
    response['X-Custom-Header'] = 'my-value'
    return response

Django 也提供了多個 HttpResponse 的子類別,對應不同的 HTTP 狀態碼:

類別狀態碼用途
HttpResponse200一般成功回應
HttpResponseRedirect302暫時重導向
HttpResponsePermanentRedirect301永久重導向
HttpResponseNotFound404找不到資源
HttpResponseForbidden403禁止存取
HttpResponseBadRequest400錯誤的請求
JsonResponse200JSON 格式回應

第一個 FBV:Hello World

讓我們從最簡單的 FBV 開始。首先在 views.py 中定義一個 View 函式:

# views.py
from django.http import HttpResponse

def hello_world(request):
    """最簡單的 FBV,回傳純文字"""
    return HttpResponse("Hello, World! 歡迎來到 Django FBV 教學。")

接著在 urls.py 中設定 URL 路由,將網址對應到這個 View:

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('hello/', views.hello_world, name='hello_world'),
]

啟動開發伺服器後,在瀏覽器訪問 http://127.0.0.1:8000/hello/,就能看到 “Hello, World! 歡迎來到 Django FBV 教學。” 的文字。


接收 URL 參數

Django 的 URL 路由(URL Routing)支援從網址中擷取參數,並將它們傳入 View 函式:

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    # <str:name> 擷取字串參數,傳入 View 的 name 參數
    path('hello/<str:name>/', views.hello_name, name='hello_name'),
    # <int:pk> 擷取整數參數,傳入 View 的 pk 參數
    path('articles/<int:pk>/', views.article_detail, name='article_detail'),
]
# views.py
from django.http import HttpResponse

def hello_name(request, name):
    """接收 URL 中的 name 參數"""
    return HttpResponse(f"Hello, {name}!")
    # 訪問 /hello/Django/ → 輸出 "Hello, Django!"

def article_detail(request, pk):
    """接收 URL 中的文章 ID"""
    return HttpResponse(f"你正在查看第 {pk} 篇文章。")
    # 訪問 /articles/42/ → 輸出 "你正在查看第 42 篇文章。"

常用的 URL 路徑轉換器(Path Converter)包括:

轉換器說明範例
str匹配任何非空字串(不含 /<str:slug>
int匹配零或正整數<int:pk>
slug匹配由字母、數字、-_ 組成的字串<slug:post_slug>
uuid匹配 UUID 格式字串<uuid:id>

處理 GET 請求:列出資料

在實際應用中,GET 請求通常用來「取得」或「列出」資料。以下範例示範如何從資料庫查詢文章列表:

# views.py
from django.shortcuts import render
from .models import Post

def post_list(request):
    """列出所有已發布的文章"""
    posts = Post.objects.filter(is_published=True).order_by('-created_at')

    # 從 GET 參數取得搜尋關鍵字(選填)
    keyword = request.GET.get('q', '')
    if keyword:
        posts = posts.filter(title__icontains=keyword)

    context = {
        'posts': posts,
        'keyword': keyword,
    }
    return render(request, 'blog/post_list.html', context)
    # render() 會載入模板、注入 context 變數、回傳 HttpResponse

當使用者訪問 /posts/?q=django 時,View 會從 GET 參數中取得搜尋關鍵字 django,過濾出標題包含該關鍵字的文章,並透過 render() 渲染模板後回傳。


處理 POST 請求:建立資料

POST 請求通常用來「提交」或「建立」資料。在同一個 View 中,我們經常需要同時處理 GET(顯示表單)和 POST(接收表單資料)兩種請求:

# views.py
from django.shortcuts import render, redirect
from .forms import PostForm

def post_create(request):
    """建立新文章:GET 顯示空白表單,POST 處理表單提交"""
    if request.method == 'POST':
        # POST 請求:接收使用者提交的表單資料
        form = PostForm(request.POST)
        if form.is_valid():
            # 表單驗證通過,儲存到資料庫
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            # 建立成功,重導向到文章詳情頁
            return redirect('post_detail', pk=post.pk)
    else:
        # GET 請求:建立空白表單
        form = PostForm()

    # GET 或表單驗證失敗時,渲染表單頁面
    return render(request, 'blog/post_form.html', {'form': form})

這個模式是 Django FBV 中最經典的「GET 顯示 / POST 處理」雙重職責(Dual Responsibility)模式,幾乎所有表單處理的 View 都遵循這個結構。


render() 快捷函數

render() 是 Django 最常用的快捷函數(Shortcut Function),它結合了「載入模板」和「回傳回應」兩個步驟:

from django.shortcuts import render

def about_page(request):
    context = {
        'title': '關於我們',
        'team_members': ['Alice', 'Bob', 'Charlie'],
    }
    # render(request, 模板路徑, context 字典)
    return render(request, 'pages/about.html', context)

render() 等同於以下寫法的簡化版:

from django.template.loader import get_template
from django.http import HttpResponse

def about_page(request):
    template = get_template('pages/about.html')
    context = {'title': '關於我們'}
    html = template.render(context, request)
    return HttpResponse(html)

實務上幾乎不會使用手動載入模板的寫法,直接用 render() 即可。


redirect() 重導向

redirect() 快捷函數用於回傳 HTTP 重導向回應(預設為 302 暫時重導向),常用於表單提交成功後將使用者引導到另一個頁面:

from django.shortcuts import redirect

def some_view(request):
    # 方式一:使用 URL name(推薦)
    return redirect('post_list')

    # 方式二:使用 URL name 並帶入參數
    return redirect('post_detail', pk=42)

    # 方式三:直接指定 URL 路徑
    return redirect('/articles/')

    # 方式四:永久重導向(301)
    return redirect('post_list', permanent=True)

最佳實踐: 建議使用 URL name 搭配 redirect(),而非寫死 URL 路徑。這樣即使未來修改了 URL 結構,也不需要逐一更新 View 中的路徑。


JsonResponse:回傳 JSON 資料

當你的 View 需要回傳 JSON(JavaScript Object Notation)格式的資料時(例如提供 API 給前端 JavaScript 呼叫),可以使用 JsonResponse

from django.http import JsonResponse

def api_post_list(request):
    """回傳文章列表的 JSON 資料"""
    posts = Post.objects.filter(is_published=True).values('id', 'title', 'created_at')
    data = {
        'status': 'ok',
        'count': posts.count(),
        'results': list(posts),
    }
    # safe=False 允許回傳非 dict 的頂層資料(如 list)
    return JsonResponse(data)
    # 輸出:{"status": "ok", "count": 3, "results": [...]}

def api_post_detail(request, pk):
    """回傳單篇文章的 JSON 資料"""
    try:
        post = Post.objects.get(pk=pk)
    except Post.DoesNotExist:
        return JsonResponse({'error': '文章不存在'}, status=404)

    data = {
        'id': post.pk,
        'title': post.title,
        'content': post.content,
        'created_at': post.created_at.isoformat(),
    }
    return JsonResponse(data)

JsonResponse 會自動將 Python 字典序列化(Serialize)為 JSON 格式,並設定回應的 Content-Typeapplication/json


get_object_or_404() 快捷函數

在查詢單一物件時,如果物件不存在,我們通常希望回傳 404 錯誤頁面,而非讓程式拋出未處理的例外。get_object_or_404() 正是為此設計的快捷函數:

from django.shortcuts import get_object_or_404, render

def post_detail(request, pk):
    # 如果 pk 對應的 Post 不存在,自動回傳 404 頁面
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/post_detail.html', {'post': post})

這等同於以下手動處理的寫法:

from django.http import Http404

def post_detail(request, pk):
    try:
        post = Post.objects.get(pk=pk)
    except Post.DoesNotExist:
        raise Http404("文章不存在")
    return render(request, 'blog/post_detail.html', {'post': post})

get_object_or_404() 也支援額外的篩選條件:

# 查詢指定作者的文章,不存在則 404
post = get_object_or_404(Post, pk=pk, author=request.user)

# 也可以傳入 QuerySet
published_posts = Post.objects.filter(is_published=True)
post = get_object_or_404(published_posts, pk=pk)

裝飾器:限制 HTTP 方法

Django 提供了多個裝飾器(Decorator)來限制 View 只接受特定的 HTTP 方法(HTTP Method),如果請求使用了不允許的方法,Django 會自動回傳 405 Method Not Allowed 錯誤。

from django.views.decorators.http import require_http_methods, require_GET, require_POST

# 只允許 GET 和 POST 方法
@require_http_methods(["GET", "POST"])
def post_create(request):
    if request.method == 'POST':
        # 處理表單提交
        pass
    else:
        # 顯示表單
        pass

# 只允許 GET 方法(語法糖)
@require_GET
def post_list(request):
    posts = Post.objects.all()
    return render(request, 'blog/post_list.html', {'posts': posts})

# 只允許 POST 方法(語法糖)
@require_POST
def post_delete(request, pk):
    post = get_object_or_404(Post, pk=pk, author=request.user)
    post.delete()
    return redirect('post_list')

@require_GET@require_POST 分別是 @require_http_methods(["GET"])@require_http_methods(["POST"]) 的簡寫形式,讓程式碼更簡潔。


@login_required 簡介

@login_required 裝飾器用於保護 View,確保只有已登入的使用者才能存取。如果使用者尚未登入,Django 會自動將他們重導向到登入頁面:

from django.contrib.auth.decorators import login_required

@login_required
def post_create(request):
    """只有登入的使用者才能建立文章"""
    # 這裡的 request.user 保證是已登入的使用者
    pass

@login_required(login_url='/accounts/login/')
def dashboard(request):
    """自訂登入頁面的 URL"""
    return render(request, 'dashboard.html')

當未登入的使用者嘗試訪問被 @login_required 保護的 View 時,Django 會將他們重導向到 settings.LOGIN_URL(預設為 /accounts/login/),並在 URL 中附帶 next 參數,讓使用者登入後能自動回到原本要訪問的頁面。


完整 CRUD 範例

讓我們把前面學到的所有知識整合起來,以一個部落格的 Post Model 為例,實作完整的 CRUD(Create、Read、Update、Delete)功能。

Model 與 Form 定義

# models.py
from django.db import models
from django.contrib.auth.models import User

class Post(models.Model):
    title = models.CharField('標題', max_length=200)
    content = models.TextField('內容')
    author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='作者')
    is_published = models.BooleanField('已發布', default=False)
    created_at = models.DateTimeField('建立時間', auto_now_add=True)
    updated_at = models.DateTimeField('更新時間', auto_now=True)

    class Meta:
        ordering = ['-created_at']

    def __str__(self):
        return self.title
# forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content', 'is_published']
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 8}),
        }

Views(完整 CRUD)

# views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods, require_GET, require_POST
from .models import Post
from .forms import PostForm


# ─── Read(列表) ───
@require_GET
def post_list(request):
    """列出所有已發布的文章"""
    posts = Post.objects.filter(is_published=True)
    return render(request, 'blog/post_list.html', {'posts': posts})


# ─── Read(詳情) ───
@require_GET
def post_detail(request, pk):
    """查看單篇文章"""
    post = get_object_or_404(Post, pk=pk, is_published=True)
    return render(request, 'blog/post_detail.html', {'post': post})


# ─── Create(建立) ───
@login_required
@require_http_methods(["GET", "POST"])
def post_create(request):
    """建立新文章"""
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user  # 自動設定作者為當前使用者
            post.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = PostForm()
    return render(request, 'blog/post_form.html', {'form': form, 'action': '建立'})


# ─── Update(更新) ───
@login_required
@require_http_methods(["GET", "POST"])
def post_update(request, pk):
    """更新文章(僅限作者本人)"""
    post = get_object_or_404(Post, pk=pk, author=request.user)
    if request.method == 'POST':
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            form.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = PostForm(instance=post)
    return render(request, 'blog/post_form.html', {
        'form': form,
        'post': post,
        'action': '更新',
    })


# ─── Delete(刪除) ───
@login_required
@require_http_methods(["GET", "POST"])
def post_delete(request, pk):
    """刪除文章(僅限作者本人)"""
    post = get_object_or_404(Post, pk=pk, author=request.user)
    if request.method == 'POST':
        post.delete()
        return redirect('post_list')
    # GET 請求:顯示確認刪除頁面
    return render(request, 'blog/post_confirm_delete.html', {'post': post})

URL 路由設定

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('posts/', views.post_list, name='post_list'),
    path('posts/<int:pk>/', views.post_detail, name='post_detail'),
    path('posts/create/', views.post_create, name='post_create'),
    path('posts/<int:pk>/update/', views.post_update, name='post_update'),
    path('posts/<int:pk>/delete/', views.post_delete, name='post_delete'),
]

以上五個 URL 對應五個 View 函式,涵蓋了完整的 CRUD 操作。注意 create/ 路由必須放在 <int:pk>/ 之前,否則 Django 會嘗試將 create 當作 pk 來匹配,導致錯誤。


總結

在這篇教學中,我們深入學習了 Django Function-Based Views(FBV)的核心知識:

  • View 的角色:在 MTV 架構中,View 負責接收 HttpRequest、執行邏輯、回傳 HttpResponse,是連接 Model 與 Template 的橋樑。
  • HttpRequest 物件:封裝了請求的所有資訊,包括 methodGETPOSTbodyusersession 等屬性。
  • HttpResponse 物件:代表伺服器的回應,可以設定 status_codecontent 以及自訂標頭。
  • 快捷函數render() 結合模板渲染、redirect() 處理重導向、get_object_or_404() 安全地查詢單一物件。
  • JsonResponse:專門用於回傳 JSON 格式的回應,自動處理序列化與 Content-Type 設定。
  • 裝飾器@require_http_methods@require_GET@require_POST 限制允許的 HTTP 方法;@login_required 保護需要登入才能存取的 View。
  • 完整 CRUD 範例:以 Post Model 為例,展示了列表、詳情、建立、更新、刪除五個 View 的實作方式與 URL 路由設定。

FBV 的最大優勢是直觀易懂,程式碼的執行流程一目了然。對於邏輯簡單或高度客製化的需求,FBV 是非常好的選擇。在後續的教學中,我們將學習 Class-Based Views(CBV),透過類別繼承和 Mixin 機制實現更高效的程式碼複用。

BenZ Software Developer

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