drf视图组件与视图基类


简介: 两个视图基类:PIView,GenericAPIView,五个扩展类:ListModelMixin,CreateModelMixin ,RetieveModelMixin,UpdateModelMixin,DestoryModelMixin,九个视图子类与视图集

drf视图组件

视图就是视图类,我们之前学过的APIView就是drf提供的基类。

APIView与原生View区别:
	1.传入到视图类中的是drf的Request对象而不是django的request对象
	2.视图类应该返回 drf的Request对象
	3.任何异常都会被捕获到,并处理
	4.在进行dispatch()前 会进行3大认证即认证,权限,频率

两个视图基类

APIView

再写一遍,练习加倍

APIView+ModelSerializer+Resposne写5个接口

from django.db import models


# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr}

    def author_list(self):
        l = []
        for author in self.authors.all():
            l.append({'name': author.name, 'phone': author.phone, 'age': author.detail.age, 'addr': author.detail.addr})
        return l


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(max_length=11)

    detail = models.OneToOneField(to='Detail', on_delete=models.CASCADE)


class Detail(models.Model):
    age = models.CharField(max_length=4)
    addr = models.CharField(max_length=32)
视图类
from .models import Book
from .serializer import BookSerializer


class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        ser = BookSerializer(instance=books, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
    """这里的ser.data为什么能拿到数据是因为他把新增的对象序列化成了一个字典,前提是必须在create方法内返回新增对象"""
        else:
            return Response({'code': 101, 'msg': ser.errors})


class BookDetailView(APIView):
    def get(self, request, pk):
        books = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=books)
        return Response(ser.data)

    def put(self, request, pk):
        books = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=books, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '删除成功'})
序列化类
### ModelSerializer的使用
class BookSerializer(serializers.ModelSerializer):
    # 跟表有关联
    class Meta:
        model = Book
        fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors']
        extra_kwargs = {'name': {'max_length': 8},
                        'publish_detail': {'read_only': True},
                        'author_list': {'read_only': True},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        }
路由
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookDetailView.as_view()),
]
APIView类属性简介
renderer_classes 		# 响应格式
parser_classes 			#能够解析的请求格式
authentication_classes	 #认证类
throttle_classes		#频率类
permission_classes		#权限类
GenericAPIView
重要属性
"""
queryset: 		  要序列化或反序列化 表模型的数据(对象)
serializer_class:  使用的序列化类
lookup_field    查询书籍路由分组匹配的名<int:pk>
				如果路由中不是pk可以改直接在视图类中 lookup_field = pk 
filter_backends  过滤类配置
pagination_class 分页类配置
"""

重要方法:
"""
1.get_queryset    获取序列化的对象 queryset对象[{对象1},{对象2}]
2.get_object     获取单个对象 不需要传pk
3.get_serializer   获取=后面的序列化类
get_serializer_class  重写指定序列化类
4.filter_queryset  过滤相关

"""

别的代码不变只是views中需要继承GenericAPIView

from rest_framework.generics import GenericAPIView
class BookView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    def get(self, request):
        """
        get_queryset    固定的方法拿到对象
        get_serializer  固定的方法 后期可以重写get_serializer_class指定序列化类
        """
        objs = self.get_queryset()
        ser = self.get_serializer(instance=objs,many=True)
        return Response(ser.data,status=201,headers={'a':'b'})

    def post(self, request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '新增成功'})
        else:
            return Response({'code': 101, 'msg': ser.errors})

class BookDetailView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    """
    get_object 获取单个对象 
    """
    def get(self, request, pk):
        obj = self.get_object()
        ser = self.get_serializer(instance=obj)
        return Response(ser.data)

    def put(self, request, pk):
        obj = self.get_object()
        ser = self.get_serializer(data=request.data, instance=obj)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '新增成功'})
        else:
            return Response({'code': 101, 'msg': ser.errors})

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '删除成功'})

五个扩展类

CreateModelMixin  新增    post
ListModelMixin    查询所有 get
RetrieveModelMixin  查询单个 
UpdateModelMixin   修改 put
DestroyModelMixin  删除 
"统一在from rest_framework.mixins import 内"
基于五个视图扩展类编写
views
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin

class BookView(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    def get(self, request):
        return self.list(request)

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

class BookDetailView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, pk):  # 参数也可以使用*args,**kwargs来传
        return self.retrieve(request,pk)

    def put(self, request, *args,**kwargs):
        return self.update(request, *args,**kwargs)

    def delete(self, request, *args,**kwargs):
        return self.destroy(request, *args,**kwargs)

九个视图子类

