drf视图家族

APIView

# APIView继承自django原生的View,它在View的基础上做了两件事:
	- 使用csrf_exempt()去除了所有请求的csrf认证
	- 重写了View的dispatch方法,用drf的Request对象替换原生的当前请求request对象
	- 做了三大认证:认证、权限、频率
	- 封装了响应对象Response

# 使用APIView写五大接口的方法,需要自己分别实现五个方法,它的好处是可控性高。
# 代码
# models.py
from django.db import models
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    author = models.CharField(max_length=32)


# ser.py
	- 使用模型类序列化器ModelSerializer
	- 好处:不用重复写模型中的字段;基于序列化器新增和修改数据不用重写create()和update()
from rest_framework import serializers
from app01 import models
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'

# view.py
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import utils, models, ser

class BookAPIView(APIView):
    def get(self, request):
        book_list = models.Book.objects.all()
        ser_obj = ser.BookModelSerializer(book_list, many=True)
        back_info_obj = utils.BackInfo(ser_obj.data)
        return Response(back_info_obj.results)


    def post(self, request):
        ser_obj = ser.BookModelSerializer(data=request.data)
        if ser_obj.is_valid():
            ser_obj.save()
            back_info_obj = utils.BackInfo(ser_obj.data)
        else:
            back_info_obj = utils.BackInfo().add_error(code='400', message='新增失败', data=ser_obj.errors)
        return Response(back_info_obj.results)


class BookAPIView2(APIView):
    def get(self, request, pk):
        book_obj = models.Book.objects.filter(pk=pk).first()
        ser_obj = ser.BookModelSerializer(instance=book_obj)
        back_info_obj = utils.BackInfo(data=ser_obj.data)
        return Response(back_info_obj.results)

    def put(self, request, pk):
        book_obj = models.Book.objects.filter(pk=pk).first()
        ser_obj = ser.BookModelSerializer(instance=book_obj, data=request.data)
        if ser_obj.is_valid():
            ser_obj.save()
            back_info_obj = utils.BackInfo(ser_obj.data)
        else:
            back_info_obj = utils.BackInfo().add_error(code='400', message='新增失败', data=ser_obj.errors)
        return Response(back_info_obj.results)

    def delete(self, request, pk):
        models.Book.objects.filter(pk=pk).delete()
        back_info_obj = utils.BackInfo()
        return Response(back_info_obj.results)
    
# urls.py
	# 基于APIView的视图
    url(r'books/$', views.BookAPIView.as_view()),
    url(r'books/(?P<pk>\d+)', views.BookAPIView2.as_view()),

GenericAPIView

# GenericAPIView继承自APIView,它在APIView的功能上增加了两个功能:
	- 通用的模型类	queryset = None; 
	- 通用的序列化器	serializer_class = None
    
# 三个常用方法
	- get_queryset() # 获取queryset对象,用于查询多条数据
	- get_object()	# 获取一条对象数据
	- get_serializer(*args, **kwargs)	# 获取序列化器对象
# GenericAPIView提供的这些方法是五个扩展类的基础
# 使用GenericAPIView写不同类的接口,只是queryset参数和serializer_class参数的不同,其他的方法几乎完全一样

# 代码: 基于GenericAPIView写的接口
# views.py
from rest_framework.generics import GenericAPIView
class BookGenericAPIView(GenericAPIView):
    queryset = models.Book.objects
    serializer_class = ser.BookModelSerializer

    def get(self, request):
        book_list = self.get_queryset()
        ser_obj = self.get_serializer(book_list, many=True)
        back_info_obj = utils.BackInfo(ser_obj.data)
        return Response(back_info_obj.results)

    def post(self, request):
        ser_obj = self.get_serializer(data=request.data)
        if ser_obj.is_valid():
            ser_obj.save()
            back_info_obj = utils.BackInfo(ser_obj.data)
        else:
            back_info_obj = utils.BackInfo().add_error(code='400', message='新增失败', data=ser_obj.errors)
        return Response(back_info_obj.results)


class BookGenericAPIView2(GenericAPIView):
    queryset = models.Book.objects
    serializer_class = ser.BookModelSerializer

    def get(self, request, pk):
        book_obj = self.get_object()
        ser_obj = self.get_serializer(book_obj)
        back_info_obj = utils.BackInfo(data=ser_obj.data)
        return Response(back_info_obj.results)

    def put(self, request, pk):
        book_obj = self.get_object()
        ser_obj = self.get_serializer(instance=book_obj, data=request.data)
        if ser_obj.is_valid():
            ser_obj.save()
            back_info_obj = utils.BackInfo(ser_obj.data)
        else:
            back_info_obj = utils.BackInfo().add_error(code='400', message='新增失败', data=ser_obj.errors)
        return Response(back_info_obj.results)

    def delete(self, request, pk):
        self.get_object().delete()
        back_info_obj = utils.BackInfo()
        return Response(back_info_obj.results)
    
