【7.0】DRF之DRF请求与响应

发布时间 2023-07-31 12:29:36作者: Chimengmeng

【一】Request类对象分析

【1】.data

  • request.data 返回解析之后的请求体数据。类似于Django中标准的request.POST和 request.FILES属性,但提供如下特性:
    • 包含了解析之后的文件和非文件数据
    • 包含了对POST、PUT、PATCH请求方式解析后的数据
    • 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据

【2】.query_params

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

【3】其他属性

  • 其他的属性用起来跟之前一样

【二】请求能够接受的编码格式

【1】支持的格式

(1)urlencoded

(2)form-data

(3)json

【2】限制只能接受某种或某几种编码格式

限制方式一:

  • 在视图类上写---》只是局部视图类有效
    • 总共有三个:
      • JSONParser
      • FormParser
      • MultiPartParser
class BookView(APIView):
    parser_classes = [JSONParser, FormParser]

限制方式二:

  • 在配置文件中写---》全局有效

  • drf的配置,统一写成如下格式

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        # 'rest_framework.parsers.FormParser',
        # 'rest_framework.parsers.MultiPartParser',
    ],
}

【3】全局配置了只支持json,局部想支持3个

  • 只需要在局部,视图类中,写3个即可
class BookView(APIView):
    parser_classes = [JSONParser, FormParser,MultiPartParser]

【4】总结

  • 能够处理的请求方式编码

    • 优先从视图类中找

    • 再去项目配置文件找

    • 再去drf默认的配置中找

【三】响应类的对象Response

return Response({code:100})

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

        if isinstance(data, Serializer):
            msg = (
                'You passed a Serializer instance as data, but '
                'probably meant to pass serialized `.data` or '
                '`.error`. representation.'
            )
            raise AssertionError(msg)

        self.data = data
        self.template_name = template_name
        self.exception = exception
        self.content_type = content_type

        if headers:
            for name, value in headers.items():
                self[name] = value
  • 参数详解

  • data:响应体的内容,可以字符串,字典,列表

  • status:http响应状态码

    • drf把所有响应码都定义成了一个常量
    from rest_framework.status import HTTP_200_OK
    
    
    HTTP_100_CONTINUE = 100
    HTTP_101_SWITCHING_PROTOCOLS = 101
    HTTP_102_PROCESSING = 102
    HTTP_103_EARLY_HINTS = 103
    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
    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
    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_418_IM_A_TEAPOT = 418
    HTTP_421_MISDIRECTED_REQUEST = 421
    HTTP_422_UNPROCESSABLE_ENTITY = 422
    HTTP_423_LOCKED = 423
    HTTP_424_FAILED_DEPENDENCY = 424
    HTTP_425_TOO_EARLY = 425
    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
    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
    
  • template_name:模板名字,用浏览器访问,看到好看的页面,用postman访问,返回正常数据

    • 自定制页面
    • 根本不用
  • headers:响应头加数据(后面讲跨域问题再讲)

    • headers=
  • content_type:响应编码,一般不用

三个重要的参数:data,status,headers

【四】请求响应的格式

  • 默认是两种:
    • 纯json
    • 浏览器看到的样子

【1】限制方式一:

  • 在视图类上写
    • 只是局部视图类有效
# 总共有两个个:JSONRenderer,BrowsableAPIRenderer
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookView(APIView):
    renderer_classes = [JSONRenderer]

【2】限制方式二:

  • 在配置文件中写
    • 全局有效
# drf的配置,统一写成它
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        # 'rest_framework.renderers.BrowsableAPIRenderer',
    ],
}

【3】全局配置了只支持json,局部想支持2个

  • 只需要在局部,视图类中,写2个即可
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookView(APIView):
    renderer_classes = [JSONRenderer,BrowsableAPIRenderer]

【补充】视图之两个视图基类

【一】APIView和GenericAPIView简解

  • APIView:之前用过

  • GenericAPIView:GenericAPIView继承了APIView

【1】APIView

APIView是Django REST Framework中的一个基类视图

  • APIView是一个非常灵活的基类视图

    • 它提供了处理HTTP请求的各种方法
    • 如GET、POST、PUT、DELETE等。
  • 使用APIView

    • 开发者需要手动编写视图函数,并且需要处理请求参数的解析、数据的序列化与反序列化、身份验证和权限控制等。
  • APIView不提供任何默认实现

    • 开发者需要根据自己的需求编写每个请求方法的具体实现。
  • 这种方式适用于需要高度定制化的API

    • 对于特殊的业务逻辑或需要直接与底层请求进行交互的情况非常有用。

【2】GenericAPIView

