Index: django/core/urlresolvers.py
===================================================================
--- django/core/urlresolvers.py	(revision 7028)
+++ django/core/urlresolvers.py	(working copy)
@@ -7,11 +7,12 @@
     (view_function, function_args, function_kwargs)
 """
 
-from django.http import Http404
+from django.http import Http404, HttpRequest
 from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
 from django.utils.encoding import iri_to_uri, force_unicode, smart_str
 from django.utils.functional import memoize
 import re
+import types
 
 try:
     reversed
@@ -201,9 +202,14 @@
 class RegexURLResolver(object):
     def __init__(self, regex, urlconf_name, default_kwargs=None):
         # regex is a string representing a regular expression.
-        # urlconf_name is a string representing the module containing urlconfs.
+        # urlconf_name could either be a string representing the module
+        # containing urlconfs or the module itself.
         self.regex = re.compile(regex, re.UNICODE)
-        self.urlconf_name = urlconf_name
+        if isinstance(urlconf_name, types.ModuleType):
+            self._urlconf_module = urlconf_name
+            self.urlconf_name = urlconf_name.__name__
+        else:
+            self.urlconf_name = urlconf_name
         self.callback = None
         self.default_kwargs = default_kwargs or {}
         self._reverse_dict = {}
@@ -294,5 +300,8 @@
 def reverse(viewname, urlconf=None, args=None, kwargs=None):
     args = args or []
     kwargs = kwargs or {}
+    # urlconf could also be an HttpRequest (abstracting the need for high-level
+    # code to check for a request.urlconf)
+    if urlconf is not None and isinstance(urlconf, HttpRequest):
+        urlconf = getattr(urlconf, 'urlconf', None)
     return iri_to_uri(u'/' + get_resolver(urlconf).reverse(viewname, *args, **kwargs))
-
Index: django/contrib/sitemaps/views.py
===================================================================
--- django/contrib/sitemaps/views.py	(revision 7028)
+++ django/contrib/sitemaps/views.py	(working copy)
@@ -1,7 +1,7 @@
 from django.http import HttpResponse, Http404
 from django.template import loader
 from django.contrib.sites.models import Site
-from django.core import urlresolvers
+from django.core.urlresolvers import reverse
 from django.utils.encoding import smart_str
 
 def index(request, sitemaps):
@@ -9,7 +9,8 @@
     sites = []
     protocol = request.is_secure() and 'https' or 'http'
     for section in sitemaps.keys():
-        sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.sitemap', kwargs={'section': section})
+        sitemap_url = reverse('django.contrib.sitemaps.views.sitemap',
+                              urlconf=request, kwargs={'section': section})
         sites.append('%s://%s%s' % (protocol, current_site.domain, sitemap_url))
     xml = loader.render_to_string('sitemap_index.xml', {'sitemaps': sites})
     return HttpResponse(xml, mimetype='application/xml')
Index: django/template/defaulttags.py
===================================================================
--- django/template/defaulttags.py	(revision 7028)
+++ django/template/defaulttags.py	(working copy)
@@ -8,7 +8,7 @@
 except NameError:
     from django.utils.itercompat import reversed     # Python 2.3 fallback
 
-from django.template import Node, NodeList, Template, Context, Variable
+from django.template import Node, NodeList, Template, Context, RequestContext, Variable
 from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
 from django.template import get_library, Library, InvalidTemplateLibrary
 from django.conf import settings
@@ -359,12 +359,16 @@
         args = [arg.resolve(context) for arg in self.args]
         kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
                        for k, v in self.kwargs.items()])
+        if isinstance(context, RequestContext):
+            urlconf = context.request
+        else:
+            urlconf = None
         try:
-            return reverse(self.view_name, args=args, kwargs=kwargs)
+            return reverse(self.view_name, urlconf, args=args, kwargs=kwargs)
         except NoReverseMatch:
             try:
                 project_name = settings.SETTINGS_MODULE.split('.')[0]
-                return reverse(project_name + '.' + self.view_name,
+                return reverse(project_name + '.' + self.view_name, urlconf,
                                args=args, kwargs=kwargs)
             except NoReverseMatch:
                 return ''
Index: django/template/context.py
===================================================================
--- django/template/context.py	(revision 7028)
+++ django/template/context.py	(working copy)
@@ -95,6 +95,7 @@
     """
     def __init__(self, request, dict=None, processors=None):
         Context.__init__(self, dict)
+        self.request = request
         if processors is None:
             processors = ()
         else:
Index: tests/regressiontests/urlpatterns_reverse/urlconf1.py
===================================================================
--- tests/regressiontests/urlpatterns_reverse/urlconf1.py	(revision 0)
+++ tests/regressiontests/urlpatterns_reverse/urlconf1.py	(revision 0)
@@ -0,0 +1,12 @@
+from django.conf.urls.defaults import *
+
+import urlconf2
+
+def empty_view(request):
+    pass
+
+urlpatterns = patterns('teacher_survey.views',
+    url(r'^test/me/$', empty_view, name='test1'),
+    url(r'^into_urlconf2/', include(urlconf2))
+)
+
Index: tests/regressiontests/urlpatterns_reverse/urlconf2.py
===================================================================
--- tests/regressiontests/urlpatterns_reverse/urlconf2.py	(revision 0)
+++ tests/regressiontests/urlpatterns_reverse/urlconf2.py	(revision 0)
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import *
+
+def empty_view2(request):
+    pass
+
+urlpatterns = patterns('teacher_survey.views',
+    url(r'^second_test/$', empty_view2, name='test2'),
+)
+
Index: tests/regressiontests/urlpatterns_reverse/tests.py
===================================================================
--- tests/regressiontests/urlpatterns_reverse/tests.py	(revision 7028)
+++ tests/regressiontests/urlpatterns_reverse/tests.py	(working copy)
@@ -1,8 +1,16 @@
 "Unit tests for reverse URL lookup"
 
