Ticket #1105: 1105.simple_tag.diff
File 1105.simple_tag.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) 73 No newline at end of file -
django/docs/howto/custom-template-tags.txt
615 615 * If the argument was a template variable, our function is passed the 616 616 current value of the variable, not the variable itself. 617 617 618 When your template tag does not need access to the current context, writing a 619 function to work with the input values and using the ``simple_tag`` helper is 620 the easiest way to create a new tag. 618 If your template tag needs to access the current context, you can use the 619 ``takes_context`` option as follows:: 621 620 621 # The first argument *must* be called "context" here. 622 def current_time(context, format_string): 623 timezone = context['timezone'] 624 ... 625 626 register.simple_tag(takes_context=True)(current_time) 627 628 You can also use the decorator syntax if running in Python 2.4:: 629 630 @register.simple_tag(takes_context=True) 631 def current_time(context, format_string): 632 ... 633 634 For more information on how the ``takes_context`` option works, see the section 635 on `inclusion tags`_. 636 637 To create a simple block tag, you can use the ``takes_nodelist`` option as 638 follows:: 639 640 # The first argument *must* be called "nodelist" here. 641 def my_bock_tag(nodelist, an_argument): 642 ... 643 register.simple_tag(takes_nodelist=True)(my_bock_tag) 644 645 In the above example, ``nodelist`` is a list of all nodes between 646 ``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}``, not counting 647 ``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}`` themselves. 648 649 It is also possible to use both ``takes_context`` and ``takes_nodelist`` at the 650 same time. For example:: 651 652 # The first argument *must* be called "context" and the second one "nodelist". 653 def my_bock_tag(context, nodelist, an_argument): 654 timezone = context['timezone'] 655 content = nodelist.render(context) 656 ... 657 register.simple_tag(takes_context=True, takes_nodelist=True)(my_bock_tag) 658 659 If you need to create a more complex block tag, refer to the section on 660 `parsing until another block tag`_. 661 662 _inclusion tags: #inclusion-tags 663 _parsing until another block tag: #parsing-until-another-block-tag 664 622 665 Inclusion tags 623 666 ~~~~~~~~~~~~~~ 624 667