Add request.current_app handling to drf reverse

This commit is contained in:
Ryan P Kilby 2017-11-18 13:54:48 -05:00
parent 1664588500
commit 4fbe3ecce6
2 changed files with 112 additions and 5 deletions

View File

@ -60,10 +60,27 @@ def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extr
if format is not None:
kwargs = kwargs or {}
kwargs['format'] = format
if request:
extra.setdefault('current_app', current_app(request))
url = django_reverse(viewname, args=args, kwargs=kwargs, **extra)
if request:
return request.build_absolute_uri(url)
return url
def current_app(request):
"""
Get the current app for the request.
This code is copied from the URL tag.
"""
try:
return request.current_app
except AttributeError:
try:
return request.resolver_match.namespace
except AttributeError:
return None
reverse_lazy = lazy(reverse, six.text_type)

View File

@ -1,21 +1,29 @@
from __future__ import unicode_literals
from django.conf.urls import url
from django.conf.urls import include, url
from django.http import HttpResponse
from django.test import TestCase, override_settings
from django.urls import NoReverseMatch
from rest_framework.reverse import reverse
from rest_framework.reverse import current_app, reverse
from rest_framework.test import APIRequestFactory
factory = APIRequestFactory()
def null_view(request):
pass
def mock_view(request):
return HttpResponse('')
apppatterns = ([
url(r'^home$', mock_view, name='home'),
], 'app')
urlpatterns = [
url(r'^view$', null_view, name='view'),
url(r'^view$', mock_view, name='view'),
url(r'^app2/', include(apppatterns, namespace='app2')),
url(r'^app1/', include(apppatterns, namespace='app1')),
]
@ -54,3 +62,85 @@ class ReverseTests(TestCase):
url = reverse('view', request=request)
assert url == 'http://testserver/view'
@override_settings(ROOT_URLCONF='tests.test_reverse')
class NamespaceTests(TestCase):
"""
Ensure reverse can handle namespaces.
Note: It's necessary to use self.client() here, as the
RequestFactory does not setup the resolver_match.
"""
def request(self, url):
return self.client.get(url).wsgi_request
def test_application_namespace(self):
url = reverse('app:home')
assert url == '/app1/home'
# instance namespace provided by current_app
url = reverse('app:home', current_app='app2')
assert url == '/app2/home'
def test_instance_namespace(self):
url = reverse('app1:home')
assert url == '/app1/home'
url = reverse('app2:home')
assert url == '/app2/home'
def test_application_namespace_with_request(self):
# request's current app affects result
request1 = self.request('/app1/home')
request2 = self.request('/app2/home')
# sanity check
assert current_app(request1) == 'app1'
assert current_app(request2) == 'app2'
assert reverse('app:home', request=request1) == 'http://testserver/app1/home'
assert reverse('app:home', request=request2) == 'http://testserver/app2/home'
def test_instance_namespace_with_request(self):
# request's current app is not relevant
request1 = self.request('/app1/home')
request2 = self.request('/app2/home')
# sanity check
assert current_app(request1) == 'app1'
assert current_app(request2) == 'app2'
assert reverse('app1:home', request=request1) == 'http://testserver/app1/home'
assert reverse('app2:home', request=request1) == 'http://testserver/app2/home'
assert reverse('app1:home', request=request2) == 'http://testserver/app1/home'
assert reverse('app2:home', request=request2) == 'http://testserver/app2/home'
@override_settings(ROOT_URLCONF='tests.test_reverse')
class CurrentAppTests(TestCase):
"""
Test current_app() function.
Note: It's necessary to use self.client() here, as the
RequestFactory does not setup the resolver_match.
"""
def request(self, url):
return self.client.get(url).wsgi_request
def test_no_namespace(self):
request = self.request('/view')
assert current_app(request) == ''
def test_namespace(self):
request = self.request('/app1/home')
assert current_app(request) == 'app1'
request = self.request('/app2/home')
assert current_app(request) == 'app2'
def test_factory_incompatibility(self):
request = factory.get('/app1/home')
assert current_app(request) is None