1. 程式人生 > >DRF視圖和路由

DRF視圖和路由

b- 什麽 9.png 系統 源碼 query app clas 默認

APIView :

在django中寫CBV的時候是繼承View, rest_framework繼承的是APIView, 這兩種具體有什麽不同呢?

urlpatterns = [
    url(r^book$, BookView.as_view()),
    url(r^book/(?P<id>\d+)$, BookEditView.as_view()),
]

無論是View還是APIView最開始調用的都是as_view()方法, 看源碼:

技術分享圖片

  可以看到, APIView繼承了View, 並且執行了View中的as_view()方法, 最後把view返回, 用csrf_exempt()方法包裹後去掉了csrf的認證.

而在View中的as_view()方法如下:

技術分享圖片

  在View中的as_view方法返回了view函數, 而view函數執行了self.dispatch()方法, 但是這裏的dispatch方法應該是APIView中的.

技術分享圖片

再去initialize_request中看下把什麽賦值給了request, 並且賦值給了self.request, 也就是在視圖中用的request.xxx到底是什麽?

技術分享圖片

  可以看到, 這個方法返回的是Request這個類的實例對象, 而這個Request類中的第一個參數request, 使我們在django中使用的request.

技術分享圖片

  可以看到, 這個Request類把原來的request賦值給了self._request, 也就是說_request就是我們原先的request, 新的request使我們這個Request類.

那繼承APIView之後請求來的數據都在哪呢?

技術分享圖片

當我們使用了rest_framework框架之後, 我們的request是重新封裝的Request類.

request.query_params 存放的是我們get請求的參數.

request.data 存放的是我們所有的數據, 包括post請求的以及put, patch請求.

相比原來的django的request, 我們現在的request更加精簡, 清晰.


封裝代碼 :

原代碼(封裝之前) :

技術分享圖片
from django.conf.urls import url
from SerDemo import views

urlpatterns 
= [ # 第一二版本 # url(r‘^book/$‘, views.BookView.as_view()), # url(r‘^book/(?P<edit_id>\d+)‘, views.BookEditView.as_view()), ]
url 技術分享圖片
from rest_framework import serializers
from app01 import models


class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)


class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)


# 一個判斷某一個字段是否含有敏感信息的函數, 直接調用即可
def my_validate(value):
    if "敏感信息" in value.lower():
        raise serializers.ValidationError("存在敏感詞匯!!!")


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # required 為False時, 反序列化不做校驗
    title = serializers.CharField(max_length=32, validators=[my_validate])
    pub_time = serializers.DateField()
    category = serializers.CharField(source="get_category_display", read_only=True)
    # 自定義一個字段只用來反序列化接收使用
    post_category = serializers.IntegerField(write_only=True)

    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    # 多對多有many參數
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    # 新增數據要重寫的create方法
    def create(self, validated_data):
        # validated_data是驗證通過的數據
        # 通過ORM操作給Book表增加數據
        # 添加除多對多字段的所有字段
        book_obj = models.Book.objects.create(
            title=validated_data["title"],
            pub_time=validated_data["pub_time"],
            category=validated_data["post_category"],
            publisher_id=validated_data["publisher_id"],
        )
        # 添加多對多字段
        book_obj.author.add(*validated_data["author_list"])
        return book_obj

    # 更新數據要重寫update方法
    def update(self, instance, validated_data):
        # instance 是要更新的對象
        # 對除多對多字段以外的字段進行更新, 並設置當前已存在的數據為默認值
        instance.title = validated_data.get("title", instance.title)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.category = validated_data.get("post_category", instance.category)
        instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
        # 判斷前端傳過來的數據是否含有author_list字段, 如果有則更新, 沒有就不變動
        if validated_data.get("author_list"):
            instance.author.set(validated_data["author_list"])
        instance.save()
        return instance


# 對前端傳過來的數據進行條件控制
def validate(self, attrs):
    # 相當於鉤子函數
    # attrs是一個字典, 含有傳過來的所有字段
    if "python" in attrs["title"].lower() and attrs["post_category"] == 1:
        return attrs
    else:
        raise serializers.ValidationError("分類或標題不匹配")
序列化器 技術分享圖片
from rest_framework.views import APIView
from app01 import models
from .serializers import BookSerializer
from rest_framework.response import Response

from rest_framework.viewsets import ViewSetMixin



# Create your views here.


# 版本一
class BookView(APIView):
    def get(self, request):
        book_queryset = models.Book.objects.all()
        # 用序列化器進行序列化
        ser_obj = BookSerializer(book_queryset, many=True)

        return Response(ser_obj.data)

    def post(self, request):
        # 接收前端傳過來的數據
        book_obj = request.data
        # 對前端傳過來的數據使用自定義序列化方法進行校驗(是否合法等)
        ser_obj = BookSerializer(data=book_obj)
        # 如果校驗通過做些什麽
        if ser_obj.is_valid():
            ser_obj.save()
            # validated_data是校驗通過之後的數據
            return Response(ser_obj.validated_data)
        # 驗證不通過返回錯誤信息
        return Response(ser_obj.errors)


