day06 drf视图组件


day06 drf视图组件

今日内容

  • 视图组件
    • 两个视图基类
    • 5个视图扩展类
    • 9个视图子类
  • 视图集
  • 路由组件

1、两个视图基类

1.1 APIView

from rest_framework.views import APIView

1.11 APIView的属性

APIView是REST framework提供的所有视图的基类,继承自Django的View父类。

APIView与View的不同之处在于:

    1.传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
    视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
    2.任何APIException异常都会被捕获到,并且处理成合适的响应信息;
    3.在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。

# 支持定义的类属性
    1.authentication_classes 列表或元祖,身份认证类
    2.permissoin_classes 列表或元祖,权限检查类
    3.throttle_classes 列表或元祖,流量控制类
    # 在APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。

1.2 GenericAPIView[通用视图类]

from rest_framework.generics import GenericAPIView

1.2.1 类属性和三个方法

# 类属性
    queryset = None  # 所有数据
    serializer_class = None # 序列化的类
    lookup_field = 'pk'  # 查询单条转换器的字段

queryset = Publish.objects.all()  # 只要配置这两行
serializer_class = PublishModelSerializer

# 三个方法
    self.get_queryset()   # 获取所有数据,指明使用的数据查询集
    self.get_serializer   # 获取序列化类,指明视图使用的序列化器
    self.get_object()    # 获取单条

1.2.1 基本使用

# 通过继承GenericAPIView 写5个接口

from rest_framework.generics import GenericAPIView  # 继承APIView,内部写了几个方法

class PublishView(GenericAPIView):
    queryset = Publish.objects.all()  # 只要配置这两行
    serializer_class = PublishModelSerializer

    def get(self, request, *args, **kwargs):
        # 查询所有的图书
        obj = self.get_queryset()  # 内部做了questset.all()
        ser = self.get_serializer(instance=obj, many=True)
        return Response(data=ser.data)

    # 新增接口
    def post(self, request, *args, **kwargs):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)


class PublishDetailView(GenericAPIView):
    queryset = Publish.objects.all()  # 只要配置这两行
    serializer_class = PublishModelSerializer
    
    # 查询单本图书
    def get(self, request, pk):
        # lookup_field = 'pk' 根据pk参数,类中定义好了
        obj = self.get_object()
        ser = self.get_serializer(instance=obj)
        return Response(ser.data)

    # 删除接口
    def delete(self, request, pk):
        res = self.get_object().delete()  # 因为返回的是一个元祖,取第一个值
        if res[0] >= 1:  # 影响了几行
            return Response()
        else:
            return Response('要删除的数据不存在')

    # 修改接口
    def put(self, request, pk):
        obj = self.get_object()
        ser = self.get_serializer(instance=obj, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)
        
'''
总结:
	1.先要继承GenericAPIView类
	2.配置这两个参数:queryset、serializer_class
	3.书写类中方法
'''

2、5个视图扩展类

2.1 基本介绍

from rest_framework.mixins import ListModelMixin,CreateModelMixin,DestroyModelMixin,UpdateModelMixin,RetrieveModelMixin

# 导入在这里面导入
    ListModelMixin:		获取所有数据的视图扩展类   # list()
    CreateModelMixin:	新增数据的视图扩展类       # create()
    DestroyModelMixin:	 删除数据的视图扩展类       # destroy()
    UpdateModelMixin:	 修改数据的视图扩展类       # update()
    RetrieveModelMixin:	 获取单条数据的视图扩展类    # retrieve()
        
# 5个视图扩展类,不是视图类,必须配合视图类才能使用

2.2 仿源码写了5个视图扩展类

2.21 my_minins.py

# 封装5个方法

from rest_framework.response import Response

# 获取所有数据的视图扩展类
class ListModelMixin:
    def list(self, request, *args, **kwargs):
        book_list = self.get_queryset()
        ser = self.get_serializer(instance=book_list, many=True)
        return Response(ser.data)

# 新增数据的视图扩展类
class CreateModelMixin:
    def create(self, request, *args, **kwargs):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

# 删除数据的视图扩展类
class DestroyModelMixin:
    def destroy(self, request, pk):
        res = self.get_object().delete()
        if res[0] >= 1:
            return Response()
        else:
            return Response('要删除的数据不存在')

