This commit is contained in:
Tommy Beadle 2017-06-05 22:31:45 +00:00 committed by GitHub
commit 81f138203b
7 changed files with 116 additions and 42 deletions

View File

@ -69,6 +69,20 @@ When using viewsets, you should use the relevant action names as delimiters.
Create a new user instance. Create a new user instance.
""" """
When using the `@detail_route` or `@list_route` decorators, the docstring for
the decorated method may use the `action:` style delimiters.
class UserViewSet(viewsets.ModelViewSet):
@detail_route(methods=('get', 'post'))
def groups(self, request, pk=None):
"""
retrieve:
Return the groups for the given user.
create:
Add the user to a new group.
"""
--- ---
## Third party packages ## Third party packages

View File

@ -8,7 +8,7 @@ from django.core.exceptions import PermissionDenied
from django.db import models from django.db import models
from django.http import Http404 from django.http import Http404
from django.utils import six from django.utils import six
from django.utils.encoding import force_text, smart_text from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import exceptions, renderers, serializers from rest_framework import exceptions, renderers, serializers
@ -19,7 +19,6 @@ from rest_framework.compat import (
from rest_framework.request import clone_request from rest_framework.request import clone_request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.utils import formatting
from rest_framework.utils.model_meta import _get_pk from rest_framework.utils.model_meta import _get_pk
from rest_framework.views import APIView from rest_framework.views import APIView
@ -469,12 +468,14 @@ class SchemaGenerator(object):
This will be based on the method docstring if one exists, This will be based on the method docstring if one exists,
or else the class docstring. or else the class docstring.
""" """
method_name = getattr(view, 'action', method.lower()) method = method.lower()
method_docstring = getattr(view, method_name, None).__doc__ method_name = getattr(view, 'action', method)
if method_docstring: view_method = getattr(view, method_name, None)
description = None
if view_method:
# An explicit docstring on the method or action. # An explicit docstring on the method or action.
return formatting.dedent(smart_text(method_docstring)) description = api_settings.VIEW_DESCRIPTION_FUNCTION(view_method)
if not description:
description = view.get_view_description() description = view.get_view_description()
lines = [line.strip() for line in description.splitlines()] lines = [line.strip() for line in description.splitlines()]
current_section = '' current_section = ''
@ -482,18 +483,21 @@ class SchemaGenerator(object):
for line in lines: for line in lines:
if header_regex.match(line): if header_regex.match(line):
current_section, seperator, lead = line.partition(':') current_section, separator, lead = line.partition(':')
sections[current_section] = lead.strip() sections[current_section] = lead.strip()
else: else:
sections[current_section] += '\n' + line sections[current_section] += '\n' + line
header = getattr(view, 'action', method.lower()) section_name = ''
if header in sections: for attempt in (method_name, self.default_mapping.get(method, method)):
return sections[header].strip() if attempt in sections:
if header in self.coerce_method_names: section_name = attempt
if self.coerce_method_names[header] in sections: break
return sections[self.coerce_method_names[header]].strip() if self.coerce_method_names.get(attempt) in sections:
return sections[''].strip() section_name = self.coerce_method_names[attempt]
break
return sections[section_name].strip()
def get_encoding(self, path, method, view): def get_encoding(self, path, method, view):
""" """

View File

@ -0,0 +1,17 @@
{% load rest_framework %}
{% for link_key, link in section.links|items %}
{% with section_key=parent_key %}
{% include "rest_framework/docs/link.html" %}
{% endwith %}
{% endfor %}
{% for section_key, sect in section.data|items %}
{% if section_key %}
<h2 id="{{ parent_key|add:'-' }}{{ section_key }}" class="coredocs-section-title">{{ parent_key|add:' / ' }}{{ section_key }} <a href="#{{ parent_key|add:'-' }}{{ section_key }}"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
{% endif %}
{% with parent_key=parent_key|add:'-'|add:section_key section=sect %}
{% include "rest_framework/docs/document-incl.html" %}
{% endwith %}
{% endfor %}

View File

@ -14,17 +14,6 @@
</div> </div>
</div> </div>
{% for section_key, section in document.data|items %} {% with parent_key=None section=document %}
{% if section_key %} {% include "rest_framework/docs/document-incl.html" %}
<h2 id="{{ section_key }}" class="coredocs-section-title">{{ section_key }} <a href="#{{ section_key }}"><i class="fa fa-link" aria-hidden="true"></i> {% endwith %}
</a></h2>
{% endif %}
{% for link_key, link in section.links|items %}
{% include "rest_framework/docs/link.html" %}
{% endfor %}
{% endfor %}
{% for link_key, link in document.links|items %}
{% include "rest_framework/docs/link.html" %}
{% endfor %}

View File

@ -0,0 +1,18 @@
{% load rest_framework %}
{% if section.links %}
<ul class="sub-menu {% if parent_key %}collapse{% endif %}" id="{{ parent_key }}-dropdown">
{% for link_key, link in section.links|items %}
<li><a href="#{{ parent_key }}-{{ link_key }}">{{ link.title|default:link_key }}</a></li>
{% endfor %}
</ul>
{% endif %}
{% for key, sect in section.data|items %}
<li data-toggle="collapse" data-target="#{{ parent_key|add:'-' }}{{ key }}-dropdown" class="collapsed">
<a><i class="fa fa-dot-circle-o fa-lg"></i> {{ parent_key|add:' / ' }}{% if key %}{{ key }}{% else %}API Endpoints{% endif %} <span class="arrow"></span></a>
</li>
{% with section=sect parent_key=parent_key|add:'-'|add:key %}
{% include 'rest_framework/docs/sidebar-menu.html' %}
{% endwith %}
{% endfor %}

View File

@ -1,20 +1,12 @@
{% load rest_framework %}
<div class="sidebar"> <div class="sidebar">
<h3 class="brand"><a href="#">{{ document.title }}</a></h3> <h3 class="brand"><a href="#">{{ document.title }}</a></h3>
<i class="fa fa-bars fa-2x toggle-btn" data-toggle="collapse" data-target="#menu-content"></i> <i class="fa fa-bars fa-2x toggle-btn" data-toggle="collapse" data-target="#menu-content"></i>
<div class="menu-list"> <div class="menu-list">
<ul id="menu-content" class="menu-content collapse out"> <ul id="menu-content" class="menu-content collapse out">
{% for section_key, section in document.data|items %} {% with parent_key=None section=document %}
<li data-toggle="collapse" data-target="#{{ section_key }}-dropdown" class="collapsed"> {% include 'rest_framework/docs/sidebar-menu.html' %}
<a><i class="fa fa-dot-circle-o fa-lg"></i> {% if section_key %}{{ section_key }}{% else %}API Endpoints{% endif %} <span class="arrow"></span></a> {% endwith %}
</li>
<ul class="sub-menu {% if section_key %}collapse{% endif %}" id="{{ section_key }}-dropdown">
{% for link_key, link in section.links|items %}
<li><a href="#{{ section_key }}-{{ link_key }}">{{ link.title|default:link_key }}</a></li>
{% endfor %}
</ul>
{% endfor %}
</ul> </ul>
<ul class="menu-list menu-list-bottom"> <ul class="menu-list menu-list-bottom">

View File

@ -55,7 +55,19 @@ class ExampleViewSet(ModelViewSet):
""" """
A description of custom action. A description of custom action.
""" """
return super(ExampleSerializer, self).retrieve(self, request) return super(ExampleViewSet, self).retrieve(self, request)
@detail_route(methods=['get', 'post'], serializer_class=EmptySerializer)
def custom_action2(self, request, pk):
"""
read: A description for getting items.
create: A description for creating items.
"""
if request.method == 'GET':
return super(ExampleViewSet, self).retrieve(self, request)
else:
return super(ExampleViewSet, self).create(self, request)
@list_route() @list_route()
def custom_list_action(self, request): def custom_list_action(self, request):
@ -105,6 +117,16 @@ class TestRouterGeneratedSchema(TestCase):
coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.'))
] ]
), ),
'custom_action2': {
'read': coreapi.Link(
url='/example/{id}/custom_action2/',
action='get',
description='A description for getting items.',
fields=[
coreapi.Field('id', required=True, location='path', schema=coreschema.String()),
],
),
},
'custom_list_action': coreapi.Link( 'custom_list_action': coreapi.Link(
url='/example/custom_list_action/', url='/example/custom_list_action/',
action='get' action='get'
@ -173,6 +195,24 @@ class TestRouterGeneratedSchema(TestCase):
coreapi.Field('d', required=False, location='form', schema=coreschema.String(title='D')), coreapi.Field('d', required=False, location='form', schema=coreschema.String(title='D')),
] ]
), ),
'custom_action2': {
'read': coreapi.Link(
url='/example/{id}/custom_action2/',
action='get',
description='A description for getting items.',
fields=[
coreapi.Field('id', required=True, location='path', schema=coreschema.String()),
],
),
'create': coreapi.Link(
url='/example/{id}/custom_action2/',
action='post',
description='A description for creating items.',
fields=[
coreapi.Field('id', required=True, location='path', schema=coreschema.String()),
],
),
},
'custom_list_action': coreapi.Link( 'custom_list_action': coreapi.Link(
url='/example/custom_list_action/', url='/example/custom_list_action/',
action='get' action='get'