Django 使用swagger自定义自动生成类
完整代码:https://gitee.com/mom925/django-system
之前写的Django配置swagger(https://www.cnblogs.com/moon3496694/p/17657283.html)其实更多还是自己手动的写代码去书写接口文档,我希望它能更加的自动化生成出接口文档,所以我需要自己重写一些函数。
安装所需的包,注册app,注册路由参考之前的即可(https://www.cnblogs.com/moon3496694/p/17657283.html),下面是在之前的基础上做的改进
自定义swagger自动生成的类需要在配置里指定自定义的类
SWAGGER_SETTINGS = {
'USE_SESSION_AUTH': False,
'SECURITY_DEFINITIONS': {
'身份验证': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization'
}
},
"DEFAULT_AUTO_SCHEMA_CLASS": "utils.swagger.CustomSwaggerAutoSchema",
}
我的swagger.py文件
from django.utils.encoding import smart_str
from drf_yasg.errors import SwaggerGenerationError
from drf_yasg.inspectors import SwaggerAutoSchema
from drf_yasg.utils import merge_params, get_object_classes
from rest_framework.parsers import FileUploadParser
from rest_framework.request import is_form_media_type
from rest_framework.schemas import AutoSchema
from rest_framework.utils import formatting from Wchime.settings import SWAGGER_SETTINGS def get_consumes(parser_classes): parser_classes = get_object_classes(parser_classes)
parser_classes = [pc for pc in parser_classes if not issubclass(pc, FileUploadParser)]
media_types = [parser.media_type for parser in parser_classes or []]
return media_types def get_summary(string):
if string is not None:
result = string.strip().replace(" ", "").split("\n")
return result[0] class CustomAutoSchema(AutoSchema):
def get_description(self, path, method):
view = self.view
return self._get_description_section(view, 'tags', view.get_view_description()) class CustomSwaggerAutoSchema(SwaggerAutoSchema): def get_tags(self, operation_keys=None):
tags = super().get_tags(operation_keys)
# print(tags)
if "api" in tags and operation_keys:
# `operation_keys` 内容像这样 ['v1', 'prize_join_log', 'create']
tags[0] = operation_keys[SWAGGER_SETTINGS.get('AUTO_SCHEMA_TYPE', 2)]
ca = CustomAutoSchema()
ca.view = self.view
tag = ca.get_description(self.path, 'get') or None
if tag:
# tags.append(tag)
tags[0] = tag
# print('===', tags)
return tags def get_summary_and_description(self):
description = self.overrides.get('operation_description', None)
summary = self.overrides.get('operation_summary', None)
# print(description, summary)
if description is None:
description = self._sch.get_description(self.path, self.method) or ''
description = description.strip().replace('\r', '') if description and (summary is None):
# description from docstring... do summary magic
summary, description = self.split_summary_from_description(description)
# print('====', summary, description)
if summary is None:
summary = description
return summary, description def get_consumes_form(self): return get_consumes(self.get_parser_classes()) def add_manual_parameters(self, parameters):
"""
重写这个函数,让他能解析json,也可以解析表单
"""
manual_parameters = self.overrides.get('manual_parameters', None) or [] if manual_parameters:
parameters = [] if any(param.in_ == openapi.IN_BODY for param in manual_parameters): # pragma: no cover
raise SwaggerGenerationError("specify the body parameter as a Schema or Serializer in request_body")
if any(param.in_ == openapi.IN_FORM for param in manual_parameters): # pragma: no cover
has_body_parameter = any(param.in_ == openapi.IN_BODY for param in parameters) if has_body_parameter or not any(is_form_media_type(encoding) for encoding in self.get_consumes_form()):
raise SwaggerGenerationError("cannot add form parameters when the request has a request body; "
"did you forget to set an appropriate parser class on the view?")
if self.method not in self.body_methods:
raise SwaggerGenerationError("form parameters can only be applied to "
"(" + ','.join(self.body_methods) + ") HTTP methods") return merge_params(parameters, manual_parameters) # -------------------------------------------------------------------------------------------------------------- from rest_framework import serializers
from drf_yasg import openapi
from rest_framework.relations import PrimaryKeyRelatedField
from rest_framework.fields import ChoiceField def serializer_to_swagger(ser_model, get_req=False):
'''
序列化转成openapi的形式
'''
if ser_model is None and get_req is True:
return {}, []
elif ser_model is None and get_req is False:
return {}
dit = {}
serializer_field_mapping = {
ChoiceField: openapi.TYPE_INTEGER,
PrimaryKeyRelatedField: openapi.TYPE_INTEGER,
serializers.IntegerField: openapi.TYPE_INTEGER,
serializers.BooleanField: openapi.TYPE_BOOLEAN,
serializers.CharField: openapi.TYPE_STRING,
serializers.DateField: openapi.TYPE_STRING,
serializers.DateTimeField: openapi.TYPE_STRING,
serializers.DecimalField: openapi.TYPE_NUMBER,
serializers.DurationField: openapi.TYPE_STRING,
serializers.EmailField: openapi.TYPE_STRING,
serializers.ModelField: openapi.TYPE_OBJECT,
serializers.FileField: openapi.TYPE_STRING,
serializers.FloatField: openapi.TYPE_NUMBER,
serializers.ImageField: openapi.TYPE_STRING,
serializers.SlugField: openapi.TYPE_STRING,
serializers.TimeField: openapi.TYPE_STRING,
serializers.URLField: openapi.TYPE_STRING,
serializers.UUIDField: openapi.TYPE_STRING,
serializers.IPAddressField: openapi.TYPE_STRING,
serializers.FilePathField: openapi.TYPE_STRING,
}
fields = ser_model().get_fields()
if get_req:
required = []
for k, v in fields.items():
description = getattr(v, 'label', '')
if isinstance(v, serializers.SerializerMethodField) or getattr(v, 'source'):
continue
elif isinstance(v, ChoiceField):
description += str(dict(getattr(v, 'choices', {}))) if getattr(v, 'required', True) is not False:
required.append(k)
typ = serializer_field_mapping.get(type(v), openapi.TYPE_STRING)
dit[k] = openapi.Schema(description=description, type=typ)
return dit, required
else:
for k, v in fields.items():
description = getattr(v, 'label', '')
if isinstance(v, ChoiceField):
description += str(dict(getattr(v, 'choices', {})))
elif isinstance(v, serializers.SerializerMethodField):
continue
typ = serializer_field_mapping.get(type(v), openapi.TYPE_STRING)
dit[k] = openapi.Schema(description=description, type=typ) return dit def serializer_to_req_form_swagger(ser_model, filter_fields):
li = list()
serializer_field_mapping = {
ChoiceField: openapi.TYPE_INTEGER,
PrimaryKeyRelatedField: openapi.TYPE_INTEGER,
serializers.IntegerField: openapi.TYPE_INTEGER,
serializers.BooleanField: openapi.TYPE_BOOLEAN,
serializers.CharField: openapi.TYPE_STRING,
serializers.DateField: openapi.TYPE_STRING,
serializers.DateTimeField: openapi.TYPE_STRING,
serializers.DecimalField: openapi.TYPE_NUMBER,
serializers.DurationField: openapi.TYPE_STRING,
serializers.EmailField: openapi.TYPE_STRING,
serializers.ModelField: openapi.TYPE_OBJECT,
serializers.FileField: openapi.TYPE_FILE,
serializers.FloatField: openapi.TYPE_NUMBER,
serializers.ImageField: openapi.TYPE_FILE,
serializers.SlugField: openapi.TYPE_STRING,
serializers.TimeField: openapi.TYPE_STRING,
serializers.URLField: openapi.TYPE_STRING,
serializers.UUIDField: openapi.TYPE_STRING,
serializers.IPAddressField: openapi.TYPE_STRING,
serializers.FilePathField: openapi.TYPE_STRING,
}
fields = ser_model().get_fields()
for k, v in fields.items():
if k in filter_fields:
continue
description = getattr(v, 'label', '')
if isinstance(v, serializers.SerializerMethodField) or getattr(v, 'source'):
continue
elif isinstance(v, ChoiceField):
description += str(dict(getattr(v, 'choices', {})))
req = getattr(v, 'required', True)
typ = serializer_field_mapping.get(type(v), openapi.TYPE_STRING)
li.append(openapi.Parameter(name=k, description=description, type=typ, required=req, in_=openapi.IN_FORM))
return li class ViewSwagger(object): get_req_params = []
get_req_body = None
get_res_data = None
get_res_examples = {'json': {}}
get_res_description = ' '
get_res_code = 200
get_tags = None
get_operation_description = None post_req_params = []
post_req_body = None
post_res_data = None
post_res_examples = {'json': {}}
post_res_description = ' '
post_res_code = 200
post_tags = None
post_operation_description = None put_req_params = []
put_req_body = None
put_res_data = None
put_res_examples = {'json': {}}
put_res_description = ' '
put_res_code = 200
put_tags = None
put_operation_description = None delete_req_params = []
delete_req_body = None
delete_res_data = None
delete_res_examples = {'json': {}}
delete_res_description = ' '
delete_res_code = 200
delete_tags = None
delete_operation_description = None @classmethod
def req_serialize_schema(cls, serializer):
return serializer_to_swagger(serializer, get_req=True) @classmethod
def res_serializer_schema(cls, serializer):
return serializer_to_swagger(serializer, get_req=False)
@classmethod
def req_serializer_form_schema(cls, serializer, filter_fields=[]):
return serializer_to_req_form_swagger(serializer, filter_fields)
@classmethod
def get(cls): ret = {
'manual_parameters': cls.get_req_params,
'request_body': cls.get_req_body,
'responses': {cls.get_res_code: openapi.Response(description=cls.get_res_description, schema=cls.get_res_data, examples=cls.get_res_examples)} if cls.get_res_data else None
}
return ret @classmethod
def post(cls):
ret = {
'manual_parameters': cls.post_req_params,
'request_body': cls.post_req_body,
'responses': {
cls.post_res_code: openapi.Response(description=cls.post_res_description, schema=cls.post_res_data,
examples=cls.post_res_examples)} if cls.post_res_data else None
}
return ret @classmethod
def put(cls):
ret = {
'manual_parameters': cls.put_req_params,
'request_body': cls.put_req_body,
'responses': {
cls.put_res_code: openapi.Response(description=cls.put_res_description, schema=cls.put_res_data,
examples=cls.put_res_examples)} if cls.put_res_data else None
}
return ret @classmethod
def delete(cls):
ret = {
'manual_parameters': cls.delete_req_params,
'request_body': cls.delete_req_body,
'responses': {
cls.delete_res_code: openapi.Response(description=cls.delete_res_description, schema=cls.delete_res_data,
examples=cls.delete_res_examples)} if cls.delete_res_data else None
}
return ret
首先重写了get_tags方法,我希望只要在视图类下面注释里写上tags:"xxxx"即可自动的读取到。
上面写的CustomAutoSchema类就是读取了视图类的注释,然后获取出里面的tags值
只需要这样写:

然后即可生成:

得到了都在 测试图片标签下
重写get_summary_and_description方法,原来的这个方法获取到summary是有可能为空的,所以改成当summary为None时summary=description
如果需要在视图类注释中写这两个描述,则像下面一样:

也可以在方法注释中写,则像下面一样:

得到的结果一样:

注意如果两个地方都写则里面的注释会覆盖外层的,也就是方法中的注释会去覆盖视图类下面的注释
重写add_manual_parameters方法,原来的自动生成时只能解析一种数据类型,当传入多种解析类型时会默认的是JSON类型(因为rest_framework就是默认解析JSON)
因为在rest_framework中我们不管是表单还是json格式都可以request.data获取,像新增时是提交表单,批量删除时提交json格式,但是一般又写在同一个视图类下
所以给视图类指定解析数据类型 parser_classes = [MultiPartParser, JSONParser]
重写以后,存在两种都有的会返回表单格式先
视图类像下面一样:

得到的post和delete:


得到了post的表达数据和delete的JSON数据
Django 使用swagger自定义自动生成类的更多相关文章
- VS2015建立一个完整的c++工程:头文件.h 源文件.cpp,自动生成类
https://blog.csdn.net/weixin_40539125/article/details/81430801 打开VS2015 ,新建VS win32工程,前面步骤很简单,不再阐述 下 ...
- WebApi使用swagger ui自动生成接口文档
之前就写到.最近正在使用webapi.这里介绍一个实用的东西swageer ui现在开发都是前后端分开.我们这里是给前端提供api.有时候对于一个api的描述,并不想专门写一份文档.很浪费时间.swa ...
- ECshop网点程序优化-自动生成类目页Keywords、Desciption Meta
ECshop支持针对每个新建的类目自定义Keywords.Description Meta信息,好处就不用说了,帮助SEO或者让浏览者了解这是什么页面,但如果有几百个类目的时候,人工去写这些类目又有点 ...
- Django restful framework中自动生成API文档
自动生成api文档(不管是函数视图还是类视图都能显示) 1.安装rest_framework_swagger库 pip install django-rest-swagger 2.在项目下的 urls ...
- 【转】Django restful framework中自动生成API文档
转自 https://www.cnblogs.com/sui776265233/p/11350434.html 自动生成api文档(不管是函数视图还是类视图都能显示) 1.安装rest_framewo ...
- SpringBoot整合MyBatis-Plus代码自动生成类
在springboot的test测试类下创建 MpGenerator.java 配置 MpGenerator.java public class MpGenerator { @Test publ ...
- XML之自动生成类,添加,修改,删除类的属性
1. class ClassHelperDemo { public static void Main() { #region 演示一:动态生成类. //生成一个类t. Type t = ClassHe ...
- .NetCore2.1 WebAPI 根据swagger.json自动生成客户端代码
前言 上一篇博客中我们可以得知通过Swagger插件可以很方便的提供给接口开发者在线调试,但是实际上Swagger附带的功能还有很多, 比如使用NSwag生成客户端调用代码,进一步解放接口开发者. N ...
- 转 Django根据现有数据库,自动生成models模型文件
Django引入外部数据库还是比较方便的,步骤如下 : 创建一个项目,修改seting文件,在setting里面设置你要连接的数据库类型和连接名称,地址之类,和创建新项目的时候一致 运行下面代码可以自 ...
- python学习-- Django根据现有数据库,自动生成models模型文件
Django引入外部数据库还是比较方便的,步骤如下 : 创建一个项目,修改seting文件,在setting里面设置你要连接的数据库类型和连接名称,地址之类,和创建新项目的时候一致 运行下面代码可以自 ...
随机推荐
- Verilog HDL门级建模
Verilog HDL内部定义了12个基本门级元件可以直接用,用这些门级原件直接对逻辑图进行描述,称为门级建模. 每个门输入可能是逻辑0,逻辑1,不确定态x和高阻态z四个值之一. 1.多输入门 主要有 ...
- DX后台截图C++实现代码
DX后台截图C++实现代码 文章仅发布于https://www.cnblogs.com/Icys/p/DXGI.html和知乎上. 传统的GDI API (BitBlt)虽然可以完美的完成后台截图的任 ...
- 🔥🔥Java开发者的Python快速进修指南:函数进阶
在上一篇文章中,我们讲解了函数最基础常见的用法,今天我想在这里简单地谈一下函数的其他用法.尽管这些用法可能不是非常常见,但我认为它们仍然值得介绍.因此,我将单独为它们开设一个章节,并探讨匿名函数和装饰 ...
- MySQL Group by 优化查询
Group by 未加索引 使用的是临时表,加文件排序(数据量小用内存排序) 加个索引(一般是联合索引) 注意:这里加的索引一般不会仅仅是group by后面的字段索引(大多数多少条件是一个以该字 ...
- .net core下优秀的日志框架使用解析,附源代码
在 .NET Core 中,日志是一个非常重要的组件,它可以帮助我们记录应用程序的运行情况,以便在出现问题时进行排查.在本文中,我们将介绍五个优秀的 .NET Core 日志框架,它们分别是 Seri ...
- Linux笔记02: Linux环境_2.3 Linux网络连接
2.3 Linux网络连接 本节介绍VMware Workstation Player 17下CentOS 7的网络连接. 2.3.1 VMware网络类型 VMware提供的网络连接有5种: ●桥接 ...
- [ABC270Ex] add 1
Problem Statement You are given a tuple of $N$ non-negative integers $A=(A_1,A_2,\ldots,A_N)$ such t ...
- MySQL日期查询
MySQL日期查询 1.今天 select * from 表名 where to_days(时间字段名) = to_days(now()); 2.昨天 SELECT * FROM 表名 WHERE T ...
- Android移动、缩放和旋转手势实现
Android的部分图片编辑应用中需要对图片进行移动.缩放和旋转,这些变化都依赖于触摸手势实现,而本文主要阐述移动.缩放和旋转手势的简单实现. 一.移动 首先需要从触摸事件(MotionEvent)中 ...
- 某RBAC管理系统审计
某RBAC管理系统审计 前言 这个管理系统的审计我去年就开始了但烂尾了,那时候太热闹了log4j2,cs的cve反制等等.这个都给忘了,所以本篇可能有些图有点老,现在就是旧图没一个个换遇到的新的就加上 ...