在上一篇文章中,我們探討了Django的類別基礎視圖(CBV)為何重要,以及與函數基礎視圖(FBV)相比的優點。本篇文章將深入了解所有CBV的根本,即 Django的基本View類別

“要正確理解CBV,首先要了解django.views.View是如何運作以及其結構。”


1. Django View類別是什麼?

Django的所有類別基礎視圖都繼承自django.views.View類別,使用的形式如下:

from django.views import View

class MyView(View):
    def get(self, request, *args, **kwargs):
        # 處理GET請求的邏輯
        ...

    def post(self, request, *args, **kwargs):
        # 處理POST請求的邏輯
        ...

View類別的特點

  1. HTTP方法的分離
    當請求進來時,Django會內部透過dispatch()方法自動調用對應請求方法的函數,如get(), post(), put(), delete()等。

    • 因此不必像FBV那樣使用“if request.method == 'GET': … else: …”的條件語句,使代碼結構更為清晰。
  2. 繼承與擴展性
    可以繼承View類別創建多個子視圖,將通用邏輯放在父類別中,將個別邏輯放在子類別中,極大化代碼重用。


2. View類別的基本結構和原理

Django View結構 LEGO風格

最簡單的示例

# views.py
from django.http import HttpResponse
from django.views import View

class HelloCBV(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse("Hello, CBV!")
  • get()方法:當瀏覽器發出GET請求(在地址欄輸入URL、點擊鏈接等)時執行。

  • HttpResponse("Hello, CBV!"):直接創建並返回文本形式的響應。

urls.py連接

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

urlpatterns = [
    path('hello/', HelloCBV.as_view(), name='hello_cbv'),
]
  • .as_view()方法將CBV轉換成Django能夠理解的形式。

  • 與URL模式連接時,當以http://example.com/hello/發出GET請求時,將調用HelloCBVget()方法。


3. View類別的用法和實務示例

如上所示,單純返回“一個字符串”的情況並不常見。在實際項目中,通常會進行JSON響應模板渲染執行數據庫查詢等操作。在此我們將以JSON響應模板渲染為例進行探討。

3.1 JSON響應處理示例

# views.py
from django.http import JsonResponse
from django.views import View

class UserDataView(View):
    def get(self, request, *args, **kwargs):
        # 假設通過GET參數接收user_id
        user_id = request.GET.get('user_id', None)
        if not user_id:
            return JsonResponse({'error': 'No user_id provided'}, status=400)

        # 實際將執行DB查詢等操作
        user_data = {
            'id': user_id,
            'name': f'User_{user_id}',
            'role': 'admin'
        }
        return JsonResponse(user_data, status=200)

    def post(self, request, *args, **kwargs):
        # 可以解析POST請求主體中的JSON或form數據。
        # 可以使用request.POST, request.body, request.FILES等
        # 在實際服務中執行JSON解析後的DB保存邏輯
        new_user_data = {
            'id': 123,
            'name': 'NewUser',
            'role': 'member'
        }
        return JsonResponse(new_user_data, status=201)
  • JsonResponse:Django中方便生成JSON響應的工具。

  • request.GET, request.POST:可從Django的HttpRequest對象中提取GET/POST參數。

  • 在實務中,數據將與DB模型關聯或透過序列化器以更結構化的方式處理。

3.2 模板渲染示例

# views.py
from django.shortcuts import render
from django.views import View

class GreetingView(View):
    def get(self, request, *args, **kwargs):
        context = {
            'title': 'Welcome to My Site',
            'greeting': 'Hello, this is a GreetingView!'
        }
        return render(request, 'greeting.html', context)
<!-- templates/greeting.html -->
<!DOCTYPE html>
<html>
<head>
  <title>{{ title }}</title>
</head>
<body>
  <h1>{{ greeting }}</h1>
</body>
</html>
  • render()函數將查找模板文件(greeting.html)並渲染成HTML以HttpResponse形式返回。

  • 以此方式,CBV內部也可以輕鬆進行模板渲染。


4. View類別的擴展與重用性

View類別有用的主要原因之一就是“代碼重用”。隨著項目規模的擴大,將多個視圖共同需要的邏輯(例如:日誌、授權檢查、公共數據處理等)放在父類別中,由子類別繼承使用會更加有效率。

4.1 將共通邏輯放在父類別中

# views.py
from django.http import JsonResponse
from django.views import View

class BaseLoggingView(View):
    def dispatch(self, request, *args, **kwargs):
        # 對所有請求(GET/POST等)執行共通邏輯
        print(f"[Log] {request.method} request at {request.path}")
        return super().dispatch(request, *args, **kwargs)

class ChildLoggingView(BaseLoggingView):
    def get(self, request, *args, **kwargs):
        data = {'message': 'GET response from child'}
        return JsonResponse(data)

    def post(self, request, *args, **kwargs):
        data = {'message': 'POST response from child'}
        return JsonResponse(data)
  • dispatch()方法:是用來控制Django執行視圖邏輯前後的核心方法。

  • 在這個示例中,對所有請求打印日誌後,通過super().dispatch()來調用實際的get(), post()等方法。

4.2 通過Mixin添加功能

Django中經常使用Mixin技術來“混合特定功能”。例如,將LoginRequiredMixin(檢查登入狀態)、PermissionRequiredMixin(檢查權限)等混合到CBV中。

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View
from django.http import JsonResponse

class SecuredView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        return JsonResponse({'message': 'You are authenticated.'})
  • Mixin的使用計劃在後續說明通用視圖或權限管理時進一步詳述,因此這裡只需了解“可以這樣組合功能”即可。

5. 與FBV的簡單比較

分類 FBV (函數基礎視圖) CBV (類別基礎視圖)
編寫方式 函數形式編寫 類別&方法形式編寫
HTTP方法處理 if request.method == 'GET': ... else: ... get(), post()等方法分離
代碼重複可能性 當邏輯變複雜時容易出現重複代碼 可透過繼承和Mixin重用共通邏輯
擴展性 共通邏輯通常混在函數內 透過類繼承結構應對複雜需求更為合適
可讀性 在簡單原型中有利 隨著規模增大,更適合保持明確結構
  • FBV適合快速編寫小型或簡單功能,而CBV在擴展性和維護性上更具優勢。

  • 特別是在團隊項目或中大型項目中,使用CBV將更有效率。


6. 更深入探討:dispatch()與URL參數

Django View請求流程圖

6.1 為何直接重寫dispatch()

  • 共通前後處理:可在每次請求時檢查DB連接、執行權限檢查或記錄日誌等。

  • 方法映射:內部使用request.method.lower()查找並調用get(), post(), put()等。如果想額外自定義處理如PATCH等方法,可以重寫dispatch()來處理。

class CustomDispatchView(View):
    def dispatch(self, request, *args, **kwargs):
        print("Custom pre-processing logic here.")
        response = super().dispatch(request, *args, **kwargs)
        print("Custom post-processing logic here.")
        return response

6.2 接收URL參數

如果在urls.py中如<int:user_id>/指定了路徑參數,則可在CBV的get(), post()方法中通過kwargs接收值。

![Django View結構 LEGO風格](/media/whitedec/blog_img/view_class_lego_structure.webp)# urls.py
urlpatterns = [
    path('user/<int:user_id>/', UserDetailView.as_view(), name='user_detail'),
]

# views.py
class UserDetailView(View):
    def get(self, request, *args, **kwargs):
        user_id = kwargs.get('user_id')
        # 以user_id查詢DB
        return HttpResponse(f"User ID: {user_id}")
  • 與FBV相同,可以從kwargs中取出URL參數使用。

7. 更豐富的實際案例:簡單留言板示例

對於想要更實際運用View類別的朋友,這裡提供一個簡單的留言板列表和詳細頁面的示例代碼。(在下一篇文章中將介紹泛型視圖,使CRUD實現更為簡便,但這裡主要展示僅使用基本View類別的可行性。)

# models.py - 示例模型
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title
# views.py
from django.views import View
from django.shortcuts import render, get_object_or_404
from .models import Post

# 查看帖子列表
class PostListView(View):
    def get(self, request, *args, **kwargs):
        posts = Post.objects.order_by('-created_at')
        context = {'posts': posts}
        return render(request, 'post_list.html', context)

# 查看帖子詳細
class PostDetailView(View):
    def get(self, request, *args, **kwargs):
        post_id = kwargs.get('pk')
        post = get_object_or_404(Post, pk=post_id)
        context = {'post': post}
        return render(request, 'post_detail.html', context)
<!-- post_list.html -->
<!DOCTYPE html>
<html>
<head>
    <title>帖子列表</title>
</head>
<body>
    <h1>帖子列表</h1>
    <ul>
        {% for post in posts %}
            <li>
                <a href="{% url 'post_detail' post.id %}">{{ post.title }}</a>
            </li>
        {% endfor %}
    </ul>
</body>
</html>
<!-- post_detail.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{{ post.title }}</title>
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
    <p>創建日期: {{ post.created_at }}</p>
</body>
</html>
# urls.py
from django.urls import path
from .views import PostListView, PostDetailView

urlpatterns = [
    path('posts/', PostListView.as_view(), name='post_list'),
    path('posts/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
]
  • PostListView:在接收到GET請求時查詢所有帖子並傳遞給模板(post_list.html)。

  • PostDetailView:根據URL參數pk查找特定帖子並傳遞給模板(post_detail.html)。

  • 這樣,我們可以僅用基本View類別輕鬆構建列表和詳細頁面。

不過,若要構成完整的CRUD,則需要Create/Update/Delete邏輯。Django提供的泛型視圖(Generic Views)豐富了這部分內容,詳細內容將在下一篇文章中介紹!


8. 結論和下一篇文章預告

至此,您對Django的基本View類別的運作方式及實際應用有了初步了解。總結要點如下:

  1. 繼承django.views.View,重寫get() / post() / dispatch()等方法來響應HTTP請求。

  2. 將共通邏輯放在父類別中,只重寫子視圖中的必要部分,這樣可以極大化代碼重用

  3. 能夠在模板渲染、JSON響應、DB查詢等多種場景中輕鬆編寫視圖邏輯。

  4. 如簡單的消息板示例,僅用基本View類別也能構建完整的項目。
    不過,若想更方便地處理CRUD操作,了解Django提供的泛型視圖(Generic Views)會更為便利。

在下一篇文章中,我們將專注於簡化表單處理的FormView類別。透過學習,我們將掌握“接收用戶輸入、保存、在驗證失敗時顯示錯誤信息”等過程的自動化方法。

CBV系列仍將持續!

  • 第3篇:“透過FormView簡化表單處理”
  • 第4篇:“ListView和DetailView的使用方法”
  • 第5篇:“透過CreateView, UpdateView, DeleteView實現CRUD”
  • …(之後還會持續更新)

查看過往文章


額外參考資料

希望這篇文章能解答你對基本View類別的結構與應用方法的疑問。在下一篇文章中,我們將學習如何通過FormView輕鬆處理“表單填寫→驗證→錯誤處理→成功重定向”等過程,期待您的關注!


“Django的View類別不僅僅是根據HTTP方法分離代碼,
它還能顯著改善大型項目的擴展性與維護性,成為強大的工具。”

期待能繼續深入探討CBV,帶來更高效的Django開發!