GenericAPIView是继承自APIView的另一个基类视图

  • GenericAPIView在APIView的基础上提供了更多的通用功能
    • 旨在简化编写通用API视图的过程。
  • GenericAPIView结合了模型(Model)和视图(View)的概念
    • 能够执行常见的CRUD(创建、读取、更新、删除)操作。
  • 通过使用GenericAPIView
    • 开发者可以更轻松地处理单个对象的操作
    • 例如获取对象、更新对象、删除对象等。
  • GenericAPIView还提供了对查询集的过滤、排序和筛选等功能。
  • 此外,GenericAPIView还支持序列化与反序列化、分页处理、身份验证和权限控制等常见需求。
  • 这种方式适用于开发者在编写API时希望更轻松地处理通用的CRUD操作,并使用DRF提供的各种便利功能的情况。
  • 类属性:
    • queryset:要序列化的所有数据
    • serializer_class:序列化类
    • lookup_field = 'pk' :查询单条时的key值
  • 方法:
    • get_queryset():获取所有要序列化的数据【后期可以重写】
    • get_serializer : 返回序列化类
    • get_object :获取单个对象

【3】小结

  • PIView是一个灵活的基类视图,需要手动编写每个请求方法的实现;
  • GenericAPIView则在此基础上提供了更多的通用功能,适用于更轻松地处理通用的CRUD操作及相关需求。
  • 以后继承GenericAPIView写接口
    • 必须配置类属性
      • queryset
        • 指定用于操作数据库的模型查询集。
        • 例如,queryset = YourModel.objects.all()
      • serializer_class
        • 指定用于序列化和反序列化数据的序列化类。
        • 例如,serializer_class = YourSerializer
    • 想获取要序列化的所有数据
      • get_queryset()
        • 获取要序列化的数据集合。
        • 该方法会返回配置的模型查询集。
        • 例如:queryset = self.get_queryset()
    • 想使用序列化类:
      • get_serializer
        • 可以在视图类的各个方法中使用该实例来进行序列化和反序列化操作。
        • 例如:serializer = self.get_serializer(data=request_data)
    • 想拿单条
      • get_object
        • 获取符合指定条件的单个对象。
        • 可以通过重载该方法来自定义获取单条数据的逻辑。
        • 例如:obj = self.get_object()

【二】使用APIView+序列化类+Response写接口

【1】准备数据

  • 模型层
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)

    # on_delete
    # CASCADE: 级联删除,只要删除 publish ,跟publish关联的book,全部被删除
    # SET_DEFAULT: 只要删除 publish ,跟publish关联的book,publish的字段都会变成默认值,(建议配合default一起使用)
    # SET_NULL: 只要删除 publish ,跟publish关联的book,publish的字段都会变成空,(建议配合null一起使用)
    # SET(): (放个值),只要删除 publish ,跟publish关联的book,publish的字段都会set设的值或执行函数
    publish = models.ForeignKey(to="Publish", on_delete=models.SET_NULL, null=True, blank=True)
    authors = models.ManyToManyField(to="Author")


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)
    # 本质上就是foreignkey,但是唯一,多的方是唯一,形成了一对一
    author_detail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)


class AuthorDetail(models.Model):
    email = models.CharField(max_length=32)
    age = models.IntegerField()

【2】路由层

from django.contrib import admin
from django.urls import path
from app01.views import BookView, BookDetailView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('book/', BookView.as_view()),
    path('book/<int:pk>/', BookDetailView.as_view()),
]

【3】序列化器

class BookSerializer(serializers.ModelSerializer):

    class Meta:
        model = Book  # 指定表模型
        # fields = "__all__" # 映射模型表中所有的字段
        fields = ["name", "price", 'publish_detail', 'author_list', "publish", "authors"]  # 映射模型表中指定的字段,自定义字段一定也要在这里注册
        extra_kwargs = {
            "name": {"max_length": 10, "required": True},  # 可以新增额外的限制条件
            "publish": {"write_only": True},
            "authors": {"write_only": True},
        }
    publish_detail = serializers.DictField(read_only=True)

    author_list = serializers.ListField(read_only=True)

【三】使用GenericAPIView+序列化类+Response写接口

【1.0】起始

  • 使用APIView+序列化类+Response写接口
from app01.serializers.MY_serializer import BookSerializer, AuthorSerializer, AuthorDetailSerializer
from rest_framework.views import APIView
from app01 import models
from rest_framework.response import Response


class BookView(APIView):
    def get(self, request):
        book_queryset = models.Book.objects.all()
        book_ser = BookSerializer(instance=book_queryset, many=True)

        return Response({"code": 200, "msg": "请求成功", "result": book_ser.data})


