Ticket #1105: 1105.simple_tag.r9050.diff
File 1105.simple_tag.r9050.diff, 11.8 KB (added by , 16 years ago) |
---|
-
django/django/template/__init__.py
809 809 else: 810 810 return force_unicode(output) 811 811 812 def generic_tag_compiler(params, defaults, name, node_class, parser, token ):812 def generic_tag_compiler(params, defaults, name, node_class, parser, token, takes_context=False, takes_nodelist=False): 813 813 "Returns a template.Node subclass." 814 814 bits = token.split_contents()[1:] 815 815 bmax = len(params) … … 821 821 else: 822 822 message = "%s takes between %s and %s arguments" % (name, bmin, bmax) 823 823 raise TemplateSyntaxError(message) 824 if takes_context: 825 node_class = curry(node_class, takes_context=takes_context) 826 if takes_nodelist: 827 nodelist = parser.parse(('end%s' % name,)) 828 parser.delete_first_token() 829 node_class = curry(node_class, nodelist=nodelist) 824 830 return node_class(bits) 825 831 826 832 class Library(object): … … 876 882 self.filters[getattr(func, "_decorated_function", func).__name__] = func 877 883 return func 878 884 879 def simple_tag(self,func): 880 params, xx, xxx, defaults = getargspec(func) 885 def simple_tag(self, compile_function=None, takes_context=None, takes_nodelist=None): 886 def dec(func): 887 params, xx, xxx, defaults = getargspec(func) 888 if takes_context and takes_nodelist: 889 if params[0] == 'context' and params[1] == 'nodelist': 890 params = params[2:] 891 else: 892 raise TemplateSyntaxError("Any tag function decorated both with takes_context=True and with takes_nodelist=True must have a first argument of 'context', and a second argument of 'nodelist'") 893 elif takes_nodelist: 894 if params[0] == 'nodelist': 895 params = params[1:] 896 else: 897 raise TemplateSyntaxError("Any tag function decorated with takes_nodelist=True must have a first argument of 'nodelist'") 898 elif takes_context: 899 if params[0] == 'context': 900 params = params[1:] 901 else: 902 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 903 class SimpleNode(Node): 904 def __init__(self, vars_to_resolve, takes_context=False, nodelist=None): 905 self.vars_to_resolve = map(Variable, vars_to_resolve) 906 self.takes_context = takes_context 907 if nodelist is not None: 908 # Only save the 'nodelist' attribute if it's not None, so that it is picked by the Node.get_nodes_by_type() method. 909 self.nodelist = nodelist 881 910 882 class SimpleNode(Node): 883 def __init__(self, vars_to_resolve): 884 self.vars_to_resolve = map(Variable, vars_to_resolve) 911 def render(self, context): 912 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] 913 if self.takes_context and hasattr(self, 'nodelist'): 914 func_args = [context, self.nodelist] + resolved_vars 915 elif hasattr(self, 'nodelist'): 916 func_args = [self.nodelist] + resolved_vars 917 elif self.takes_context: 918 func_args = [context] + resolved_vars 919 else: 920 func_args = resolved_vars 921 return func(*func_args) 922 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode, takes_nodelist=takes_nodelist, takes_context=takes_context) 923 compile_func.__doc__ = func.__doc__ 924 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 925 return func 926 927 if takes_context is not None or takes_nodelist is not None: 928 # Examples: @register.simple_tag(takes_context=True) or @register.simple_tag(takes_context=True, takes_nodelist=True) 929 return dec 930 elif compile_function is None: 931 # @register.simple_tag() 932 return dec 933 elif callable(compile_function): 934 # @register.simple_tag 935 return dec(compile_function) 936 else: 937 raise TemplateSyntaxError("Incorrect parameters for the simple_tag decorator.") 885 938 886 def render(self, context):887 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]888 return func(*resolved_vars)889 890 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)891 compile_func.__doc__ = func.__doc__892 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)893 return func894 895 939 def inclusion_tag(self, file_name, context_class=Context, takes_context=False): 896 940 def dec(func): 897 941 params, xx, xxx, defaults = getargspec(func) -
django/tests/regressiontests/templates/tests.py
18 18 from django.utils.safestring import mark_safe 19 19 from django.utils.tzinfo import LocalTimezone 20 20 21 from decorators import DecoratorsTest 21 22 from unicode import unicode_tests 22 23 from context import context_tests 23 24 -
django/tests/regressiontests/templates/decorators.py
1 from unittest import TestCase 2 from sys import version_info 3 4 from django import template 5 6 register = template.Library() 7 8 # Very simple tag, with no parameters. 9 def a_simple_tag_without_parameters(arg): 10 """Expected __doc__""" 11 return "Expected result" 12 a_simple_tag_without_parameters.anything = "Expected __dict__" 13 14 # Tag that takes the context. 15 def a_simple_tag_with_context(context, arg): 16 """Expected __doc__""" 17 return "Expected result" 18 a_simple_tag_with_context.anything = "Expected __dict__" 19 20 # Tag that takes the inner block's node list. 21 def a_simple_tag_with_nodelist(nodelist, arg): 22 """Expected __doc__""" 23 return "Expected result" 24 a_simple_tag_with_nodelist.anything = "Expected __dict__" 25 26 # Tag that takes both the context and the inner block's node list. 27 def a_simple_tag_with_context_and_nodelist(context, nodelist, arg): 28 """Expected __doc__""" 29 return "Expected result" 30 a_simple_tag_with_context_and_nodelist.anything = "Expected __dict__" 31 32 # Tag that *wants* to take both the context and the inner block's node list, but that has arguments in wrong order. 33 def a_simple_tag_with_context_and_nodelist_wrong_order(nodelist, context, arg): 34 """Expected __doc__""" 35 return "Expected result" 36 a_simple_tag_with_context_and_nodelist_wrong_order.anything = "Expected __dict__" 37 38 39 40 41 class DecoratorsTest(TestCase): 42 def verify_decorator(self, decorator, func_name): 43 # Only check __name__ on Python 2.4 or later since __name__ can't be 44 # assigned to in earlier Python versions. 45 if version_info[0] >= 2 and version_info[1] >= 4: 46 self.assertEquals(decorator.__name__, func_name) 47 self.assertEquals(decorator.__doc__, 'Expected __doc__') 48 self.assertEquals(decorator.__dict__['anything'], 'Expected __dict__') 49 50 def test_simple_tag(self): 51 # Test that the decorators preserve the decorated function's docstring, name and attributes. 52 decorator = register.simple_tag(a_simple_tag_without_parameters) 53 self.verify_decorator(decorator, 'a_simple_tag_without_parameters') 54 55 decorator = register.simple_tag(takes_context=True)(a_simple_tag_with_context) 56 self.verify_decorator(decorator, 'a_simple_tag_with_context') 57 58 decorator = register.simple_tag(takes_nodelist=True)(a_simple_tag_with_nodelist) 59 self.verify_decorator(decorator, 'a_simple_tag_with_nodelist') 60 61 decorator = register.simple_tag(takes_context=True, takes_nodelist=True)(a_simple_tag_with_context_and_nodelist) 62 self.verify_decorator(decorator, 'a_simple_tag_with_context_and_nodelist') 63 64 # Now test that 'context' and 'nodelist' arguments and their order are correct. 65 decorator = register.simple_tag(takes_context=True) 66 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 67 68 decorator = register.simple_tag(takes_nodelist=True) 69 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 70 71 decorator = register.simple_tag(takes_nodelist=True, takes_context=True) 72 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_with_context_and_nodelist_wrong_order) -
django/docs/howto/custom-template-tags.txt
587 587 * If the argument was a template variable, our function is passed the 588 588 current value of the variable, not the variable itself. 589 589 590 When your template tag does not need access to the current context, writing a 591 function to work with the input values and using the ``simple_tag`` helper is 592 the easiest way to create a new tag. 590 If your template tag needs to access the current context, you can use the 591 ``takes_context`` option as follows:: 593 592 593 # The first argument *must* be called "context" here. 594 def current_time(context, format_string): 595 timezone = context['timezone'] 596 ... 597 598 register.simple_tag(takes_context=True)(current_time) 599 600 You can also use the decorator syntax if running in Python 2.4:: 601 602 @register.simple_tag(takes_context=True) 603 def current_time(context, format_string): 604 ... 605 606 For more information on how the ``takes_context`` option works, see the section 607 on `inclusion tags`_. 608 609 To create a simple block tag, you can use the ``takes_nodelist`` option as 610 follows:: 611 612 # The first argument *must* be called "nodelist" here. 613 def my_bock_tag(nodelist, an_argument): 614 ... 615 register.simple_tag(takes_nodelist=True)(my_bock_tag) 616 617 In the above example, ``nodelist`` is a list of all nodes between 618 ``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}``, not counting 619 ``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}`` themselves. 620 621 It is also possible to use both ``takes_context`` and ``takes_nodelist`` at the 622 same time. For example:: 623 624 # The first argument *must* be called "context" and the second one "nodelist". 625 def my_bock_tag(context, nodelist, an_argument): 626 timezone = context['timezone'] 627 content = nodelist.render(context) 628 ... 629 register.simple_tag(takes_context=True, takes_nodelist=True)(my_bock_tag) 630 631 If you need to create a more complex block tag, refer to the section on 632 `parsing until another block tag`_. 633 634 _inclusion tags: #inclusion-tags 635 _parsing until another block tag: #parsing-until-another-block-tag 636 594 637 .. _howto-custom-template-tags-inclusion-tags: 595 638 596 639 Inclusion tags