drf请求、响应与视图

一、请求

1 定义

REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。

由于这个Request类内部重写了__getattr__方法,所以原来的request类的方法在新的request也可以点出来使用

2 常用属性

1).data

request.data 返回解析之后的请求体数据。类似于Django中标准的request.POSTrequest.FILES属性,但提供如下特性:

  • 包含了解析之后的文件和非文件数据
  • 包含了对POST、PUT、PATCH请求方式解析后的数据
  • 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据

2).query_params

request.query_params与Django标准的request.GET相同,只是更换了更正确的名称而已。

总结:在drf中基本上使用的数据都是request.data,因为django原生的request不会解析json数据

二、响应

1 Response

rest_framework.response.Response

drf提供了一个响应类Response,该类在构造响应对象的时候,响应会被转化(render)成符合前端需求的类型,这一步在drf重写的dispatch源码中有。

但是我们可以发现,如果我们是浏览器发起请求得到的是一个页面,如果是postman发起请求,得到的直接就是json数据,这是内部帮我们判断了一下。

drf也提供了修改默认值的方法,让我们可以在前端也直接接受json格式的数据

# 在settings.py中添加(这是默认的,不加也是这样)
# 默认原来的出处在drf的settings里
# 当发起响应的时候,会先在我们项目的settings内找,找不到再去默认的里找
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
        # 注释掉下面任意一条就只能返回另一条了
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
    )
}

2 构造方法

Response(data, status=None, template_name=None, headers=None, content_type=None)

参数说明:

  • data: 为响应准备的序列化处理后的数据,通常放一个字典;
  • status: 状态码,默认200;
  • template_name: 模板名称,如果使用HTMLRenderer 时需指明,通常不用;
  • headers: 用于存放响应头信息的字典,放一个字典;
  • content_type: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。

3 状态码

为了方便设置状态码,REST framewrok在rest_framework.status模块中提供了常用状态码常量。

1)信息告知 - 1xx

HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS

2)成功 - 2xx

HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS

3)重定向 - 3xx

HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT

4)客户端错误 - 4xx

HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS

5)服务器错误 - 5xx

HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED

三、视图

1 基类视图 APIView

apiview是drf中所有视图类的基类,它继承自djangoview

在前几篇中有对应的源码分析,apiview和view的区别:

  • 传入到视图类中的request是drf新封装的,是在原来request上扩展的,所以原来的属性方法也能直接使用
  • 返回的response,会在apiview内部渲染成符合前端要求的格式(json或者html)
  • 任何APIException都会被捕获,并且处理成合适的响应信息
  • 在执行apiview内的dispatch方法,会对请求身份认证,权限检查,流量控制

支持定义的类属性

  • authentication_classes 列表或元祖,身份认证类
  • permissoin_classes 列表或元祖,权限检查类
  • throttle_classes 列表或元祖,流量控制类

APIView实现5个常用的api接口

class StudentAPI(APIView):
    def get(self,request,pk):
        obj = models.Student.objects.filter(pk = pk).first()
        ser_boj = ser.StudentSerializer(obj)
        return JsonResponse(ser_boj.data)

    def put(self,request,pk):
        back_dic = {'code':200,'msg':'成功'}
        obj = models.Student.objects.filter(pk = pk).first()
        ser_obj = ser.StudentSerializer(obj,request.data)
        if ser_obj.is_valid():
            ser_obj.save()
            back_dic['data'] = ser_obj.data
        else:
            back_dic['code'] = 100
            back_dic['msg'] = '出错'
            back_dic['error_msg'] = ser_obj.errors
        return JsonResponse(back_dic)
    def delete(self,request,pk):
        obj = models.Student.objects.filter(pk=pk).first()
        if obj:
            obj.delete()
            back_dic = {'code': 200, 'msg': '成功','data':{}}
        else:
            back_dic = {'code': 100, 'msg': '失败','error_msg':'用户不存在'}
        return Response(back_dic)


