DRF视图类关系详解

Posted franciszw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DRF视图类关系详解相关的知识,希望对你有一定的参考价值。

概要:
# 1 请求和响应
# 2 请求 Request对象,drf新包装的,Request.data,Request.query_params, 重写了__getattr__,  request._request
# 3 json模块是否执行反序列化bytes格式
# 4 考你:视图类的方法中:self.request,就是当次请求的request

# 5 Response:类,实例化传一堆参,data=字典,status=状态码(有一堆常量),headers=响应头(字典),content_type=响应的编码方式
# 6 全局和局部配置,响应格式
# 7 drf默认配置文件,查找顺序--》先从类中属性找---》项目的setting找---》drf默认的配置找


# 8 视图家族
	-APIView---》继承自View
    -GenicAPIView---》APIView,做了一些扩展:
    	-queryset = None
    	-serializer_class = None
        -get_queryset()  经常用
        -get_serializer() 经常用
        -get_serializer_class() 内部来用,外部会重写
        -get_object()  经常用,获取一条数据(pk传过来)
        	-源码解析
            queryset = self.filter_queryset(self.get_queryset()) #返回所有数据queryset对象
            # lookup_url_kwarg就是pk,路由中有名分组分出来的pk
            lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
            # {pk:4}  4 浏览器地址中要查询的id号http://127.0.0.1:8000/books6/4/
            filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
            # 根据pk=4去queryset中get单个对象
            obj = get_object_or_404(queryset, **filter_kwargs)
            self.check_object_permissions(self.request, obj)
            return obj
   -5 个视图扩展类(继承了object),每个里面写了一个方法(ListModelMixin:list方法)		
        ListModelMixin,
        CreateModelMixin,
        UpdateModelMixin,
        DestroyModelMixin,
        RetrieveModelMixin
   -GenericAPIView的视图子类,9个,继承了GenicAPIView+一个或者两个或者三个视图扩展类
        CreateAPIView,
        ListAPIView,
        UpdateAPIView,
        RetrieveAPIView,
        DestroyAPIView,
        ListCreateAPIView,
        RetrieveUpdateDestroyAPIView,
        RetrieveDestroyAPIView,
        RetrieveUpdateAPIView
  -视图集:ModelViewSet,ReadOnlyModelViewSet:继承了上面一堆(5个视图扩展和GenicAPIView)+自己写了一个ViewSetMixin(as_view方法),只要继承它的,路由得写成{‘get’:‘自己定义的方法’}
	-ViewSet=ViewSetMixin, views.APIView :ViewSetMixin要放在前面
    -GenericViewSet=ViewSetMixin+GenicAPIView
    
    -ViewSetMixin(as_view方法)
    -ViewSetMixin+APIView=ViewSet
    -ViewSetMixin+GenicAPIView=GenericViewSet

1 请求和响应

1.1请求

  • 1.1.1,认证类的两个request
源码:
def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    if hasattr(self, ‘get‘) and not hasattr(self, ‘head‘):
        self.head = self.get
    self.request = request #将原生的request赋值各了对象,类的对象可以通过self.取到request
    self.args = args
    self.kwargs = kwargs
    return self.dispatch(request, *args, **kwargs)#函数返回了request所以视图函数可以接收request
  • 1.1.2 经过APIView时,rest_framework重新封装了request

    导入使用: from rest_framework.request import Request

# 源码分析
1,重写了init方法对象装饰时字典将原request作为新request的属性
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        
        # 二次封装request,将原生request作为drf request对象的 _request 属性
        self._request = request
        
2,重写了getattr方法,获取新request的属性时先去原request里查找       
    def __getattr__(self,item):
    	return getattr(self._request,item)
# 请求对象.data:前端以三种编码方式传入的数据,都可以取出来
# 请求对象..query_params 与Django标准的request.GET相同,只是更换了更正确的名称而已。

1.2响应

rest_framework重新封装了response,

导入:from rest-framework.response import Response

源码分析:
def __init__(self, data=None, status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None):
        
#data:你要返回的数据,字典
#status:返回的状态码,默认是200,
	-from rest_framework import status在这个路径下,它把所有使用到的状态码都定义成了常量
#template_name 渲染的模板名字(自定制模板)
#headers:响应头,可以往响应头放东西,就是一个字典
#content_type:响应的编码格式,application/json和text/html;

