0x01
最近在项目中要对api进行大规模重构,并且工具源码注释生成文档,比较swagger和spihx之后,觉得swagger更适合做api文档,可以在web页面中直接请求对应的api,很是方便。使用最新版的django-rest-swagger(drs)生成djangorestframework(drf)生成文档的时候,drs只会根据视图中的docstring来生成Link(swagger的单条文档记录)的描述,根据ViewSet中的serializer中定义的fields以及pagination_class定义的分页类来生成parameters,如果想要根据文档来插入一些parameters时,可以发现drs的可定制化程度很低,于是我决定做一些定制化。
0x02 代码
在制定花的过程中我主要重写了drf中的SchemaGenerator的get_description,把视图中的docstring按照yaml格式解析出来,并且返回docstring中的第一行作为description,如果视图没有docstring的话,_SchemaGenerator会按照视图名称自动生成相应的description。接下来是重写get_link方法,前面说过,一条Link是swagger页面中的一条文档记录,如果有yamlobj的话,则根据固定的格式解析yamlobj,然后解析出来的属性加到Link当中去。最后重写get_swagger_view 方法,使用新写的SchemaGenerator。
from rest_framework.schemas import SchemaGenerator as _SchemaGenerator
from rest_framework_swagger import renderers
from rest_framework.compat import coreapi, urlparse
from django.utils.encoding import smart_text
from rest_framework.utils import formatting
class SchemaGenerator(_SchemaGenerator):
def get_description(self, path, method, view):
method_name = getattr(view, 'action', method.lower())
method_docstring = getattr(view, method_name, None).__doc__
self.yamlobj = None
if method_docstring:
method_docstring = formatting.dedent(smart_text(method_docstring))
self.yamlobj = yaml.load(method_docstring)
return method_docstring.splitlines()[0]
return super(SchemaGenerator, self).get_description(path, method, view)
def get_link(self, path, method, view):
"""
Return a `coreapi.Link` instance for the given endpoint.
"""
fields = self.get_path_fields(path, method, view)
fields += self.get_serializer_fields(path, method, view)
fields += self.get_pagination_fields(path, method, view)
fields += self.get_filter_fields(path, method, view)
if fields and any([field.location in ('form', 'body') for field in fields]):
encoding = self.get_encoding(path, method, view)
else:
encoding = None
description = self.get_description(path, method, view)
if isinstance(self.yamlobj, dict):
def get_localtion(parameter):
if method == 'GET':
return 'query'
return parameter.get('paramType', 'formData')
parameters = self.yamlobj.get('parameters', [])
fields += [coreapi.Field(name=x['name'], location=get_localtion(x), required=x.get('required', True, ), description=x.get('description', ''), type=x.get('type', 'string')) for x in parameters]
if self.url and path.startswith('/'):
path = path[1:]
return coreapi.Link(
url=urlparse.urljoin(self.url, path),
action=method.lower(),
encoding=encoding,
fields=fields,
description=description
)
def get_swagger_view(title=None, url=None, patterns=None, urlconf=None):
"""
Returns schema view which renders Swagger/OpenAPI.
"""
class SwaggerSchemaView(APIView):
_ignore_model_permissions = True
exclude_from_schema = True
permission_classes = [AllowAny]
renderer_classes = [
CoreJSONRenderer,
renderers.OpenAPIRenderer,
renderers.SwaggerUIRenderer
]
def get(self, request):
generator = SchemaGenerator(
title=title,
url=url,
patterns=patterns,
urlconf=urlconf
)
schema = generator.get_schema(request=request)
if not schema:
raise exceptions.ValidationError(
'The schema generator did not return a schema Document'
)
return Response(schema)
return SwaggerSchemaView.as_view()