6.REST 高阶
一、 自定义接口规范(渲染器)
1. 渲染器的基本原理

序列化在返回数据后并不是直接做为响应数据,而是经过渲染器的渲染,生成不同格式的响应内容 。
重构渲染器就是重写父类渲染器的 render 方法 。
render(self, data, accepted_media_type=None, renderer_context=None)- data: 响应数据(序列化器的.data 属性),等同于 renderer_context[" response "].data 的值
- accepted_media_type=None: 可选的。如果提供,这是由内容协商阶段确定的所接受的媒体类型。- 根据客户端的 Accept: 头,这可能比渲染器的 media_type 属性更具体,可能包括媒体类型参数。例如 "application/json; nested=true" 。
 
- renderer_context=None: 可选的。如果提供,这是一个由 view 提供的上下文信息的字典。- 默认情况下这个字典会包括以下键: view , request , response , args , kwargs 。
- renderer_context[" view "]对应调用的视图函数
- renderer_context[" request "]对应本次请求对象,包含所有请求数据,如请求头,请求参数等等
- renderer_context[" response "]对应本次响应对象,包含所有响应数据,如响应头,状态码,响应数据
 
2. REST 框架 渲染器配置
在 settings.py 中添加 drf 全局配置
# settings.py
# rest框架配置
REST_FRAMEWORK = {
    # 添加全局异常处理模块
    'EXCEPTION_HANDLER': 'utils.exception.my_exception_handler',
    # 默认的渲染器
    'DEFAULT_RENDERER_CLASSES': (
        # 默认的渲染器 配置
        # 'rest_framework.renderers.JSONRenderer',
        # 'rest_framework.renderers.BrowsableAPIRenderer',
        'utils.renderers.MyRenderer',
    )
}utils.renderers.MyRenderer 对应的是你文件路径
创建 utils 目录,然后在下面新建 renderers.py 文件 和 exception.py 文件

3. 正常信息获取
# renderers.py
# 通用返回过滤器
from rest_framework.renderers import JSONRenderer
# 继承空 返回 JSON 的渲染器
class MyRenderer(JSONRenderer):
    # 重构 render 方法
    def render(self, data, accepted_media_type=None, renderer_context=None):
        # 默认把 data 作为响应数据
        resp_content = data
        if renderer_context:
            status_code = renderer_context['response'].status_code  # 响应状态码
            if str(status_code).startswith('2'):    # 以 2 开头表示 响应正常
                # 判断响应内容是否为列表,如果不是则需要转换为列表形式(接口要求)
                if not isinstance(resp_content, list):
                    resp_content = [resp_content]
                res = {'msg': 'success', 'retcode': status_code, 'retlist': resp_content}
                return super().render(res, accepted_media_type, renderer_context)
        # 异常情况
        return super().render(resp_content, accepted_media_type, renderer_context)3. 异常信息获取
REST 默认情况可以处理的异常有
- 在 REST framework 内部产生的 APIException 的子类异常。
- 原生 Django 的 Http404 异常.
- 原生 Django 的 PermissionDenied 异常.

异常处理:
# exception.py
# REST 框架异常处理器
from rest_framework.views import exception_handler
# 处理异常返回 过滤器
def my_exception_handler(exc, context):
    # 获取标准的错误响应
    response = exception_handler(exc, context)
    if response:
        # 成功捕获到异常
        response.data['msg'] = 'error'  # 错误信息标记
        response.data['retcode'] = response.status_code     # 同步状态码
        response.data['error'] = str(exc)   # 采用详细的议程消息
        response.data.pop('detail')     # 去除 detail 消息
    return response二、 在线接口文档 --- swagger