# 浏览器响应成浏览器的格式,postman响应成json格式,通过配置实现的(默认配置)
#不管是postman还是浏览器,都返回json格式数据

drf配置文件的查找顺序:
# drf的配置信息,先从自己类中找--》项目的setting中找---》默认的找
	-局部使用:对某个视图类有效
        -在视图类中写如下,drf会优先使用自己类的配置,只对自己的类有效
        from rest_framework.renderers import JSONRenderer
        renderer_classes=[JSONRenderer,]
        
    -全局使用:全局的视图类,所有请求,都有效
    	-在setting.py中加入如下
        REST_FRAMEWORK = {
            ‘DEFAULT_RENDERER_CLASSES‘: (  # 默认响应渲染类
                ‘rest_framework.renderers.JSONRenderer‘,  # json渲染器
                ‘rest_framework.renderers.BrowsableAPIRenderer‘,  # 浏览API渲染器
            )
        }
  • 1.2.1自己封装一个response对象

2 视图

2.1 基于APIView写接口:

1,手动写orm语句

book_list=Book.objects.all()

2,手动调用序列化器

book_ser=BookSerializer(book_list,many=True)

3,手动写业务逻辑处理不同的请求

def get(self,request):

def post(self,request):

4,视图的类需要写两个,分别对应url的有名分组传参和不传参情况.

url(r‘^api/books2/$‘, views.Books2.as_view()),
url(r‘^api/book2/(?P<num>d+)/‘, views.Book2.as_view())
class BookView(APIView):
    def get(self,request):
        book_list=Book.objects.all()
        book_ser=BookSerializer(book_list,many=True)

        return Response(book_ser.data)
    def post(self,request):
        book_ser = BookSerializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({‘status‘:101,‘msg‘:‘校验失败‘})


