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里面设置你要连接的数据库类型和连接名称,地址之类,和创建新项目的时候一致 运行下面代码可以自 ...
随机推荐
- python之继承及其实现方法
目录 继承 语法格式 继承的代码实现 多继承 继承 语法格式 class 子类类名(父类1, 父类2...): pass r如果一个类没有继承任何类,则默认继承object python支持多继承 定 ...
- LOG日志系统
# coding=utf-8 import datetime import logging import os import sys from logging.handlers import Time ...
- C/C++ Zlib库封装MyZip压缩类
Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法.它最初由Jean-Loup Gailly和Mark Adler开发,旨在成为一个高效.轻量级的压缩库,其被广泛应用于许多领域,包括 ...
- 外包杯学习进度(一) | 【Android】【Javaweb】Android与JavaWeb服务器交互教程——搭建环境
前言 我们老师留了一个题目,这里就不写了,第一需要攻破的问题就是如何将app中的数据域javaweb进行传递,并可以回弹消息等问题.所以就开始了解一下这方面的信息. 资料积累 参考胡大炮的妖孽人生的博 ...
- Centos8离线安装golang
一.下载golang 1.下载安装包 2.下载后解压. tar -zxvf go1.17.linux-amd64.tar.gz 二.配置环境变量 vim /etc/profile 1.在环境变量最后添 ...
- 前端优化之路:git commit 校验拦截
[前言] 前面在git分支规范那篇文章里,介绍了commit提交规范,如下图 但是想要做到高效落地执行,就需要做些别的功课,先展示下成果图 没错,对不符合规范的commit进行了拦截,符合才可以成功提 ...
- Kernel Memory 入门系列:生成并获取文档摘要
Kernel Memory 入门系列:生成并获取文档摘要 前面在RAG和文档预处理的流程中,我们得到一个解决方案,可以让用户直接获取最终的问题答案. 但是实际的业务场景中,仍然存在一些基础的场景,不需 ...
- 为什么许多数字孪生产品开始了GIS融合的尝试?
随着数字孪生技术的发展,越来越多的产品意识到要实现数字孪生的最大价值,需要考虑多个维度的数据,包括空间信息.地理位置.环境条件等.因此,许多数字孪生产品开始了与GIS系统的融合尝试,以进一步提升其功能 ...
- 万界星空科技电子电器装配行业云MES解决方案
电子电器装配属于劳动密集型.科技含量较高的行业,产品零部件种类繁多,生产组装困难,生产过程存在盲点,同时也决定了生产流水线多且对自动化水平要求较高. 万界星空科技提供的电子行业解决方案,从仓储管理.生 ...
- 从零玩转第三方登录之WeChat公众号扫码关注登陆 -wechatgzh
title: 从零玩转第三方登录之WeChat公众号扫码关注登陆 date: 2022-09-27 22:46:53.362 updated: 2023-03-30 13:28:41.359 url: ...