class BookDetailView(APIView):
    def get(self, request, pk):
        book_queryset = models.Book.objects.filter(pk=pk).first()
        book_ser = BookSerializer(instance=book_queryset)
        return Response({"code": 200, "msg": "请求成功", "result": book_ser.data})

    def post(self, request):
        book_ser = BookSerializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response({"code": 100, "msg": "保存成功", "result": []})
        else:

            return Response({"code": 101, "msg": "保存失败", "result": book_ser.errors})

    def put(self, request, pk):
        book_obj = models.Book.objects.filter(pk=pk).first()
        book_ser = BookSerializer(data=request.data, instance=book_obj)
        if book_ser.is_valid():
            book_ser.save()
            return Response({"code": 100, "msg": "更新成功", "result": []})
        else:

            return Response({"code": 101, "msg": "保存失败", "result": book_ser.errors})

【2.0】优化

  • 使用自定义GenericAPIView+序列化类+Response写接口
class GenericAPIView(APIView):
    query_set = None
    serializer_class = None

    def get_queryset(self):
        return self.query_set

    def get_serializer(self):
        return self.serializer_class

    def get_obj(self):
        return self.query_set.filter(pk=pk).first


class BookView(GenericAPIView):
    query_set = models.Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        # book_queryset = models.Book.objects.all()
        book_queryset = self.get_queryset()
        # book_ser = BookSerializer(instance=book_queryset, many=True)
        book_ser = self.get_serializer(instance=book_queryset, many=True)

        return Response({"code": 200, "msg": "请求成功", "result": book_ser.data})


class BookDetailView(GenericAPIView):
    query_set = models.Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, pk):
        # book_queryset = models.Book.objects.filter(pk=pk).first()
        book_queryset = self.get_obj()
        # book_ser = BookSerializer(instance=book_queryset)
        book_ser = self.get_serializer(instance=book_queryset)
        return Response({"code": 200, "msg": "请求成功", "result": book_ser.data})

    def post(self, request):
        # book_ser = BookSerializer(data=request.data)
        book_ser = self.get_serializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response({"code": 100, "msg": "保存成功", "result": []})
        else:

            return Response({"code": 101, "msg": "保存失败", "result": book_ser.errors})

    def put(self, request, pk):
        # book_obj = models.Book.objects.filter(pk=pk).first()
        book_obj = self.get_obj()
        # book_ser = BookSerializer(data=request.data, instance=book_obj)
        book_ser = self.get_serializer(data=request.data, instance=book_obj)
        if book_ser.is_valid():
            book_ser.save()
            return Response({"code": 100, "msg": "更新成功", "result": []})
        else:

            return Response({"code": 101, "msg": "保存失败", "result": book_ser.errors})

【3.0】迭代

  • 使用DRF中的GenericAPIView+序列化类+Response写接口
from rest_framework.generics import GenericAPIView


