Ticket #14389: future_url-1.diff

File future_url-1.diff, 7.4 KB (added by Sean Brant, 14 years ago)
  • django/templatetags/future_url.py

     
     1from django.conf import settings
     2from django.template import Library, Node, TemplateSyntaxError
     3from django.template.defaulttags import kwarg_re
     4
     5
     6register = Library()
     7
     8
     9class URLNode(Node):
     10
     11    def __init__(self, view_name, args, kwargs, asvar):
     12        self.view_name = view_name
     13        self.args = args
     14        self.kwargs = kwargs
     15        self.asvar = asvar
     16
     17    def render(self, context):
     18        from django.core.urlresolvers import reverse, NoReverseMatch
     19        args = [arg.resolve(context) for arg in self.args]
     20        kwargs = dict([(smart_str(k, 'ascii'), v.resolve(context))
     21                       for k, v in self.kwargs.items()])
     22
     23        view_name = self.view_name.resolve(context)
     24
     25        # Try to look up the URL twice: once given the view name, and again
     26        # relative to what we guess is the "main" app. If they both fail,
     27        # re-raise the NoReverseMatch unless we're using the
     28        # {% url ... as var %} construct in which cause return nothing.
     29        url = ''
     30        try:
     31            url = reverse(view_name, args=args, kwargs=kwargs,
     32                current_app=context.current_app)
     33        except NoReverseMatch, e:
     34            if settings.SETTINGS_MODULE:
     35                project_name = settings.SETTINGS_MODULE.split('.')[0]
     36                try:
     37                    url = reverse(project_name + '.' + view_name,
     38                              args=args, kwargs=kwargs,
     39                              current_app=context.current_app)
     40                except NoReverseMatch:
     41                    if self.asvar is None:
     42                        # Re-raise the original exception, not the one with
     43                        # the path relative to the project. This makes a
     44                        # better error message.
     45                        raise e
     46            else:
     47                if self.asvar is None:
     48                    raise e
     49
     50        if self.asvar:
     51            context[self.asvar] = url
     52            return ''
     53        else:
     54            return url
     55
     56
     57def url(parser, token):
     58    """
     59    Returns an absolute URL matching given view with its parameters.
     60
     61    This is a way to define links that aren't tied to a particular URL
     62    configuration::
     63
     64        {% url "path.to.some_view" arg1 arg2 %}
     65
     66        or
     67
     68        {% url "path.to.some_view" name1=value1 name2=value2 %}
     69
     70    The first argument is a path to a view. It can be an absolute python path
     71    or just ``app_name.view_name`` without the project name if the view is
     72    located inside the project.  Other arguments are comma-separated values
     73    that will be filled in place of positional and keyword arguments in the
     74    URL. All arguments for the URL should be present.
     75
     76    For example if you have a view ``app_name.client`` taking client's id and
     77    the corresponding line in a URLconf looks like this::
     78
     79        ('^client/(\d+)/$', 'app_name.client')
     80
     81    and this app's URLconf is included into the project's URLconf under some
     82    path::
     83
     84        ('^clients/', include('project_name.app_name.urls'))
     85
     86    then in a template you can create a link for a certain client like this::
     87
     88        {% url "app_name.client" client.id %}
     89
     90    The URL will look like ``/clients/client/123/``.
     91    """
     92    bits = token.split_contents()
     93    if len(bits) < 2:
     94        raise TemplateSyntaxError("'%s' takes at least one argument"
     95                                  " (path to a view)" % bits[0])
     96    viewname = parser.compile_filter(bits[1])
     97    args = []
     98    kwargs = {}
     99    asvar = None
     100    bits = bits[2:]
     101    if len(bits) >= 2 and bits[-2] == 'as':
     102        asvar = bits[-1]
     103        bits = bits[:-2]
     104
     105    if len(bits):
     106        for bit in bits:
     107            match = kwarg_re.match(bit)
     108            if not match:
     109                raise TemplateSyntaxError("Malformed arguments to url tag")
     110            name, value = match.groups()
     111            if name:
     112                kwargs[name] = parser.compile_filter(value)
     113            else:
     114                args.append(parser.compile_filter(value))
     115
     116    return URLNode(viewname, args, kwargs, asvar)
     117url = register.tag(url)
  • django/template/defaulttags.py

     
    11111111
    11121112    The URL will look like ``/clients/client/123/``.
    11131113    """
     1114
     1115    import warnings
     1116    warnings.warn('The old style url tag is being deprecated. '
     1117                  'You can start using the new style url (link to docs).',
     1118                  category=PendingDeprecationWarning)
     1119
    11141120    bits = token.split_contents()
    11151121    if len(bits) < 2:
    11161122        raise TemplateSyntaxError("'%s' takes at least one argument"
  • tests/regressiontests/templates/tests.py

     
    12741274            'url-asvar02': ('{% url regressiontests.templates.views.index as url %}{{ url }}', {}, '/url_tag/'),
    12751275            'url-asvar03': ('{% url no_such_view as url %}{{ url }}', {}, ''),
    12761276
     1277            # Future url tag
     1278            'url-future01': ('{% load future_url %}{% url "regressiontests.templates.views.client" client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'),
     1279            'url-future02': ('{% load future_url %}{% url url_from_var client.id %}', {'client': {'id': 1}, 'url_from_var': 'regressiontests.templates.views.client'}, '/url_tag/client/1/'),
     1280
     1281            # Future url failures
     1282            'url-future-fail01': ('{% load future_url %}{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, urlresolvers.NoReverseMatch),
     1283
    12771284            ### CACHE TAG ######################################################
    12781285            'cache01': ('{% load cache %}{% cache -1 test %}cache01{% endcache %}', {}, 'cache01'),
    12791286            'cache02': ('{% load cache %}{% cache -1 test %}cache02{% endcache %}', {}, 'cache02'),
  • docs/ref/templates/builtins.txt

     
    10051005signs. Did we mention you shouldn't use this syntax in any new
    10061006projects?
    10071007
     1008.. versionchanged:: 1.3
     1009
     1010If you find yourself needing to use a context variable as the path
     1011to view, you can now do that. To do so you must load a new url tag
     1012that overwrites the previous tag.
     1013
     1014For example::
     1015
     1016    {% load future_url %}
     1017    {% url path_to_view_var arg1 arg2 %}
     1018
     1019Now if you have ``path_to_view_var`` defined in your template context
     1020it will use that value. Please note that old paths or patterns that are
     1021not context variables need to be quoted using the new url tag.
     1022
     1023For example::
     1024
     1025    {% url "path.to.view" arg1 arg2 %}
     1026
     1027The new url tag also drops support for the old style comma separated
     1028arguments.
     1029
     1030In version 1.3 the old url tag throws a ``PendingDeprecationWarning``,
     1031then is version 1.4 this will move to a ``DeprecationWarning`` and finally
     1032in version 1.5 the old tag will be removed and replaced by the new tag.
     1033
    10081034.. templatetag:: widthratio
    10091035
    10101036widthratio
Back to Top