Commit 6c61ca3d authored by Brian Rosner's avatar Brian Rosner
Browse files

Fixed #5034 -- honor request.urlconf in reverse and resolve.

This enables {% url %} to honor request.urlconf set from process_request
middleware methods.

Thanks SmileyChris for the initial patch work.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11740 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent c169f8cb
Loading
Loading
Loading
Loading
+58 −47
Original line number Diff line number Diff line
@@ -68,6 +68,9 @@ class BaseHandler(object):
        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)
@@ -77,7 +80,11 @@ class BaseHandler(object):
        # Get urlconf from request object, if available.  Otherwise use default.
        urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)

        # Set the urlconf for this thread to the one specified above.
        urlresolvers.set_urlconf(urlconf)

        resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
        try:
            try:
                callback, callback_args, callback_kwargs = resolver.resolve(
                        request.path_info)
@@ -132,6 +139,10 @@ class BaseHandler(object):
                exc_info = sys.exc_info()
                receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
                return self.handle_uncaught_exception(request, resolver, exc_info)
        finally:
            # Reset URLconf for this thread on the way out for complete
            # isolation of request.urlconf
            urlresolvers.set_urlconf(None)

    def handle_uncaught_exception(self, request, resolver, exc_info):
        """
+30 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ a string) and returns a tuple in this format:
import re

from django.http import Http404
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
@@ -32,6 +33,9 @@ _callable_cache = {} # Maps view and url pattern names to their view functions.
# be empty.
_prefixes = {}

# Overridden URLconfs for each thread are stored here.
_urlconfs = {}

class Resolver404(Http404):
    pass

@@ -300,9 +304,13 @@ class RegexURLResolver(object):
                "arguments '%s' not found." % (lookup_view_s, args, kwargs))

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, current_app=None):
    if urlconf is None:
        urlconf = get_urlconf()
    resolver = get_resolver(urlconf)
    args = args or []
    kwargs = kwargs or {}
@@ -371,3 +379,25 @@ def get_script_prefix():
    """
    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:
        # 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()
    if thread in _urlconfs:
        return _urlconfs[thread]
    return default
+2 −1
Original line number Diff line number Diff line
@@ -40,7 +40,8 @@ algorithm the system follows to determine which Python code to execute:

    1. Django determines the root URLconf module to use. Ordinarily,
       this is the value of the ``ROOT_URLCONF`` setting, but if the incoming
       ``HttpRequest`` object has an attribute called ``urlconf``, its value
       ``HttpRequest`` object has an attribute called ``urlconf`` (set by
       middleware :ref:`request processing <request-middleware>`), its value
       will be used in place of the ``ROOT_URLCONF`` setting.

    2. Django loads that Python module and looks for the variable
+7 −0
Original line number Diff line number Diff line
from django.core.urlresolvers import set_urlconf

import urlconf_inner

class ChangeURLconfMiddleware(object):
    def process_request(self, request):
        request.urlconf = urlconf_inner.__name__
+37 −0
Original line number Diff line number Diff line
@@ -16,11 +16,16 @@ ImproperlyConfigured: The included urlconf regressiontests.urlpatterns_reverse.n

import unittest

from django.conf import settings
from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
from django.shortcuts import redirect
from django.test import TestCase

import urlconf_outer
import urlconf_inner
import middleware

test_data = (
    ('places', '/places/3/', [3], {}),
    ('places', '/places/3/', ['3'], {}),
@@ -239,3 +244,35 @@ class NamespaceTests(TestCase):
        self.assertEquals('/other1/inner/37/42/', reverse('nodefault:urlobject-view', args=[37,42], current_app='other-ns1'))
        self.assertEquals('/other1/inner/42/37/', reverse('nodefault:urlobject-view', kwargs={'arg1':42, 'arg2':37}, current_app='other-ns1'))


class RequestURLconfTests(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_urlconf(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_urlconf_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/')
Loading