class BookView(GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        # book_queryset = models.Book.objects.all()
        book_queryset = self.get_queryset()
        # book_ser = BookSerializer(instance=book_queryset, many=True)
        book_ser = self.get_serializer(instance=book_queryset, many=True)

        return Response({"code": 200, "msg": "请求成功", "result": book_ser.data})


class BookDetailView(GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, pk):
        # book_queryset = models.Book.objects.filter(pk=pk).first()
        book_queryset = self.get_object()
        # book_ser = BookSerializer(instance=book_queryset)
        book_ser = self.get_serializer(instance=book_queryset)
        return Response({"code": 200, "msg": "请求成功", "result": book_ser.data})

    def post(self, request):
        # book_ser = BookSerializer(data=request.data)
        book_ser = self.get_serializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response({"code": 100, "msg": "保存成功", "result": []})
        else:

            return Response({"code": 101, "msg": "保存失败", "result": book_ser.errors})

    def put(self, request, pk):
        # book_obj = models.Book.objects.filter(pk=pk).first()
        book_obj = self.get_object()
        # book_ser = BookSerializer(data=request.data, instance=book_obj)
        book_ser = self.get_serializer(data=request.data, instance=book_obj)
        if book_ser.is_valid():
            book_ser.save()
            return Response({"code": 100, "msg": "更新成功", "result": []})
        else:

            return Response({"code": 101, "msg": "保存失败", "result": book_ser.errors})

【总结】

  • 类属性:
    • queryset:要序列化的所有数据
    • serializer_class:序列化类
    • lookup_field = 'pk' :查询单条时的key值
  • 方法:
    • get_queryset():获取所有要序列化的数据【后期可以重写】
    • get_serializer : 返回序列化类
    • get_object :获取单个对象

【大总结】

【1】反序列化校验源码分析

  • validate_字段名
  • validate
  • 序列化类.is_valiad----->
    • 全局钩子:self.validate()--->
    • for循环一个个字段类,通过反射找到validate_字段名,如果有就会执行,如果没有就不会执行

【2】DRF之请求

  • Request:新的request对象

    • data
      • POST请求携带的参数
    • query_params
      • GET请求携带的参数
    • 其它用起来一样
  • 前端传入的编码格式

    • 默认三种:urlencoded,for-data,json
    • 局部,全局控制能够解析的编码格式

【3】DRF之响应

  • Response:响应对象

    • data

      • 响应体的内容
    • status

      • http 响应状态码

        from rest_framework import statu
        # 查看响应状态码---》常量
        
    • header:

      • 响应头

      • 原生django,设置响应头

        # 新手四件套:render,redirect,JsonResponse,HttpResponse
        def res():
            res=新手四件套之一
            res['name']="添加的内容" # 向原生的响应对象中添加自定义内容
            return res
        
  • 响应的样子:两种:json,浏览器

    • 全局配置

    • 局部配置

【4】DRF之视图--两个视图基类

  • APIView:drf最顶层的视图类,继承了原生djagno的View

    • 1 去除了csrf
    • 2 包装了新的request
    • 3 在执行视图类的方法之前,执行了三大认证
    • 4 处理了全局异常
  • GenericAPIView:继承了APIView

    • 类属性

      • queryset:要序列化的数据
      • serializer_class:指定序列化类
      • lookup_field:获取单挑数据的查询条件
      • filter_backends:过滤类
      • pagination_class:分页类
    • 如何使用

      • self:一个对象 等同于下面写的p

        class Person:
            name='dream'
            
        p=Person()
        p1=Person()
        print(p.name)
        # dream
        p.name='梦梦'
        print(p1.name)
        # 梦梦
        
      • self.queryset--->先找对象自己,而自己没有,才用了类的

    • 方法:绑定给对象的方法(写成方法的目的是解耦合,提高扩展性)

      • get_queryset:返回所有要序列化的数据

      • get_object:获取单条数据

        • 路由必须:path('/<int:pk>/', PublishDetailView.as_view()),
      • get_serializer:真正的实例化得到序列化类

        • ser=get_serializer(instance=qs,many=True)
      • get_serializer_class:仅仅是返回序列化类,不能传任何参数

        • serializer_class=self.get_serializer_class()

        • serializer_class(instance=qs,many=True)

  • 如果涉及到数据库操作,尽量用GenericAPIView

【拓展】断言

  • assert:if判断加抛异常

【拓展】AOP/OOP

面向切面编程(AOP)和面向对象编程(OOP)是两种不同的编程范式。

它们都是用于解决软件系统设计和开发中的各种问题的方法。

(1)面向对象编程(OOP):

OOP是一种以对象作为程序的基本单元,将数据和操作数据的行为封装在对象中的编程范式。

主要特点包括封装、继承和多态。

  • 封装:OOP通过将数据和方法封装在对象内部,使得外部无法直接访问对象内部的数据,只能通过对象提供的公有接口来访问和操作数据。这种封装性可以保证数据的安全性和完整性。
  • 继承:OOP通过类与类之间的继承关系,实现了代码复用和层次结构的建立。子类可以继承父类的属性和方法,并可在此基础上进行扩展和修改。
  • 多态:OOP允许对象根据其具体类型来表现出不同的行为。这意味着父类的引用可以指向子类的对象,并根据具体引用所指向的对象来调用适当的方法。多态性增加了代码的灵活性和可扩展性。

(2)面向切面编程(AOP):

AOP是一种将系统中的横切关注点(如日志记录、性能统计、事务管理等)从业务逻辑代码中分离出来的编程范式。

主要特点包括切面、连接点和通知。

  • 切面:切面是横切关注点的模块化表示。它由切点和通知组成,切点定义了在应用程序执行过程中哪些位置需要应用通知,而通知则定义了具体要执行的操作。
  • 连接点:连接点是在应用程序中可以插入切面的特定位置。例如,在方法调用、方法执行前后、抛出异常时等都是连接点。
  • 通知:通知是在切面的特定连接点上所执行的代码。通知可以分为前置通知(在连接点之前执行)、后置通知(在连接点之后执行,无论连接点是否发生异常)、环绕通知(在连接点前后执行,并可控制连接点的执行)、异常通知(在连接点发生异常时执行)等。

(3)AOP的优势

  • AOP的优势在于通过切面的方式对系统功能进行横向切割,避免了代码的重复和冗余。
  • 同时,AOP使得程序的架构更加清晰和可维护,增强了系统的可扩展性与可重用性。

(4)总结

  • OOP和AOP都是用于解决软件设计和开发中的问题的编程范式。
  • OOP通过封装、继承和多态等方式实现面向对象的特性
  • AOP则通过切面、连接点和通知等方式实现对系统横切关注点的分离。