From d0451e4cadf4277f117b9b581530c68d8dc43c6a Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Mon, 14 Dec 2015 12:36:09 -0500 Subject: [PATCH] Fixed #26295 -- Allowed using i18n_patterns() in any root URLconf. Thanks Tim for the review. --- django/middleware/locale.py | 19 ++++++++++--------- docs/releases/1.10.txt | 4 +++- docs/topics/i18n/translation.txt | 10 ++++++++-- tests/i18n/patterns/tests.py | 13 +++++++++++++ 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/django/middleware/locale.py b/django/middleware/locale.py index 063a4896bc..c9db673759 100644 --- a/django/middleware/locale.py +++ b/django/middleware/locale.py @@ -5,9 +5,8 @@ from django.http import HttpResponseRedirect from django.urls import ( LocaleRegexURLResolver, get_resolver, get_script_prefix, is_valid_path, ) -from django.utils import translation +from django.utils import lru_cache, translation from django.utils.cache import patch_vary_headers -from django.utils.functional import cached_property class LocaleMiddleware(object): @@ -21,17 +20,19 @@ class LocaleMiddleware(object): response_redirect_class = HttpResponseRedirect def process_request(self, request): + urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) language = translation.get_language_from_request( - request, check_path=self.is_language_prefix_patterns_used) + request, check_path=self.is_language_prefix_patterns_used(urlconf) + ) translation.activate(language) request.LANGUAGE_CODE = translation.get_language() def process_response(self, request, response): language = translation.get_language() language_from_path = translation.get_language_from_path(request.path_info) + urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) if (response.status_code == 404 and not language_from_path - and self.is_language_prefix_patterns_used): - urlconf = getattr(request, 'urlconf', None) + and self.is_language_prefix_patterns_used(urlconf)): language_path = '/%s%s' % (language, request.path_info) path_valid = is_valid_path(language_path, urlconf) path_needs_slash = ( @@ -52,20 +53,20 @@ class LocaleMiddleware(object): ) return self.response_redirect_class(language_url) - if not (self.is_language_prefix_patterns_used + if not (self.is_language_prefix_patterns_used(urlconf) and language_from_path): patch_vary_headers(response, ('Accept-Language',)) if 'Content-Language' not in response: response['Content-Language'] = language return response - @cached_property - def is_language_prefix_patterns_used(self): + @lru_cache.lru_cache(maxsize=None) + def is_language_prefix_patterns_used(self, urlconf): """ Returns `True` if the `LocaleRegexURLResolver` is used at root level of the urlpatterns, else it returns `False`. """ - for url_pattern in get_resolver(None).url_patterns: + for url_pattern in get_resolver(urlconf).url_patterns: if isinstance(url_pattern, LocaleRegexURLResolver): return True return False diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt index 436160e336..8622c34e89 100644 --- a/docs/releases/1.10.txt +++ b/docs/releases/1.10.txt @@ -244,7 +244,9 @@ Generic Views Internationalization ~~~~~~~~~~~~~~~~~~~~ -* ... +* The :func:`~django.conf.urls.i18n.i18n_patterns` helper function can now be + used in a root URLConf specified using :attr:`request.urlconf + `. Management Commands ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 0ce746cbad..86778859e9 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -1328,7 +1328,7 @@ Language prefix in URL patterns .. function:: i18n_patterns(*pattern_list) -This function can be used in your root URLconf and Django will automatically +This function can be used in a root URLconf and Django will automatically prepend the current active language code to all url patterns defined within :func:`~django.conf.urls.i18n.i18n_patterns`. Example URL patterns:: @@ -1373,10 +1373,16 @@ function. Example:: .. warning:: - :func:`~django.conf.urls.i18n.i18n_patterns` is only allowed in your root + :func:`~django.conf.urls.i18n.i18n_patterns` is only allowed in a root URLconf. Using it within an included URLconf will throw an :exc:`~django.core.exceptions.ImproperlyConfigured` exception. +.. versionchanged:: 1.10 + + In older version, using ``i18n_patterns`` in a root URLconf different from + :setting:`ROOT_URLCONF` by setting :attr:`request.urlconf + ` wasn't supported. + .. warning:: Ensure that you don't have non-prefixed URL patterns that might collide diff --git a/tests/i18n/patterns/tests.py b/tests/i18n/patterns/tests.py index 83c7fd93e5..c533a57204 100644 --- a/tests/i18n/patterns/tests.py +++ b/tests/i18n/patterns/tests.py @@ -7,6 +7,7 @@ from django.http import HttpResponsePermanentRedirect from django.middleware.locale import LocaleMiddleware from django.template import Context, Template from django.test import SimpleTestCase, override_settings +from django.test.client import RequestFactory from django.test.utils import override_script_prefix from django.urls import clear_url_caches, reverse, translate_url from django.utils import translation @@ -92,6 +93,18 @@ class URLDisabledTests(URLTestCaseBase): self.assertEqual(reverse('prefixed'), '/prefixed/') +class RequestURLConfTests(SimpleTestCase): + + @override_settings(ROOT_URLCONF='i18n.patterns.urls.path_unused') + def test_request_urlconf_considered(self): + request = RequestFactory().get('/nl/') + request.urlconf = 'i18n.patterns.urls.default' + middleware = LocaleMiddleware() + with translation.override('nl'): + middleware.process_request(request) + self.assertEqual(request.LANGUAGE_CODE, 'nl') + + @override_settings(ROOT_URLCONF='i18n.patterns.urls.path_unused') class PathUnusedTests(URLTestCaseBase): """