Ticket #1105: improved_simple_tag.r7818.diff
File improved_simple_tag.r7818.diff, 11.7 KB (added by , 16 years ago) |
---|
-
django/django/template/__init__.py
792 792 else: 793 793 return force_unicode(output) 794 794 795 def generic_tag_compiler(params, defaults, name, node_class, parser, token ):795 def generic_tag_compiler(params, defaults, name, node_class, parser, token, takes_context=False, takes_nodelist=False): 796 796 "Returns a template.Node subclass." 797 797 bits = token.split_contents()[1:] 798 798 bmax = len(params) … … 804 804 else: 805 805 message = "%s takes between %s and %s arguments" % (name, bmin, bmax) 806 806 raise TemplateSyntaxError(message) 807 if takes_context: 808 node_class = curry(node_class, takes_context=takes_context) 809 if takes_nodelist: 810 nodelist = parser.parse(('end%s' % name,)) 811 parser.delete_first_token() 812 node_class = curry(node_class, nodelist=nodelist) 807 813 return node_class(bits) 808 814 809 815 class Library(object): … … 859 865 self.filters[getattr(func, "_decorated_function", func).__name__] = func 860 866 return func 861 867 862 def simple_tag(self,func): 863 params, xx, xxx, defaults = getargspec(func) 868 def simple_tag(self, compile_function=None, takes_context=None, takes_nodelist=None): 869 def dec(func): 870 params, xx, xxx, defaults = getargspec(func) 871 if takes_context and takes_nodelist: 872 if params[0] == 'context' and params[1] == 'nodelist': 873 params = params[2:] 874 else: 875 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'") 876 elif takes_nodelist: 877 if params[0] == 'nodelist': 878 params = params[1:] 879 else: 880 raise TemplateSyntaxError("Any tag function decorated with takes_nodelist=True must have a first argument of 'nodelist'") 881 elif takes_context: 882 if params[0] == 'context': 883 params = params[1:] 884 else: 885 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 864 886 865 class SimpleNode(Node): 866 def __init__(self, vars_to_resolve): 867 self.vars_to_resolve = map(Variable, vars_to_resolve) 887 class SimpleNode(Node): 888 def __init__(self, vars_to_resolve, takes_context=False, nodelist=None): 889 self.vars_to_resolve = map(Variable, vars_to_resolve) 890 self.takes_context = takes_context 891 if nodelist is not None: 892 # Only save the 'nodelist' attribute if it's not None, so that it is picked by the Node.get_nodes_by_type() method. 893 self.nodelist = nodelist 868 894 869 def render(self, context): 870 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] 871 return func(*resolved_vars) 895 def render(self, context): 896 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] 897 if self.takes_context and hasattr(self, 'nodelist'): 898 func_args = [context, self.nodelist] + resolved_vars 899 elif hasattr(self, 'nodelist'): 900 func_args = [self.nodelist] + resolved_vars 901 elif self.takes_context: 902 func_args = [context] + resolved_vars 903 else: 904 func_args = resolved_vars 905 return func(*func_args) 872 906 873 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode) 874 compile_func.__doc__ = func.__doc__ 875 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 876 return func 907 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode, takes_nodelist=takes_nodelist, takes_context=takes_context) 908 compile_func.__doc__ = func.__doc__ 909 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 910 return func 911 912 if takes_context is not None or takes_nodelist is not None: 913 # Examples: @register.simple_tag(takes_context=True) or @register.simple_tag(takes_context=True, takes_nodelist=True) 914 return dec 915 elif compile_function is None: 916 # @register.simple_tag() 917 return dec 918 elif callable(compile_function): 919 # @register.simple_tag 920 return dec(compile_function) 921 else: 922 raise TemplateSyntaxError("Incorrect parameters for the simple_tag decorator.") 877 923 878 924 def inclusion_tag(self, file_name, context_class=Context, takes_context=False): 879 925 def dec(func): -
django/tests/regressiontests/templates/tests.py
17 17 from django.utils.safestring import mark_safe 18 18 from django.utils.tzinfo import LocalTimezone 19 19 20 from decorators import DecoratorsTest 20 21 from unicode import unicode_tests 21 22 from context import context_tests 22 23 -
django/tests/regressiontests/templates/decorators.py
1 from unittest import TestCase 2 from unittest import TestCase 3 from sys import version_info 4 5 from django import template 6 7 register = template.Library() 8 9 # Very simple tag, with no parameters. 10 def a_simple_tag_without_parameters(arg): 11 """Expected __doc__""" 12 return "Expected result" 13 a_simple_tag_without_parameters.anything = "Expected __dict__" 14 15 # Tag that takes the context. 16 def a_simple_tag_with_context(context, arg): 17 """Expected __doc__""" 18 return "Expected result" 19 a_simple_tag_with_context.anything = "Expected __dict__" 20 21 # Tag that takes the inner block's node list. 22 def a_simple_tag_with_nodelist(nodelist, arg): 23 """Expected __doc__""" 24 return "Expected result" 25 a_simple_tag_with_nodelist.anything = "Expected __dict__" 26 27 # Tag that takes both the context and the inner block's node list. 28 def a_simple_tag_with_context_and_nodelist(context, nodelist, arg): 29 """Expected __doc__""" 30 return "Expected result" 31 a_simple_tag_with_context_and_nodelist.anything = "Expected __dict__" 32 33 # Tag that *wants* to take both the context and the inner block's node list, but that has arguments in wrong order. 34 def a_simple_tag_with_context_and_nodelist_wrong_order(nodelist, context, arg): 35 """Expected __doc__""" 36 return "Expected result" 37 a_simple_tag_with_context_and_nodelist_wrong_order.anything = "Expected __dict__" 38 39 40 41 42 class DecoratorsTest(TestCase): 43 def verify_decorator(self, decorator, func_name): 44 # Only check __name__ on Python 2.4 or later since __name__ can't be 45 # assigned to in earlier Python versions. 46 if version_info[0] >= 2 and version_info[1] >= 4: 47 self.assertEquals(decorator.__name__, func_name) 48 self.assertEquals(decorator.__doc__, 'Expected __doc__') 49 self.assertEquals(decorator.__dict__['anything'], 'Expected __dict__') 50 51 def test_simple_tag(self): 52 # Test that the decorators preserve the decorated function's docstring, name and attributes. 53 decorator = register.simple_tag(a_simple_tag_without_parameters) 54 self.verify_decorator(decorator, 'a_simple_tag_without_parameters') 55 56 decorator = register.simple_tag(takes_context=True)(a_simple_tag_with_context) 57 self.verify_decorator(decorator, 'a_simple_tag_with_context') 58 59 decorator = register.simple_tag(takes_nodelist=True)(a_simple_tag_with_nodelist) 60 self.verify_decorator(decorator, 'a_simple_tag_with_nodelist') 61 62 decorator = register.simple_tag(takes_context=True, takes_nodelist=True)(a_simple_tag_with_context_and_nodelist) 63 self.verify_decorator(decorator, 'a_simple_tag_with_context_and_nodelist') 64 65 # Now test that 'context' and 'nodelist' arguments and their order are correct. 66 decorator = register.simple_tag(takes_context=True) 67 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 68 69 decorator = register.simple_tag(takes_nodelist=True) 70 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 71 72 decorator = register.simple_tag(takes_nodelist=True, takes_context=True) 73 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_with_context_and_nodelist_wrong_order) -
django/docs/templates_python.txt
1181 1181 * If the argument was a template variable, our function is passed the 1182 1182 current value of the variable, not the variable itself. 1183 1183 1184 When your template tag does not need access to the current context, writing a 1185 function to work with the input values and using the ``simple_tag`` helper is 1186 the easiest way to create a new tag. 1184 If your template tag needs to access the current context, you can use the 1185 ``takes_context`` option as follows:: 1187 1186 1187 # The first argument *must* be called "context" here. 1188 def current_time(context, format_string): 1189 timezone = context['timezone'] 1190 ... 1191 1192 register.simple_tag(takes_context=True)(current_time) 1193 1194 You can also use the decorator syntax if running in Python 2.4:: 1195 1196 @register.simple_tag(takes_context=True) 1197 def current_time(context, format_string): 1198 ... 1199 1200 For more information on how the ``takes_context`` option works, see the section 1201 on `inclusion tags`_. 1202 1203 If your template is a simple block tag, you can use the ``takes_nodelist`` option as 1204 follows:: 1205 1206 # The first argument *must* be called "nodelist". 1207 def my_bock_tag(nodelist, an_argument): 1208 ... 1209 register.simple_tag(takes_nodelist=True)(my_bock_tag) 1210 1211 In the above example, ``nodelist`` is a list of all nodes between 1212 ``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}``, not counting 1213 ``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}`` themselves. 1214 1215 It is also possible to use both ``takes_context`` and ``takes_nodelist`` at the 1216 same time. For example:: 1217 1218 # The first argument *must* be called "context" and the second one "nodelist". 1219 def my_bock_tag(context, nodelist, an_argument): 1220 timezone = context['timezone'] 1221 content = nodelist.render(context) 1222 ... 1223 register.simple_tag(takes_context=True, takes_nodelist=True)(my_bock_tag) 1224 1225 If you need to create a more complex block tag, refer to the section on 1226 `parsing until another block tag`_. 1227 1228 _inclusion tags: #inclusion-tags 1229 _block tags: #parsing-until-another-block-tag 1230 1188 1231 Inclusion tags 1189 1232 ~~~~~~~~~~~~~~ 1190 1233