class BookEditView(APIView):
    def get(self, request, edit_id):
        book_obj = models.Book.objects.filter(id=edit_id).first()
        ser_obj = BookSerializer(book_obj)
        return Response(ser_obj.data)

    def put(self, request, edit_id):
        book_obj = models.Book.objects.filter(id=edit_id).first()
        ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)

    def delete(self, request, edit_id):
        book_obj = models.Book.objects.filter(id=edit_id).first()
        if not book_obj:
            return Response("刪除對象不存在!")
        book_obj.delete()
        return Response("刪除成功了呢!")
view.py

第一次封裝 :

技術分享圖片
from django.conf.urls import url
from SerDemo import views

urlpatterns = [
    # 第一二版本
    # url(r‘^book/$‘, views.BookView.as_view()),
    # url(r‘^book/(?P<edit_id>\d+)‘, views.BookEditView.as_view()),


]
url 技術分享圖片
class GenericAPIView(APIView):
    """
    定義一個公共的類, 用來獲取需要的資源
    """
    # 設置默認操作的數據為空
    queryset = None
    # 設置需要使用的序列化器為空
    serializer_class = None

    def get_queryset(self):
        """
        定義一個獲取需要操作的數據的函數
        子類函數直接調用即可
        子類中調用此方法, 返回值中的self指的是調用此方法的子類的實力化對象
        queryset屬性在本類中是None, 每一個繼承此類的子類中都會重寫queryset和serializer_class方法
        然後子類中執行此類中的方法是去執行子類對應的屬性.
        :return:
        """
        return self.queryset.all()

    def get_serializer_class(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)


class ListModelMixin:
    """
    定義一個展示類,將展示方法統一寫成一個方法
    """

    def list(self, request):
        queryset = self.get_queryset()
        ser_obj = self.get_serializer_class(queryset, many=True)
        return Response(ser_obj.data)


class CreateModeMixin:
    """
    新增的視圖類
    """

    def create(self, request):
        ser_obj = self.get_serializer_class(data=request.data)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)


class EditModeMixin:
    """
    編輯的視圖類
    """

    def retrieve(self, request, id):
        obj = self.get_queryset().filter(id=id).first()
        ser_obj = self.get_serializer_class(obj)
        return Response(ser_obj.data)


class UpdateModeMixin:
    """
    更新的視圖類
    """

    def update(self, request, id):
        obj = self.get_queryset().filter(id=id).first()
        ser_obj = self.get_serializer_class(instance=obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)


class DeleteModeMixin:
    """
    刪除的視圖類
    """

    def destroy(self, request, id):
        obj = self.get_queryset().filter(id=id).first()
        if not obj:
            return Response("刪除的對象不存在!")
        obj.delete()
        return Response("刪除了呢")


class ListCreateAPIview(GenericAPIView, ListModelMixin, CreateModeMixin):
    pass


class OperationAPIview(GenericAPIView, EditModeMixin, UpdateModeMixin, DeleteModeMixin):
    pass


class BookView(ListCreateAPIview):
    # 定義queryset屬性時, DRF方法內部和關鍵字重名, 內部會識別並將此屬性做緩存
    # 換個名字DRF不識別屬性, 不做緩存, 也就不需要.all()
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)


class BookEditView(OperationAPIview):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, edit_id):
        return self.retrieve(request, edit_id)

    def put(self, request, edit_id):
        return self.update(request, edit_id)

    def delete(self, request, edit_id):
        return self.destroy(request, edit_id)
view.py

  我們封裝的GenericAPIView, , 包括封裝的每個方法的類, 其實框架都幫我們封裝好了,

  我們可以繼承這個二類, 來實現上面的視圖.

其中框架還給我們提供了一個路由傳參的方法:

技術分享圖片

  actioon這個默認參數其實就是接收路由參數的參數.

再次封裝 :

技術分享圖片
from SerDemo import views

urlpatterns = [

    # 第三版
    # url(r‘^book/$‘, views.BookModelView.as_view({"get": "list", "post": "create"})),
    # url(r‘^book/(?P<pk>\d+)‘, views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]
url 技術分享圖片
from rest_framework.viewsets import ViewSetMixin


# 重寫了源碼中的as_view()方法, 是as_view方法可以傳參數
# 在執行dispatch()方法之前
class ModelViewSet(ViewSetMixin, ListCreateAPIview, OperationAPIview):
    pass


class BookModelView(viewsets.ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer
view.py

  這樣我們的視圖只要寫兩行就可以了

  其實我們所寫的所有視圖, 框架都幫我們封裝好了.

  註意 :

    應框架封裝的視圖, url上的那個關鍵字參數要用pk, 系統默認的


繼承順序圖 :

技術分享圖片


DRF路由 :

# 最終版
# 幫助我們生成帶參數的路由
from rest_framework.routers import DefaultRouter
# 實例化DefaultRouter對象
router = DefaultRouter()
# 註冊我們的路由以及視圖
router.register(r"book", views.BookModelView)


urlpatterns = [

]

urlpatterns += router.urls

可以看到, 通過框架可以把路由視圖都變得非常簡單, 但是需要自定制的時候還是需要自己用APIView寫, 當不需要那麽多路由時候, 也不需藥使用這種路由註冊.

DRF視圖和路由