不需要额外继承GenericAPIView 只要继承9个视图子类的其中一个,就会有部分接口。

"""
一,查询所有与新增
listAPIView 继承了 ListModelMixin,GenericAPIView      get方法
CreateAPIView 继承了CreateModelMixin,GenericAPIView   post方法

ListCreateAPIView 视图子类呢继承了ListModelMixin,CreateModelMixin,GenericAPIView 也就是说拥有了上面两个子类的功能 

"""
查询所有增加一个
class BookView(ListCreateAPIView):
    "ListCreateAPIView内部继承了ListModelMixin,CreateModelMixin已经拥有get与post方法了"
    queryset = Book.objects.all()
    serializer_class = BookSerializer

"""
二,查询一个,修改一个,删除一个
RetrieveView 继承了 RetrieveModelMixin,GenericAPIView ,get方法
UpdateView 继承了 UpdateModelMixin,GenericAPIView     ,put方法
DestroyView 继承了 DestroyModelMixin,GenericAPIView   ,delete方法

RetrieveUpdateDestroyAPIView 继承了上述三个继承的父类RetrieveModelMixin,
                                                mixins.UpdateModelMixin,
                                                mixins.DestroyModelMixin,
                                                GenericAPIView  
                                    拥有了他们含有的所有方法
"""

# 查询一个,修改一个,删除一个
class BookView(RetrieveUpdateDestroyAPIView):
    "父类继承了RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView 拥有了get,put ,delete方法"
    queryset = Book.objects.all()
    serializer_class = BookSerializer

总结


ListAPIView                   ---> ListModelMixin,GenericAPIView                   get方法,查询所有
CreateAPIView                 ---> CreateModelMixin,GenericAPIView                 post方法,增加一个
ListCreateAPIView             ---> ListModelMixin,CreateModelMixin,GenericAPIView  get方法,post方法 查询所有增加一个
RetrieveAPIView               ---> RetrieveModelMixin,GenericAPIView               get 查询一个
DestroyAPIView                ---> DestroyModelMixin,GenericAPIView                delete 删除一个
UpdateAPIView                 ---> UpdateModelMixin,GenericAPIView                 put   修改一个
RetrieveDestroyAPIVIew        ---> RetrieveModelMixin,DestroyModelMixin,GenericAPIView
RetrieveUpdateAPIView         ---> RetrieveModelMixin,UpdateModelMixin,GenericAPIView 
RetrieveUpdateDestroyAPIView  ---> RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView
 

image

我们发现使用这种方法还是要写两个视图,不然会有2个get方法,路由的路径也不一样,drf 给我们提供了一个类。

ModelViewSet 类

这个类继承了5个扩展类。那么它就拥有了5个接口,但是一旦继承了这个类路由写法就变了。

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):

基于ModelViewSet 类编写接口

# 查询所有,新增一个
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

路由层

# 根据请求方法,映射对应的方法
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/',views.BookView.as_view({'get':'list','post':'create'})),
    path('books/<int:pk>/',views.BookView.as_view({'get':'retrieve','put':'update','delete':'destroy'}))

ReadOnlyModelViewSet 类 只读

如果继承它那么就只有查询一个和查询所有接口

class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
                           mixins.ListModelMixin,
                           GenericViewSet):
  他继承了查询一个接口和查询所有接口 

基于ReadOnlyModelViewSet编写 2个只读接口

from rest_framework.viewsets import ReadOnlyModelViewSet
class BookView(ReadOnlyModelViewSet): # 只有查询单个和查询所有
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
   "需要注意的是他的路由只能填写2个接口"
路由层:
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/',views.BookView.as_view({'get':'list'})),
    path('books/<int:pk>/',views.BookView.as_view({'get':'retrieve'}))

使用两种这种方法我们只需要编写一个视图类,但是路由的写法变了,为什么变?需要研究源码

路由写法源码研究

我们在继承ModelViewSet与ReadOnlyModelViewSet的时候发现它们两还继承了一个GenericViewSet类,进入其源码发现他继承了

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    ViewSetMixin  # 没见过这个
    GenericAPIView # 两个视图基类其中一个

之前在继承GenericAPIView编写接口的时候路由并没有变化,所以问题出在我们没见过的ViewSetMixin上。研究它的源码

