Skip to content

Commit

Permalink
Add superpowers to Router's URLconf generation
Browse files Browse the repository at this point in the history
  • Loading branch information
akx committed Jul 31, 2017
1 parent b477c78 commit bc73c2d
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 2 deletions.
3 changes: 3 additions & 0 deletions lepo/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def csrf_exempt(view):
view.csrf_exempt = True
return view
55 changes: 53 additions & 2 deletions lepo/router.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from collections import Iterable
from copy import deepcopy
from functools import reduce
from importlib import import_module

from django.conf.urls import url
from django.http import HttpResponse
from django.utils.text import camel_case_to_spaces
from jsonschema import RefResolver

Expand All @@ -10,6 +13,10 @@
from lepo.utils import maybe_resolve


def root_view(request):
return HttpResponse('API root')


class Router:
path_class = Path

Expand Down Expand Up @@ -46,10 +53,54 @@ def get_paths(self):
for path in self.api['paths']:
yield self.get_path(path)

def get_urls(self):
def get_urls(
self,
root_view_name=None,
optional_trailing_slash=False,
decorate=(),
name_template='{name}',
):
"""
Get the router's URLs, ready to be installed in `urlpatterns` (directly or via `include`).
:param root_view_name: The optional url name for an API root view.
This may be useful for projects that do not explicitly know where the
router is mounted; those projects can then use `reverse('api:root')`,
for instance, if they need to construct URLs based on the API's root URL.
:type root_view_name: str|None
:param optional_trailing_slash: Whether to fix up the regexen for the router to make any trailing
slashes optional.
:type optional_trailing_slash: bool
:param decorate: A function to decorate view functions with, or an iterable of such decorators.
Use `(lepo.decorators.csrf_exempt,)` to mark all API views as CSRF exempt.
:type decorate: function|Iterable[function]
:param name_template: A `.format()` template for view naming.
:type name_template: str
:return: List of URL tuples.
:rtype: list[tuple]
"""
if isinstance(decorate, Iterable):
decorators = decorate
def decorate(view):
return reduce(lambda view, decorator: decorator(view), decorators, view)

urls = []
for path in self.get_paths():
urls.append(url(path.regex, path.view_class.as_view(), name=path.name))
regex = path.regex
if optional_trailing_slash:
regex = regex.rstrip('$')
if not regex.endswith('/'):
regex += '/'
regex += '?$'
view = decorate(path.view_class.as_view())
urls.append(url(regex, view, name=name_template.format(name=path.name)))

if root_view_name:
urls.append(url(r'^$', root_view, name=name_template.format(name=root_view_name)))
return urls

def get_handler(self, operation_id):
Expand Down

0 comments on commit bc73c2d

Please sign in to comment.