class StudentsAPI(APIView):
    def post(self,request):
        # print(request.data)
        back_dic = {'code': 200, 'msg': '成功'}
        obj = ser.StudentSerializer(data = request.data)
        if obj.is_valid():
            obj.save()
            back_dic['data'] = obj.data
        else:
            back_dic['code'] = 100
            back_dic['msg'] = '错误'
            back_dic['error_msg'] = obj.errors

        return (back_dic)

    def get(self,request):
        obj = models.Student.objects.all()
        obj_ser = ser.StudentSerializer(obj,many=True)
        back_dic = {'code':200,'msg':'正确','data':obj_ser.data}
        return Response(back_dic,status=201)

2 通用视图类 GenericAPIView

继承自APIView,主要增加了统一视图类的查询集和序列化器,作为给下面的Mixin扩展类的执行提供方法支持,通常在使用的时候回搭配Mixin扩展类使用

内部提供的关于序列化器的属性和方法

  • 属性:serializer_class指明视图使用的序列化器

  • 这个属性对应的方法:

    • get_serializer_class(self)

    • 这个方法返回的是是序列化器类,如果我们这个视图类中要根据不同情况使用不同的序列化器类的时候即可重写这个方法

    • def get_serializer_class(self):
          if self.request.user.is_staff:
              return FullAccountSerializer
          return BasicAccountSerializer
      
    • 这个方法如果不重写,默认会返回我们在属性里定义的serializer_class指定的序列化器类

    • get_serializer(self, args, *kwargs)

    • 这个方法返回的是序列化器对象,主要用来提供给后面介绍的Mixin扩展类使用,如果我们想在视图类中获取序列化器对象,也可以直接使用这个方法

    • 内部传参和调用序列化器类一样

    • stu_ser = self.get_serializer(instance = self.get_queryset(),many = True)
      # data 接受修改的数据
      
    • 注意,该方法在提供序列化器对象的时候,会向序列化器对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。

      • request 当前视图的请求对象
      • view 当前请求的类视图对象
      • format 当前请求期望返回的数据格式
  • 属性:queryset:指明使用的数据查询集

  • 方法:get_queryset(self)

    • 返回视图使用的查询集,通常提供给Mixin扩展类使用

    • 若返回的模型对象不存在,返回404

    • 该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。

      举例:

      # url(r'^books/(?P<pk>d+)/$', views.BookDetailView.as_view()),
      class BookDetailView(GenericAPIView):
          queryset = BookInfo.objects.all()
          serializer_class = BookInfoSerializer
      
          def get(self, request, pk):
              book = self.get_object() # get_object()方法根据pk参数查找queryset中的数据对象
              serializer = self.get_serializer(book)
              return Response(serializer.data)
      

    其他可以设置的属性

  • pagination_class 指明分页控制类

  • filter_backends 指明过滤控制后端

基于GenericAPIView写5个api

# get全部,post单个
class StudentsGenericAPI(GenericAPIView):
    queryset = models.Student.objects.all()
    serializer_class = ser.StudentModelSerializer
    def get(self,request):
        stu_ser = self.get_serializer(instance = self.get_queryset(),many = True)
        return Response(stu_ser.data)
    def post(self,request):
        print(request.data)
        stu_ser = self.get_serializer(data=request.data)
        if stu_ser.is_valid():
            stu_query = stu_ser.save()
            stu_ser = self.get_serializer(stu_query)
            return Response(stu_ser.data)
        return Response(stu_ser.errors)

# get单个,put单个,del单个
class StudentGenericAPI(GenericAPIView):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    def get(self,request,pk):
        stu_ser = self.get_serializer(self.get_queryset().filter(pk=pk).first())
        return Response(stu_ser.data)
    def put(self,request,pk):
        stu_ser = self.get_serializer(self.get_queryset().filter(pk=pk).first(),data=request.data)
        if stu_ser.is_valid():
            stu_ser.save()
            return Response(stu_ser.data)
        return Response(stu_ser.errors)
    def delete(self,request,pk):
        self.get_queryset().filter(pk=pk).first().delete()
        return Response({'msg':'删除成功'})

序列化器类:

from rest_framework import serializers

