0x01
最近在項目中要對api進行大規(guī)模重構(gòu)礁凡,并且工具源碼注釋生成文檔响禽,比較swagger和spihx之后骏全,覺得swagger更適合做api文檔,可以在web頁面中直接請求對應(yīng)的api画切,很是方便。使用最新版的django-rest-swagger(drs)生成djangorestframework(drf)生成文檔的時候囱怕,drs只會根據(jù)視圖中的docstring來生成Link(swagger的單條文檔記錄)的描述霍弹,根據(jù)ViewSet中的serializer中定義的fields以及pagination_class定義的分頁類來生成parameters,如果想要根據(jù)文檔來插入一些parameters時娃弓,可以發(fā)現(xiàn)drs的可定制化程度很低典格,于是我決定做一些定制化。
0x02 代碼
在制定花的過程中我主要重寫了drf中的SchemaGenerator的get_description台丛,把視圖中的docstring按照yaml格式解析出來耍缴,并且返回docstring中的第一行作為description,如果視圖沒有docstring的話齐佳,_SchemaGenerator會按照視圖名稱自動生成相應(yīng)的description私恬。接下來是重寫get_link方法,前面說過炼吴,一條Link是swagger頁面中的一條文檔記錄本鸣,如果有yamlobj的話,則根據(jù)固定的格式解析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()