Ticket #5034: request_aware_reversing.diff

File request_aware_reversing.diff, 10.1 KB (added by Chris Beaven, 16 years ago)
  • django/core/urlresolvers.py

     
    77    (view_function, function_args, function_kwargs)
    88"""
    99
    10 from django.http import Http404
     10from django.http import Http404, HttpRequest
    1111from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
    1212from django.utils.encoding import iri_to_uri, force_unicode, smart_str
    1313from django.utils.functional import memoize
    1414import re
     15import types
    1516
    1617try:
    1718    reversed
     
    201202class RegexURLResolver(object):
    202203    def __init__(self, regex, urlconf_name, default_kwargs=None):
    203204        # regex is a string representing a regular expression.
    204         # urlconf_name is a string representing the module containing urlconfs.
     205        # urlconf_name could either be a string representing the module
     206        # containing urlconfs or the module itself.
    205207        self.regex = re.compile(regex, re.UNICODE)
    206         self.urlconf_name = urlconf_name
     208        if isinstance(urlconf_name, types.ModuleType):
     209            self._urlconf_module = urlconf_name
     210            self.urlconf_name = urlconf_name.__name__
     211        else:
     212            self.urlconf_name = urlconf_name
    207213        self.callback = None
    208214        self.default_kwargs = default_kwargs or {}
    209215        self._reverse_dict = {}
     
    294300def reverse(viewname, urlconf=None, args=None, kwargs=None):
    295301    args = args or []
    296302    kwargs = kwargs or {}
     303    # urlconf could also be an HttpRequest (abstracting the need for high-level
     304    # code to check for a request.urlconf)
     305    if urlconf is not None and isinstance(urlconf, HttpRequest):
     306        urlconf = getattr(urlconf, 'urlconf', None)
    297307    return iri_to_uri(u'/' + get_resolver(urlconf).reverse(viewname, *args, **kwargs))
    298 
  • django/contrib/sitemaps/views.py

     
    11from django.http import HttpResponse, Http404
    22from django.template import loader
    33from django.contrib.sites.models import Site
    4 from django.core import urlresolvers
     4from django.core.urlresolvers import reverse
    55from django.utils.encoding import smart_str
    66
    77def index(request, sitemaps):
     
    99    sites = []
    1010    protocol = request.is_secure() and 'https' or 'http'
    1111    for section in sitemaps.keys():
    12         sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.sitemap', kwargs={'section': section})
     12        sitemap_url = reverse('django.contrib.sitemaps.views.sitemap',
     13                              urlconf=request, kwargs={'section': section})
    1314        sites.append('%s://%s%s' % (protocol, current_site.domain, sitemap_url))
    1415    xml = loader.render_to_string('sitemap_index.xml', {'sitemaps': sites})
    1516    return HttpResponse(xml, mimetype='application/xml')
  • django/template/defaulttags.py

     
    88except NameError:
    99    from django.utils.itercompat import reversed     # Python 2.3 fallback
    1010
    11 from django.template import Node, NodeList, Template, Context, Variable
     11from django.template import Node, NodeList, Template, Context, RequestContext, Variable
    1212from 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
    1313from django.template import get_library, Library, InvalidTemplateLibrary
    1414from django.conf import settings
     
    359359        args = [arg.resolve(context) for arg in self.args]
    360360        kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
    361361                       for k, v in self.kwargs.items()])
     362        if isinstance(context, RequestContext):
     363            urlconf = context.request
     364        else:
     365            urlconf = None
    362366        try:
    363             return reverse(self.view_name, args=args, kwargs=kwargs)
     367            return reverse(self.view_name, urlconf, args=args, kwargs=kwargs)
    364368        except NoReverseMatch:
    365369            try:
    366370                project_name = settings.SETTINGS_MODULE.split('.')[0]
    367                 return reverse(project_name + '.' + self.view_name,
     371                return reverse(project_name + '.' + self.view_name, urlconf,
    368372                               args=args, kwargs=kwargs)
    369373            except NoReverseMatch:
    370374                return ''
  • django/template/context.py

     
    9595    """
    9696    def __init__(self, request, dict=None, processors=None):
    9797        Context.__init__(self, dict)
     98        self.request = request
    9899        if processors is None:
    99100            processors = ()
    100101        else:
  • tests/regressiontests/urlpatterns_reverse/tests.py

     
    11"Unit tests for reverse URL lookup"
    22
    3 from django.core.urlresolvers import reverse_helper, NoReverseMatch
    4 import re, unittest
     3import re
     4import unittest
    55
     6from django.core.urlresolvers import reverse_helper, NoReverseMatch, reverse
     7from django.conf import settings
     8from django.http import HttpRequest
     9from django.template import Template, Context, RequestContext
     10
     11import urlconf1
     12import urlconf2
     13
    614test_data = (
    715    ('^places/(\d+)/$', 'places/3/', [3], {}),
    816    ('^places/(\d+)/$', 'places/3/', ['3'], {}),
     
    3543            else:
    3644                self.assertEquals(got, expected)
    3745
    38 if __name__ == "__main__":
    39     run_tests(1)
     46class RequestAwareReversing(unittest.TestCase):
     47    def setUp(self):
     48        self.root_urlconf = settings.ROOT_URLCONF
     49
     50    def tearDown(self):
     51        settings.ROOT_URLCONF = self.root_urlconf
     52
     53    def test_reverse(self):
     54        settings.ROOT_URLCONF = urlconf1
     55        # Control
     56        self.assertEqual(reverse('test1'), '/test/me/')
     57        self.assertEqual(reverse('test2'), '/into_urlconf2/second_test/')
     58        # urlconf1 (== control)
     59        self.assertEqual(reverse('test1', urlconf=urlconf1), '/test/me/')
     60        self.assertEqual(reverse('test2', urlconf=urlconf1),
     61                         '/into_urlconf2/second_test/')
     62        # urlconf2
     63        self.assertRaises(NoReverseMatch,
     64                          lambda: reverse('test1', urlconf=urlconf2))
     65        self.assertEqual(reverse('test2', urlconf=urlconf2), '/second_test/')
     66        # urlconf2 via a request object
     67        request = HttpRequest()
     68        request.urlconf = urlconf2
     69        self.assertRaises(NoReverseMatch,
     70                          lambda: reverse('test1', urlconf=request))
     71        self.assertEqual(reverse('test2', urlconf=request), '/second_test/')
     72
     73    def test_url_tag(self):
     74        settings.ROOT_URLCONF = urlconf1
     75        request = HttpRequest()
     76        # Rendered with Context rather than RequestContext
     77        c = Context()
     78        self.assertEqual(Template('{% url test1 %}').render(c), '/test/me/')
     79        self.assertEqual(Template('{% url test2 %}').render(c),
     80                         '/into_urlconf2/second_test/')
     81        # Rendered with RequestContext without a request.urlconf
     82        c = RequestContext(request)
     83        self.assertEqual(Template('{% url test1 %}').render(c), '/test/me/')
     84        self.assertEqual(Template('{% url test2 %}').render(c),
     85                         '/into_urlconf2/second_test/')
     86        # Rendered with RequestContext with a request.urlconf
     87        request.urlconf = urlconf2
     88        c = RequestContext(request)
     89        self.assertEqual(Template('{% url test1 %}').render(c), '')
     90        self.assertEqual(Template('{% url test2 %}').render(c),
     91                         '/second_test/')
  • docs/url_dispatch.txt

     
    3232When a user requests a page from your Django-powered site, this is the
    3333algorithm the system follows to determine which Python code to execute:
    3434
    35     1. Django looks at the ``ROOT_URLCONF`` setting in your `settings file`_.
     35    1. Django decides on which URLconf module to use. First, Django looks for a
     36       ``urlconf`` attribute on the ``request`` object.
     37       Under most situations, this attribute will not be found, in which case
     38       Django looks at the ``ROOT_URLCONF`` setting in your `settings file`_.
    3639       This should be a string representing the full Python import path to your
    3740       URLconf. For example: ``"mydjangoapps.urls"``.
    3841    2. Django loads that Python module and looks for the variable
     
    596599
    597600.. _model API documentation: ../model-api/#the-permalink-decorator
    598601
     602Overriding the URLconf
     603======================
     604
     605As explained in `how Django processes a request`_, the URLconf module used by
     606Django can be altered via custom middleware by setting ``request.urlconf`` to a
     607string representing the full Python import path to the URLconf.
     608
     609If you are using any such middleware, you will need to consider that Django's
     610reverse URL resolution methods need to be made aware of the ``request`` object
     611so they can use the right URLconf:
     612
     613    * When you use the ``{% url %}`` tag in a template, render it with a
     614      ``RequestContext`` so the tag can check the ``request`` object.
     615
     616    * For ease of use, the ``urlconf`` argument of ``reverse()`` also accepts a
     617      ``request`` object. So when you use ``reverse()``, pass ``request`` as
     618      the ``urlconf`` argument::
     619
     620          reverse('appname.viewname', urlconf=request)
     621
     622Using ``reverse()`` without access to ``request`` (such as outside a view) or
     623using the ``permalink`` decorator will mean that the returning URL will only be
     624resolved from the default URLconf as defined in ``settings.ROOT_URLCONF``.
Back to Top