# urls.py
url(r'books2/$', views.BookGenericAPIView.as_view()),
url(r'books2/(?P<pk>\d+)', views.BookGenericAPIView2.as_view()),

五个扩展类

# 使用GenericAPIView写不同类的接口,只是queryset参数和serializer_class参数的不同,其他的方法几乎完全一样
# 这部分代码drf帮我们实现了,在五个扩展类中
# 提供了几种后端视图(对数据资源进行曾删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。
# 这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。
ListModelMixin
	提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。
    该Mixin的list方法会对数据进行过滤和分页。
CreateModelMixin
	提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。
	如果序列化器对前端发送的数据验证失败,返回400错误。
RetrieveModelMixin
	提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。
	如果存在,返回200, 否则返回404。
UpdateModelMixin
	提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。
    同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。
    成功返回200,序列化器校验数据失败时,返回400错误。
DestroyModelMixin
	提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。
	成功返回204,不存在返回404。
    
# 代码:基于GenericAPIView和5个视图扩展类写的接口
# views.py
from rest_framework.mixins import ListModelMixin, CreateModelMixin
from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
class BookGenericAPIVMixinView(GenericAPIView, ListModelMixin, CreateModelMixin) :
    queryset = models.Book.objects
    serializer_class = ser.BookModelSerializer

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

    def post(self, request):
        # back_info_obj = utils.BackInfo(ser_obj.data)
        return self.create(request)


