diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 791382bac0591..39d109405bae2 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -95,14 +95,15 @@ def get_response(self, request): break if response is None: - if hasattr(request, "urlconf"): + if hasattr(request, 'urlconf'): # Reset url resolver with a custom urlconf. urlconf = request.urlconf urlresolvers.set_urlconf(urlconf) resolver = urlresolvers.RegexURLResolver(r'^/', urlconf) - callback, callback_args, callback_kwargs = resolver.resolve( - request.path_info) + resolver_match = resolver.resolve(request.path_info) + callback, callback_args, callback_kwargs = resolver_match + request.resolver_match = resolver_match # Apply view middleware for middleware_method in self._view_middleware: diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 21e99de10d937..50301b8567803 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -192,6 +192,17 @@ All attributes should be considered read-only, unless stated otherwise below. URLconf for the current request, overriding the :setting:`ROOT_URLCONF` setting. See :ref:`how-django-processes-a-request` for details. +.. attribute:: HttpRequest.resolver_match + + .. versionadded:: 1.5 + + An instance of :class:`~django.core.urlresolvers.ResolverMatch` representing + the resolved url. This attribute is only set after url resolving took place, + which means it's available in all views but not in middleware methods which + are executed before url resolving takes place (like ``process_request``, you + can use ``process_view`` instead). + + Methods ------- diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 26b6ad1bfadc5..f1fcd923b1b06 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -127,6 +127,9 @@ Django 1.5 also includes several smaller improvements worth noting: configuration duplication. More information can be found in the :func:`~django.contrib.auth.decorators.login_required` documentation. +* An instance of :class:`~django.core.urlresolvers.ResolverMatch` is stored on + the request as ``resolver_match``. + Backwards incompatible changes in 1.5 ===================================== diff --git a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py index fa892a4346f33..ab2e77af24c31 100644 --- a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py +++ b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py @@ -28,6 +28,7 @@ def urls(self): urlpatterns = patterns('regressiontests.urlpatterns_reverse.views', url(r'^normal/$', 'empty_view', name='normal-view'), url(r'^normal/(?P\d+)/(?P\d+)/$', 'empty_view', name='normal-view'), + url(r'^resolver_match/$', 'pass_resolver_match_view', name='test-resolver-match'), url(r'^\+\\\$\*/$', 'empty_view', name='special-view'), diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py index 0ea5ffe3802ab..234897d267370 100644 --- a/tests/regressiontests/urlpatterns_reverse/tests.py +++ b/tests/regressiontests/urlpatterns_reverse/tests.py @@ -512,6 +512,11 @@ def test_urlpattern_resolve(self): self.assertEqual(match[1], args) self.assertEqual(match[2], kwargs) + def test_resolver_match_on_request(self): + response = self.client.get('/resolver_match/') + resolver_match = response.resolver_match + self.assertEqual(resolver_match.url_name, 'test-resolver-match') + class ErroneousViewTests(TestCase): urls = 'regressiontests.urlpatterns_reverse.erroneous_urls' diff --git a/tests/regressiontests/urlpatterns_reverse/views.py b/tests/regressiontests/urlpatterns_reverse/views.py index f631acf3ec193..88d169a118284 100644 --- a/tests/regressiontests/urlpatterns_reverse/views.py +++ b/tests/regressiontests/urlpatterns_reverse/views.py @@ -19,6 +19,11 @@ def defaults_view(request, arg1, arg2): def erroneous_view(request): import non_existent +def pass_resolver_match_view(request, *args, **kwargs): + response = HttpResponse('') + response.resolver_match = request.resolver_match + return response + uncallable = "Can I be a view? Pleeeease?" class ViewClass(object):