23.4.21. drf视图

之前在Django中,我们已经接触了视图。在DRF中对视图做了进一步的封装。

1.请求与响应

request

DRF传入视图的request对象不再是Django默认的HttpRequest对象(from django.http import HttpRequest),而是DRF提供的Request对象(from rest_framework.views import Request)。

DRF 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典对象保存到Request对象中。

request.data

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

  • 包含了解析之后的文件和非文件数据;

  • 包含了对POSTPUTPATCH请求方式解析后的数据;

  • 利用了DRFparsers解析器,不仅支持表单类型数据,也支持JSON数据;

request.query_params

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

Response

from rest_framework.response import Response

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

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会根据前端所需类型数据来设置该参数。

data数据不要是render处理之后的数据,只需传递``python``的内建类型数据即可DRF会使用renderer渲染器处理data(例如会渲染成前端可以识别的json格式)。

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

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (   #  设置全局的默认渲染器
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器,上线后尽量关闭
    )
}

data不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用Serializer序列化器序列化处理后(转为了Python字典类型)再传递给data参数。

状态码

为了方便设置状态码,DRFrest_framework.status模块中提供了常用状态码常量(用于Response(status=status.HTTP_200_OK))。

# 信息告知 - 1xx
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS

# 成功 - 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

# 重定向 - 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

# 客户端错误 - 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


# 服务器错误 - 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

DRF中最基本的视图就是APIView,它直接继承djangoView类,在它的基础上对request进一步封装,加了权限,认证,限流。

2.APIView

2.1 支持定义的属性

  • authentication_classes 列表或元祖,身份认证类

  • permissoin_classes 列表或元祖,权限检查类

  • throttle_classes 列表或元祖,流量控制类

APIView中仍以常规的类视图定义方法来实现get()post()或者其他请求方式的方法。

2.2 模型

from django.db import models


# Create your models here.
class Column(models.Model):
    """栏目"""
    name = models.CharField(max_length=20, unique=True, verbose_name='栏目')
    # link_url = models.URLField(verbose_name= '链接')
    link_url = models.CharField(max_length=200, verbose_name='链接')

    index = models.IntegerField(verbose_name='位置', default=1)

    class Meta:  # 模型元选项
        db_table = 'tb_column'  # 在数据库中的表名,否则Django自动生成为app名字_类名
        ordering = ['index']
        verbose_name = '栏目'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

2.3 序列化

from rest_framework import serializers
from app1.models import BookInfo, Column


class BookInfoSerializer(serializers.ModelSerializer):
    """图书数据序列化器"""

    class Meta:
        mode = BookInfo
        filelds = "__all__"


class ColumnSerializer(serializers.ModelSerializer):
    class Meta:
        model = Column
        fields = '__all__'

2.4 视图:

2.4.1 查询所有栏目

import json
from django.http import JsonResponse
from django.views import View
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from app1.models import BookInfo, Column
from app1.serializers import BookInfoSerializer, ColumnSerializer


class BooksView(View):

    def get(self, request):
        """
        :param request:
        :return: 查询所有图书
        """
        books = BookInfo.objects.all()
        ser = BookInfoSerializer(books, many=True)
        data = ser.data
        return JsonResponse(data, safe=False)


class ColumnView(APIView):
    """ 查询所有的栏目 """
    def get(self, request):
        columns = Column.objects.all()
        serializers = ColumnSerializer(instance=columns, many=True)
        return Response(serializers.data, status=status.HTTP_200_OK)

我们可以用postman和网页同时测试一下。

表面看结果是一样的,但是其实渲染的不一样,使用浏览器请求结果渲染成HTML了。

DRF根据请求头Accept: text/html,将结果渲染为HTML

而使用POSTMAN,未进行Accept声明,则采用默认方式处理响应数据,即JSON格式,我们可以手工在postman请求头中设置Accept=text/html

2.4.2 新增一条数据

class ColumnView(APIView):
    """ 查询所有的栏目 """
    def get(self, request):
        columns = Column.objects.all()
        serializers = ColumnSerializer(instance=columns, many=True)
        return Response(serializers.data, status=status.HTTP_200_OK)

    def post(self, request):
        # 1、接收参数,获取参数
        # Django:request.POST,request.body
        # DRF:request.data
        data = request.data
        print(data)

        # 2、反序列化数据
        serializer = ColumnSerializer(data=data)

        # 3、验证数据
        if serializer.is_valid():
            # 4、保存数据
            serializer.save()
            # 返回响应
            return Response(serializer.data)

        return Response(serializer.errors)

使用postman测试

2.4.3 查看某一个分类的数据

class ColumnDetailView(APIView):
    def get(self, request):
        # 获取参数
        # Django:request.GET
        # DRF:request.query_params

        # 测试使用URL:http://47.107.69.21:5044/articles/columns?id=1
        # print(request.query_params)  # <QueryDict: {'id': ['1']}>
        # print(request.query_params.get('id'))  # 1
        id = request.query_params.get('id')
        column = Column.objects.get(pk=id)
        serializer = ColumnSerializer(instance=column)
        if serializer.is_valid():
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

上面查询集参数,只是为了演示request.query_params属性,按照RESTful风格,路径,标识api的具体网址,每个网址代表一种资源。

如果要查某一个分类column信息,建议改写成:

class ColumnDetailViewid(APIView):
    def get(self, request, id):  # 这个参数需要与url中的参数一致
        """
        查询某个对象
        """
        column = Column.objects.get(pk=id)
        serializer = ColumnSerializer(instance=column)
        return Response(serializer.data)

2.4.4 修改和删除

class ColumnDetailViewid(APIView):
    def get(self, request, id):  # 这个参数需要与url中的参数一致
        """
        查询某个对象
        """
        column = Column.objects.get(pk=id)
        serializer = ColumnSerializer(instance=column)
        return Response(serializer.data)

    def put(self, request, id):
        """ 修改某个对象"""
        column = Column.objects.get(pk=id)
        serializer = ColumnSerializer(instance=column, data=request.data)

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, id):
        """删除文章"""
        Column.objects.get(pk=id).delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

分别使用postman进行测试,验证数据的正确性。

2.4.5 总结方法

基于DRF的增删改查操作

APIView

urls.py

from django.urls import path
from .views import PersontView
app_name = 'classview'
urlpatterns = [
    # 列表: /person/ get
    # 新增: /person/ post
    # 详情: /person/[pk]/ get
    # 修改: /person/[pk]/ put
    # 删除: /person/[pk]/ delete
    path('person/',PersontView.as_view()),
    path('person/<int:pk>/',PersontView.as_view()),
]

view.py

from rest_framework.views import APIView
from django.http import Http404
from rest_framework.response import Response
from apps.drf_demo.models import Person
from .serializers import PersonSerializer
from rest_framework import status

class PersontView(APIView):
    """
    检索, 更新和删除一个merchant实例对象.
    """
    def get_object(self, pk):
        try:
            return Person.objects.get(pk=pk)
        except Person.DoesNotExist:
            raise Http404

    def get(self, request, pk=None):
        if pk:
            merchant= self.get_object(pk)
            serializer = PersonSerializer(merchant)
            return Response(serializer.data)
        else:
            queryset = Person.objects.all()
            serializer = PersonSerializer(instance=queryset,many=True)
            return Response(serializer.data)

    def put(self, request, pk):
        merchant = self.get_object(pk)
        serializer = PersonSerializer(merchant, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        merchant= self.get_object(pk)
        merchant.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

3. GenericAPIView