完整代码: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 importsmart_strfrom drf_yasg.errors importSwaggerGenerationErrorfrom drf_yasg.inspectors importSwaggerAutoSchemafrom drf_yasg.utils importmerge_params, get_object_classesfrom rest_framework.parsers importFileUploadParserfrom rest_framework.request importis_form_media_typefrom rest_framework.schemas importAutoSchemafrom rest_framework.utils importformattingfrom Wchime.settings importSWAGGER_SETTINGSdefget_consumes(parser_classes):

parser_classes
=get_object_classes(parser_classes)
parser_classes
= [pc for pc in parser_classes if notissubclass(pc, FileUploadParser)]
media_types
= [parser.media_type for parser in parser_classes or[]]returnmedia_typesdefget_summary(string):if string is notNone:
result
= string.strip().replace(" ", "").split("\n")returnresult[0]classCustomAutoSchema(AutoSchema):defget_description(self, path, method):
view
=self.viewreturn self._get_description_section(view, 'tags', view.get_view_description())classCustomSwaggerAutoSchema(SwaggerAutoSchema):def get_tags(self, operation_keys=None):
tags
=super().get_tags(operation_keys)#print(tags) if "api" in tags andoperation_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') orNoneiftag:#tags.append(tag) tags[0] =tag#print('===', tags) returntagsdefget_summary_and_description(self):
description
= self.overrides.get('operation_description', None)
summary
= self.overrides.get('operation_summary', None)#print(description, summary) if description isNone:
description
= self._sch.get_description(self.path, self.method) or ''description= description.strip().replace('\r', '')if description and (summary isNone):#description from docstring... do summary magic summary, description =self.split_summary_from_description(description)#print('====', summary, description) if summary isNone:
summary
=descriptionreturnsummary, descriptiondefget_consumes_form(self):returnget_consumes(self.get_parser_classes())defadd_manual_parameters(self, parameters):"""重写这个函数,让他能解析json,也可以解析表单"""manual_parameters= self.overrides.get('manual_parameters', None) or[]ifmanual_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 inparameters)if has_body_parameter or not any(is_form_media_type(encoding) for encoding inself.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 inself.body_methods:raise SwaggerGenerationError("form parameters can only be applied to" "(" + ','.join(self.body_methods) + ") HTTP methods")returnmerge_params(parameters, manual_parameters)#-------------------------------------------------------------------------------------------------------------- from rest_framework importserializersfrom drf_yasg importopenapifrom rest_framework.relations importPrimaryKeyRelatedFieldfrom rest_framework.fields importChoiceFielddef serializer_to_swagger(ser_model, get_req=False):'''序列化转成openapi的形式''' if ser_model is None and get_req isTrue:return{}, []elif ser_model is None and get_req isFalse: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()ifget_req:
required
=[]for k, v infields.items():
description
= getattr(v, 'label', '')if isinstance(v, serializers.SerializerMethodField) or getattr(v, 'source'):continue elifisinstance(v, ChoiceField):
description
+= str(dict(getattr(v, 'choices', {})))if getattr(v, 'required', True) is notFalse:
required.append(k)
typ
=serializer_field_mapping.get(type(v), openapi.TYPE_STRING)
dit[k]
= openapi.Schema(description=description, type=typ)returndit, requiredelse:for k, v infields.items():
description
= getattr(v, 'label', '')ifisinstance(v, ChoiceField):
description
+= str(dict(getattr(v, 'choices', {})))elifisinstance(v, serializers.SerializerMethodField):continuetyp=serializer_field_mapping.get(type(v), openapi.TYPE_STRING)
dit[k]
= openapi.Schema(description=description, type=typ)returnditdefserializer_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 infields.items():if k infilter_fields:continuedescription= getattr(v, 'label', '')if isinstance(v, serializers.SerializerMethodField) or getattr(v, 'source'):continue elifisinstance(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))returnliclassViewSwagger(object):

get_req_params
=[]
get_req_body
=None
get_res_data
=None
get_res_examples
= {'json': {}}
get_res_description
= ' 'get_res_code= 200get_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= 200post_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= 200put_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= 200delete_tags=None
delete_operation_description
=None

@classmethod
defreq_serialize_schema(cls, serializer):return serializer_to_swagger(serializer, get_req=True)

@classmethod
defres_serializer_schema(cls, serializer):return serializer_to_swagger(serializer, get_req=False)
@classmethod
def req_serializer_form_schema(cls, serializer, filter_fields=[]):returnserializer_to_req_form_swagger(serializer, filter_fields)
@classmethod
defget(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 elseNone
}
returnret

@classmethod
defpost(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 elseNone
}
returnret

@classmethod
defput(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 elseNone
}
returnret

@classmethod
defdelete(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 elseNone
}
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数据



标签: none

添加新评论