1、请求与响应

1.1 Request(请求)

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

drf提供了Parser解析器,在接收到请求之后会根据Content-Type指明的请求数据类型(json、表单等)将请求数据进行解析,解析为类字典<QueryDict> 对象保存到Request 对象之中。

Request对象的数据是根据前端发送数据的格式进行解析之后的结果。不论前端发送的是那种格式的数据,我们都可以使用统一的方式读取数据。

常用属性:

  • .data

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

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

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

1.2 Response(响应)

drf 提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。

drf 提供了Renderer 渲染器,用来根据请求头中的Accept(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。

可以在rest_framework.settings 查找所有的drf默认配置项。其中配置响应格式的配置项为:

1594192554676

局部配置使用,即只对某一个视图类配置生效。在视图类中加入以下代码:

from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
renderer_classes=[JSONRenderer,]  #配置使用json渲染器
# renderer_classes=[BrowsableAPIRenderer,] # 配置使用浏览API渲染器

在全局配置使用,即对所有的视图类、所有的请求都生效。在setting.py 中加入以下代码:

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
    )
}

1.2.1 构造方式

找到drf 中的Response类,可以获知其有以下代码:

class Response(SimpleTemplateResponse):
        def __init__(self, data=None, status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None):
            ...

则Response对象的构造方式为:

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

其中data数据不要是render处理之后的数据,只需要传递你需要返回的数据。drf 会自动使用renderer 渲染器处理该数据。

参数说明:

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

1.2.2 常用属性

  • .data 传给Response对象的,序列化后但尚未render处理的数据
  • .status_code 状态码的数字
  • .content 经过render处理后的响应数据

1.2.3 状态码

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

  • 信息告知 - 1xx

    HTTP_100_CONTINUE = 100
    HTTP_101_SWITCHING_PROTOCOLS = 101
    
  • 成功 - 2xx

    HTTP_200_OK = 200
    HTTP_201_CREATED = 201
    HTTP_202_ACCEPTED = 202
    HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203
    HTTP_204_NO_CONTENT = 204
    HTTP_205_RESET_CONTENT = 205
    HTTP_206_PARTIAL_CONTENT = 206
    HTTP_207_MULTI_STATUS = 207
    HTTP_208_ALREADY_REPORTED = 208
    HTTP_226_IM_USED = 226
    
  • 重定向 - 3xx

    HTTP_300_MULTIPLE_CHOICES = 300
    HTTP_301_MOVED_PERMANENTLY = 301
    HTTP_302_FOUND = 302
    HTTP_303_SEE_OTHER = 303
    HTTP_304_NOT_MODIFIED = 304
    HTTP_305_USE_PROXY = 305
    HTTP_306_RESERVED = 306
    HTTP_307_TEMPORARY_REDIRECT = 307
    HTTP_308_PERMANENT_REDIRECT = 308
    
  • 客户端错误 - 4xx

    HTTP_400_BAD_REQUEST = 400
    HTTP_401_UNAUTHORIZED = 401
    HTTP_402_PAYMENT_REQUIRED = 402
    HTTP_403_FORBIDDEN = 403
    HTTP_404_NOT_FOUND = 404
    HTTP_405_METHOD_NOT_ALLOWED = 405
    HTTP_406_NOT_ACCEPTABLE = 406
    HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407
    HTTP_408_REQUEST_TIMEOUT = 408
    HTTP_409_CONFLICT = 409
    HTTP_410_GONE = 410
    HTTP_411_LENGTH_REQUIRED = 411
    HTTP_412_PRECONDITION_FAILED = 412
    HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413
    HTTP_414_REQUEST_URI_TOO_LONG = 414
    HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415
    HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416
    HTTP_417_EXPECTATION_FAILED = 417
    HTTP_422_UNPROCESSABLE_ENTITY = 422
    HTTP_423_LOCKED = 423
    HTTP_424_FAILED_DEPENDENCY = 424
    HTTP_426_UPGRADE_REQUIRED = 426
    HTTP_428_PRECONDITION_REQUIRED = 428
    HTTP_429_TOO_MANY_REQUESTS = 429
    HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
    HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451
    
  • 服务器错误- 5xx

    HTTP_500_INTERNAL_SERVER_ERROR = 500
    HTTP_501_NOT_IMPLEMENTED = 501
    HTTP_502_BAD_GATEWAY = 502
    HTTP_503_SERVICE_UNAVAILABLE = 503
    HTTP_504_GATEWAY_TIMEOUT = 504
    HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505
    HTTP_506_VARIANT_ALSO_NEGOTIATES = 506
    HTTP_507_INSUFFICIENT_STORAGE = 507
    HTTP_508_LOOP_DETECTED = 508
    HTTP_509_BANDWIDTH_LIMIT_EXCEEDED = 509
    HTTP_510_NOT_EXTENDED = 510
    HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511
    

