From a4267e78e09de4706ad79bbdc700efeef139988e Mon Sep 17 00:00:00 2001 From: Dimitri Justeau <dimitri.justeau@gmail.com> Date: Mon, 22 Aug 2016 19:16:30 +1100 Subject: [PATCH 01/11] Enhance api_endpoint model to track the tree structure of an api, and add router settings for a better integration of viewsets. --- rest_framework_docs/api_docs.py | 128 +++++++++++++++--- rest_framework_docs/api_endpoint.py | 75 ++++++++-- rest_framework_docs/settings.py | 5 +- .../templates/rest_framework_docs/home.html | 2 +- 4 files changed, 178 insertions(+), 32 deletions(-) diff --git a/rest_framework_docs/api_docs.py b/rest_framework_docs/api_docs.py index d22dd4c..bd1f667 100644 --- a/rest_framework_docs/api_docs.py +++ b/rest_framework_docs/api_docs.py @@ -1,9 +1,15 @@ from importlib import import_module +from types import ModuleType from django.conf import settings from django.core.urlresolvers import RegexURLResolver, RegexURLPattern from django.utils.module_loading import import_string from rest_framework.views import APIView -from rest_framework_docs.api_endpoint import ApiEndpoint +from rest_framework.routers import BaseRouter +from rest_framework_docs.api_endpoint import ApiNode, ApiEndpoint +from rest_framework_docs.settings import DRFSettings + + +drf_settings = DRFSettings().settings class ApiDocumentation(object): @@ -11,6 +17,7 @@ class ApiDocumentation(object): def __init__(self, drf_router=None): self.endpoints = [] self.drf_router = drf_router + try: root_urlconf = import_string(settings.ROOT_URLCONF) except ImportError: @@ -21,26 +28,113 @@ def __init__(self, drf_router=None): else: self.get_all_view_names(root_urlconf.urlpatterns) - def get_all_view_names(self, urlpatterns, parent_pattern=None): + def get_all_view_names(self, urlpatterns, parent_api_node=None): for pattern in urlpatterns: if isinstance(pattern, RegexURLResolver): - parent_pattern = None if pattern._regex == "^" else pattern - self.get_all_view_names(urlpatterns=pattern.url_patterns, parent_pattern=parent_pattern) - elif isinstance(pattern, RegexURLPattern) and self._is_drf_view(pattern) and not self._is_format_endpoint(pattern): - api_endpoint = ApiEndpoint(pattern, parent_pattern, self.drf_router) + # Try to get router from settings, if no router is found, + # Use the instance drf_router property. + router = get_router(pattern) + if router is None: + parent_router = None + if parent_api_node is not None: + parent_router = parent_api_node.drf_router + if parent_router is not None: + router = parent_router + else: + router = self.drf_router + if pattern._regex == "^": + parent = parent_api_node + else: + parent = ApiNode( + pattern, + parent_node=parent_api_node, + drf_router=router + ) + self.get_all_view_names(urlpatterns=pattern.url_patterns, parent_api_node=parent) + elif isinstance(pattern, RegexURLPattern) and _is_drf_view(pattern) and not _is_format_endpoint(pattern): + router = parent_api_node.drf_router + router = self.drf_router if router is None else router + api_endpoint = ApiEndpoint(pattern, parent_api_node, router) self.endpoints.append(api_endpoint) - def _is_drf_view(self, pattern): - """ - Should check whether a pattern inherits from DRF's APIView - """ - return hasattr(pattern.callback, 'cls') and issubclass(pattern.callback.cls, APIView) + def get_endpoints(self): + return self.endpoints - def _is_format_endpoint(self, pattern): - """ - Exclude endpoints with a "format" parameter + +def _is_drf_view(pattern): + """ + Should check whether a pattern inherits from DRF's APIView + """ + return hasattr(pattern.callback, 'cls') and issubclass(pattern.callback.cls, + APIView) + + +def _is_format_endpoint(pattern): + """ + Exclude endpoints with a "format" parameter + """ + return '?P<format>' in pattern._regex + + +def get_router(pattern): + urlconf = pattern.urlconf_name + router = None + if isinstance(urlconf, ModuleType): + # First: try MODULE_ROUTERS setting - Don't ignore errors + router = get_module_router(urlconf) + if router is not None: + return router + # Second: try DEFAULT_MODULE_ROUTER setting - Ignore errors + try: + router = get_default_module_router(urlconf) + if router is not None: + return router + except: + pass + # Third: try DEFAULT_ROUTER setting - Don't ignore errors + router = get_default_router() + if router is not None: + return router + return router + + +def get_module_router(module): + routers = drf_settings['MODULE_ROUTERS'] + if routers is None: + return None + if module.__name__ in routers: + router_name = routers[module.__name__] + router = getattr(module, router_name) + assert isinstance(router, BaseRouter), \ + """ + drfdocs 'ROUTERS' setting does not correspond to + a router instance for module {}. + """.format(module.__name__) + return router + return None + + +def get_default_module_router(module): + default_module_router = drf_settings['DEFAULT_MODULE_ROUTER'] + if default_module_router is None: + return None + router = getattr(module, default_module_router) + assert isinstance(router, BaseRouter), \ """ - return '?P<format>' in pattern._regex + drfdocs 'DEFAULT_MODULE_ROUTER' setting does not correspond to + a router instance for module {}. + """.format(module.__name__) + return router - def get_endpoints(self): - return self.endpoints + +def get_default_router(): + default_router_path = drf_settings['DEFAULT_ROUTER'] + if default_router_path is None: + return None + router = import_string(default_router_path) + assert isinstance(router, BaseRouter), \ + """ + drfdocs 'DEFAULT_ROUTER_NAME' setting does not correspond to + a router instance {}. + """.format(router.__name__) + return router diff --git a/rest_framework_docs/api_endpoint.py b/rest_framework_docs/api_endpoint.py index 89a33f8..7fb116e 100644 --- a/rest_framework_docs/api_endpoint.py +++ b/rest_framework_docs/api_endpoint.py @@ -5,18 +5,73 @@ from rest_framework.serializers import BaseSerializer -class ApiEndpoint(object): +class ApiNode(object): - def __init__(self, pattern, parent_pattern=None, drf_router=None): - self.drf_router = drf_router + def __init__(self, pattern, parent_node=None, drf_router=None): self.pattern = pattern + self.parent_node = parent_node + self.drf_router = drf_router + + @property + def parent_pattern(self): + if self.parent_node is None: + return None + return self.parent_node.pattern + + @property + def name_parent(self): + if self.parent_pattern is None: + return None + return simplify_regex(self.parent_pattern.regex.pattern).strip('/') + + @property + def name_parent_full(self): + name_parent_full = self.name_parent + if name_parent_full is None: + return None + parent_node = self.parent_node + parent_name = parent_node.name_parent + while parent_name is not None: + name_parent_full = "{}/{}".format(parent_name, name_parent_full) + parent_node = parent_node.parent_node + parent_name = parent_node.name_parent + return name_parent_full + + @property + def path(self): + pattern = self.__get_path__(self.parent_pattern) + if pattern is None: + return None + parent = self.parent_node + name_parent = None if parent is None else parent.name_parent + while name_parent is not None: + pattern = "{}/{}".format(name_parent, pattern) + parent = parent.parent_node + name_parent = parent.name_parent + if pattern[0] != "/": + pattern = "/{}".format(pattern) + return pattern + + def __get_path__(self, parent_pattern): + if parent_pattern: + return "{0}{1}".format( + self.name_parent, + simplify_regex(self.pattern.regex.pattern) + ) + return simplify_regex(self.pattern.regex.pattern) + + +class ApiEndpoint(ApiNode): + + def __init__(self, pattern, parent_node=None, drf_router=None): + super(ApiEndpoint, self).__init__( + pattern, + parent_node=parent_node, + drf_router=drf_router + ) self.callback = pattern.callback - # self.name = pattern.name self.docstring = self.__get_docstring__() - self.name_parent = simplify_regex(parent_pattern.regex.pattern).strip('/') if parent_pattern else None - self.path = self.__get_path__(parent_pattern) self.allowed_methods = self.__get_allowed_methods__() - # self.view_name = pattern.callback.__name__ self.errors = None self.serializer_class = self.__get_serializer_class__() if self.serializer_class: @@ -26,13 +81,7 @@ def __init__(self, pattern, parent_pattern=None, drf_router=None): self.permissions = self.__get_permissions_class__() - def __get_path__(self, parent_pattern): - if parent_pattern: - return "/{0}{1}".format(self.name_parent, simplify_regex(self.pattern.regex.pattern)) - return simplify_regex(self.pattern.regex.pattern) - def __get_allowed_methods__(self): - viewset_methods = [] if self.drf_router: for prefix, viewset, basename in self.drf_router.registry: diff --git a/rest_framework_docs/settings.py b/rest_framework_docs/settings.py index 2853a7b..e27070a 100644 --- a/rest_framework_docs/settings.py +++ b/rest_framework_docs/settings.py @@ -5,7 +5,10 @@ class DRFSettings(object): def __init__(self): self.drf_settings = { - "HIDE_DOCS": self.get_setting("HIDE_DOCS") or False + "HIDE_DOCS": self.get_setting("HIDE_DOCS") or False, + "MODULE_ROUTERS": self.get_setting("MODULE_ROUTERS"), + "DEFAULT_MODULE_ROUTER": self.get_setting("DEFAULT_MODULE_ROUTER"), + "DEFAULT_ROUTER": self.get_setting("DEFAULT_ROUTER") } def get_setting(self, name): diff --git a/rest_framework_docs/templates/rest_framework_docs/home.html b/rest_framework_docs/templates/rest_framework_docs/home.html index 235a6ee..0ba24d5 100644 --- a/rest_framework_docs/templates/rest_framework_docs/home.html +++ b/rest_framework_docs/templates/rest_framework_docs/home.html @@ -15,7 +15,7 @@ {% block content %} - {% regroup endpoints by name_parent as endpoints_grouped %} + {% regroup endpoints by name_parent_full as endpoints_grouped %} {% if endpoints_grouped %} {% for group in endpoints_grouped %} From 4470670383e01981fce344183e88a0358f46bfee Mon Sep 17 00:00:00 2001 From: Dimitri Justeau <dimitri.justeau@gmail.com> Date: Wed, 24 Aug 2016 12:34:29 +1100 Subject: [PATCH 02/11] Add viewsets in demo project --- demo/project/accounts/urls.py | 12 +++++++++++- demo/project/accounts/views.py | 6 ++++++ demo/project/organisations/urls.py | 13 +++++++++++-- demo/project/organisations/views.py | 9 ++++++++- demo/project/settings.py | 8 ++++++++ demo/project/urls.py | 4 ++++ 6 files changed, 48 insertions(+), 4 deletions(-) diff --git a/demo/project/accounts/urls.py b/demo/project/accounts/urls.py index 1486675..9fb4c43 100644 --- a/demo/project/accounts/urls.py +++ b/demo/project/accounts/urls.py @@ -1,15 +1,25 @@ -from django.conf.urls import url +from django.conf.urls import url, include +from rest_framework import routers from project.accounts import views +router = routers.DefaultRouter() +router.register( + r'user_viewset', + views.UserModelViewSet, +) + + urlpatterns = [ url(r'^test/$', views.TestView.as_view(), name="test-view"), url(r'^login/$', views.LoginView.as_view(), name="login"), url(r'^register/$', views.UserRegistrationView.as_view(), name="register"), + url(r'^register/$', views.UserRegistrationView.as_view(), name="register"), url(r'^reset-password/$', view=views.PasswordResetView.as_view(), name="reset-password"), url(r'^reset-password/confirm/$', views.PasswordResetConfirmView.as_view(), name="reset-password-confirm"), url(r'^user/profile/$', views.UserProfileView.as_view(), name="profile"), + url(r'^viewset_test/', include(router.urls), name="user_viewset"), ] diff --git a/demo/project/accounts/views.py b/demo/project/accounts/views.py index e1bd9c0..6c1bb48 100644 --- a/demo/project/accounts/views.py +++ b/demo/project/accounts/views.py @@ -6,6 +6,7 @@ from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework.viewsets import ModelViewSet from project.accounts.models import User from project.accounts.serializers import ( UserRegistrationSerializer, UserProfileSerializer, ResetPasswordSerializer @@ -81,3 +82,8 @@ def post(self, request, *args, **kwargs): if not serializer.is_valid(): return Response({'errors': serializer.errors}, status=status.HTTP_400_BAD_REQUEST) return Response({"msg": "Password updated successfully."}, status=status.HTTP_200_OK) + + +class UserModelViewSet(ModelViewSet): + queryset = User.objects.all() + serializer_class = UserProfileSerializer diff --git a/demo/project/organisations/urls.py b/demo/project/organisations/urls.py index 4d0311e..0f57c91 100644 --- a/demo/project/organisations/urls.py +++ b/demo/project/organisations/urls.py @@ -1,12 +1,21 @@ -from django.conf.urls import url +from django.conf.urls import url, include +from rest_framework import routers from project.organisations import views +router = routers.DefaultRouter() +router.register( + r'organisation_viewset', + views.OrganisationViewSet, +) + + urlpatterns = [ url(r'^create/$', view=views.CreateOrganisationView.as_view(), name="create"), url(r'^(?P<slug>[\w-]+)/$', view=views.RetrieveOrganisationView.as_view(), name="organisation"), url(r'^(?P<slug>[\w-]+)/members/$', view=views.OrganisationMembersView.as_view(), name="members"), - url(r'^(?P<slug>[\w-]+)/leave/$', view=views.LeaveOrganisationView.as_view(), name="leave") + url(r'^(?P<slug>[\w-]+)/leave/$', view=views.LeaveOrganisationView.as_view(), name="leave"), + url(r'^', include(router.urls), name="organisation_viewset"), ] diff --git a/demo/project/organisations/views.py b/demo/project/organisations/views.py index 1e2d5fb..c40f630 100644 --- a/demo/project/organisations/views.py +++ b/demo/project/organisations/views.py @@ -1,8 +1,10 @@ from rest_framework import generics, status from rest_framework.response import Response +from rest_framework.viewsets import ModelViewSet from project.organisations.models import Organisation, Membership from project.organisations.serializers import ( - CreateOrganisationSerializer, OrganisationMembersSerializer, RetrieveOrganisationSerializer + CreateOrganisationSerializer, OrganisationMembersSerializer, + RetrieveOrganisationSerializer, OrganisationDetailSerializer ) @@ -34,3 +36,8 @@ def delete(self, request, *args, **kwargs): instance = self.get_object() self.perform_destroy(instance) return Response(status=status.HTTP_204_NO_CONTENT) + + +class OrganisationViewSet(ModelViewSet): + queryset = Organisation.objects.all() + serializer_class = OrganisationDetailSerializer diff --git a/demo/project/settings.py b/demo/project/settings.py index 5e06207..0b34c30 100644 --- a/demo/project/settings.py +++ b/demo/project/settings.py @@ -101,6 +101,14 @@ ) } +REST_FRAMEWORK_DOCS = { + 'HIDE_DOCS': False, + 'MODULE_ROUTERS': { + 'project.accounts.urls': 'router', + }, + 'DEFAULT_MODULE_ROUTER': 'router', +} + # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ diff --git a/demo/project/urls.py b/demo/project/urls.py index d8e049f..f764dcf 100644 --- a/demo/project/urls.py +++ b/demo/project/urls.py @@ -16,11 +16,15 @@ from django.conf.urls import include, url from django.contrib import admin + urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'^docs/', include('rest_framework_docs.urls')), # API url(r'^accounts/', view=include('project.accounts.urls', namespace='accounts')), + + url(r'^organisations/', view=include('project.organisations.urls', namespace='organisations')), + ] From f33aa98050bbe94b075ce20f26c518ac552c57c5 Mon Sep 17 00:00:00 2001 From: Dimitri Justeau <dimitri.justeau@gmail.com> Date: Wed, 24 Aug 2016 14:59:08 +1100 Subject: [PATCH 03/11] Fix case when RegexURLPattern has no parent --- rest_framework_docs/api_docs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rest_framework_docs/api_docs.py b/rest_framework_docs/api_docs.py index bd1f667..99fad59 100644 --- a/rest_framework_docs/api_docs.py +++ b/rest_framework_docs/api_docs.py @@ -52,8 +52,10 @@ def get_all_view_names(self, urlpatterns, parent_api_node=None): ) self.get_all_view_names(urlpatterns=pattern.url_patterns, parent_api_node=parent) elif isinstance(pattern, RegexURLPattern) and _is_drf_view(pattern) and not _is_format_endpoint(pattern): - router = parent_api_node.drf_router - router = self.drf_router if router is None else router + router = self.drf_router + if parent_api_node is not None: + if parent_api_node.drf_router is not None: + router = parent_api_node.drf_router api_endpoint = ApiEndpoint(pattern, parent_api_node, router) self.endpoints.append(api_endpoint) From ab5e821a682bf47db125d7ec0b851ae6b883873f Mon Sep 17 00:00:00 2001 From: Dimitri Justeau <dimitri.justeau@gmail.com> Date: Wed, 24 Aug 2016 15:36:54 +1100 Subject: [PATCH 04/11] Document new router settings --- docs/settings.md | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/docs/settings.md b/docs/settings.md index abd2106..b0c5643 100755 --- a/docs/settings.md +++ b/docs/settings.md @@ -6,7 +6,12 @@ source_filename: settings To set DRF docs' settings just include the dictionary below in Django's `settings.py` file. REST_FRAMEWORK_DOCS = { - 'HIDE_DOCS': True + 'HIDE_DOCS': True, + 'MODULE_ROUTERS': { + 'project.accounts.urls': 'accounts_router', + }, + 'DEFAULT_MODULE_ROUTER': 'router', + 'DEFAULT_ROUTER': 'project.urls.default_router', } @@ -21,9 +26,29 @@ You can use hidden to prevent your docs from showing up in different environment Then set the value of the environment variable `HIDE_DRFDOCS` for each environment (ie. Use `.env` files) +##### MODULE_ROUTERS +Use this setting to manually bind url modules to a router instance. The router must be defined in the module, or imported in the module. +For instance, if the router of an app called 'gifts' is 'gifts_router', and the router of another app called 'scuba_diving' is 'scuba_diving_router', the MODULE_ROUTERS setting should look like: + + 'MODULE_ROUTERS': { + 'gifts.urls': 'gift_router', + 'scuba_diving.urls': 'scuba_diving_router' + } + +If there is no entry for a given module, if this setting is not set, or if it is set to None, the value of the DEFAULT_MODULE_ROUTER setting is used. + +##### DEFAULT_MODULE_ROUTER +When set, the value of this setting will be used to find a router for a urls module. If there is no router having the DEFAULT_MODULE_ROUTER name, the setting is ignored and the value of DEFAULT_ROUTER is used. + +##### DEFAULT_ROUTER +When defined, this setting must describe a python dotted path leading to the router that should be used when MODULE_ROUTERS and DEFAULT_MODULE_ROUTER are not set. +This parameter is useful when there is only one router for your whole API. + ### List of Settings -| Setting | Type | Options | Default | -|---------|---------|-----------------|---------| -|HIDE_DOCS| Boolean | `True`, `False` | `False` | -| | | | | +| Setting | Type | Options | Default | +|---------------------|-----------------------------------------------------------|-----------------|---------| +|HIDE_DOCS | Boolean | `True`, `False` | `False` | +|MODULE_ROUTERS | dict of python dotted paths -> router instance name | | `None` | +|DEFAULT_MODULE_ROUTER| str representing a default router instance name | | `None` | +|DEFAULT_ROUTER | str representing a python dotted path to a router instance| | `None` | From 77dc48978764240a5e3af51e6ae23239ad0d8555 Mon Sep 17 00:00:00 2001 From: Dimitri Justeau <dimitri.justeau@gmail.com> Date: Wed, 24 Aug 2016 16:23:21 +1100 Subject: [PATCH 05/11] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6c82768..50b0382 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,9 @@ You can find detailed information about the package's settings at [the docs](htt REST_FRAMEWORK_DOCS = { 'HIDE_DOCS': True # Default: False + 'MODULE_ROUTERS': {}, + 'DEFAULT_MODULE_ROUTER': 'router', + 'DEFAULT_ROUTER': None, } From 7a561755c2b1b4ab2757c46be601e720ab1d7f2c Mon Sep 17 00:00:00 2001 From: Dimitri Justeau <dimitri.justeau@gmail.com> Date: Thu, 25 Aug 2016 16:50:54 +1100 Subject: [PATCH 06/11] Fix empty doctstring for viewsets when docstring is only defined at class-level --- rest_framework_docs/api_endpoint.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rest_framework_docs/api_endpoint.py b/rest_framework_docs/api_endpoint.py index 7fb116e..0e74d25 100644 --- a/rest_framework_docs/api_endpoint.py +++ b/rest_framework_docs/api_endpoint.py @@ -110,8 +110,9 @@ def __get_allowed_methods__(self): ) viewset_methods = list(viewset_methods) if len(set(funcs)) == 1: - self.docstring = inspect.getdoc(getattr(self.callback.cls, funcs[0])) - + func_docstring = inspect.getdoc(getattr(self.callback.cls, funcs[0])) + if func_docstring is not None: + self.docstring = func_docstring view_methods = [force_str(m).upper() for m in self.callback.cls.http_method_names if hasattr(self.callback.cls, m)] return viewset_methods + view_methods From 19f499380735a9f2ae22b292d2512ad4f1a4ebf2 Mon Sep 17 00:00:00 2001 From: Emmanouil Konstantinidis <hello@manos.im> Date: Sun, 28 Aug 2016 23:46:28 +0100 Subject: [PATCH 07/11] Update docs --- docs/contributing.md | 2 +- docs/template/base.html | 4 ++-- docs/template/content.html | 2 +- docs/templates.md | 4 ++-- mkdocs.yml | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 13938f8..305e06c 100755 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -5,7 +5,7 @@ source_filename: contributing ### Development If you want to **use the demo** app to work on this package: -In the project [repository](https://github.com/ekonstantinidis/django-rest-framework-docs) you can find the demo app(at /demo). It is a project with Django & Django Rest Framework that will allow you to work with this project. +In the project [repository](https://github.com/manosim/django-rest-framework-docs) you can find the demo app(at /demo). It is a project with Django & Django Rest Framework that will allow you to work with this project. From the root of the repository: diff --git a/docs/template/base.html b/docs/template/base.html index 6ea52fa..0db72a9 100755 --- a/docs/template/base.html +++ b/docs/template/base.html @@ -56,8 +56,8 @@ <footer class="col-md-12 footer"> <div class="links"> - <a href="http://www.iamemmanouil.com"><i class="fa fa-link"></i></a> - <a href="http://www.github.com/ekonstantinidis"><i class="fa fa-github"></i></a> + <a href="http://www.manos.im/"><i class="fa fa-link"></i></a> + <a href="http://www.github.com/manosim"><i class="fa fa-github"></i></a> <a href="http://www.twitter.com/iamemmanouil"><i class="fa fa-twitter"></i></a> </div> {% if copyright %} diff --git a/docs/template/content.html b/docs/template/content.html index b4fbb00..1c98a57 100755 --- a/docs/template/content.html +++ b/docs/template/content.html @@ -11,6 +11,6 @@ {{ content }} {% for filename in meta.source_filename %} - <a class="btn btn-default btn-edit" href="https://github.com/ekonstantinidis/django-rest-framework-docs/blob/master/docs/{{ filename }}.md"><i class="fa fa-github"></i> Edit on Github</a> + <a class="btn btn-default btn-edit" href="https://github.com/manosim/django-rest-framework-docs/blob/master/docs/{{ filename }}.md"><i class="fa fa-github"></i> Edit on Github</a> {% endfor %} </div> diff --git a/docs/templates.md b/docs/templates.md index 1702337..1480ca8 100755 --- a/docs/templates.md +++ b/docs/templates.md @@ -45,8 +45,8 @@ To hide the GitHub badge from the page, just override it with an empty block. {% block footer %} <div class="footer"> <div class="links"> - <a href="http://www.iamemmanouil.com"><i class="fa fa-link"></i></a> - <a href="http://www.github.com/ekonstantinidis"><i class="fa fa-github"></i></a> + <a href="http://www.manosim.com"><i class="fa fa-link"></i></a> + <a href="http://www.github.com/manosim"><i class="fa fa-github"></i></a> <a href="http://www.twitter.com/iamemmanouil"><i class="fa fa-twitter"></i></a> </div> Copyright © 2016 Emmanouil Konstantinidis. diff --git a/mkdocs.yml b/mkdocs.yml index 260b3d6..d022ebd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,6 +18,6 @@ extra_css: extra_javascript: - js/index.js -copyright: Copyright © 2015 Emmanouil Konstantinidis. +copyright: Copyright © 2016 Emmanouil Konstantinidis. google_analytics: ['UA-71505240-1', 'drfdocs.com'] -repo_url: https://github.com/ekonstantinidis/django-rest-framework-docs +repo_url: https://github.com/manosim/django-rest-framework-docs From 0f8f4b99f163d4dd5911caecda8a82600e7fe222 Mon Sep 17 00:00:00 2001 From: Emmanouil Konstantinidis <hello@manos.im> Date: Sun, 28 Aug 2016 23:51:18 +0100 Subject: [PATCH 08/11] Minor Changes --- README.md | 8 ++++---- .../templates/rest_framework_docs/base.html | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f51ca5d..d740982 100644 --- a/README.md +++ b/README.md @@ -71,14 +71,14 @@ You can find detailed information about the package's settings at [the docs](htt First of all thanks to the [Django](http://www.djangoproject.com/) core team and to all the contributors of [Django REST Framework](http://www.django-rest-framework.org/) for their amazing work. Also I would like to thank [Marc Gibbons](https://github.com/marcgibbons) for his *django-rest-framework-docs* project. Both projects share the same idea, it is just that Marc's is not maintained anymore and does not support DRF 3+ & Python 3. -[travis-image]: https://travis-ci.org/ekonstantinidis/django-rest-framework-docs.svg?branch=master -[travis-url]: https://travis-ci.org/ekonstantinidis/django-rest-framework-docs +[travis-image]: https://travis-ci.org/manosim/django-rest-framework-docs.svg?branch=master +[travis-url]: https://travis-ci.org/manosim/django-rest-framework-docs [pypi-image]: https://badge.fury.io/py/drfdocs.svg [pypi-url]: https://pypi.python.org/pypi/drfdocs/ -[codecov-image]: https://codecov.io/github/ekonstantinidis/django-rest-framework-docs/coverage.svg?branch=master -[codecov-url]:https://codecov.io/github/ekonstantinidis/django-rest-framework-docs?branch=master +[codecov-image]: https://codecov.io/github/manosim/django-rest-framework-docs/coverage.svg?branch=master +[codecov-url]:https://codecov.io/github/manosim/django-rest-framework-docs?branch=master [slack-image]: https://img.shields.io/badge/slack-pythondev/drfdocs-e01563.svg [slack-url]: https://pythondev.slack.com diff --git a/rest_framework_docs/templates/rest_framework_docs/base.html b/rest_framework_docs/templates/rest_framework_docs/base.html index 430ce54..5a2e6fc 100644 --- a/rest_framework_docs/templates/rest_framework_docs/base.html +++ b/rest_framework_docs/templates/rest_framework_docs/base.html @@ -16,7 +16,7 @@ <body> {% block github_badge %} - <a href="https://github.com/ekonstantinidis/drf-docs/" class="github-corner" target="_blank"> + <a href="https://github.com/manosim/drf-docs/" class="github-corner" target="_blank"> <svg width="80" height="80" viewBox="0 0 250 250" style="fill:#18bc9c; color:#fff; position: absolute; top: 0; border: 0; right: 0;"> <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path> <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path> @@ -66,8 +66,8 @@ <h3>Document Web APIs made with <a href="http://www.django-rest-framework.org/" {% block footer %} <div class="footer"> <div class="links"> - <a href="http://www.iamemmanouil.com"><i class="fa fa-link"></i></a> - <a href="http://www.github.com/ekonstantinidis"><i class="fa fa-github"></i></a> + <a href="http://www.manos.im/"><i class="fa fa-link"></i></a> + <a href="http://www.github.com/manosim"><i class="fa fa-github"></i></a> <a href="http://www.twitter.com/iamemmanouil"><i class="fa fa-twitter"></i></a> </div> Copyright © 2016 Emmanouil Konstantinidis. From 6b403a82c477465a71543c34da7a1ba8e017840b Mon Sep 17 00:00:00 2001 From: Jared Mackey <jmackey@perfectpitchtech.com> Date: Mon, 19 Sep 2016 15:39:54 -0600 Subject: [PATCH 09/11] Adding in MarkDown support. Original contributed here https://github.com/manosim/django-rest-framework-docs/pull/127 --- .../templates/rest_framework_docs/home.html | 3 ++- rest_framework_docs/templatetags/__init__.py | 0 rest_framework_docs/templatetags/drfdocs_filters.py | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 rest_framework_docs/templatetags/__init__.py create mode 100644 rest_framework_docs/templatetags/drfdocs_filters.py diff --git a/rest_framework_docs/templates/rest_framework_docs/home.html b/rest_framework_docs/templates/rest_framework_docs/home.html index 0ba24d5..be1778c 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 @@ <h4 class="panel-title title"> <div id="{{ endpoint.path|slugify }}" class="panel-collapse collapse" role="tabpanel"> <div class="panel-body"> {% if endpoint.docstring %} - <p class="lead">{{ endpoint.docstring }}</p> + <p class="lead">{{ endpoint.docstring|markdown }}</p> {% 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..cd662f0 --- /dev/null +++ b/rest_framework_docs/templatetags/drfdocs_filters.py @@ -0,0 +1,11 @@ +from django import template +from django.template.defaultfilters import stringfilter +from rest_framework.utils.formatting import markup_description + +register = template.Library() + + +@register.filter(name='markdown') +@stringfilter +def markdown(value): + return markup_description(value) From 5c91f222fbfeae4dcea50fa2f2b58f227698cd71 Mon Sep 17 00:00:00 2001 From: Jared Mackey <jmackey@perfectpitchtech.com> Date: Mon, 19 Sep 2016 15:43:34 -0600 Subject: [PATCH 10/11] updated gitignore for pycharm --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bde1b9d..da87042 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ rest_framework_docs/static/rest_framework_docs/js/dist.min.js rest_framework_docs/static/node_modules/ rest_framework_docs/static/rest_framework_docs/js/dist.min.js.map +.idea \ No newline at end of file From 2315399ff0d920c63842a5da9c5e87da535f9dc5 Mon Sep 17 00:00:00 2001 From: Jared Mackey <jmackey@perfectpitchtech.com> Date: Mon, 19 Sep 2016 15:48:37 -0600 Subject: [PATCH 11/11] Allow json header. Originally contributed at https://github.com/manosim/django-rest-framework-docs/pull/126/commits/089f75418be439ed8d8b7359a5a901805cf13618 --- .../static/rest_framework_docs/js/components/liveapi.js | 1 + 1 file changed, 1 insertion(+) diff --git a/rest_framework_docs/static/rest_framework_docs/js/components/liveapi.js b/rest_framework_docs/static/rest_framework_docs/js/components/liveapi.js index 9e7bb62..4312688 100644 --- a/rest_framework_docs/static/rest_framework_docs/js/components/liveapi.js +++ b/rest_framework_docs/static/rest_framework_docs/js/components/liveapi.js @@ -37,6 +37,7 @@ var LiveAPIEndpoints = React.createClass({ // Now Make the Request APIRequest(request.selectedMethod, request.endpoint.path) .set(headers) + .accept("json") .send(data) .end(function (err, res) { self.setState({