Hệ thống view trong Django REST Framework
Hệ thống view trong Django REST Framework được tổ chức theo cấu trúc phân cấp như sau:
rest_framework.views: View cơ bản (APIView)rest_framework.generics: View công cụ (GenericAPIView)rest_framework.mixins: Bộ công cụ view (Create/Destroy/List/Retrieve/Update)rest_framework.viewsets: ViewSet
APIView
APIView thực hiện ba chức năng chính:
- Ghi đè phương thức
as_view(), trong đó gọi phương thứcas_view()của lớp cha và thêm xác thực csrf:
view = super().as_view(**initkwargs)
return csrf_exempt(view)
- Ghi đè phương thức
dispatch(), đây là hàm cốt lõi của DRF thực hiện các chức năng chính của framework.
GenericAPIView
GenericAPIView nằm tại rest_framework.generics.GenericAPIView, kế thừa từ APIView và cung cấp thêm các thuộc tính tiện dụng:
class GenericAPIView(views.APIView):
"""
Lớp cơ sở cho tất cả các view generic khác.
"""
# Bạn cần thiết lập các thuộc tính này,
# hoặc ghi đè `get_queryset()`/`get_serializer_class()`.
queryset = None
serializer_class = None
# Nếu muốn sử dụng đối tượng lookup khác pk, hãy đặt 'lookup_field'.
# Đối với các yêu cầu lookup phức tạp, hãy ghi đè `get_object()`.
lookup_field = 'pk'
lookup_url_kwarg = None
# Các lớp backend filter để sử dụng cho việc lọc queryset
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
# Phong cách sử dụng cho phân trang queryset.
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
def get_queryset(self):
.......
Sử dụng GenericAPIView
So sánh cách triển khai API truy vấn bằng APIView và GenericAPIView:
# Sử dụng APIView
class SachAPIView(APIView):
def get(self, request, *args, **kwargs):
ma_so = kwargs.get('ma_so')
if ma_so:
# Truy vấn đơn
sach = Sach.objects.get(ma_so=ma_so)
nhieu = False
else:
# Truy vấn nhiều
sach = Sach.objects.filter(is_delete=False).all()
nhieu = True
serializer = SachSerializer(instance=sach, many=nhieu)
return MyResponse(result=serializer.data)
# Sử dụng GenericAPIView
class SachGenericAPIView(GenericAPIView):
queryset = Sach.objects.filter(is_delete=False)
serializer_class = SachSerializer
def get(self, request, *args, **kwargs):
ma_so = kwargs.get('ma_so')
if ma_so:
# Truy vấn đơn
return self.retrieve(request, *args, **kwargs)
# Truy vấn nhiều
return self.list(request, *args, **kwargs)
def retrieve(self, request, *args, **kwargs):
sach = self.get_object()
serializer = self.get_serializer(instance=sach)
return MyResponse(result=serializer.data)
def list(self, request, *args, **kwargs):
sach = self.get_queryset()
serializer = self.get_serializer(instance=sach, many=True)
return MyResponse(result=serializer.data)
Thay đổi khi sử dụng GenericAPIView
- Định nghĩa hai thuộc tính:
querysetcho tập kết quả truy vấn vàserializer_classcho lớp serializer. - Thay thế ORM bằng
self.get_queryset()hoặcself.get_object(). - Thay thế việc khởi tạo serializer bằng
self.get_serializer().
Bộ công cụ Mixins
Mixins cung cấp các lớp đã được đóng gói sẵn cho các thao tác CRUD:
CreateModelMixin: Tạo đối tượngDestroyModelMixin: Xóa đối tượngListModelMixin: Liệt kê nhiều đối tượngRetrieveModelMixin: Lấy một đối tượngUpdateModelMixin: Cập nhật đối tượng
Sử dụng Mixins
class SachMixinGenericAPIView(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericAPIView):
queryset = Sach.objects.filter(is_delete=False)
serializer_class = SachSerializer
def get(self, request, *args, **kwargs):
ma_so = kwargs.get('ma_so')
if ma_so:
# Truy vấn đơn
response = self.retrieve(request, *args, **kwargs)
else:
# Truy vấn nhiều
response = self.list(request, *args, **kwargs)
return MyResponse(result=response.data)
Hàm hook trong Mixins
Mixins cung cấp các hàm hook để tùy chỉnh hành vi:
def perform_create(self, serializer):
serializer.save()
def perform_destroy(self, instance):
instance.delete()
def perform_update(self, serializer):
serializer.save()
# Ví dụ tùy chỉnh xóa
def perform_destroy(self, instance):
instance.is_delete = True
instance.save()
Lớp Generics
Django REST Framework cung cấp các lớp đã kết hợp sẵn giữa Mixins và GenericAPIView:
CreateAPIView: Tạo đối tượngListAPIView: Liệt kê đối tượngRetrieveAPIView: Lấy một đối tượngDestroyAPIView: Xóa đối tượngUpdateAPIView: Cập nhật đối tượngListCreateAPIView: Liệt kê và tạoRetrieveUpdateAPIView: Lấy và cập nhậtRetrieveDestroyAPIView: Lấy và xóaRetrieveUpdateDestroyAPIView: Lấy, cập nhật và xóa
ViewSet
ViewSet giúp đơn giản hóa việc ánh xạ HTTP methods với các actions:
ViewSetMixin
ViewSetMixin ghi đè as_view() để thêm tham số actions:
class ViewSetMixin:
"""
Đây là phần magic.
Ghi đè `.as_view()` để lấy tham số `actions` thực hiện ánh xạ
HTTP methods đến actions trên Resource.
"""
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.action_map = actions
# Ánh xạ methods với actions
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
return self.dispatch(request, *args, **kwargs)
view.actions = actions
return csrf_exempt(view)
GenericViewSet và ViewSet
Các lớp kết hợp giữa ViewSetMixin và GenericAPIView/APIView:
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
pass
class ViewSet(ViewSetMixin, views.APIView):
pass
ModelViewSet và ReadOnlyModelViewSet
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
Một viewset cung cấp các actions mặc định: create, retrieve, update,
partial_update, destroy và list.
"""
pass
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
Một viewset chỉ cung cấp các actions list và retrieve.
"""
pass
Hệ thống định tuyến (Router)
Router giúp tự động tạo URL patterns cho ViewSet:
from rest_framework.routers import DefaultRouter
# 1. Tạo đối tượng router
router = DefaultRouter()
# 2. Đăng ký viewset
router.register(r'sach', views.SachModelViewSet, basename='sach')
router.register(r'tacgia', views.TacGiaModelViewSet, basename='tacgia')
urlpatterns = [
# 3. Thêm URLs của router vào urlpatterns
path('', include(router.urls)),
]
Router tự động tạo các URLs cho các actions được định nghĩa trong ViewSet, giúp giảm thiểu việc viết thủ công URL patterns.