diff --git a/demo/project/accounts/models.py b/demo/project/accounts/models.py index 069a0c6..91339ac 100644 --- a/demo/project/accounts/models.py +++ b/demo/project/accounts/models.py @@ -7,7 +7,7 @@ class User(AbstractBaseUser): modified = models.DateTimeField(auto_now=True) email = models.EmailField(unique=True, verbose_name='email address', max_length=255) - full_name = models.CharField(max_length=255) + full_name = models.CharField(max_length=255, help_text='Yes, the full name!') is_active = models.BooleanField(default=False) is_admin = models.BooleanField(default=False) diff --git a/requirements.txt b/requirements.txt index f449bbb..055dfc1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ -Django==1.8.7 +Django djangorestframework==3.3.2 coverage==4.0.3 flake8==2.5.1 mkdocs==0.15.3 +markdown2 diff --git a/rest_framework_docs/api_docs.py b/rest_framework_docs/api_docs.py index d14fae4..1364041 100644 --- a/rest_framework_docs/api_docs.py +++ b/rest_framework_docs/api_docs.py @@ -7,7 +7,6 @@ class ApiDocumentation(object): - def __init__(self, drf_router=None): self.endpoints = [] self.drf_router = drf_router @@ -21,13 +20,16 @@ def __init__(self, drf_router=None): else: self.get_all_view_names(root_urlconf.urlpatterns) - def get_all_view_names(self, urlpatterns, parent_regex=''): + def get_all_view_names(self, urlpatterns, previous_parent_patterns=None): for pattern in urlpatterns: + parent_patterns = list(previous_parent_patterns or []) if isinstance(pattern, RegexURLResolver): - regex = '' if pattern._regex == "^" else pattern._regex - self.get_all_view_names(urlpatterns=pattern.url_patterns, parent_regex=parent_regex + regex) - elif isinstance(pattern, RegexURLPattern) and self._is_drf_view(pattern) and not self._is_format_endpoint(pattern): - api_endpoint = ApiEndpoint(pattern, parent_regex, self.drf_router) + if not pattern._regex == "^": + parent_patterns.append(pattern) + self.get_all_view_names(urlpatterns=pattern.url_patterns, previous_parent_patterns=parent_patterns) + elif isinstance(pattern, RegexURLPattern) and self._is_drf_view(pattern) \ + and not self._is_format_endpoint(pattern): + api_endpoint = ApiEndpoint(pattern, parent_patterns, self.drf_router) self.endpoints.append(api_endpoint) def _is_drf_view(self, pattern): diff --git a/rest_framework_docs/api_endpoint.py b/rest_framework_docs/api_endpoint.py index f598e34..ae425ce 100644 --- a/rest_framework_docs/api_endpoint.py +++ b/rest_framework_docs/api_endpoint.py @@ -7,14 +7,16 @@ class ApiEndpoint(object): - def __init__(self, pattern, parent_regex=None, drf_router=None): + def __init__(self, pattern, parent_patterns=None, drf_router=None): self.drf_router = drf_router self.pattern = pattern self.callback = pattern.callback # self.name = pattern.name self.docstring = self.__get_docstring__() - self.name_parent = simplify_regex(parent_regex).strip('/') if parent_regex else None - self.path = self.__get_path__(parent_regex) + self.name_parent = ''.join([parent_pattern.regex.pattern for parent_pattern in (parent_patterns or [])]) + self.name_parent_suffix = '/' if self.name_parent.endswith('/') else '' + self.name_parent = simplify_regex(self.name_parent).strip('/') + self.path = self.__get_path__(parent_patterns) self.allowed_methods = self.__get_allowed_methods__() # self.view_name = pattern.callback.__name__ self.errors = None @@ -26,9 +28,9 @@ def __init__(self, pattern, parent_regex=None, drf_router=None): self.permissions = self.__get_permissions_class__() - def __get_path__(self, parent_regex): - if parent_regex: - return "/{0}{1}".format(self.name_parent, simplify_regex(self.pattern.regex.pattern)) + def __get_path__(self, parent_patterns): + if parent_patterns: + return simplify_regex("{}{}{}".format(self.name_parent, self.name_parent_suffix, self.pattern.regex.pattern)) return simplify_regex(self.pattern.regex.pattern) def __get_allowed_methods__(self): @@ -104,13 +106,14 @@ def __get_serializer_fields__(self, serializer): "type": str(field.__class__.__name__), "sub_fields": sub_fields, "required": field.required, - "to_many_relation": to_many_relation + "to_many_relation": to_many_relation, + "help_text": force_str(field.help_text) or '', }) # FIXME: # Show more attibutes of `field`? return fields - + def __get_serializer_fields_json__(self): # FIXME: # Return JSON or not? diff --git a/rest_framework_docs/templates/rest_framework_docs/components/fields_list.html b/rest_framework_docs/templates/rest_framework_docs/components/fields_list.html index a351946..72e2887 100644 --- a/rest_framework_docs/templates/rest_framework_docs/components/fields_list.html +++ b/rest_framework_docs/templates/rest_framework_docs/components/fields_list.html @@ -6,10 +6,12 @@ R {% endif %} + {% if field.help_text %}
Help: {{ field.help_text }}
{% endif %} + {% if field.to_many_relation %} Array of objects {% endif %} - + {% if field.sub_fields %} {%include "rest_framework_docs/components/fields_list.html" with fields=field.sub_fields %} {% endif %} diff --git a/rest_framework_docs/templates/rest_framework_docs/home.html b/rest_framework_docs/templates/rest_framework_docs/home.html index 235a6ee..e13e5a5 100644 --- a/rest_framework_docs/templates/rest_framework_docs/home.html +++ b/rest_framework_docs/templates/rest_framework_docs/home.html @@ -1,4 +1,5 @@ {% extends "rest_framework_docs/docs.html" %} +{% load drfdocs_filters %} {% block apps_menu %} {% regroup endpoints by name_parent as endpoints_grouped %} @@ -56,7 +57,7 @@{{ endpoint.docstring }}
+{{ endpoint.docstring|markdown }}
{% endif %} {% if endpoint.errors %} diff --git a/rest_framework_docs/templatetags/__init__.py b/rest_framework_docs/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rest_framework_docs/templatetags/drfdocs_filters.py b/rest_framework_docs/templatetags/drfdocs_filters.py new file mode 100644 index 0000000..99d347e --- /dev/null +++ b/rest_framework_docs/templatetags/drfdocs_filters.py @@ -0,0 +1,16 @@ +import markdown2 +from django import template +from django.template.defaultfilters import stringfilter +from django.utils.safestring import mark_safe +from rest_framework.utils.formatting import markup_description + + + +register = template.Library() + + +@register.filter(name='markdown') +@stringfilter +def markdown(value): + + return mark_safe(markdown2.markdown(value)) diff --git a/tests/tests.py b/tests/tests.py index 998faee..4f4903c 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -27,7 +27,7 @@ def test_index_view_with_endpoints(self): response = self.client.get(reverse('drfdocs')) self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.context["endpoints"]), 15) + self.assertEqual(len(response.context["endpoints"]), 17) # Test the login view self.assertEqual(response.context["endpoints"][0].name_parent, "accounts") @@ -49,6 +49,16 @@ def test_index_view_with_endpoints(self): # The view "OrganisationErroredView" (organisations/(?P