# 修改数据的视图扩展类
class UpdateModelMixin:
    def update(self, request, pk):
        book_list = self.get_object()
        ser = self.get_serializer(instance=book_list, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

# 获取单条数据的视图扩展类
class RetrieveModelMixin:
    def retrieve(self, request, pk):
        book_list = self.get_object()
        ser = self.get_serializer(instance=book_list)
        return Response(ser.data)

'''根据之前有封装了一层方法,是为第三层,还能在简写代码'''

2.22 views.py

# 通过GenericAPIView+5个视图扩展类来实现5个接口

from rest_framework.response import Response
from app01.models import Book, Publish, Author, AuthorDetail
from app01.serializer import BookModelSerializer
from rest_framework.generics import GenericAPIView
from app01.my_mixins import ListModelMixin,CreateModelMixin,DestroyModelMixin,UpdateModelMixin,RetrieveModelMixin


# 用哪个功能就继承相应的类
class BookViews(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer

    # 查询所有图书接口
    def get(self, request, *args, **kwargs):
        return self.list(request)

    def post(self, request, *args, **kwargs):
        return self.create(request)


# 用哪个功能就继承相应的类
class BookDetailViews(GenericAPIView, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer

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

    def delete(self, request, pk):
        return self.destroy(request, pk)

    def put(self, request, pk):
        return self.update(request, pk)
    
'''
总结:
	1.使用这5个方法类的前提是:必须继承GenericAPIView类
	1.把5个功能封装成5个扩展类
	2.通过继承使用该方法
'''

3、9个视图子类

3.1 基本介绍

from rest_framework.generics import ListAPIView, CreateAPIView, ListCreateAPIView, UpdateAPIView, DestroyAPIView, RetrieveAPIView  

from rest_framework.generics import RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView

'''
原理:
	其实把一个个接口封装个一个类,视图函数使用哪个功能就继承哪个类即可
'''
# 视图子类详解:
    1.ListAPIView:		     获取所有数据接口
    2.CreateAPIView:		新增数据接口
    3.ListCreateAPIView:	 获取所有数据接口和新增数据接口
    4.UpdateAPIView:		修改数据接口
    5.DestroyAPIView:		删除数据接口
    6.RetrieveAPIView:	    获取单条数据接口
    7.RetrieveUpdateDestroyAPIView:   获取单条数据、删除、修改接口
    8.RetrieveDestroyAPIView:        获取单条数据和删除接口
    9.RetrieveUpdateAPIView:         获取单条数据和修改接口

3.2 仿写源码写9个视图子类

3.2.1 my_generics.py

'''5个视图扩展类+9个视图子类才算是完整的功能'''

from app01.my_mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin
from rest_framework.generics import GenericAPIView
from app01.my_generics import RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView

class ListAPIView(GenericAPIView, ListModelMixin):
    # 查询所有图书接口
    def get(self, request, *args, **kwargs):
        return self.list(request)


class CreateAPIView(GenericAPIView, CreateModelMixin):
    # 新增数据接口
    def post(self, request, *args, **kwargs):
        return self.create(request)


class RetrieveAPIView(GenericAPIView, RetrieveModelMixin):
    # 获取单条数据接口
    def get(self, request, pk):
        return self.retrieve(request, pk)


class DestroyAPIView(GenericAPIView, DestroyModelMixin):
    # 删除接口
    def delete(self, request, pk):
        return self.destroy(request, pk)


class UpdateAPIView(GenericAPIView, UpdateModelMixin):
    # 修改数据接口
    def put(self, request, pk):
        return self.update(request, pk)


# 获取所有和新增
class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
    def get(self, request, *args, **kwargs):
        return self.list(request)

    def post(self, request, *args, **kwargs):
        return self.create(request)


# 获取单条数据、删除、修改接口
class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin):
    # 获取单条数据接口
    def get(self, request, pk):
        return self.retrieve(request, pk)

    # 删除接口
    def delete(self, request, pk):
        return self.destroy(request, pk)

    # 修改数据接口
    def put(self, request, pk):
        return self.update(request, pk)


# 获取单条数据和删除接口
class RetrieveDestroyAPIView(GenericAPIView, RetrieveModelMixin, DestroyModelMixin):
    # 获取单条数据接口
    def get(self, request, pk):
        return self.retrieve(request, pk)

    # 删除接口
    def delete(self, request, pk):
        return self.destroy(request, pk)


# 获取单条数据和修改接口
class RetrieveUpdateAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin):
    # 获取单条数据接口
    def get(self, request, pk):
        return self.retrieve(request, pk)

    # 修改数据接口
    def put(self, request, pk):
        return self.update(request, pk)
    
    '''根据之前有封装了一层接口,是为第四层,还能在简写代码'''

3.2.2 views.py

from app01.my_generics import ListAPIView, CreateAPIView, ListCreateAPIView, UpdateAPIView, DestroyAPIView, RetrieveAPIView
class BookViews(ListCreateAPIView):  # 想使用什么功能继承什么类
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer

    
class BookDetailViews(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer
    
'''
总结:
	1.把5个接口封装成9个功能结合类,通过继承视图子类实现该接口功能
	2.使用什么功能,继承该接口视图子类
'''

4、视图集

4.1 常用视图集父类

from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet, ViewSetMixin, GenericViewSet

4.1.1 ViewSet

# 继承自APIView与ViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。

    ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{‘get’:’list’})的映射处理工作。

    在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

class LoginView(ViewSet): # 继承ViewSet和继承下面的是一个效果
class LoginView(ViewSetMixin, APIView):  # ViewSetMixin只是重写了as_view,但是没有继承View,所以还有继承APIView才是视图基类

4.1.2 GenericViewSet

	使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。

	# GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
    
class LoginView(GenericViewSet):  # 其实就是同时继承了下面两个
class LoginView(ViewSetMixin, GenericAPIView):

4.1.3 ViewSetMixin

# 视图层
from rest_framework.viewsets import ViewSetMixin,ViewSet
'''同时继承两个类太不方便了,'''
# class LoginView(ViewSet): 继承ViewSet和继承下面的是一个效果

class LoginView(ViewSetMixin, APIView):  # ViewSetMixin只是重写了as_view,但是没有继承View,所以还有继承APIView才是视图基类
    def login(self,request):
        return Response('登录成功')
    
# 路由层
from app01 import views
urlpatterns = [
    path('login/', views.LoginView.as_view({'post': 'login'})),  # 请求去视图函数里面,是哪个请求执行对应方法
]
'''
总结:
	1.ViewSetMixin必须和视图基类同时被继承
	2.ViewSetMixin中可以书写别的方法
	3.以后视图类中可以写任意的方法,只需要继承ViewSetMixin后,使用路由做映射即可
'''

4.1.4 ModlViewSet

# 继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
例子:
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet, ViewSetMixin, GenericViewSet

# 继承这个5个接口都有了,但是5个接口有两个get
class PublishViews(ModelViewSet):
    queryset = Publish.objects.all()
    serializer_class = PublishModelSerializer
    
# 必须去路由层从新配置({'get':'list'})的映射处理工作
from app01 import views

urlpatterns = [
    path('publish/', views.PublishViews.as_view({'get': 'list', 'post': 'create'})),  # 请求去视图函数里面,是哪个请求执行对应方法
    path('publish/', views.PublishViews.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update'})),
]

'''
总结:
	1.继承ModelViewSet,然后在修改路由,就可以实现5个接口
	2.但是里面必须重写许多方法,比较麻烦
	3.为什么要这样书写路由?
		因为ModelViewSet里面有一个GenericViewSet类里面,
		重新书写了as_view()方法,所以这里执行的是GenericViewSet里面的as_view()方法
		
'''

4.1.5 ReadOnlyModelViewSet

# ReadOnlyModelViewSet:只读,只能获取所有和单条数据,其余操作不可以。

from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet, ViewSetMixin, GenericViewSet
class PublishView(ReadOnlyModelViewSet): 
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer
    
    
from app01 import views
urlpatterns = [
    path('publish/', views.PublishViews.as_view({'get': 'list'})),  # 请求去视图函数里面,是哪个请求执行对应方法
    path('publish/', views.PublishViews.as_view({'get': 'retrieve'})),
]

5、ModelViewSet源码解析

from rest_framework.viewsets import ModelViewSet   # 入口

1.进入ModelViewSet里面
class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):  # 进入这里面
				# 因为ModelViewSet视图集中里面有mixins的5个视图扩展类
        
2. 进入GenericViewSet里面
	class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        # ViewSetMixin重要的在里面
        '''
        	ViewSetMixin在最左面,优先级最高,ViewSetMixin也继承了View
        	优先执行ViewSetMixin中as_view方法
        '''
        
3.进入ViewSetMixin里面
	# 路由之所以可以这么书写:views.PublishViews.as_view({'get': 'list'}),是因为ViewSetMixin里面重新写了as_view方法
class ViewSetMixin:
	@classonlymethod
    def as_view(cls, actions=None, **initkwargs):  # actions就是{'get': 'list'}
    """
    This is the magic.
    view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
    """
	    # 如果没有传,直接抛异常
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")
            
        # views.PublishViews.as_view这里是ViewSetMixin里面的view内存地址,加括号调用
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            
		   # 其中mothod:get(请求),action:list(方法)
            for method, action in actions.items():
                # 利用反射机制去视图类调用action方法
                handler = getattr(self, action)  # handler:get..
                # 再利用反射机制映射方法:{'get':'list '}
                setattr(self, method, handler)

            self.request = request
            self.args = args
            self.kwargs = kwargs
            # 调用dispatch方法,按照顺序找这个方法
            return self.dispatch(request, *args, **kwargs)
        return csrf_exempt(view)

镇楼图

drf