class BookGenericAPIVMixinView2(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = models.Book.objects
    serializer_class = ser.BookModelSerializer
    
# urls.py
url(r'books3/$', views.BookGenericAPIVMixinView.as_view()),
url(r'books3/(?P<pk>\d+)', views.BookGenericAPIVMixinView2.as_view()),

ModelViewSet

# ModelViewSet继承五个扩展类和GenericViewSet
# GenericViewSet继承ViewSetMixin和GenericAPIView
# 继承该类可以简单实现5个接口的,且只需要一个视图类,关键点就在于其父类ViewSetMixin
# 这里面的重点在于ViewSetMixin,该类有一个as_view()方法,GenericViewSet优先使用该as_view()方法
# 该as_view()方法内通过url()的参数actions实现请求方法的分配

# 代码:基于ModelViewSet
# views.py
from rest_framework.viewsets import ModelViewSet                                                  
class BookModelViewSetMixinView(ModelViewSet):    
   queryset = models.Book                        
   serializer_class = ser.BookModelSerializer   
    
# urls.py
#当路径匹配,是get请求,会执行Book5View的list方法
url(r'books4/$', views.BookModelViewSetMixinView.as_view(actions={'get': 'list', 'post': 'create'})),
url(r'books4/(?P<pk>\d+)', views.BookModelViewSetMixinView.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

ViewSetMixin源码阅读

ViewSetMixin类的核心是提供了两个方法:as_view()initialize_request()方法;

as_view()方法

# as_view()方法,且配合url的actions参数,可以实现请求方式的动态指定分配
# 核心代码(所以路由中只要配置了对应关系,比如actions={'get':'list'}),当get请求来,就会执行list方法
def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    self.action_map = actions
    for method, action in actions.items():	# actions指的是url中指定的字典actions = {'get': 'list', }
        handler = getattr(self, action)		# handler = view.list
        setattr(self, method, handler)		# view.get = view.list
       
"""
   如果url中通过actions字典,指定了get和list的键值对,那么通过上述两个反射操作,就将view.get属性赋值了一个list方法的内存地址。
   等url匹配成功后,通过dispatch反射调用请求方法时,此时该view有了get属性,那么就执行get加括号执行,其实执行的就是被替换的list方法。
   如果此时没有通过actions指定get个list的关系,那程序运行到dispathc里面就会找当前对象的get的方法;
   自己有的话就用自己的,自己找不到就一层层找父类的。

"""

initialize_request()方法

# initialize_request()方法
# ViewSetMixin也提供了initialize_request方法,如果使用了该类,且在该方法被优先调用到,那么该方法就会被执行
	- 该方法内部其实调用了super().initialize_request(),即调用当前视图对象的下一个父类的initialize_request方法,
    - 该方法的核心点是该当前视图对象增加一个action属性,action的值就是当前请求方式
     def initialize_request(self, request, *args, **kwargs):
        """
        Set the `.action` attribute on the view, depending on the request method.
        """
        request = super().initialize_request(request, *args, **kwargs)
        method = request.method.lower()
        if method == 'options':
            # This is a special case as we always provide handling for the
            # options method in the base `View` class.
            # Unlike the other explicitly defined actions, 'metadata' is implicit.
            self.action = 'metadata'
        else:
            self.action = self.action_map.get(method)	# 通过该方法后,视图对象中就会增加一个action属性
        return request
    
 """
 该方法被调用后,就会给当前视图对象增加一个action属性,属性值是当前请求的类型,如:get、post等
 这样做了之后,以后在视图类中不仅可以通过request取到当前请求方式,还可以通过当前视图类取到请求方式。
 
 """

九个通用API视图子集

# 九个通用API视图子集: 继承GenericAPIView和五个扩展类,实现不同请求方式的定制
CreateAPIView(CreateModelMixin, GenericAPIView):写了post()
DestroyAPIView(DestroyModelMixin, GenericAPIView):写了delete()
ListAPIView(ListModelMixin, GenericAPIView):写了get()
RetrieveAPIView(RetrieveModelMixin, GenericAPIView):写了get()
UpdateAPIView(UpdateModelMixin, GenericAPIView):put()、patch()
ListCreateAPIView(ListModelMixin, CreateModelMixin, GenericAPIView):get()、post()
RetrieveDestroyAPIView(RetrieveModelMixin, DestroyModelMixin, GenericAPIView):get()、delete()
RetrieveUpdateAPIView(RetrieveModelMixin, UpdateModelMixin, GenericAPIView):get()、put()、patch()
RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):get()、put()、patch()、delete()

# 代码:仅实现查询图书所有的接口
# views.py
class BookListAPIView(ListAPIView):
    queryset = models.Book.objects              
    serializer_class = ser.BookModelSerializer  
# urls.py
url(r'books6/$', views.BookListAPIView.as_view()),

ViewSetMixin & APIView

其实drf给我们提供了一个同时继承了这两个类的一个视图集合类ViewSet

class ViewSet(ViewSetMixin, views.APIView):
    """
    The base ViewSet class does not provide any actions by default.
    """
    pass
# 基于ViewSetMixin的视图类和APIView编写接口
# 利用ViewSetMixin的as_view()方法,在url中通过actions分配请求处理的方法
# 因为仅使用APIView,所有需要自己实现接口功能
# 自定制化强,可控性高

# 代码:
# views.py
from rest_framework.viewsets import ViewSetMixin
class BookViewSetMixin(ViewSetMixin, APIView):	
    # ViewSetMixin一定要放在第一个父类位置,才能使用到ViewSetMixin的as_views()
    
    def get_all_book(self,request):
        book_list = models.Book.objects.all()
        book_ser = ser.BookModelSerializer(book_list, many=True)
        return Response(book_ser.data)
    
# urls.py:指定处理get请求的视图方法
url(r'books5/$', views.BookViewSetMixin.as_view(actions={'get': 'get_all_book'})),

ReadOnlyModelViewSet

# ReadOnlyModelViewSet继承自五个扩展类中的两个取数据的扩展类,又继承GenericViewSet,
# GenericViewSet继承了ViewSetMixin,可以使用其as_view()方式
# 因此,它只可以取数据,并且通过url中actions参数指定get的请求处理方法,list()、retrieve()

# 代码:
# views.py
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookReadOnlyModelViewSet(ReadOnlyModelViewSet):
    queryset = models.Book.objects
    serializer_class = ser.BookModelSerializer

# urls.py
url(r'books7/$', views.BookReadOnlyModelViewSet.as_view(actions={'get': 'list'})),
url(r'books7/(?P<pk>\d+)', views.BookReadOnlyModelViewSet.as_view(actions={'get': 'retrieve'})),

视图类继承关系

两个基本视图类

# APIView			# 去除csrf认证、封装request、三大认证、封装reaponse等
# GenericAPIView	# 提供了通用基于类的视图处理办法,两个参数:queryset\serializer_class

五个视图扩展类:分别提供了具体的增删该查方法:list\get\update\create\destroy\retrieve

"""
CreateModelMixin
DestroyModelMixin
ListModelMixin
RetrieveModelMixin
UpdateModelMixin
"""

九个通用视图子类:GenericAPIView和五个视图扩展类的组合

CreateAPIView
DestroyAPIView
GenericAPIView
ListAPIView
ListCreateAPIView
RetrieveAPIView
RetrieveDestroyAPIView
RetrieveUpdateAPIView
RetrieveUpdateDestroyAPIView
UpdateAPIView

1个视图集合扩展类

ViewSetMixin

"""
This is the magic.

    Overrides `.as_view()` so that it takes an `actions` keyword that performs
    the binding of HTTP methods to actions on the Resource.

    For example, to create a concrete view binding the 'GET' and 'POST' methods
    to the 'list' and 'create' actions...

    view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
"""

视图集合扩展类ViewSetMixin和其他的组合

ViewSetMixin + APIView 			-----> ViewSet
ViewSetMixin + GenericAPIView  		-----> GenericViewSet

GenericViewSet + RetrieveModelMixin + ListModelMixin	------> ReadOnlyModelViewSet
GenericViewSet + 五个视图扩展类 				-------> ModelViewSet

视图继承关系图

img

posted @ 2020-07-08 23:23  the3times  阅读(274)  评论(0编辑  收藏  举报