请求来了(get,post),路由匹配成功,匹配成功books,会执行views.BookView.as_view({'get': 'list', 'post': 'create'})()
读as_view此时这个我们的视图类中内并没有as_view去父类中查找 在GenericViewSet的父类ViewSetMixin中找到了as_view方法


    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        # 如果没有传actions(路由中的{'get':'list'}),直接抛异常,路由写法变了后,as_view中不传字典,直接报错
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if 'get' in actions and 'head' not in actions:
                actions['head'] = actions['get']
            self.action_map = actions
            for method, action in actions.items():
                handler = getattr(self, action)
                setattr(self, method, handler)

            return self.dispatch(request, *args, **kwargs)
        # 去除了csrf校验
        return csrf_exempt(view)
    
    
2. 路由匹配成功执行views.BookView.as_view({'get': 'list', 'post': 'create'})() 返回了view并去除了csrf校验,执行view(),# 此时执行ViewSetMixin了as_view内的view()
    def view(request, *args, **kwargs):
            #actions 是路由层传入的字典--->{'get': 'list', 'post': 'create'}
            self.action_map = actions
            # 第一次循环:method:get,action:list
            # 第一次循环:method:post,action:create
            for method, action in actions.items():
                # 反射:去视图类中反射,action对应的方法,action第一次是list,去视图类中反射list方法
                # handler就是视图类中的list方法
                handler = getattr(self, action)
                # 反射修改:把method:get请求方法,handler:list
                # 视图类的对象的get方法,变成了list
                setattr(self, method, handler)

            return self.dispatch(request, *args, **kwargs) #从视图类往上寻找,dispatch是APIView的                

其本质就是:

  1. 只要继承了ViewSetMixin的视图类,路由的写法就变了,根本原因是重写了as_view

  2. 路由编写的方法变成了as_view({‘get’:‘list’})

    只要传入actions参数,以后get就会被as_view内的view方法利用反射编程list,访问get就是访问list的(映射)

  3. 其他执行顺序和以前一样,以后在视图类编写方法名只需要在路由层中做好映射。

扩展

我们在继承ModelViewSet时候发现这个rest_framework.viewsets包下还有几个类。

'''
ModelViewSet		5个试图扩展类+ViewSetMixin+GenericAPIView
ReadOnlyModelViewSet 2个试图扩展类+ViewSetMixin+GenericAPIView   只读的两个
ViewSetMixin		重写了as_view,只要继承他,以后路由写法变成了映射方法 需要配合9个视图类与GenericAPIView
ViewSet			   ViewSetMixin+ APIView
GenericViewSet      ViewSetMixin+ GenericAPIView
'''

 继承APIView来写接口,变路由写法({'请求方法':'任意方法名'})就要继承ViewSet
 继承GenericAPIView来写接口,变路由写法({'请求方法':'任意方法名'})就要继承 GenericViewSet

总结

1. 两个试图基类
	-APIView,GenericAPIView
    
2. 5个试图扩展类,不是视图类,必须配合GenericAPIView
	 -ListModelMixin    # get 查询所有
    -CreateModelMixin  # post 新增一个
    -RetieveModelMixin  # get 查询一个
    -UpdateModelMixin   # put 修改一个
    -DestoryModelMixin  # delete 删除一个
    
3. 9 个视图子类,是视图类,只需要继承其中某一个即可就可以使用那一个的功能

ListAPIView                   ---> ListModelMixin,GenericAPIView                   get方法,查询所有
CreateAPIView                 ---> CreateModelMixin,GenericAPIView                 post方法,增加一个
ListCreateAPIView             ---> ListModelMixin,CreateModelMixin,GenericAPIView  get方法,post方法 查询所有增加一个
RetrieveAPIView               ---> RetrieveModelMixin,GenericAPIView               get 查询一个
DestroyAPIView                ---> DestroyModelMixin,GenericAPIView                delete 删除一个
UpdateAPIView                 ---> UpdateModelMixin,GenericAPIView                 put   修改一个
RetrieveDestroyAPIVIew        ---> RetrieveModelMixin,DestroyModelMixin,GenericAPIView
RetrieveUpdateAPIView         ---> RetrieveModelMixin,UpdateModelMixin,GenericAPIView 
RetrieveUpdateDestroyAPIView  ---> RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView

4. 视图集 
ModelViewSet		5个试图扩展类+ViewSetMixin+GenericAPIView
"只需要写两行五个接口都有"
ReadOnlyModelViewSet 2个试图扩展类+ViewSetMixin+GenericAPIView   只读的两个
"只需要写两行两个只读接口有了"
ViewSetMixin		重写了as_view,只要继承他,以后路由写法变成了映射方法 需要配合9个视图类与GenericAPIView
ViewSet			   ViewSetMixin+ APIView
GenericViewSet      ViewSetMixin+ GenericAPIView
posted @ 2023-02-03 20:32  李阿鸡  阅读(30)  评论(0编辑  收藏  举报
Title