from students.models import Student

class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model= Student
        fields = "__all__"


class StudentModel2Serializer(serializers.ModelSerializer):
    class Meta:
        model= Student
        fields = ("name","class_null")

3 扩展类Mixin

# 基于视图扩展类写的api
# 相比于只用Generic,把5个api方法都在对应的视图扩展类中写了,只要继承调用即可
# 对应关系如下
# list:get全部,create:post,update:put,destroy:delete,retrieve:get单个
from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin

class Students2API(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    def get(self,request):
        return self.list(request)
    def post(self,request):
        return self.create(request)
class Student2API(GenericAPIView,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    def put(self,request,pk):
        return self.update(request,pk)
    def get(self,request,pk):
        return self.retrieve(request,pk)
    def delete(self,request,pk):
        return self.destroy(request,pk)

4 GenericAPIView的视图子类

#GenericAPIView的视图子类 9个
from rest_framework.generics import CreateAPIView,ListAPIView,UpdateAPIView,RetrieveAPIView,DestroyAPIView,ListCreateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView
# 视图子类在5个扩展类的基础上,把我们函数的方法写到内部去了
# 所以下面我们只要继承这些视图类,然后定义查询集和序列化器就能根据请求的不同自动调用对应的视图函数
# 对应关系如下
# 单个的对应关系,list:get全部,create:post,update:put,destroy:delete,retrieve:get单个
# 多个的对应关系,ListCreate:get全部+post,RetrieveUpdateDestroy:get单个+put+delete
# 多个的对应关系,RetrieveDestroy:get单个+delete,RetrieveUpdate:get单个+put
class Students3API(ListCreateAPIView):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
class Student3API(RetrieveUpdateDestroyAPIView):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    

5 视图集

1) ViewSet

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

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

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

总结:扩展性高,自动化程度低

2)GenericViewSet

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

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

举例:

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

url的定义

urlpatterns = [
    path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students7/(?P<pk>d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})),

]

3)ModelViewSet

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

# 使用ModelViewSet写5个api,url需要修改
# ModelViewSet继承了5中扩展视图类,所以只要配置好就可以使用5个对应的api方法
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSetMixin
class Students4API(ModelViewSet):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    
# urls
 url(r'^student4/$',views.Students4API.as_view(actions={
        'get':'list','post':'create'
    })),
    url(r'^student4/(?P<pk>d+)',views.Students4API.as_view(actions={
        'get':'retrieve','put':'update','delete':'destroy'
    })),
    

4)ReadOnlyModelViewSet

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

# ReadOnlyModelViewSet对应get全部和get单个
class Student5API(ReadOnlyModelViewSet):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    
# urls
url(r'^student5/$',views.Student5API.as_view(actions={'get':'list'})),
 url(r'^student5/(?P<pk>d+)',views.Student5API.as_view(actions={'get':'retrieve'})),

四、视图类继承关系

视图扩展类Mixin都是顶级继承

ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin 都继承自object

ViewSetMixin 它是视图集的顶级父类,所有视图集最终都继承它

GenericAPIView视图子类都继承它,也继承对应关系的视图扩展Mixin类

CreateAPIView,ListAPIView,UpdateAPIView,RetrieveAPIView,DestroyAPIView,ListCreateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView

通用视图子类继承关系

image-20200708195416552

视图基类继承关系

GenericAPIView----》APIView----》View

视图集继承关系

image-20200708194530502

xxx视图集继承关系查找方式:

我们可以先看名字,如果结尾是ViewSet,可以确定是一个视图集

普通视图集---》GenericViewSet----》ViewSetMixin

普通视图集也会继承对应的Mixin扩展类

比如ReadOnlyModelViewSet,是只读视图集,就是只有get全部和get单个的方法,那它必定还继承ListModelMixin和RetrieveModelMixin扩展类

总结:继承关系只要知道继承的方式就很好判断,所以要明白什么是视图集,什么是基类视图,什么是扩展类,通用视图和通用视图子类

原文地址:https://www.cnblogs.com/hz2lxt/p/13268967.html