2、视图

drf 的提供的视图的主要作用有:

  • 控制序列化器的执行(检验、保存、转换数据)
  • 控制数据库查询的执行

它还提供了许多的视图基类和拓展类,用于简化视图的编写。

2.1 两个视图基类

2.1.1 APIView

APIViewdrf 提供的所有视图的基类,继承自Django的View父类。

APIViewView的不同之处在于:

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

APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。例如使用APIView简单的实现五个增删查改的API接口。

models.py 创建数据模型类:

from django.db import models

class Book(models.Model):
    name=models.CharField(max_length=32)
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publish=models.CharField(max_length=32)

urls.py 中创建路由匹配:

from django.urls import path,re_path
from app01 import views

urlpatterns = [
    path('books/', views.BookView.as_view()),
    re_path('books/(?P<pk>\d+)', views.BookDetailView.as_view()),
]

ser.py 中创建ModelSerializer 类:

from rest_framework import serializers
from app01.models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model=Book
        fields='__all__'

views.py中基于APIView创建视图类

from rest_framework.views import  APIView
from app01.models import Book
from app01.ser import BookSerializer

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.1.2 GenericAPIView(通用视图类)

导入:

from rest_framework.generics import GenericAPIView

继承关系如下:

GenericAPIView 继承自APIVIew,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类。

提供的关于序列化器使用的属性与方法

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

  • 方法:

    get_serializer_class(self) : 当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了。返回序列化器类,默认返回serializer_class,可以重写。

    get_serializer(self, *args, **kwargs) :返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法。

    注意:

    该方法在提供序列化器对象的时候,会向序列化器对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。
    
    -request	当前视图的请求对象
    -view		当前请求的类视图对象
    -format		当前请求期望返回的数据格式
    

提供的关于数据库查询的属性与方法

  • 属性:queryset  指明使用的数据查询集

  • 方法:

    get_queryset(self) :返回视图使用的查询集,主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写

    get_object(self) :返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用。在试图中可以调用该方法获取详情信息的模型类对象。**若详情访问的模型类对象不存在,会返回404。**该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。

示例:使用GenericAPIView 将之前创建的接口进行改写:

urls.py :

from django.urls import path,re_path
from app01 import views

urlpatterns = [
    # 使用GenericAPIView重写的
    path('books2/', views.Book2View.as_view()),
    re_path('books2/(?P<pk>\d+)', views.Book2DetailView.as_view()),
]

views.py :

class Book2View(GenericAPIView):
    # queryset要传queryset对象,查询了所有的图书
    # serializer_class使用哪个序列化类来序列化这堆数据
    queryset = Book.objects
    # queryset=Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        book_list = self.get_queryset()
        book_ser = self.get_serializer(book_list, many=True)

        return Response(book_ser.data)

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


