diff --git a/rest_framework/reverse.py b/rest_framework/reverse.py index a51b07f54..d4240533b 100644 --- a/rest_framework/reverse.py +++ b/rest_framework/reverse.py @@ -4,6 +4,9 @@ Provide reverse functions that return fully qualified URLs from __future__ import unicode_literals from django.core.urlresolvers import reverse as django_reverse from django.utils.functional import lazy +from django.core.urlresolvers import resolve +from django.http import Http404 + def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): @@ -14,10 +17,26 @@ def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra if format is not None: kwargs = kwargs or {} kwargs['format'] = format - url = django_reverse(viewname, args=args, kwargs=kwargs, **extra) + + if request: + try: + namespace = request.resolver_match.namespace + except AttributeError: + try: + namespace = resolve(request.path).namespace + except Http404: + namespace=None + + if namespace and ':' not in viewname: + viewname = '{namespace}:{viewname}'.format(namespace=namespace, + viewname=viewname) + + url = django_reverse(viewname, args=args, kwargs=kwargs, + **extra) if request: return request.build_absolute_uri(url) + return url -reverse_lazy = lazy(reverse, str) +reverse_lazy = lazy(reverse, str) \ No newline at end of file diff --git a/rest_framework/tests/test_reverse.py b/rest_framework/tests/test_reverse.py index 690a30b11..1bb7882cb 100644 --- a/rest_framework/tests/test_reverse.py +++ b/rest_framework/tests/test_reverse.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals from django.test import TestCase -from rest_framework.compat import patterns, url +from rest_framework.compat import patterns, url, include from rest_framework.reverse import reverse from rest_framework.test import APIRequestFactory @@ -10,10 +10,25 @@ factory = APIRequestFactory() def null_view(request): pass -urlpatterns = patterns('', + +v0_urlpatterns = patterns('', url(r'^view$', null_view, name='view'), ) +v1_urlpatterns = patterns('', + url(r'^view$', null_view, name='view'), +) + +v2_urlpatterns = patterns('', + url(r'^view$', null_view, name='view'), +) + +urlpatterns = patterns('', + url(r'', include(v0_urlpatterns)), # Un-versioned. + url(r'v1/', include(v1_urlpatterns, namespace='v1')), + url(r'v2/', include(v2_urlpatterns, namespace='v2')), +) + class ReverseTests(TestCase): """ @@ -25,3 +40,32 @@ class ReverseTests(TestCase): request = factory.get('/view') url = reverse('view', request=request) self.assertEqual(url, 'http://testserver/view') + + + def test_namespaced_request_to_non_namespaced_view(self): + """ + Main test for #1143. + + (Code moved unchanged to v2 will reverse to v2.) + """ + request = factory.get('/v2/view') + url = reverse('view', request=request) + self.assertEqual(url, 'http://testserver/v2/view') + + # Additional tests for #1143 + # Covering cases mentioned + # https://github.com/tomchristie/django-rest-framework/pull/1143#issuecomment-30031591 + def test_non_namespaced_request_to_namespaced_view(self): + request = factory.get('/view') + url = reverse('v2:view', request=request) + self.assertEqual(url, 'http://testserver/v2/view') + + def test_namespaced_request_to_same_namespaced_view(self): + request = factory.get('/v2/view') + url = reverse('v2:view', request=request) + self.assertEqual(url, 'http://testserver/v2/view') + + def test_namespaced_request_to_different_namespaced_view(self): + request = factory.get('/v2/view') + url = reverse('v1:view', request=request) + self.assertEqual(url, 'http://testserver/v1/view') \ No newline at end of file