-from django.core.urlresolvers import reverse_helper, NoReverseMatch
-import re, unittest
+import re
+import unittest
 
+from django.core.urlresolvers import reverse_helper, NoReverseMatch, reverse
+from django.conf import settings
+from django.http import HttpRequest
+from django.template import Template, Context, RequestContext
+
+import urlconf1
+import urlconf2
+
 test_data = (
     ('^places/(\d+)/$', 'places/3/', [3], {}),
     ('^places/(\d+)/$', 'places/3/', ['3'], {}),
@@ -35,5 +43,49 @@
             else:
                 self.assertEquals(got, expected)
 
-if __name__ == "__main__":
-    run_tests(1)
+class RequestAwareReversing(unittest.TestCase):
+    def setUp(self):
+        self.root_urlconf = settings.ROOT_URLCONF
+
+    def tearDown(self):
+        settings.ROOT_URLCONF = self.root_urlconf
+
+    def test_reverse(self):
+        settings.ROOT_URLCONF = urlconf1
+        # Control
+        self.assertEqual(reverse('test1'), '/test/me/')
+        self.assertEqual(reverse('test2'), '/into_urlconf2/second_test/')
+        # urlconf1 (== control)
+        self.assertEqual(reverse('test1', urlconf=urlconf1), '/test/me/')
+        self.assertEqual(reverse('test2', urlconf=urlconf1),
+                         '/into_urlconf2/second_test/')
+        # urlconf2
+        self.assertRaises(NoReverseMatch,
+                          lambda: reverse('test1', urlconf=urlconf2))
+        self.assertEqual(reverse('test2', urlconf=urlconf2), '/second_test/')
+        # urlconf2 via a request object
+        request = HttpRequest()
+        request.urlconf = urlconf2
+        self.assertRaises(NoReverseMatch,
+                          lambda: reverse('test1', urlconf=request))
+        self.assertEqual(reverse('test2', urlconf=request), '/second_test/')
+
+    def test_url_tag(self):
+        settings.ROOT_URLCONF = urlconf1
+        request = HttpRequest()
+        # Rendered with Context rather than RequestContext
+        c = Context()
+        self.assertEqual(Template('{% url test1 %}').render(c), '/test/me/')
+        self.assertEqual(Template('{% url test2 %}').render(c),
+                         '/into_urlconf2/second_test/')
+        # Rendered with RequestContext without a request.urlconf
+        c = RequestContext(request)
+        self.assertEqual(Template('{% url test1 %}').render(c), '/test/me/')
+        self.assertEqual(Template('{% url test2 %}').render(c),
+                         '/into_urlconf2/second_test/')
+        # Rendered with RequestContext with a request.urlconf
+        request.urlconf = urlconf2
+        c = RequestContext(request)
+        self.assertEqual(Template('{% url test1 %}').render(c), '')
+        self.assertEqual(Template('{% url test2 %}').render(c),
+                         '/second_test/')
Index: docs/url_dispatch.txt
===================================================================
--- docs/url_dispatch.txt	(revision 7028)
+++ docs/url_dispatch.txt	(working copy)
@@ -32,7 +32,10 @@
 When a user requests a page from your Django-powered site, this is the
 algorithm the system follows to determine which Python code to execute:
 
-    1. Django looks at the ``ROOT_URLCONF`` setting in your `settings file`_.
+    1. Django decides on which URLconf module to use. First, Django looks for a
+       ``urlconf`` attribute on the ``request`` object.
+       Under most situations, this attribute will not be found, in which case
+       Django looks at the ``ROOT_URLCONF`` setting in your `settings file`_.
        This should be a string representing the full Python import path to your
        URLconf. For example: ``"mydjangoapps.urls"``.
     2. Django loads that Python module and looks for the variable
@@ -596,3 +599,26 @@
 
 .. _model API documentation: ../model-api/#the-permalink-decorator
 
+Overriding the URLconf
+======================
+
+As explained in `how Django processes a request`_, the URLconf module used by
+Django can be altered via custom middleware by setting ``request.urlconf`` to a
+string representing the full Python import path to the URLconf.
+
+If you are using any such middleware, you will need to consider that Django's
+reverse URL resolution methods need to be made aware of the ``request`` object
+so they can use the right URLconf:
+
+    * When you use the ``{% url %}`` tag in a template, render it with a
+      ``RequestContext`` so the tag can check the ``request`` object.
+
+    * For ease of use, the ``urlconf`` argument of ``reverse()`` also accepts a
+      ``request`` object. So when you use ``reverse()``, pass ``request`` as
+      the ``urlconf`` argument::
+
+          reverse('appname.viewname', urlconf=request)
+
+Using ``reverse()`` without access to ``request`` (such as outside a view) or
+using the ``permalink`` decorator will mean that the returning URL will only be
+resolved from the default URLconf as defined in ``settings.ROOT_URLCONF``.
