rest_framework(8)ViewSet、GenericViewSet、ModelViewSet

本系列文章中的上一篇文章:ListModelMixin, CreateModelMixin, RetrieveModelMixin, ListCreateAPIView

ViewSet

from rest_framework.generics import ListCreateAPIView, RetrieveUpdateAPIView
from rest_framework import serializers
from rest_framework.response import Response

from models import Book


class BookViewSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"


# 查看所有书籍的 get 和 添加一本书籍的 post 方法
class BookView(ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookViewSerializers


# 查看某本书籍的 get 和 编辑某本书籍的 put
class BookDetailView(RetrieveUpdateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookViewSerializers
"""
BookView 中的 get 方法是查看所有书籍,post 方法是添加一本书籍
而 BookDetailView 中的 get 和 put 分别是查看和编辑某本书籍信息
因为两个类都有 get 方法,所以不能写在一个类中,否则会冲突
urls.py 文件中的代码
path("book/", BookView.as_view())
re_path("book/(?P\d+)", BookDetailView.as_view())
这两个类对应的路由不一样,BookDetailView 需要在路由中传递书籍的 id 值
现在 BookView 和 BookDetailView 两个类中还是有相同的部分
可以把它们合并在一起写,只需要继承 ViewSet 类
"""
from rest_framework.viewsets import ViewSet


class BookView(ViewSet):
    """
    我们可以为增删改查自定义方法名字,但是这样做原生的 APIView 的分发机制就找不到对应的请求方法
    从而就不能根据 URL 匹配执行正确的视图函数
    而 ViewSet 的作用就是重写原生 APIView 的分发机制,见以下 ViewSet 源码
    """
    def get_all(self, request):
        return Response("查看所有资源")

    def add_object(self, request):
        return Response("添加资源")

    def get_object(self, request, pk):
        return Response("查看单一资源")

    def update_object(self, request, pk):
        return Response("更新单一资源")

    def delete_object(self, request, pk):
        return Response("删除单一资源")

urls.py 中的代码如下: 

"""
urls.py 中的代码
path("book/", BookView.as_view({"get": "get_all", "post": "add_object"}))
re_path("book/(?P\d+)", 
    BookView.as_view({
        "get": "get_object",
        "put": "update_object",
        "delete": "delete_object"
    }))
"""

ViewSet 源码如下:

"""
ViewSet 源码
class ViewSet(ViewSetMixin, views.APIView):
    pass

class ViewSetMixin:
    # ViewSetMixin 集重写了 as_view() 方法
    # 当有请求过来时,匹配到某个 URL 时调用的 as_view() 方法会调用本类中的 as_view
    # 而不是调用 APIView 中 as_view ,因为 ViewSet 是先继承 ViewSetMixin 再继承 APIView
    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        # ...
        def view(request, *args, **kwargs):
            # ...

            # 与 APIView 中的 as_view 方法不同的地方就在此处
            # actions 接收的就是 as_view 方法中传递过来的字典
            # 如 actions = {"get": "get_all", "post": "add_object"}
            for method, action in actions.items():
                # handler 得到的就是我们在 BookView 类中自定义的那些方法
                # 如 handler = self.get_all => handler = get_all
                handler = getattr(self, action)
                # 再将获取到的自定义方法和对应的请求方法进行绑定
                # 如 self.get = get_all
                # 当有一个 get 请求时,就会执行对应的自定义方法
                setattr(self, method, handler)
            # ...

            # 调用的还是 APIView 中的 dispatch() ,因为 ViewSetMixin 类没有实现该方法
            return self.dispatch(request, *args, **kwargs)
        # ...
        return csrf_exempt(view)
"""

GenericViewSet

使用 ViewSet 中继承的是原生的 APIView ,不能使用 get_object、get_serializer 等方法
所以使用 GenericViewSet ,它继承的是 GenericAPIView

urls.py 中的代码

urls.py 中的代码
path("book/", BookView.as_view({"get": "list", "post": "create"}))
re_path("book/(?P\d+)", 
    BookView.as_view({
        "get": "retrieve",
        "put": "destroy",
        "delete": "update"
    }))
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin


class BookView(GenericViewSet, ListModelMixin, CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin):
    queryset = BookView.objects.all()
    serializer_class = BookViewSerializers

GenericViewSet 源码: 

"""
GenericViewSet 源码
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    pass
    
假设匹配路由 path("book/", BookView.as_view({"get": "list", "post": "create"}))
当执行到 ViewSetMixin 中的 as_view 方法时,里面的 view 方法:
    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        # ...
        def view(request, *args, **kwargs):
            # ...
            
            # actions = {"get": "list", "post": "create"}
            for method, action in actions.items():
                # handler = self.list
                # BookView 中没有 list 方法,但是它的父类 ListModelMixin 中有
                # 所以 handler = ListModelMixin.list
                # 同理,下一次循环 handler = ListModelMixin.create
                handler = getattr(self, action)
                setattr(self, method, handler)
"""

ModelViewSet

from rest_framework.viewsets import ModelViewSet

class BookView(ModelViewSet):
    queryset = BookView.objects.all()
    serializer_class = BookViewSerializers

ModelViewSet 源码如下:

"""
ModelViewSet 类就是继承了 GenericViewSet, ListModelMixin, CreateModelMixin, 
RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin 这几个类

源码如下:
class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    pass
"""

你可能感兴趣的:(restful,python,后端,django)