Ticket #14908: 14908_simple_tag_takes_context_r14922.diff

File 14908_simple_tag_takes_context_r14922.diff, 7.3 KB (added by Julien Phalip, 13 years ago)
  • django/template/base.py

     
    805805            return ''
    806806        return _render_value_in_context(output, context)
    807807
    808 def generic_tag_compiler(params, defaults, name, node_class, parser, token):
     808def generic_tag_compiler(params, defaults, name, node_class, parser, token, takes_context=False):
    809809    "Returns a template.Node subclass."
    810810    bits = token.split_contents()[1:]
    811811    bmax = len(params)
     
    817817        else:
    818818            message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
    819819        raise TemplateSyntaxError(message)
     820    if takes_context: 
     821            node_class = curry(node_class, takes_context=takes_context)
    820822    return node_class(bits)
    821823
    822824class Library(object):
     
    872874        self.filters[getattr(func, "_decorated_function", func).__name__] = func
    873875        return func
    874876
    875     def simple_tag(self,func):
    876         params, xx, xxx, defaults = getargspec(func)
     877    def simple_tag(self, compile_function=None, takes_context=None):
     878        def dec(func):
     879            params, xx, xxx, defaults = getargspec(func)
     880            if takes_context:
     881                if params[0] == 'context':
     882                    params = params[1:]
     883                else:
     884                    raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'")
     885           
     886            class SimpleNode(Node):
     887                def __init__(self, vars_to_resolve, takes_context=False):
     888                    self.vars_to_resolve = map(Variable, vars_to_resolve)
     889                    self.takes_context = takes_context
    877890
    878         class SimpleNode(Node):
    879             def __init__(self, vars_to_resolve):
    880                 self.vars_to_resolve = map(Variable, vars_to_resolve)
     891                def render(self, context):
     892                    resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
     893                    if self.takes_context:
     894                        func_args = [context] + resolved_vars
     895                    else:
     896                        func_args = resolved_vars
     897                    return func(*func_args)
     898            compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode, takes_context=takes_context)
     899            compile_func.__doc__ = func.__doc__
     900            self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
     901            return func
     902         
     903        if takes_context is not None:
     904            # Example: @register.simple_tag(takes_context=True)
     905            return dec
     906        elif compile_function is None:
     907            # @register.simple_tag()
     908            return dec
     909        elif callable(compile_function):
     910            # @register.simple_tag
     911            return dec(compile_function)
     912        else:
     913            raise TemplateSyntaxError("Incorrect parameters for the simple_tag decorator.")
    881914
    882             def render(self, context):
    883                 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
    884                 return func(*resolved_vars)
    885 
    886         compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
    887         compile_func.__doc__ = func.__doc__
    888         self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
    889         return func
    890 
    891915    def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
    892916        def dec(func):
    893917            params, xx, xxx, defaults = getargspec(func)
  • tests/regressiontests/templates/tests.py

     
    2222from django.utils.safestring import mark_safe
    2323from django.utils.tzinfo import LocalTimezone
    2424
     25from decorators import DecoratorsTest
    2526from context import ContextTests
    2627from custom import CustomTests
    2728from parser import ParserTests
  • tests/regressiontests/templates/decorators.py

     
     1from unittest import TestCase
     2from sys import version_info
     3
     4from django import template
     5
     6register = template.Library()
     7
     8# Very simple tag, with no parameters.
     9def a_simple_tag_without_parameters(arg):
     10    """Expected __doc__"""
     11    return "Expected result"
     12a_simple_tag_without_parameters.anything = "Expected __dict__"
     13
     14# Tag that takes the context.
     15def a_simple_tag_with_context(context, arg):
     16    """Expected __doc__"""
     17    return "Expected result"
     18a_simple_tag_with_context.anything = "Expected __dict__"
     19
     20class DecoratorsTest(TestCase):
     21    def verify_decorator(self, decorator, func_name):
     22        self.assertEquals(decorator.__name__, func_name)
     23        self.assertEquals(decorator.__doc__, 'Expected __doc__')
     24        self.assertEquals(decorator.__dict__['anything'], 'Expected __dict__')
     25
     26    def test_simple_tag(self):
     27        # Test that the decorators preserve the decorated function's docstring, name and attributes.
     28        decorator = register.simple_tag(a_simple_tag_without_parameters)
     29        self.verify_decorator(decorator, 'a_simple_tag_without_parameters')
     30       
     31        decorator = register.simple_tag(takes_context=False)(a_simple_tag_without_parameters)
     32        self.verify_decorator(decorator, 'a_simple_tag_without_parameters')
     33       
     34        decorator = register.simple_tag(takes_context=True)(a_simple_tag_with_context)
     35        self.verify_decorator(decorator, 'a_simple_tag_with_context')
     36       
     37        # Test that the 'context' parameter is present when takes_context is True
     38        decorator = register.simple_tag(takes_context=True)
     39        self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters)
     40
  • docs/howto/custom-template-tags.txt

     
    669669    * If the argument was a template variable, our function is passed the
    670670      current value of the variable, not the variable itself.
    671671
    672 When your template tag does not need access to the current context, writing a
    673 function to work with the input values and using the ``simple_tag`` helper is
    674 the easiest way to create a new tag.
     672If your template tag needs to access the current context, you can use the
     673``takes_context`` option as follows::
    675674
     675    # The first argument *must* be called "context" here.
     676    def current_time(context, format_string):
     677        timezone = context['timezone']
     678        ...
     679
     680    register.simple_tag(takes_context=True)(current_time)
     681
     682You may also use the decorator syntax::
     683
     684    @register.simple_tag(takes_context=True)
     685    def current_time(context, format_string):
     686        ...
     687
     688For more information on how the ``takes_context`` option works, see the section
     689on `inclusion tags`_.
     690
    676691.. _howto-custom-template-tags-inclusion-tags:
    677692
    678693Inclusion tags
Back to Top