class BookDetailView(APIView):
    def get(self, request,pk):
        book = Book.objects.all().filter(pk=pk).first()
        book_ser = BookSerializer(book)
        return Response(book_ser.data)

    def put(self, request,pk):
        book = Book.objects.all().filter(pk=pk).first()
        book_ser = BookSerializer(instance=book,data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({‘status‘: 101, ‘msg‘: ‘校验失败‘})

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

下面的类分别解简化了上述步骤大量减少代码的书写:

2.2 基于GenericAPIView写的接口:

简化步骤,在类里直接申明了操作哪张表,调用哪个序列化器:

queryset = models.Booklib.objects
serializer_class = BookModelSerializer

在请求的函数种调用申明的列表和序列化器会自动传入对象

book=self.get_queryset().filter(pk=num).first()
ser_obj=self.get_serializer(book)

下面分别对应5种方法的注意事项:

  • queryset只有一个对象时有个大坑,需要用.first()取到
  • 反序列化数据化后要判断数据是否合法,调用序列化器的save方法
  • 多条数据序列化用many=True
class Books2(GenericAPIView):

    queryset = models.Booklib.objects.all()
    serializer_class = BookModelSerializer

    def get(self,request):

        book=self.get_queryset()
        book_ser=self.get_serializer(book,many=True)

        return CommomResponse(data=book_ser.data,headers={‘key‘:‘value‘})


    def post(self,request):#创建,修改了id为只读属性,创建时不需要id参数
        book_ser=self.get_serializer(data=request.data)

        if book_ser.is_valid():
            book_ser.save()

            return CommomResponse(data=book_ser.data)
        else:

            return CommomResponse(101,‘数据不和法‘,book_ser.errors)

class Book2(GenericAPIView):
    queryset = models.Booklib.objects
    serializer_class = ser.BookModelSerializer

    def get(self,request,num):
        book=self.get_queryset().filter(pk=num).first()
        ser_obj=self.get_serializer(book)

        return CommomResponse(data=ser_obj.data)


    def delete(self,request,num):
        book=self.get_queryset().filter(pk=num).delete()

        return CommomResponse(msg=‘删除成功‘)

    def put(self,request,num):
        book=self.get_queryset().filter(pk=num).first()#只有一个值时一定有用.first()
        ser_obj=self.get_serializer(book,request.data)
        if ser_obj.is_valid():
            ser_obj.save()  #这是序列化器的save方法,不是queryset的
            return CommomResponse(data=ser_obj.data)
        else:
            return CommomResponse(msg=‘数据不合法‘)

2.3 基于GenericAPIView和5个视图扩展类写的接口

5个视图扩展类:直接继承object的类,这些类能自动能更具请求方式相应

ListModelMixin:获取所有数据

RetrieveModelMixin:获取一条数据

CreateModelMixin:创建

UpdateModelMixin:更新

DestroyModelMixin:删除

使用方法:在相应的请求函数下调用对应的函数,有参数的还得传参:

class Books3(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = models.Booklib.objects
    serializer_class = ser.BookModelSerializer

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


class Book3(GenericAPIView,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin):
    queryset = models.Booklib.objects
    serializer_class = ser.BookModelSerializer

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

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

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

2.4 基于GenericAPIView的9个视图拓展类编写接口:

分别是:

5个基础组合类
CreateAPIView,ListAPIView,RetrieveAPIView,DestroyAPIView,UpdateAPIView 
4个扩展组合类
ListCreateAPIView,RetrieveUpdateAPIView,RetrieveDestroyAPIView,RetrieveUpdateDestroyAPIView 

这9个类集合了API类和ModelMixin类的优点,同时又更进一步,类体写了请求对应的操作,继承这个类之后只需要申明操作票那个表和指定那个序列化器.

使用方法:只需要这6行代码实现面的功能

class Books4(ListCreateAPIView):
    queryset = models.Booklib.objects 
    #这里需要一个queryse对像所以要去到object
    serializer_class = ser.BookModelSerializer

class Book4(RetrieveUpdateDestroyAPIView):
    queryset = models.Booklib.objects
    serializer_class = ser.BookModelSerializer

到此为止,试图函数的代码已经大大的减少 ,但是还得自己手动配置路由路径和两个视图类的对应关系.

接下来介绍的两个类分别解决了需要写两个视图类的问提和手动配置路由匹配的问提

2.5继承ViewSetMixin的视图类

作用:只需要编写一个视图类来对应两个url

该类重写了的as_view功能匹配了请求的method和url视图层action,并且调用函数

注意:因为ViewSetMixin重写了as_view,查找as_view时会现去继承的第一父类查找,如果APIView在前则,不会执行重写的as_view导致功能失效.

当然这里的APIView也可以是GenericAPIView

class Book05(ViewSetMixin,APIView):
    def get_one_book(self,request,pk):

        obj_list = models.Booklib.objects.filter(pk=pk).first()
        ser_book = ser.BookModelSerializer(obj_list)

        return CommomResponse(data=ser_book.data)

    def get_books(self,request):
        obj_list = models.Booklib.objects.all()
        ser_book=ser.BookModelSerializer(obj_list,many=True)

        return CommomResponse(data=ser_book.data)
    
    URL写法:
   url(r‘^api/books5/$‘, views.Book05.as_view(actions={‘get‘:‘get_books‘})),
   url(r‘^api/book5/(?P<pk>d+)/‘, views.Book05.as_view(actions={‘get‘:‘get_one_book‘})),

2.6 基于ModelViewSet类编写接口

ModelViewSet的父类:

GenericViewSet 5个视图拓展类(这5个类都直接继承object)

GenericViewSet继承了GenericAPIView和ViewSetMixin(该类直接继承object)

GenericAPIView的继承关系就比较清晰了:

GenericAPIView>>>APIView>>>View>>>object

请求与函数的对应:

get-->list 获取多个

get-->retrieve 获取一个

post-->create

put-->update

delete-->delete

class Book06(ModelViewSet):
    queryset = models.Booklib.objects
    serializer_class = BookModelSerializer

    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data[0:2])#此处使得获取所有数据的请求改为只获取前两个数据,这是通过重写list方法实现的

    @action([‘GET‘], False) #通过action装饰器自动生成list_3对应的url,False即不接受参数
    def list_3(self, request):
        pass

2.7 SimpleRouter类

该类写在路由层,作用为自动生成与视图类关联的路由

router=routers.SimpleRouter()
router.register(‘book‘,views.Book06)

urlpatterns+=router.urls#将自动生成的路由加入到原路由列表中

到此为止,ModelViewSet类和SimpleRouter类最终将上述的问题都解决了

以上是关于DRF视图类关系详解的主要内容,如果未能解决你的问题,请参考以下文章

Django(52)APIView详解

04 drf视图组件

DRF视图-请求与响应

4DRF实战总结:序列化器(Serializer)数据验证重写序列化器方法详解(附源码)

067.Python框架Django之DRF视图类

drf序列化器之请求响应以及视图