class Book2DetailView(GenericAPIView):
    queryset = Book.objects
    serializer_class = BookSerializer

    def get(self, request, pk):
        book = self.get_object()
        book_ser = self.get_serializer(book)
        return Response(book_ser.data)

    def put(self, request, pk):
        book = self.get_object()
        book_ser = self.get_serializer(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 = self.get_object().delete()
        return Response({'status': 100, 'msg': '删除成功'})

2.2 五个视图拓展类

作用:

​ 提供了几种后端视图(对数据资源进行曾删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。

​ 这五个扩展类需要搭配GenericAPIView 父类,因为五个扩展类的实现需要调用GenericAPIView 提供的序列化器与数据库查询的方法。

导入:

from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin
from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin
  • ListModelMixin :列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。该Mixin的list方法会对数据进行过滤和分页。

  • RetrieveModelMixin :详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。如果存在,返回200, 否则返回404。

  • CreateModelMixin :创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。如果序列化器对前端发送的数据验证失败,返回400错误。

  • UpdateModelMixin :更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。

    成功返回200,序列化器校验数据失败时,返回400错误。

  • DestroyModelMixin : 删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。成功返回204,不存在返回404。

使用示例,基于GenericAPIView和5个视图拓展类改写接口:

urls.py :

from django.urls import path,re_path
from app01 import views

urlpatterns = [
    # 使用GenericAPIView+5 个视图扩展类 
    path('books3/', views.Book3View.as_view()),
    re_path('books3/(?P<pk>\d+)', views.Book3DetailView.as_view()),
]

views.py

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


class Book3View(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects
    serializer_class = BookSerializer

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

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


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

    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.3 GenericAPIView的视图子类

导入:

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

这些视图子类分别部分提供了get post put patch delete 方法,继承自GenericAPIView及其拓展类。

应用示例:

urls.py

from django.urls import path,re_path
from app01 import views

urlpatterns = [
    path('books4/', views.Book4View.as_view()),
    re_path('books4/(?P<pk>\d+)', views.Book4DetailView.as_view()),
]

views.py

from rest_framework.generics import CreateAPIView, ListAPIView, UpdateAPIView, RetrieveAPIView, DestroyAPIView, \
    ListCreateAPIView, RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView


# class Book4View(ListAPIView,CreateAPIView):  #获取所有,新增一个
class Book4View(ListCreateAPIView):  # 获取所有,新增一个
    queryset = Book.objects
    serializer_class = BookSerializer


# class Book4DetailView(UpdateAPIView,RetrieveAPIView,DestroyAPIView):
class Book4DetailView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects
    serializer_class = BookSerializer

3、视图集

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

  • list() 提供一组数据
  • retrieve() 提供单个数据
  • create() 创建数据
  • update() 保存数据
  • destory() 删除数据

ViewSet视图集类不再实现get()post()等方法,而是实现动作 action 如 list() 、create() 等。

视图集只在使用as_view()方法的时候,才会将action 动作与具体请求方式对应上。

3.1 常用的视图集父类

  • ViewSet : 继承自APIView与ViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。**ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{‘get’:’list’})的映射处理工作。**在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
  • GenericViewSet :使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView 。GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
  • ModelViewSet :继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
  • ReadOnlyModelViewSet :继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

3.2 action

在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。除了默认的方法动作外,还可以添加自定义动作。

示例:

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def login(self,request):
        """学生登录功能"""
        return Response({"message":"登录成功"})

url的定义:

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

]

视图集应用改写接口示例:

urls.py

urlpatterns = [
    # 使用ModelViewSet编写5个接口
    path('books5/', views.Book5View.as_view(actions={'get': 'list', 'post': 'create'})),
    # 当路径匹配,又是get请求,会执行Book5View的list方法
    re_path('books5/(?P<pk>\d+)',
            views.Book5View.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]

views.py

# 使用ModelViewSet编写5个接口
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet

class Book5View(ModelViewSet):
    queryset = Book.objects
    serializer_class = BookSerializer
    
# class Book5View(ReadOnlyModelViewSet):  # 2个接口,获取一条,和获取所有两个
#     queryset = Book.objects
#     serializer_class = BookSerializer

4、总结-视图类之间的继承关系图

GenericAPIView的视图子类继承关系:

ListAPIView
ListCreateAPIView
RetrieveUpdateAPIView
RetrieveDestroyAPIView
UpdateAPIView
RetrieveAPIView
CreateAPIView
RetrieveUpdateDestroyAPIView

视图集:

ViewSet
ModelViewSet
ReadOnlyModelViewSet

Author:Laoqi