Phân tích mã nguồn Django REST Framework: Hệ thống view và định tuyến

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:

  1. Ghi đè phương thức as_view(), trong đó gọi phương thức as_view() của lớp cha và thêm xác thực csrf:

view = super().as_view(**initkwargs)
return csrf_exempt(view)

  1. 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

  1. Định nghĩa hai thuộc tính: queryset cho tập kết quả truy vấn và serializer_class cho lớp serializer.
  2. Thay thế ORM bằng self.get_queryset() hoặc self.get_object().
  3. 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ượng
  • DestroyModelMixin: Xóa đối tượng
  • ListModelMixin: Liệt kê nhiều đối tượng
  • RetrieveModelMixin: Lấy một đối tượng
  • UpdateModelMixin: 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ượng
  • ListAPIView: Liệt kê đối tượng
  • RetrieveAPIView: Lấy một đối tượng
  • DestroyAPIView: Xóa đối tượng
  • UpdateAPIView: Cập nhật đối tượng
  • ListCreateAPIView: Liệt kê và tạo
  • RetrieveUpdateAPIView: Lấy và cập nhật
  • RetrieveDestroyAPIView: Lấy và xóa
  • RetrieveUpdateDestroyAPIView: 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.

Thẻ: Django REST Framework APIView GenericAPIView Mixins ViewSet

Đăng vào ngày 26 tháng 5 lúc 10:00