Open leonardoarroyo opened 8 years ago
It also seems that coreapi is less descriptive than openapi so the openapi schema will be incomplete since we generate from rest_frameworks coreapi document (via https://github.com/core-api/python-openapi-codec). I need the ability to specify response codes and bodies. IIUC the old way to get around this was with YAML docstrings but those are deprecated.
I still do not know how to document parameters.
Hope there will be an example in the tutorial app.
for example, how to document the parameters(mobile
) in the method sendcode
?
class UserViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsUserOrReadOnly, IsAuthenticated)
@list_route(methods=['post'])
def sendcode(self, request):
"""send verify code"""
from moore.services.yimei import send_sms
username = request.data.get('mobile')
verifycode = random.randint(1000, 9999)
send_sms(username, verifycode)
return Response('data': 'send ok')
I write a custom schemagenerator to deal with parameter:
class SchemaGenerator(schemas.SchemaGenerator):
def get_link(self, path, method, callback, view):
"""Custom the coreapi using the func.__doc__ .
if __doc__ of the function exsit, use the __doc__ building the coreapi. else use the default serializer.
__doc__ in yaml format, eg:
desc: the desc of this api.
ret: when success invoked, return xxxx
err: when error occured, return xxxx
input:
- name: mobile
desc: the mobile number
type: string
required: true
location: form
- name: promotion
desc: the activity id
type: int
required: true
location: form
"""
fields = self.get_path_fields(path, method, callback, view)
yaml_doc = None
func = getattr(view, view.action) if getattr(view, 'action', None) else None
if func and func.__doc__:
try:
yaml_doc = yaml.load(func.__doc__)
except:
yaml_doc = None
if yaml_doc and 'desc' in yaml_doc:
desc = yaml_doc.get('desc', '')
ret = yaml_doc.get('ret', '')
err = yaml_doc.get('err', '')
_method_desc = desc + '<br>' + 'return: ' + ret + '<br>' + 'error: ' + err
params = yaml_doc.get('input', [])
for i in params:
_name = i.get('name')
_desc = i.get('desc')
_required = i.get('required', True)
_type = i.get('type', 'string')
_location = i.get('location', 'form')
field = coreapi.Field(
name=_name,
location=_location,
required=_required,
description=_desc,
type=_type
)
fields.append(field)
else:
_method_desc = func.__doc__ if func and func.__doc__ else ''
fields += self.get_serializer_fields(path, method, callback, view)
fields += self.get_pagination_fields(path, method, callback, view)
fields += self.get_filter_fields(path, method, callback, view)
if fields and any([field.location in ('form', 'body') for field in fields]):
encoding = self.get_encoding(path, method, callback, view)
else:
encoding = None
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=_method_desc
)
@daimon99, on list routes, you can set the filter_backends property on the ViewSet to correctly generate the schema, still it feels a little hacky.
just wait rest framework 3.5. tomchristie said: "Gothca, understand the issue now! Yes, this has been an issue in how we generate the schema for swagger UI. It's resolved as part of the upcoming 3.5 release."
So no updates yet regarding this issue??
Given that the 3.5 release is out, has this ticket been handled?
Maybe mentioning @tomchristie ?
There's some level of support for this, but it's a little patchy.
You'd need to look at the implementation of get_link()
to see how the fields are built up. The description
attribute on fields can then be pulled out by the swagger UI renderer...
help_text
attribute pulled into the description.get_schema_fields
returns (currently I don't think that the various get_schema_fields()
implementations populate any field descriptions, tho you could override the get_schema_fields()
method using a custom filter/paginator class)Does CoreAPI
even support custom type definition? I know you can specify anything in the field.type
.
I think you would need to change rest_framework_swagger.renderers.OpenAPICodec
so that the returned Swagger object would be extended with definitions:
.
Those definitions could be based on Serializers
and pushed to coreapi.document.Document
with SchemaGenerator
maybe.
The coreapi.document.Document
seem to be accepting any data.
class Document(itypes.Dict):
# ...
self._data = {key: _to_immutable(value) for key, value in content.items()}
@slykar - I think you're mixing up different concerns in that comment. (What datatypes can be included in a Document, vs. how do we describe parameter help text and parameter types.
I'm currently working on a documentation generator to tie in with Core API, which is helping tease out some of the docs related requirements. I'd suggest we hold further discussion until we can use that as a starting point.
Aside: Also worth re-iterating that I want to see any spec changes to Core API be driven by very explicit tooling requirements. Eg. "Adding X to Core API will allow me to do Y".
@tomchristie - To give some background. What I was trying to achieve was a documentation of profile
parameter on my endpoint, which is an embedded document with it's own schema. SchemaGenerator
specifies the type as 'object'
, where it should be a type on it's own, like 'UserProfile'
.
When using OpenAPI
, you can document such field by setting custom type. In OpenAPI
this type is defined by definitions
section. This is something you can not do with CoreAPI
as far as I know, even tough the CoreAPI
Field.type
takes arbitrary string and you could put 'UserProfile'
there.
The spec mismatch makes it hard to properly document endpoint parameters, utilizing OpenAPI
type definitions. This is something I require, otherwise the generated OpenAPI spec is worthless for me.
I was even trying to figure out how to push this type information through SchemaGenerator
to CoreAPI
to OpenAPI
.
Right now I'm back to writing the OpenAPI spec file by hand.
Gotcha. And yes, we may well move towards allowing for annotation information about more complex types. Out of interest what tooling are you using to document the API - Swagger UI, or something else?
@tomchristie I'm using Swagger UI, tough, when writing spec by hand I write it using API Blueprint (IMO easier to write and read than OpenAPI), then convert it to OpenAPI spec for Swagger UI.
Hey guys any update on this issue?
Yeah upcoming version of REST framework is working towards improving this.
look forward to the new version of REST framework!
for the time being i'm using this class decorator: https://github.com/ministryofjustice/moj-product-dashboard/blob/develop/dashboard/libs/swagger_tools.py#L5
@cliffxuan Interesting solution.
@tomchristie What are your plans for the next version? Would you consider extending the override methods like get_path_fields
and get_filter_fields
currently on the schema generator onto APIView?
@cliffxuan https://github.com/marcgibbons/django-rest-swagger/issues/549#issuecomment-276993383 Your solution works fine for list action. what if we want to customize that at action level, i.e i want a specific query param for retrieve method or update or create method for that matter. How are we going to do that. can we modify your decorator to support at method level?
@gjain0 my decorator works only for list view. i don't have a use case for yours yet. feel free to modify it.
Has this issue been addressed in any way over the past 8 months? It appears that currently there is no concise readable way to specify parameters for API endpoints, which effectively renders Swagger as unusable.
@FilmCoder I'm using @daimon99 solution. modified for my use case. It's working perfect.
here is the complete code https://gist.github.com/gjain0/f536d3988eb61e693ea306305c441bb7
It really is mind boggling that this issue remains open after all this time. I should also note that all the presented solutions above are not applicable to function based views
Using help_text
on Serializer works great for parameters - but I'm still missing a way to add a description to the Path parameters.
Would be great if that could be implemented!
If all you care about is showing parameter types, is required or optional, and help_text, the "Model" view on the swagger page is an easy alternative, as described here (edit: just part 1, rest of article is out of date): http://idratherbewriting.com/2015/12/10/ten-realizations-using-swagger-and-swagger-ui/
Pros: Can be implemented with just Serializers and isn't a hack. Cons: Bad at displaying request data's nested objects and lists.
@bwmello , this is about documenting input parameters and (more importantly) the ability to enter parameter content within swagger. Your page suggests using markdown, but this ability was removed when the software went to coreAPI, without any viable replacement. This thread is not related to documenting the output parameters, which is what your page also describes.
Hi, Any update on this issue ?
Bizarre that a documentation tool for an API, doesn't- wait for it- allow you to document parameters. Kinda like a car that doesn't drive, or a plane that doesn't fly. Can I have the past hour of googling in disbelief back?
Here's my workaround, param_schema.py
--
from rest_framework.compat import coreapi, coreschema
from rest_framework.filters import BaseFilterBackend
class ParamSchemaFilter(BaseFilterBackend):
def get_schema_fields(self, view):
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
fields = super().get_schema_fields(view)
if hasattr(view, 'get_param_fields'):
print(view)
fields += view.get_param_fields(view)
return fields
And in your settings --
REST_FRAMEWORK = {
...
'DEFAULT_FILTER_BACKENDS': (
...
'param_schema.ParamSchemaFilter',
),
And in your view/viewset --
class MyAwesomeView(viewsets.GenericViewSet):
def get_param_fields(self, view):
fields = [
coreapi.Field(
name='my_field',
required=True,
location='query',
schema=coreschema.String(
title='My awesome field',
description='This is my really awesome field right here, so awesome'
)
),
]
return fields
Seems like things are moving forward: https://github.com/encode/django-rest-framework/issues/4502
Hi all, I have recently integrated Django Rest Swagger 2 into an existing project, I faced alot of issues, and resolved them by finding solutions at different forums. Now I have documented all the required steps here: https://github.com/m-haziq/django-rest-swagger-docs with all possible issues I could face and their solutions.
This is the way to go: https://github.com/axnsan12/drf-yasg
There are currently two decent Swagger schema generators that I could find for django-rest-framework:
django-rest-swagger drf-openapi Out of the two, django-rest-swagger is just a wrapper around DRF 3.7 schema generation with an added UI, and thus presents the same problems. drf-openapi is a bit more involved and implements some custom handling for response schemas, but ultimately still falls short in code generation because the responses are plain of lacking support for named schemas.
Is anyone currently working on this?
@gregorypease280 yes I am. Try reading https://github.com/m-haziq/django-rest-swagger-docs
For people interested by making swagger work with the last version of django rest framework (3.7.7), you'll notice the tweak gave by @daimon99 in this comment https://github.com/marcgibbons/django-rest-swagger/issues/549#issuecomment-250145712 which I used is no longer working.
I've just ported the new code for making it work with django rest framework 3.7.7 and django rest swagger 2.1.1
Create a file schema_view.py:
from django.utils.six.moves.urllib import parse as urlparse
from rest_framework.schemas import AutoSchema
import yaml
import coreapi
from rest_framework_swagger.views import get_swagger_view
class CustomSchema(AutoSchema):
def get_link(self, path, method, base_url):
view = self.view
method_name = getattr(view, 'action', method.lower())
method_docstring = getattr(view, method_name, None).__doc__
_method_desc = ''
fields = self.get_path_fields(path, method)
try:
a = method_docstring.split('---')
except:
fields += self.get_serializer_fields(path, method)
else:
yaml_doc = None
if method_docstring:
try:
yaml_doc = yaml.load(a[1])
except:
yaml_doc = None
# Extract schema information from yaml
if yaml_doc and type(yaml_doc) != str:
_desc = yaml_doc.get('desc', '')
_ret = yaml_doc.get('ret', '')
_err = yaml_doc.get('err', '')
_method_desc = _desc + '\n<br/>' + 'return: ' + _ret + '<br/>' + 'error: ' + _err
params = yaml_doc.get('input', [])
for i in params:
_name = i.get('name')
_desc = i.get('desc')
_required = i.get('required', False)
_type = i.get('type', 'string')
_location = i.get('location', 'form')
field = coreapi.Field(
name=_name,
location=_location,
required=_required,
description=_desc,
type=_type
)
fields.append(field)
else:
_method_desc = a[0]
fields += self.get_serializer_fields(path, method)
fields += self.get_pagination_fields(path, method)
fields += self.get_filter_fields(path, method)
manual_fields = self.get_manual_fields(path, method)
fields = self.update_fields(fields, manual_fields)
if fields and any([field.location in ('form', 'body') for field in fields]):
encoding = self.get_encoding(path, method)
else:
encoding = None
if base_url and path.startswith('/'):
path = path[1:]
return coreapi.Link(
url=urlparse.urljoin(base_url, path),
action=method.lower(),
encoding=encoding,
fields=fields,
description=_method_desc
)
schema_view = get_swagger_view(title='Product Testing API')
Your urls.py file references it like:
from .schema_view import schema_view
urlpatterns = [
url(r'^v1/api/', include([
url(r'^doc/', schema_view),
])),
No the sad
part is that you have to override all your APIView or ModelViewSet with
schema = CustomSchema()
as described in the 3.7.7 release : http://www.django-rest-framework.org/api-guide/schemas/#per-view-schema-customisation
@m-haziq Hope it will help for making the migration in your README file ;)
There are also problems with adding parameters to function-based views, like this:
def register(request):
#read parameter "code" and check it
response_data = {'code': 'success.', 'token': 'sometoken'}
return Response(response_data, status = status.HTTP_201_CREATED)
how to add "code" from here to swagger I can't find. Unfortunately, don't have enough time to work on it and post an answer, so maybe someone else will post the answer here.
@GuillaumeCisco Great. Thanks for sharing it, I will definitely take some time out of my routine to add this update to my documentation sometimes soon!
@Swich1987 Please read this part of doc. You can document FBVs smoothly: https://github.com/m-haziq/django-rest-swagger-docs#advance-usage
New django-rest-swagger versions have deprecated YAML docstrings. There's no documentation demonstrating how to document request fields anymore.
There's some things you can do by changing the filter_backends on ViewSets, for example, but it just doesn't feel right. I still haven't found a way to do this on function based views.