Index: django/core/urlresolvers.py
===================================================================
--- django/core/urlresolvers.py	(revision 8032)
+++ django/core/urlresolvers.py	(working copy)
@@ -28,6 +28,9 @@
 # be empty.
 _prefixes = {}
 
+# Overridden URLconfs for each thread are stored here.
+_urlconfs = {}
+
 class Resolver404(Http404):
     pass
 
@@ -296,9 +299,13 @@
         return result + sub_match
 
 def resolve(path, urlconf=None):
+    if urlconf is None:
+        urlconf = get_urlconf()
     return get_resolver(urlconf).resolve(path)
 
 def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
+    if urlconf is None:
+        urlconf = get_urlconf()
     args = args or []
     kwargs = kwargs or {}
     if prefix is None:
@@ -328,3 +335,26 @@
     """
     return _prefixes.get(currentThread(), u'/')
 
+def set_urlconf(urlconf_name):
+    """
+    Sets the URLconf for the current thread (overriding the default one in
+    settings). Set to None to revert back to the default.
+    """
+    thread = currentThread()
+    if urlconf_name:
+        _urlconfs[thread] = urlconf_name
+    else:
+        # This is a lot faster than wrapping in a try/except.
+        if thread in _urlconfs:
+            del _urlconfs[thread]
+
+def get_urlconf(default=None):
+    """
+    Returns the root URLconf to use for the current thread if it has been
+    changed from the default one.
+    """
+    thread = currentThread()
+    # It's faster to check first than to do _urlconfs.get(thread)
+    if thread in _urlconfs:
+        return _urlconfs[thread]
+    return default
Index: django/core/handlers/base.py
===================================================================
--- django/core/handlers/base.py	(revision 8032)
+++ django/core/handlers/base.py	(working copy)
@@ -63,14 +63,29 @@
         from django.core import exceptions, urlresolvers
         from django.conf import settings
 
+        # Reset the urlconf for this thread.
+        urlresolvers.set_urlconf(None)
+
         # Apply request middleware
         for middleware_method in self._request_middleware:
             response = middleware_method(request)
             if response:
                 return response
 
-        # Get urlconf from request object, if available.  Otherwise use default.
-        urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
+        # Get overridden urlconf for this thread, if available. Otherwise use
+        # default.
+        urlconf = urlresolvers.get_urlconf()
+        if urlconf is None:
+            # Get urlconf from request object, if available (deprecated).
+            if hasattr(request, 'urlconf'):
+                import warnings
+                warnings.warn("The 'request.urlconf' attribute is deprecated. "
+                              "Use the 'django.core.urlresolvers::set_urlconf'"
+                              " method instead.", DeprecationWarning)
+                urlconf = request.urlconf
+                urlresolvers.set_urlconf(urlconf)
+            else:
+                urlconf = settings.ROOT_URLCONF
 
         resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
         try:
Index: tests/regressiontests/urlpatterns_reverse/middleware.py
===================================================================
--- tests/regressiontests/urlpatterns_reverse/middleware.py	(revision 0)
+++ tests/regressiontests/urlpatterns_reverse/middleware.py	(revision 0)
@@ -0,0 +1,7 @@
+from django.core.urlresolvers import set_urlconf
+
+import urlconf_inner
+
+class ChangeUrlconfMiddleware:
+    def process_request(self, request):
+        set_urlconf(urlconf_inner.__name__)
Index: tests/regressiontests/urlpatterns_reverse/tests.py
===================================================================
--- tests/regressiontests/urlpatterns_reverse/tests.py	(revision 8032)
+++ tests/regressiontests/urlpatterns_reverse/tests.py	(working copy)
@@ -1,8 +1,16 @@
 "Unit tests for reverse URL lookup"
 
+import re
+import unittest
+
 from django.core.urlresolvers import reverse_helper, NoReverseMatch
-import re, unittest
+from django.conf import settings
+from django.test import TestCase
 
+import urlconf_outer
+import urlconf_inner
+import middleware
+
 test_data = (
     ('^places/(\d+)/$', 'places/3/', [3], {}),
     ('^places/(\d+)/$', 'places/3/', ['3'], {}),
@@ -36,5 +44,34 @@
             else:
                 self.assertEquals(got, expected)
 
-if __name__ == "__main__":
-    run_tests(1)
+class SetUrlConf(TestCase):
+    def setUp(self):
+        self.root_urlconf = settings.ROOT_URLCONF
+        self.middleware_classes = settings.MIDDLEWARE_CLASSES
+        settings.ROOT_URLCONF = urlconf_outer.__name__
+
+    def tearDown(self):
+        settings.ROOT_URLCONF = self.root_urlconf
+        settings.MIDDLEWARE_CLASSES = self.middleware_classes
+
+    def test_handler(self):
+        response = self.client.get('/test/me/')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.content, 'outer:/test/me/,'
+                                           'inner:/inner_urlconf/second_test/')
+        response = self.client.get('/inner_urlconf/second_test/')
+        self.assertEqual(response.status_code, 200)
+        response = self.client.get('/second_test/')
+        self.assertEqual(response.status_code, 404)
+
+    def test_handler_overridden(self):
+        settings.MIDDLEWARE_CLASSES += (
+            '%s.ChangeUrlconfMiddleware' % middleware.__name__,
+        )
+        response = self.client.get('/test/me/')
+        self.assertEqual(response.status_code, 404)
+        response = self.client.get('/inner_urlconf/second_test/')
+        self.assertEqual(response.status_code, 404)
+        response = self.client.get('/second_test/')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.content, 'outer:,inner:/second_test/')
Index: tests/regressiontests/urlpatterns_reverse/urlconf_inner.py
===================================================================
--- tests/regressiontests/urlpatterns_reverse/urlconf_inner.py	(revision 0)
+++ tests/regressiontests/urlpatterns_reverse/urlconf_inner.py	(revision 0)
@@ -0,0 +1,13 @@
+from django.conf.urls.defaults import *
+from django.template import Template, Context
+from django.http import HttpResponse
+
+def inner_view(request):
+    content = Template('outer:{% url outer %},'
+                       'inner:{% url inner %}').render(Context())
+    return HttpResponse(content)
+
+urlpatterns = patterns('teacher_survey.views',
+    url(r'^second_test/$', inner_view, name='inner'),
+)
+
Index: tests/regressiontests/urlpatterns_reverse/urlconf_outer.py
===================================================================
--- tests/regressiontests/urlpatterns_reverse/urlconf_outer.py	(revision 0)
+++ tests/regressiontests/urlpatterns_reverse/urlconf_outer.py	(revision 0)
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import *
+
+import urlconf_inner
+
+urlpatterns = patterns('',
+    url(r'^test/me/$', urlconf_inner.inner_view, name='outer'),
+    url(r'^inner_urlconf/', include(urlconf_inner.__name__))
+)
+
Index: docs/url_dispatch.txt
===================================================================
--- docs/url_dispatch.txt	(revision 8032)
+++ docs/url_dispatch.txt	(working copy)
@@ -34,9 +34,9 @@
 
     1. Django determines the root URLconf module to use. Ordinarily,
        this is the value of the ``ROOT_URLCONF`` setting in your
-       `settings file`_, but if the incoming ``HttpRequest`` object
-       has an attribute called ``urlconf``, its value will be used in
-       place of the ``ROOT_URLCONF`` setting.
+       `settings file`_, but this can be overridden per request by calling the
+       ``set_urlconf`` method found in ``django.core.urlresolvers`` (passing in
+       the value to be used in place of the ``ROOT_URLCONF`` setting).
     2. Django loads that Python module and looks for the variable
        ``urlpatterns``. This should be a Python list, in the format returned by
        the function ``django.conf.urls.defaults.patterns()``.
Index: docs/settings.txt
===================================================================
--- docs/settings.txt	(revision 8032)
+++ docs/settings.txt	(working copy)
@@ -388,7 +388,7 @@
 
 Still, note that there are always going to be sections of your debug output that
 are inappropriate for public consumption. File paths, configuration options, and
-the like all give attackers extra information about your server. 
+the like all give attackers extra information about your server.
 
 It is also important to remember that when running with ``DEBUG`` turned on, Django
 will remember every SQL query it executes. This is useful when you are debugging,
@@ -820,10 +820,10 @@
 
 Default: Not defined
 
-A string representing the full Python import path to your root URLconf. For example:
-``"mydjangoapps.urls"``. Can be overridden on a per-request basis by
-setting the attribute ``urlconf`` on the incoming ``HttpRequest``
-object. See `How Django processes a request`_ for details.
+A string representing the full Python import path to your root URLconf. For
+example: ``"mydjangoapps.urls"``. Can be overridden on a per-request basis by
+using the ``set_urlconf`` method in ``django.core.urlresolvers``. See
+`How Django processes a request`_ for details.
 
 .. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request
 
