以下文章是 Django 類別基於視圖(CBV)探索系列的 第七篇,主要探討如何利用 Mixin重複使用共通功能,並有效地實現 權限管理登入檢查。在上一篇文章(第六篇)中,我們使用 TemplateViewRedirectView 自動化了簡單的頁面渲染和重定向,現在讓我們來了解如何最大化 CBV 的 重用性擴展性

前篇直接連結,請點擊以下鏈接!

TemplateView & RedirectView 使用方法


“提高 Django Mixin 的代碼重用性,  

輕鬆實現權限及登入檢查!”


1. Mixin,為何需要?

Mixin 概念圖 - 功能區塊組合的類別結構

Django CBV 本身提供了強大的重用性,但如果在多個視圖中有 共通需要的功能,該如何處理呢?例如,進入特定頁面時,通常需要 登入,或僅允許具備 特定權限 的用戶進入。這時出現的概念就是 Mixin

Mixin 是利用 多重繼承 的概念,將 相同的方法或屬性注入 多個類別。透過在視圖類中繼承特定功能的 Mixin 類別,我們可以減少 重複代碼,提高 維護性

主要 Mixin 使用範例:

  1. 權限管理: 驗證訪問特定視圖的用戶是否已登入,或其是否擁有特定權限。
  2. 表單處理邏輯: 處理多個表單所需的共通數據預處理或後處理邏輯。
  3. 模板上下文: 添加必須傳遞到所有視圖的上下文數據。
  4. 日誌記錄: 為特定視圖的執行前後添加日誌功能。

2. Django 的代表性 Mixin 使用法: LoginRequiredMixin

最常使用的 Mixin 之一就是 LoginRequiredMixin。顧名思義,該視圖必須強制要求用戶 登入 才可進入。

2.1 LoginRequiredMixin 使用法

LoginRequiredMixin 包含在 django.contrib.auth.mixins 模組中。

# views.py
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Post # 示例模型

class MyPostListView(LoginRequiredMixin, ListView):
    model = Post
    template_name = 'posts/my_posts.html'
    context_object_name = 'posts'

    # 登入狀態下的用戶進入時的重定向 URL 設定 (選擇性)
    # login_url = '/accounts/login/' # settings.LOGIN_URL 為預設值
    # redirect_field_name = 'next'  # 登入後返回原頁面的查詢參數名稱 (預設值)
  • 繼承順序: 首先繼承 LoginRequiredMixin,再繼承 Django 的 Generic View (此處為 ListView)。通常因為 Mixin 是為了添加功能,所以把它放在核心視圖類之前是慣例。
  • LoginRequiredMixin 如果用戶未登入,將自動重定向到 settings.LOGIN_URL 指定的登入頁面。成功登入時亦可透過 next 查詢參數返回到原請求的頁面。

2.2 urls.py 連結

應用 LoginRequiredMixin 的視圖與一般 CBV 相同,連結到 urls.py

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

urlpatterns = [
    path('my-posts/', MyPostListView.as_view(), name='my_posts'),
]

現在當訪問 /my-posts/ 路徑時,如果未登入,將自動重定向到登入頁面。


3. 權限管理的 Mixin: PermissionRequiredMixin & UserPassesTestMixin

如果 LoginRequiredMixin 強制要求登入,那麼 PermissionRequiredMixinUserPassesTestMixin 則需要 具備特定權限 或滿足 特定條件 才可以訪問。

登入權限 Mixin 概念圖 - 訪問權限檢查

3.1 PermissionRequiredMixin 使用法

與 Django 的權限系統整合,僅允許擁有特定 permission 的用戶訪問。

# views.py
from django.views.generic import CreateView
from django.contrib.auth.mixins import PermissionRequiredMixin
from .models import Article
from .forms import ArticleForm

class ArticleCreateView(PermissionRequiredMixin, CreateView):
    model = Article
    form_class = ArticleForm
    template_name = 'articles/article_form.html'
    success_url = '/articles/'

    # 用 'app_label.permission_codename' 格式指定權限
    permission_required = 'app_label.add_article'
    # 若有多種權限之一也允許的情況下:
    # permission_required = ('app_label.add_article', 'app_label.change_article')
    # raise_exception = True # 無權限時拋出 403 Forbidden 錯誤 (預設為重定向到登入頁)
  • permission_required: 將訪問所需的權限指定為字符串(一個或元組)。主要使用 Django 的 auth 應用自動生成的權限 (add_model, change_model, delete_model, view_model)。

3.2 UserPassesTestMixin 使用法

