diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index a35287e..52d9090 100644
a
|
b
|
from django.conf import settings
|
15 | 15 | from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist |
16 | 16 | from django.utils.datastructures import MultiValueDict |
17 | 17 | from django.utils.encoding import iri_to_uri, force_unicode, smart_str |
18 | | from django.utils.functional import memoize |
| 18 | from django.utils.functional import memoize, lazy |
19 | 19 | from django.utils.importlib import import_module |
20 | 20 | from django.utils.regex_helper import normalize |
21 | 21 | |
… |
… |
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current
|
390 | 390 | return iri_to_uri(u'%s%s' % (prefix, resolver.reverse(view, |
391 | 391 | *args, **kwargs))) |
392 | 392 | |
| 393 | reverse_lazy = lazy(reverse, str) |
| 394 | |
393 | 395 | def clear_url_caches(): |
394 | 396 | global _resolver_cache |
395 | 397 | global _callable_cache |
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
index a63fff7..8ceed20 100644
a
|
b
|
compatibility with old browsers, this change means that you can use any HTML5
|
37 | 37 | features you need in admin pages without having to lose HTML validity or |
38 | 38 | override the provided templates to change the doctype. |
39 | 39 | |
| 40 | ``reverse_lazy`` |
| 41 | ~~~~~~~~~~~~~~~~ |
| 42 | |
| 43 | A lazily evaluated version of ``django.core.urlresolvers.reverse()`` was added |
| 44 | to allow using URL reversals before the project's URLConf gets loaded. |
| 45 | |
40 | 46 | .. _backwards-incompatible-changes-1.4: |
41 | 47 | |
42 | 48 | Backwards incompatible changes in 1.4 |
diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt
index d721012..af66fa8 100644
a
|
b
|
namespaces into URLs on specific application instances, according to the
|
827 | 827 | ``urllib.quote``) to the ouput of :meth:`~django.core.urlresolvers.reverse` |
828 | 828 | may produce undesirable results. |
829 | 829 | |
| 830 | reverse_lazy() |
| 831 | -------------- |
| 832 | |
| 833 | .. versionadded:: 1.4 |
| 834 | |
| 835 | A lazily evaluated version of `reverse()`_. |
| 836 | |
| 837 | It is useful for when you need to use a URL reversal before your project's |
| 838 | URLConf is loaded. Some common cases where this method is necessary are:: |
| 839 | |
| 840 | * providing a reversed URL as the ``url`` attribute of a generic class-based |
| 841 | view. |
| 842 | |
| 843 | * providing a reversed URL to a decorator (such as the ``login_url`` argument |
| 844 | for the ``django.contrib.auth.decorators.permission_required`` decorator). |
| 845 | |
| 846 | * providing a reversed URL as a default value for a parameter in a function's |
| 847 | signature. |
| 848 | |
830 | 849 | resolve() |
831 | 850 | --------- |
832 | 851 | |
diff --git a/tests/regressiontests/urlpatterns_reverse/reverse_lazy_urls.py b/tests/regressiontests/urlpatterns_reverse/reverse_lazy_urls.py
new file mode 100644
index 0000000..8f7b8a9
-
|
+
|
|
| 1 | from django.conf.urls.defaults import * |
| 2 | |
| 3 | from views import empty_view, LazyRedictView, login_required_view |
| 4 | |
| 5 | |
| 6 | urlpatterns = patterns('', |
| 7 | url(r'^redirected_to/$', empty_view, name='named-lazy-url-redirected-to'), |
| 8 | url(r'^login/$', empty_view, name='some-login-page'), |
| 9 | url(r'^login_required_view/$', login_required_view), |
| 10 | url(r'^redirect/$', LazyRedictView.as_view()), |
| 11 | ) |
diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py
index 198d556..0cb678c 100644
a
|
b
|
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
|
10 | 10 | from django.shortcuts import redirect |
11 | 11 | from django.test import TestCase |
12 | 12 | from django.utils import unittest |
| 13 | from django.contrib.auth.models import User |
13 | 14 | |
14 | 15 | import urlconf_outer |
15 | 16 | import urlconf_inner |
… |
… |
class ResolverTests(unittest.TestCase):
|
218 | 219 | else: |
219 | 220 | self.assertEqual(t.name, e['name'], 'Wrong URL name. Expected "%s", got "%s".' % (e['name'], t.name)) |
220 | 221 | |
| 222 | class ReverseLazyTest(TestCase): |
| 223 | urls = 'regressiontests.urlpatterns_reverse.reverse_lazy_urls' |
| 224 | |
| 225 | def test_redirect_with_lazy_reverse(self): |
| 226 | response = self.client.get('/redirect/') |
| 227 | self.assertRedirects(response, "/redirected_to/", status_code=301) |
| 228 | |
| 229 | def test_user_permission_with_lazy_reverse(self): |
| 230 | user = User.objects.create_user('alfred', 'alfred@example.com', password='testpw') |
| 231 | response = self.client.get('/login_required_view/') |
| 232 | self.assertRedirects(response, "/login/?next=/login_required_view/", status_code=302) |
| 233 | self.client.login(username='alfred', password='testpw') |
| 234 | response = self.client.get('/login_required_view/') |
| 235 | self.assertEqual(response.status_code, 200) |
| 236 | |
221 | 237 | class ReverseShortcutTests(TestCase): |
222 | 238 | urls = 'regressiontests.urlpatterns_reverse.urls' |
223 | 239 | |
diff --git a/tests/regressiontests/urlpatterns_reverse/views.py b/tests/regressiontests/urlpatterns_reverse/views.py
index fdd7423..271b0e9 100644
a
|
b
|
|
1 | 1 | from django.http import HttpResponse |
| 2 | from django.contrib.auth.decorators import user_passes_test |
| 3 | from django.views.generic import RedirectView |
| 4 | from django.core.urlresolvers import reverse_lazy |
2 | 5 | |
3 | 6 | def empty_view(request, *args, **kwargs): |
4 | 7 | return HttpResponse('') |
… |
… |
class ViewClass(object):
|
15 | 18 | |
16 | 19 | view_class_instance = ViewClass() |
17 | 20 | |
| 21 | class LazyRedictView(RedirectView): |
| 22 | url = reverse_lazy('named-lazy-url-redirected-to') |
| 23 | |
| 24 | @user_passes_test(lambda u: u.is_authenticated(), login_url=reverse_lazy('some-login-page')) |
| 25 | def login_required_view(request): |
| 26 | return HttpResponse('Hello you') |
| 27 | |
18 | 28 | def bad_view(request, *args, **kwargs): |
19 | 29 | raise ValueError("I don't think I'm getting good value for this view") |