Add default app namespace to drf reverse

This commit is contained in:
Ryan P Kilby 2017-11-18 17:27:33 -05:00
parent 4fbe3ecce6
commit ec7f358010
2 changed files with 49 additions and 10 deletions

View File

@ -34,10 +34,29 @@ def preserve_builtin_query_params(url, request=None):
def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra):
"""
Extends `django.urls.reverse` with behavior specific to rest framework.
The `viewname` will be prepended with the 'rest_framework' application
namespace if no namspace is included in the `viewname` argument. The
framework fundamentally assumes that the router urls will be included with
the 'rest_framework' namespace, so ensure that your root url patterns are
configured accordingly. Assuming you use the default router, you can check
this with:
from django.urls import reverse
reverse('rest_framework:api-root')
If versioning is being used then we pass any `reverse` calls through
to the versioning scheme instance, so that the resulting URL
can be modified if needed.
Optionally takes a `request` object (see `_reverse` for details).
"""
# prepend the 'rest_framework' application namespace
if ':' not in viewname:
viewname = 'rest_framework:' + viewname
scheme = getattr(request, 'versioning_scheme', None)
if scheme is not None:
try:
@ -56,6 +75,8 @@ def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extr
"""
Same as `django.urls.reverse`, but optionally takes a request
and returns a fully qualified URL, using the request to get the base URL.
Additionally, the request is used to determine the `current_app` instance.
"""
if format is not None:
kwargs = kwargs or {}

View File

@ -19,11 +19,16 @@ apppatterns = ([
url(r'^home$', mock_view, name='home'),
], 'app')
apipatterns = ([
url(r'^root$', mock_view, name='root'),
], 'rest_framework')
urlpatterns = [
url(r'^view$', mock_view, name='view'),
url(r'^app2/', include(apppatterns, namespace='app2')),
url(r'^app1/', include(apppatterns, namespace='app1')),
url(r'^api/', include(apipatterns)),
]
@ -36,7 +41,7 @@ class MockVersioningScheme(object):
if self.raise_error:
raise NoReverseMatch()
return 'http://scheme-reversed/view'
return 'http://scheme-reversed/api/root'
@override_settings(ROOT_URLCONF='tests.test_reverse')
@ -44,24 +49,33 @@ class ReverseTests(TestCase):
"""
Tests for fully qualified URLs when using `reverse`.
"""
def test_reverse_non_drf_view(self):
request = factory.get('/api/root')
# DRF reverse should not match non-DRF views
with self.assertRaises(NoReverseMatch):
reverse('view', request=request)
def test_reversed_urls_are_fully_qualified(self):
request = factory.get('/view')
url = reverse('view', request=request)
assert url == 'http://testserver/view'
request = factory.get('/api/root')
url = reverse('root', request=request)
assert url == 'http://testserver/api/root'
def test_reverse_with_versioning_scheme(self):
request = factory.get('/view')
request = factory.get('/api/root')
request.versioning_scheme = MockVersioningScheme()
url = reverse('view', request=request)
assert url == 'http://scheme-reversed/view'
url = reverse('root', request=request)
assert url == 'http://scheme-reversed/api/root'
def test_reverse_with_versioning_scheme_fallback_to_default_on_error(self):
request = factory.get('/view')
request = factory.get('/api/root')
request.versioning_scheme = MockVersioningScheme(raise_error=True)
url = reverse('view', request=request)
assert url == 'http://testserver/view'
url = reverse('root', request=request)
assert url == 'http://testserver/api/root'
@override_settings(ROOT_URLCONF='tests.test_reverse')
@ -76,6 +90,10 @@ class NamespaceTests(TestCase):
def request(self, url):
return self.client.get(url).wsgi_request
def test_default_namespace(self):
url = reverse('root')
assert url == '/api/root'
def test_application_namespace(self):
url = reverse('app:home')
assert url == '/app1/home'