Sử dụng APIView, Phân tích mã nguồn Request và Serializer trong Django REST Framework

  1. Sử dụng cơ bản của APIView =============

1.1 Sử dụng View + JsonResponse

from django.http import JsonResponse
from .models import Book

class BookView(View):
    def get(self, request):
        books = Book.objects.all()
        result = []
        for book in books:
            result.append({'tieu_de': book.name, 'gia': book.price, 'nha_xuat_ban': book.publish})
        return JsonResponse(result, safe=False, json_dumps_params={'ensure_ascii': False})  # Chỉ có thể序列 hóa danh sách hoặc từ điển

1.2 Sử dụng APIView + Response của DRF

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book

class BookView(APIView):
    def get(self, request):  # Đối tượng request ở đây không phải là request gốc mà là request mới được tạo bởi DRF (xem phân tích mã nguồn bên dưới)
        print(type(request._request))  # Kiểm tra kiểu request gốc
        books = Book.objects.all()
        data = []
        for b in books:
            data.append({'tieu_de': b.name, 'gia': b.price, 'nha_xuat_ban': b.publish})
        return Response(data)
  1. Phân tích mã nguồn của APIView ==============
# Khi một lớp view kế thừa từ APIView, luồng thực thi sẽ thay đổi hoàn toàn theo quy trình của DRF.

# Sau khi kế thừa APIView:
    - Cấu hình đường dẫn giống như khi kế thừa từ View ---> Gọi as_view ---> [as_view của APIView]
        @classmethod
        def as_view(cls, **initkwargs):
            # Gọi tới as_view của lớp cha (View)
            view = super().as_view(**initkwargs)
            '''
            # Từ giờ trở đi, tất cả các yêu cầu đều không còn kiểm tra CSRF
            # Thêm decorator tương tự như sau:
            @csrf_exempt
            def index(request):
                pass
            Tương đương với: index = csrf_exempt(index)
            '''
            return csrf_exempt(view)

    - Khi yêu cầu đến, sau khi khớp route, hàm closure trong as_view của View sẽ được gọi (không có xác thực CSRF),
    - Thực thi self.dispatch ---> dispatch của APIView [Điểm quan trọng]
     def dispatch(self, request, *args, **kwargs):
        # `request` ban đầu là đối tượng request gốc của Django
        # `request` sau đó sẽ được chuyển thành đối tượng Request của DRF --- > return Request(...)
        request = self.initialize_request(request, *args, **kwargs)
        # Đặt thuộc tính request cho đối tượng view hiện tại
        self.request = request
        try:
            # Thực thi xác thực, quyền hạn, giới hạn tần suất
            self.initial(request, *args, **kwargs)
            # Xác định phương thức HTTP để gọi handler tương ứng
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            response = handler(request, *args, **kwargs)
        except Exception as exc:
            # Bắt lỗi trong quá trình thực thi xác thực hoặc phương thức view
            response = self.handle_exception(exc)
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

Tóm lại, khi sử dụng APIView, không còn xác thực CSRF, đối tượng request sẽ được chuyển thành request của DRF, và trước khi thực thi phương thức của view sẽ tiến hành xác thực, kiểm tra quyền hạn và giới hạn tần suất. Nếu xảy ra lỗi trong quá trình này hoặc thực thi phương thức view, lỗi sẽ được bắt và xử lý.

  1. Phân tích mã nguồn của lớp Request ===============
# Trong view, đối tượng request đã được chuyển thành đối tượng Request của DRF.
    - Request gốc của Django là: django.core.handlers.wsgi.WSGIRequest
    - Request của DRF là: rest_framework.request.Request
    
# Liệu request mới có thể sử dụng giống như request gốc không?
    - Có thể sử dụng y hệt:
        print(request.method)  # GET
        print(request.path)  # /books/
        print(request.GET)   # Các tham số của GET
        print(request.POST)  # Các tham số của POST
        
# Phân tích mã nguồn của Request:
    - Lớp có phương thức magic __getattr__ để xử lý khi truy cập thuộc tính không tồn tại.
    def __getattr__(self, attr): 
        try:
            # Sử dụng phản xạ để lấy thuộc tính từ request gốc
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)
        
    - Tất cả các thuộc tính và phương thức đều có thể dùng trực tiếp thông qua request (sử dụng phản xạ để lấy từ request gốc).
    
    - Bên trong request mới có chứa request gốc thông qua thuộc tính request._request.
    
    - `data`: Là một phương thức được property trang trí, dùng để lấy dữ liệu từ body (đẳng cấp với request.POST).
        - Dữ liệu urlencoded/form-data nằm trong request.POST.
        - Dữ liệu JSON nằm trong request.body.
        - Giờ bất kỳ định dạng nào cũng có thể lấy từ request.data.
    - `query_params`: Các tham số của GET, tương đương với request.GET.
    - `FILES`: Vẫn lấy từ request.FILES như bình thường.
    
# Kiểm tra: Dữ liệu JSON gửi qua body không xuất hiện trong request.POST nhưng có trong request.data của DRF.

# Đôi khi request.data là QueryDict (urlencoded/form-data), đôi khi là từ điển (JSON).

# 4. Magic method là gì?
    1. Các phương thức bắt đầu và kết thúc bằng __ được gọi là magic method.
    2. Không cần gọi thủ công, chúng sẽ tự động kích hoạt trong các tình huống cụ thể.
    3. Ví dụ: __init__, __str__, __call__, ...
  1. Sử dụng cơ bản của Serializer ============
# models.py
from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    tieu_de = serializers.CharField()
    nha_xuat_ban = serializers.CharField()
# views.py
class BookView(APIView): 
    def get(self, request):
        sach = Book.objects.all()
        seri = BookSerializer(instance=sach, many=True)
        return Response(seri.data)
class BookDetailView(APIView):
    def get(self, request, pk):
        sach = Book.objects.filter(pk=pk).first()
        seri = BookSerializer(instance=sach)
        return Response(seri.data)
  1. Phản chuỗi hóa (Deserialization) ======

Thêm mới

def post(self, request):
    seri = BookSerializer(data=request.data)
    if seri.is_valid():
        seri.save()
        return Response(seri.data)
    else:
        return Response(seri.errors)

Cập nhật

def put(self, request, pk):
    sach = Book.objects.filter(pk=pk).first()
    seri = BookSerializer(instance=sach, data=request.data)
    if seri.is_valid():
        seri.save()
        return Response(seri.data)
    else:
        return Response(seri.errors)

Thẻ: DjangoRESTFramework APIView Serialization

Đăng vào ngày 19 tháng 6 lúc 20:23