當想檢查更為 複雜或自定義的條件 時使用。例如,要限制只能修改自己寫的評論,或者只能讓特定組的用戶進入等邏輯上非常有用。

# views.py
from django.views.generic import UpdateView
from django.contrib.auth.mixins import UserPassesTestMixin
from .models import Comment
from .forms import CommentForm

class CommentUpdateView(UserPassesTestMixin, UpdateView):
    model = Comment
    form_class = CommentForm
    template_name = 'comments/comment_form.html'
    success_url = '/comments/'

    # 覆蓋 test_func 方法來檢查條件
    def test_func(self):
        comment = self.get_object()
        # 確認當前登入用戶是否為評論作者
        return self.request.user == comment.author or self.request.user.is_superuser

    # raise_exception = True # 測試失敗時拋出 403 Forbidden 錯誤
  • test_func() 方法返回 True 將允許訪問,返回 False 則拒絕訪問。
  • self.request.user 可以用來存取當前登入的用戶對象。

4. 自定義 Mixin 的製作

除了 Django 提供的 Mixin 外,還可以製作 自己的 Mixin 以便在多個視圖中重複使用。例如,讓我們製作一個添加所有頁面共通的 current_year 上下文的 Mixin。

# common/mixins.py
from datetime import datetime

class CurrentYearMixin:
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['current_year'] = datetime.now().year
        return context

# views.py (使用範例)
from django.views.generic import TemplateView
from .common.mixins import CurrentYearMixin

class MyCustomPageView(CurrentYearMixin, TemplateView):
    template_name = 'my_custom_page.html'

# my_custom_page.html (在模板中使用)
<footer>&copy; {{ current_year }} 我的網站</footer>
  • CurrentYearMixin 覆蓋 get_context_data 方法以添加 current_year 到上下文中。呼叫 super().get_context_data(**kwargs) 以保持父類(或其他 Mixin)的上下文數據非常重要。

5. 與 FBV 的比較

功能 FBV (函數基於視圖) CBV + Mixin
登入/權限檢查 @login_required, @permission_required 裝飾器使用。 <br> 自定義條件可以在函數內直接編寫 if not request.user.is_authenticated: return redirect(...) LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin 等 Mixin 繼承簡單應用。 <br> 雖然與裝飾器相似,但更符合 CBV 結構。
代碼重用性 將共通邏輯分離為單獨函數以呼叫,或直接編寫函數裝飾器。 <br> 容易出現重複代碼。 通過 Mixin 將所需功能模塊化,通過多重繼承注入。 <br> 結構遠比 FBV 更清晰,重用性高。
代碼可讀性 每個函數中可能重複條件檢查邏輯。 僅查看視圖類的繼承列表即可輕鬆了解已應用的功能(權限、登入等)。
維護性 添加新功能時,可能需要修改多個函數。 僅修改 Mixin 類即可在所有使用該 Mixin 的視圖中生效。
開發生產力(關鍵字) “每個函數單獨處理,重複工作的可能性” “模組化,易於功能擴展,遵循 DRY(不要重複自己)原則,提高生產力”

6. 總結及下一篇預告

Mixin 是 Django 類別基於視圖的 真正力量,使我們能夠最大化 代碼重用性,模組化 共通功能,降低視圖類的複雜性,特別是在 身份驗證及權限管理 等網絡應用的必備元素上更加高效實現。

到目前為止,我們已經了解了從 CBV 的基本使用方法到複雜的 CRUD,以及通過 Mixin 擴展功能,幾乎探索了 Django CBV 所有的核心要素。

下一篇(系列第八篇)將會總結如何在實際項目中應用之前所講的 CBV,在 複雜場景 中的組合運用,以及 CBV 的優點與局限


重新查看舊文

  1. 類別基於視圖(CBV)探索系列 #1 – 為何從 FBV 轉向 CBV 及開發者心態
  2. 類別基於視圖(CBV)探索系列 #2 – 理解 Django 的基本 View 類別
  3. 類別基於視圖(CBV)探索系列 #3 – 通過 FormView 簡化表單處理
  4. 類別基於視圖(CBV)探索系列 #4 – ListView & DetailView 使用法
  5. 類別基於視圖(CBV)探索系列 #5 – 使用 CreateView、UpdateView、DeleteView 實現 CRUD
  6. 類別基於視圖(CBV)探索系列 #6 – TemplateView & RedirectView 使用法

“利用 Mixin 最大化 Django CBV 的潛力,  

構建易於維護且可擴展的網絡應用!”