Hệ thống View trong Django REST Framework
1. Lớp APIView
APIView là lớp cơ sở cho tất cả các view trong DRF, kế thừa trực tiếp từ lớp View của Django nhưng được bổ sung các tính năng quan trọng để xây dựng Web API:
- Request và Response mới: DRF đóng gói
HttpRequestcủa Django thành đối tượngRequestlinh hoạt hơn, cho phép truy cập dữ liệu quarequest.datathay vì phải phân biệt giữaPOSThayPUT. - Xử lý định dạng: Sử dụng
parser_classesđể giải mã dữ liệu đầu vào vàrenderer_classesđể định dạng dữ liệu đầu ra. - Chính sách bảo mật: Tự động loại bỏ kiểm tra CSRF (vì REST thường dùng Token/Session riêng) và thực thi chu kỳ: Xác thực (Authentication) -> Phân quyền (Permission) -> Giới hạn lưu lượng (Throttling).
# Cấu trúc cơ bản của APIView nội bộ
def dispatch(self, request, *args, **kwargs):
request = self.initialize_request(request, *args, **kwargs)
self.request = request
try:
self.initial(request, *args, **kwargs) # Thực hiện 3 đại xác thực
# ... xử lý method (GET, POST, ...)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
2. Lớp GenericAPIView
Kế thừa từ APIView, GenericAPIView cung cấp các thuộc tính và phương thức hỗ trợ làm việc với Model và Serializer một cách hệ thống hơn thông qua queryset và serializer_class.
from rest_framework.generics import GenericAPIView
from .models import Product
from .serializers import ProductSerializer, ProductDetailSerializer
class ProductManagementView(GenericAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
def get_serializer_class(self):
# Thay đổi serializer dựa trên phương thức yêu cầu
if self.request.method == 'GET':
return ProductDetailSerializer
return super().get_serializer_class()
def get(self, request, *args, **kwargs):
objs = self.get_queryset()
serializer = self.get_serializer(objs, many=True)
return Response(serializer.data)
3. Mixins và View con (Concrete View Classes)
DRF cung cấp 5 Mixin cơ bản tương ứng với các thao tác CRUD:
ListModelMixin: Cung cấp phương thứclist().CreateModelMixin: Cung cấp phương thứccreate().RetrieveModelMixin: Cung cấp phương thứcretrieve().UpdateModelMixin: Cung cấp phương thứcupdate().DestroyModelMixin: Cung cấp phương thứcdestroy().
Các lớp View con (như ListCreateAPIView, RetrieveUpdateDestroyAPIView) đơn giản là sự kết hợp giữa GenericAPIView và các Mixin này để rút ngắn mã nguồn.
ViewSet và ViewSetMixin
ViewSet không cung cấp các trình xử lý phương thức (get, post), thay vào đó nó cung cấp các hành động (list, create, retrieve, update, destroy). Việc ánh xạ hành động vào phương thức HTTP chỉ diễn ra khi View được định nghĩa trong URL.
from rest_framework import viewsets
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
# custom action
@action(detail=True, methods=['post'])
def mark_completed(self, request, pk=None):
task = self.get_object()
task.status = 'completed'
task.save()
return Response({'status': 'task marked as done'})
Hệ thống Routing tự động
Khi sử dụng ViewSet, chúng ta không cần khai báo từng URL thủ công mà sử dụng các lớp Router.
1. SimpleRouter và DefaultRouter
SimpleRouter: Tạo ra các đường dẫn cơ bản cho tập hợp dữ liệu và từng mục riêng lẻ.DefaultRouter: Tương tự như SimpleRouter nhưng bổ sung một trang API Root mặc định giúp kiểm tra các endpoint trên trình duyệt dễ dàng hơn.
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'tasks', TaskViewSet, basename='task')
urlpatterns = [
# Các route khác
]
urlpatterns += router.urls
2. Decorator @action
Để thêm các endpoint tùy chỉnh ngoài các thao tác CRUD mặc định, DRF cung cấp decorator @action.
methods: Danh sách các phương thức HTTP (GET, POST...).detail: Nếu làTrue, URL sẽ bao gồm ID (ví dụ:/tasks/1/mark_completed/). NếuFalse, nó áp dụng cho toàn bộ danh sách (ví dụ:/tasks/export_csv/).url_path: Tên đường dẫn trên URL (mặc định lấy tên hàm).
class OrderViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
@action(detail=False, methods=['get'], url_path='recent-orders')
def get_recent(self, request):
recent_orders = self.get_queryset().order_by('-created_at')[:5]
serializer = self.get_serializer(recent_orders, many=True)
return Response(serializer.data)