Ticket #14262: 14262.assignment_tag.diff
File 14262.assignment_tag.diff, 13.6 KB (added by , 14 years ago) |
---|
-
django/template/base.py
diff --git a/django/template/base.py b/django/template/base.py index 08ff5c6..ed1ae86 100644
a b class Library(object): 901 901 else: 902 902 raise TemplateSyntaxError("Invalid arguments provided to simple_tag") 903 903 904 def assignment_tag(self, func=None, takes_context=None): 905 def dec(func): 906 params, xx, xxx, defaults = getargspec(func) 907 if takes_context: 908 if params[0] == 'context': 909 params = params[1:] 910 else: 911 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 912 913 class AssignmentNode(Node): 914 def __init__(self, params_vars, target_var): 915 self.params_vars = map(Variable, params_vars) 916 self.target_var = target_var 917 918 def render(self, context): 919 resolved_vars = [var.resolve(context) for var in self.params_vars] 920 if takes_context: 921 func_args = [context] + resolved_vars 922 else: 923 func_args = resolved_vars 924 context[self.target_var] = func(*func_args) 925 return '' 926 927 def compile_func(parser, token): 928 bits = token.split_contents() 929 tag_name = bits[0] 930 bits = bits[1:] 931 params_max = len(params) 932 defaults_length = defaults and len(defaults) or 0 933 params_min = params_max - defaults_length 934 if (len(bits) < 2 or bits[-2] != 'as'): 935 raise TemplateSyntaxError("'%s' tag takes at least 2 arguments and the second last argument must be 'as'" % tag_name) 936 params_vars = bits[:-2] 937 target_var = bits[-1] 938 if (len(params_vars) < params_min or len(params_vars) > params_max): 939 if params_min == params_max: 940 raise TemplateSyntaxError("%s takes %s arguments" % (tag_name, params_min)) 941 else: 942 raise TemplateSyntaxError("%s takes between %s and %s arguments" % (tag_name, params_min, params_max)) 943 return AssignmentNode(params_vars, target_var) 944 945 compile_func.__doc__ = func.__doc__ 946 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 947 return func 948 949 if func is None: 950 # @register.assignment_tag(...) 951 return dec 952 elif callable(func): 953 # @register.assignment_tag 954 return dec(func) 955 else: 956 raise TemplateSyntaxError("Invalid arguments provided to assignment_tag") 957 904 958 def inclusion_tag(self, file_name, context_class=Context, takes_context=False): 905 959 def dec(func): 906 960 params, xx, xxx, defaults = getargspec(func) -
docs/howto/custom-template-tags.txt
diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt index 7dc6cce..770e823 100644
a b for example:: 624 624 Variable resolution will throw a ``VariableDoesNotExist`` exception if it cannot 625 625 resolve the string passed to it in the current context of the page. 626 626 627 .. _howto-custom-template-tags-simple-tags: 628 627 629 Shortcut for simple tags 628 630 ~~~~~~~~~~~~~~~~~~~~~~~~ 629 631 630 Many template tags take a number of arguments -- strings or atemplate variables632 Many template tags take a number of arguments -- strings or template variables 631 633 -- and return a string after doing some processing based solely on 632 634 the input argument and some external information. For example, the 633 635 ``current_time`` tag we wrote above is of this variety: we give it a format 634 636 string, it returns the time as a string. 635 637 636 To ease the creation of th e typesof tags, Django provides a helper function,638 To ease the creation of this type of tags, Django provides a helper function, 637 639 ``simple_tag``. This function, which is a method of 638 640 ``django.template.Library``, takes a function that accepts any number of 639 641 arguments, wraps it in a ``render`` function and the other necessary bits … … Or, using decorator syntax:: 681 683 return your_get_current_time_method(timezone, format_string) 682 684 683 685 For more information on how the ``takes_context`` option works, see the section 684 on `inclusion tags`_. 686 on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`. 687 688 .. _howto-custom-template-tags-assignment-tags: 689 690 Assignment tags 691 ~~~~~~~~~~~~~~~ 692 693 .. versionadded:: 1.4 694 695 Another common type of template tag is the type that fetches some data and 696 stores it in a context variable. To ease the creation of this type of tags, 697 Django provides a helper function, ``assignment_tag``. This function works 698 the same way as :ref:`simple_tag<howto-custom-template-tags-simple-tags>`, 699 except that it stores the tag's result in a specified context variable instead 700 of directly outputting it. 701 702 Our earlier ``current_time`` function could thus be written like this:: 703 704 .. code-block:: python 705 706 def get_current_time(format_string): 707 return datetime.datetime.now().strftime(format_string) 708 709 register.assignment_tag(get_current_time) 710 711 The decorator syntax also works:: 712 713 .. code-block:: python 714 715 @register.assignment_tag 716 def get_current_time(format_string): 717 ... 718 719 You may then store the result in a template variable using the ``as`` argument 720 followed by the variable name, and output it yourself where you see fit:: 721 722 .. code-block:: html+django 723 724 {% get_current_time "%Y-%m-%d %I:%M %p" as the_time %} 725 <p>The time is {{ the_time }}.</p> 726 727 If your template tag needs to access the current context, you can use the 728 ``takes_context`` argument when registering your tag:: 729 730 # The first argument *must* be called "context" here. 731 def get_current_time(context, format_string): 732 timezone = context['timezone'] 733 return your_get_current_time_method(timezone, format_string) 734 735 register.assignment_tag(takes_context=True)(get_current_time) 736 737 Or, using decorator syntax:: 738 739 @register.assignment_tag(takes_context=True) 740 def get_current_time(context, format_string): 741 timezone = context['timezone'] 742 return your_get_current_time_method(timezone, format_string) 743 744 For more information on how the ``takes_context`` option works, see the section 745 on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`. 685 746 686 747 .. _howto-custom-template-tags-inclusion-tags: 687 748 -
docs/releases/1.4.txt
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index a63fff7..162b493 100644
a b compatibility with old browsers, this change means that you can use any HTML5 37 37 features you need in admin pages without having to lose HTML validity or 38 38 override the provided templates to change the doctype. 39 39 40 Assignment template tags 41 ~~~~~~~~~~~~~~~~~~~~~~~~ 42 43 A new helper function, 44 :ref:`assignment_tag<howto-custom-template-tags-assignment-tags>`, was added to 45 ``template.Library`` to ease the creation of template tags that store some 46 data in a specified context variable. 47 40 48 .. _backwards-incompatible-changes-1.4: 41 49 42 50 Backwards incompatible changes in 1.4 -
tests/regressiontests/templates/custom.py
diff --git a/tests/regressiontests/templates/custom.py b/tests/regressiontests/templates/custom.py index fe5b095..6e7f842 100644
a b 1 from __future__ import with_statement 2 1 3 from django import template 2 4 from django.utils.unittest import TestCase 3 5 from templatetags import custom … … class CustomTagTests(TestCase): 78 80 self.verify_tag(custom.inclusion_explicit_no_context, 'inclusion_explicit_no_context') 79 81 self.verify_tag(custom.inclusion_no_params_with_context, 'inclusion_no_params_with_context') 80 82 self.verify_tag(custom.inclusion_params_and_context, 'inclusion_params_and_context') 83 84 def test_assignment_tags(self): 85 c = template.Context({'value': 42}) 86 87 t = template.Template('{% load custom %}{% assignment_no_params as var %}The result is: {{ var }}') 88 self.assertEqual(t.render(c), u'The result is: assignment_no_params - Expected result') 89 90 t = template.Template('{% load custom %}{% assignment_one_param 37 as var %}The result is: {{ var }}') 91 self.assertEqual(t.render(c), u'The result is: assignment_one_param - Expected result: 37') 92 93 t = template.Template('{% load custom %}{% assignment_explicit_no_context 37 as var %}The result is: {{ var }}') 94 self.assertEqual(t.render(c), u'The result is: assignment_explicit_no_context - Expected result: 37') 95 96 t = template.Template('{% load custom %}{% assignment_no_params_with_context as var %}The result is: {{ var }}') 97 self.assertEqual(t.render(c), u'The result is: assignment_no_params_with_context - Expected result (context value: 42)') 98 99 t = template.Template('{% load custom %}{% assignment_params_and_context 37 as var %}The result is: {{ var }}') 100 self.assertEqual(t.render(c), u'The result is: assignment_params_and_context - Expected result (context value: 42): 37') 101 102 with self.assertRaises(template.TemplateSyntaxError) as context_manager: 103 template.Template('{% load custom %}{% assignment_one_param 37 %}The result is: {{ var }}') 104 self.assertEqual(context_manager.exception.message, "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'") 105 106 with self.assertRaises(template.TemplateSyntaxError) as context_manager: 107 template.Template('{% load custom %}{% assignment_one_param 37 as %}The result is: {{ var }}') 108 self.assertEqual(context_manager.exception.message, "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'") 109 110 with self.assertRaises(template.TemplateSyntaxError) as context_manager: 111 template.Template('{% load custom %}{% assignment_one_param 37 ass var %}The result is: {{ var }}') 112 self.assertEqual(context_manager.exception.message, "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'") 113 114 def test_assignment_tag_registration(self): 115 # Test that the decorators preserve the decorated function's docstring, name and attributes. 116 self.verify_tag(custom.assignment_no_params, 'assignment_no_params') 117 self.verify_tag(custom.assignment_one_param, 'assignment_one_param') 118 self.verify_tag(custom.assignment_explicit_no_context, 'assignment_explicit_no_context') 119 self.verify_tag(custom.assignment_no_params_with_context, 'assignment_no_params_with_context') 120 self.verify_tag(custom.assignment_params_and_context, 'assignment_params_and_context') 121 122 def test_assignment_tag_missing_context(self): 123 # That the 'context' parameter must be present when takes_context is True 124 def an_assignment_tag_without_parameters(arg): 125 """Expected __doc__""" 126 return "Expected result" 127 128 register = template.Library() 129 decorator = register.assignment_tag(takes_context=True) 130 131 with self.assertRaises(template.TemplateSyntaxError) as context_manager: 132 decorator(an_assignment_tag_without_parameters) 133 self.assertEqual(context_manager.exception.message, "Any tag function decorated with takes_context=True must have a first argument of 'context'") -
tests/regressiontests/templates/templatetags/custom.py
diff --git a/tests/regressiontests/templates/templatetags/custom.py b/tests/regressiontests/templates/templatetags/custom.py index b2e8a16..f27890e 100644
a b def inclusion_params_and_context(context, arg): 69 69 return {"result" : "inclusion_params_and_context - Expected result (context value: %s): %s" % (context['value'], arg)} 70 70 inclusion_params_and_context.anything = "Expected inclusion_params_and_context __dict__" 71 71 72 @register.assignment_tag 73 def assignment_no_params(): 74 """Expected assignment_no_params __doc__""" 75 return "assignment_no_params - Expected result" 76 assignment_no_params.anything = "Expected assignment_no_params __dict__" 77 78 @register.assignment_tag 79 def assignment_one_param(arg): 80 """Expected assignment_one_param __doc__""" 81 return "assignment_one_param - Expected result: %s" % arg 82 assignment_one_param.anything = "Expected assignment_one_param __dict__" 83 84 @register.assignment_tag(takes_context=False) 85 def assignment_explicit_no_context(arg): 86 """Expected assignment_explicit_no_context __doc__""" 87 return "assignment_explicit_no_context - Expected result: %s" % arg 88 assignment_explicit_no_context.anything = "Expected assignment_explicit_no_context __dict__" 89 90 @register.assignment_tag(takes_context=True) 91 def assignment_no_params_with_context(context): 92 """Expected assignment_no_params_with_context __doc__""" 93 return "assignment_no_params_with_context - Expected result (context value: %s)" % context['value'] 94 assignment_no_params_with_context.anything = "Expected assignment_no_params_with_context __dict__" 95 96 @register.assignment_tag(takes_context=True) 97 def assignment_params_and_context(context, arg): 98 """Expected assignment_params_and_context __doc__""" 99 return "assignment_params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) 100 assignment_params_and_context.anything = "Expected assignment_params_and_context __dict__"