Trong Django, cách triển khai logic xử lý yêu cầu HTTP được chia thành hai mô hình chính: Function-Based Views (FBV) và Class-Based Views (CBV). Sự lựa chọn giữa hai kiểu này ảnh hưởng trực tiếp đến khả năng tái sử dụng, mở rộng và bảo trì mã nguồn.
View là gì?
Một view là một thành phần nhận vào một đối tượng HttpRequest và trả về một đối tượng HttpResponse. Đây có thể là một hàm Python (FBV) hoặc một lớp kế thừa từ django.views.View (CBV). Tất cả các view đều được định nghĩa trong tệp views.py của ứng dụng.
Ví dụ đơn giản trả về thời gian hiện tại dưới dạng HTML:
from django.http import HttpResponse
import datetime
def show_timestamp(request):
current = datetime.datetime.now()
content = f"<html><body>Thời điểm hiện tại: {current}</body></html>"
return HttpResponse(content)
Function-Based Views (FBV)
FBV là hàm Python chuẩn, nhận tham số đầu tiên là request, thực hiện xử lý và trả về phản hồi. Cấu trúc rõ ràng, dễ hiểu cho các tác vụ đơn giản.
Ví dụ xử lý cả GET và POST trong một hàm:
from django.shortcuts import render, redirect
from app01.models import Product
def create_product(request):
if request.method == "POST":
name = request.POST.get("product_name")
price = request.POST.get("price")
Product.objects.create(name=name, price=price)
return redirect("product_list")
return render(request, "create_product.html")
Đăng ký trong urls.py:
from django.urls import path
from app01 import views
urlpatterns = [
path("product/new/", views.create_product),
]
Class-Based Views (CBV)
CBV tổ chức logic theo phương thức tương ứng với HTTP method (GET, POST, PUT…), giúp phân tách trách nhiệm rõ ràng hơn. Lớp phải kế thừa từ View và triển khai các phương thức như get(), post(), v.v.
Ví dụ minh họa:
from django.views import View
from django.shortcuts import render, redirect
from app01.models import Category
class CreateCategory(View):
def get(self, request):
return render(request, "create_category.html")
def post(self, request):
title = request.POST.get("title")
Category.objects.create(title=title)
return redirect("category_index")
Đăng ký URL với cú pháp đặc biệt — gọi phương thức tĩnh as_view():
urlpatterns = [
path("category/add/", views.CreateCategory.as_view()),
]
Cơ chế hoạt động của CBV
Khi một yêu cầu đến đường dẫn được ánh xạ tới CBV, Django thực hiện tuần tự:
- Gọi
CreateCategory.as_view()→ trả về một hàmviewbao bọc. - Hàm
viewkhởi tạo instance lớp (self), gánself.request = request. - Gọi
self.dispatch(request, *args, **kwargs)— đây là cổng trung tâm. dispatchkiểm trarequest.methodcó nằm trongself.http_method_nameshay không.- Nếu hợp lệ, dùng
getattr(self, method_name)để lấy phương thức tương ứng (ví dụself.post) và thực thi. - Nếu không hợp lệ, gọi
self.http_method_not_allowed().
Giá trị mặc định của http_method_names gồm: ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']. Bạn có thể ghi đè để giới hạn phương thức hỗ trợ:
class ReadOnlyView(View):
http_method_names = ['get', 'head']
def get(self, request):
return render(request, "read_only.html")
Áp dụng decorator vào FBV và CBV
Decorator trong FBV được áp dụng trực tiếp như thông thường:
import time
from functools import wraps
def measure_time(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
start = time.time()
response = func(request, *args, **kwargs)
print(f"[{func.__name__}] Thời gian thực thi: {time.time() - start:.4f}s")
return response
return wrapper
@measure_time
def dashboard(request):
return render(request, "dashboard.html")
Với CBV, cần chuyển đổi decorator sang dạng áp dụng cho phương thức bằng method_decorator:
- Áp dụng trên từng phương thức:
from django.utils.decorators import method_decorator
@method_decorator(measure_time, name="get")
class StatsView(View):
def get(self, request):
return render(request, "stats.html")
- Áp dụng trên
dispatchđể bao phủ toàn bộ phương thức:
class AuthView(View):
@method_decorator(measure_time)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
def get(self, request):
return render(request, "login.html")
def post(self, request):
# xử lý đăng nhập
return redirect("home")
- Áp dụng trên toàn lớp với nhiều phương thức:
@method_decorator(measure_time, name="get")
@method_decorator(measure_time, name="post")
class ContactView(View):
def get(self, request):
return render(request, "contact_form.html")
def post(self, request):
# lưu form
return redirect("contact_success")