diff --git a/django/contrib/auth/decorators.py b/django/contrib/auth/decorators.py
index 97db3bb..b815c2b 100644
--- a/django/contrib/auth/decorators.py
+++ b/django/contrib/auth/decorators.py
@@ -14,17 +14,17 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
     redirecting to the log-in page if necessary. The test should be a callable
     that takes the user object and returns True if the user passes.
     """
-    if not login_url:
-        from django.conf import settings
-        login_url = settings.LOGIN_URL
-
     def decorator(view_func):
         def _wrapped_view(request, *args, **kwargs):
             if test_func(request.user):
                 return view_func(request, *args, **kwargs)
             path = urlquote(request.get_full_path())
-            tup = login_url, redirect_field_name, path
-            return HttpResponseRedirect('%s?%s=%s' % tup)
+            url = login_url
+            if not url:
+                from django.conf import settings
+                url = settings.LOGIN_URL
+            return HttpResponseRedirect('%s?%s=%s' % (url, redirect_field_name,
+                                                      path))
         return wraps(view_func)(_wrapped_view)
     return decorator
 
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index 3bb2c1c..b106470 100644
--- a/django/core/urlresolvers.py
+++ b/django/core/urlresolvers.py
@@ -14,7 +14,7 @@ from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
 from django.utils.datastructures import MultiValueDict
 from django.utils.encoding import iri_to_uri, force_unicode, smart_str
-from django.utils.functional import memoize
+from django.utils.functional import memoize, lazy
 from django.utils.importlib import import_module
 from django.utils.regex_helper import normalize
 from django.utils.thread_support import currentThread
@@ -355,6 +355,8 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current
     return iri_to_uri(u'%s%s' % (prefix, resolver.reverse(view,
             *args, **kwargs)))
 
+reverse_lazy = lazy(reverse, str)
+
 def clear_url_caches():
     global _resolver_cache
     global _callable_cache
diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt
index 0a5d04c..9baa911 100644
--- a/docs/topics/http/urls.txt
+++ b/docs/topics/http/urls.txt
@@ -824,6 +824,22 @@ namespaces into URLs on specific application instances, according to the
     be imported correctly. Do not include lines that reference views you
     haven't written yet, because those views will not be importable.
 
+reverse_lazy()
+--------------
+
+.. versionadded:: 1.2
+
+A lazily evaluated version of `reverse()`_.
+
+It is useful for when you need to use a URL reversal before Django's URL names
+map is loaded. Some common cases where this method is necessary are:
+
+* in your URL configuration (such as the ``url`` argument for the
+  ``django.views.generic.simple.redirect_to`` generic view).
+
+* providing a reversed URL to a decorator (such as the ``login_url`` argument
+  for the ``django.contrib.auth.decorators.permission_required`` decorator).
+
 resolve()
 ---------
 