Django REST Swagger 项目已经不维护了,并且不支持最新的 Django
drf-yasg 项目作为接口文档生成器。yasg 的功能非常强大,可以同时支持多种文档格式。
1. 配置安装
1. 安装最新版 drf-yasg
pip install -U drf-yasg2. 注册
drf-yasg 属于 django 的插件,因此需要注册到配置文件中 settings.py
staticfiles : 默认的静态文件。默认是已经注册了的,如果没有注册需要手动注册
INSTALLED_APPS = [
    'django.contrib.staticfiles',
    'drf_yasg',
]3. 配置路由
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
# 文档视图
from rest_framework import permissions
# 定义 swagger 视图
swagger_view = get_schema_view(
    openapi.Info(
        title='SQPT API',
        default_version='v1',
        description='SQPT接口文档',
        terms_of_service='https://www.pupper.cn',
        contact=openapi.Contact(email='pupper.cheng@gmail.com'),
        license=openapi.License(name='BSD License'),
    ),
    public=True,     # 是否公开
    # 权限 class 是元组类型
    permission_classes=(permissions.AllowAny,),
)
urlpatterns = [
    # 互动模式
    path('swagger/', swagger_view.with_ui('swagger', cache_timeout=0,)),
    # 文档模式
    path('redoc/', swagger_view.with_ui('redoc', cache_timeout=0,)),
]2. 定制化用法(viewset 模式)
函数视图 : 采用swagger_auto_schema装饰器 修饰视图函数
# views.py
from drf_yasg.utils import swagger_auto_schema
@swagger_auto_schema(method='GET',operation_summary='定制化API',operation_description='接口描述。。')
@api_view(['GET'])
def customer_api(request):
	return Response(data={"retcode":status.HTTP_200_OK,'msg':'building...'})类视图 : 采用 django 装饰器 配合 swagger 的 装饰器 来实现
# views.py
from django.utils.decorators import method_decorator
from drf_yasg.utils import swagger_auto_schema
@method_decorator(name='list', decorator=swagger_auto_schema(operation_description="列出所有步骤数据"))
@method_decorator(name='create', decorator=swagger_auto_schema(operation_description="创建步骤"))
@method_decorator(name='update', decorator=swagger_auto_schema(operation_description="更新步骤"))
@method_decorator(name='destroy', decorator=swagger_auto_schema(operation_description="删除步骤"))
@method_decorator(name='retrieve', decorator=swagger_auto_schema(operation_description="提取单个步骤数据"))
class CaseViewSet(viewsets.ModelViewSet):
    queryset = Case.objects.all()
    serializer_class = CaseSerializer
三、 前端对接
前端样式地址 提取码:sqpt
配置 Django 静态文件服务, 是 开发时 使用的 一种 临时方案 ,
在 autotpsite/urls.py 文件 中配置静态服务
# autotpsite/urls.py
# 静态文件服务
from django.conf.urls.static import static
urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('sqpt.urls')),
]+static('/', document_root='dist')     # 配置静态文件访问路径如果 http 请求的 url 不是以 api/project 开头, Django 就会认为是要访问 dist 目录下面的静态文件。

当我们访问 html 页面时,js 代码会自动请求后台数据,渲染到当前页面上,为了适配前端,减少改动工作,先将后台 URL 进行改动
# autotpsite/urls.py
urlpatterns = [
    path('api/', include('sqpt.urls')),
]四、 接口开发完善
1. 嵌套字段内容显示
{"msg":"success","retcode":200,"retlist":
[{"id":1,"file_path":"haiwen_Test.yml","config":1,"suite":null}]}显示 config 的具体内容:指定 config 为对应的序列化器对象
# serializers.py
class ConfigSerializer(serializers.ModelSerializer):
    class Meta:
        model = Config
        fields = '__all__'
class CaseSerializer(serializers.ModelSerializer):
    # config 字段为 Config 序列化器 , REST 会自动提取其值
    config = ConfigSerializer()
    class Meta:
        model = Case
        fields = '__all__'如果 ConfigSerializer 在 CaseSerializer 的 下方,那么 config = ConfigSerializer() 会报错

已经展示了config 嵌套字段
{
    "msg": "success",
    "retcode": 200,
    "retlist": [
        {
            "id": 1,
            "config": {
                "id": 1,
                "name": "用例1",
                "base_url": "http://localhost",
                "variables": null,
                "parameters": null,
                "verify": false,
                "export": null
            },
            "file_path": "haiwen_Test.yml",
            "suite": null
        }
    ]
}2. displayname

获取 method 具体的值:
# serializers.py
# 请求模型的序列化器
class RequestSerializer(serializers.ModelSerializer):
    method = serializers.SerializerMethodField()  # 自定义字段序列化返回方法
    def get_method(self, obj):  # rest 框架获取 method 时,自动调用该方法
        return obj.get_method_display()  # 返回 choice 的 displayname 而不是实际值
    class Meta:
        model = Request  # 指定对应的模型
        fields = '__all__'  # 显示对应模型的所有字段重新访问接口,method 字段返回了可读内容
