Ticket #14262: 14262.assignment_tag.3.diff
File 14262.assignment_tag.3.diff, 12.3 KB (added by , 14 years ago) |
---|
-
django/template/base.py
diff --git a/django/template/base.py b/django/template/base.py index b6470f3..37b6fab 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 362d358..16f25ae 100644
a b Or, using decorator syntax:: 681 681 return your_get_current_time_method(timezone, format_string) 682 682 683 683 For more information on how the ``takes_context`` option works, see the section 684 on `inclusion tags`_. 684 on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`. 685 686 .. _howto-custom-template-tags-assignment-tags: 687 688 Assignment tags 689 ~~~~~~~~~~~~~~~ 690 691 .. versionadded:: 1.4 692 693 Another common type of template tag is the type that fetches some data and 694 stores it in a context variable. To ease the creation of this type of tags, 695 Django provides a helper function, ``assignment_tag``. This function works 696 the same way as :ref:`simple_tag<howto-custom-template-tags-simple-tags>`, 697 except that it stores the tag's result in a specified context variable instead 698 of directly outputting it. 699 700 Our earlier ``current_time`` function could thus be written like this: 701 702 .. code-block:: python 703 704 def get_current_time(format_string): 705 return datetime.datetime.now().strftime(format_string) 706 707 register.assignment_tag(get_current_time) 708 709 The decorator syntax also works: 710 711 .. code-block:: python 712 713 @register.assignment_tag 714 def get_current_time(format_string): 715 ... 716 717 You may then store the result in a template variable using the ``as`` argument 718 followed by the variable name, and output it yourself where you see fit: 719 720 .. code-block:: html+django 721 722 {% get_current_time "%Y-%m-%d %I:%M %p" as the_time %} 723 <p>The time is {{ the_time }}.</p> 724 725 If your template tag needs to access the current context, you can use the 726 ``takes_context`` argument when registering your tag: 727 728 .. code-block:: python 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 .. code-block:: python 740 741 @register.assignment_tag(takes_context=True) 742 def get_current_time(context, format_string): 743 timezone = context['timezone'] 744 return your_get_current_time_method(timezone, format_string) 745 746 For more information on how the ``takes_context`` option works, see the section 747 on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`. 685 748 686 749 .. _howto-custom-template-tags-inclusion-tags: 687 750 -
docs/releases/1.4.txt
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index 617c3ad..aea9c75 100644
a b override the provided templates to change the doctype. 43 43 A lazily evaluated version of :func:`django.core.urlresolvers.reverse` was 44 44 added to allow using URL reversals before the project's URLConf gets loaded. 45 45 46 Assignment template tags 47 ~~~~~~~~~~~~~~~~~~~~~~~~ 48 49 A new helper function, 50 :ref:`assignment_tag<howto-custom-template-tags-assignment-tags>`, was added to 51 ``template.Library`` to ease the creation of template tags that store some 52 data in a specified context variable. 53 46 54 .. _backwards-incompatible-changes-1.4: 47 55 48 56 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 7ca359d..5453657 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): 102 104 103 105 c.use_l10n = True 104 106 self.assertEquals(t.render(c).strip(), u'True') 107 108 def test_assignment_tags(self): 109 c = template.Context({'value': 42}) 110 111 t = template.Template('{% load custom %}{% assignment_no_params as var %}The result is: {{ var }}') 112 self.assertEqual(t.render(c), u'The result is: assignment_no_params - Expected result') 113 114 t = template.Template('{% load custom %}{% assignment_one_param 37 as var %}The result is: {{ var }}') 115 self.assertEqual(t.render(c), u'The result is: assignment_one_param - Expected result: 37') 116 117 t = template.Template('{% load custom %}{% assignment_explicit_no_context 37 as var %}The result is: {{ var }}') 118 self.assertEqual(t.render(c), u'The result is: assignment_explicit_no_context - Expected result: 37') 119 120 t = template.Template('{% load custom %}{% assignment_no_params_with_context as var %}The result is: {{ var }}') 121 self.assertEqual(t.render(c), u'The result is: assignment_no_params_with_context - Expected result (context value: 42)') 122 123 t = template.Template('{% load custom %}{% assignment_params_and_context 37 as var %}The result is: {{ var }}') 124 self.assertEqual(t.render(c), u'The result is: assignment_params_and_context - Expected result (context value: 42): 37') 125 126 with self.assertRaises(template.TemplateSyntaxError) as context_manager: 127 template.Template('{% load custom %}{% assignment_one_param 37 %}The result is: {{ var }}') 128 self.assertEqual(context_manager.exception.message, "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'") 129 130 with self.assertRaises(template.TemplateSyntaxError) as context_manager: 131 template.Template('{% load custom %}{% assignment_one_param 37 as %}The result is: {{ var }}') 132 self.assertEqual(context_manager.exception.message, "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'") 133 134 with self.assertRaises(template.TemplateSyntaxError) as context_manager: 135 template.Template('{% load custom %}{% assignment_one_param 37 ass var %}The result is: {{ var }}') 136 self.assertEqual(context_manager.exception.message, "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'") 137 138 def test_assignment_tag_registration(self): 139 # Test that the decorators preserve the decorated function's docstring, name and attributes. 140 self.verify_tag(custom.assignment_no_params, 'assignment_no_params') 141 self.verify_tag(custom.assignment_one_param, 'assignment_one_param') 142 self.verify_tag(custom.assignment_explicit_no_context, 'assignment_explicit_no_context') 143 self.verify_tag(custom.assignment_no_params_with_context, 'assignment_no_params_with_context') 144 self.verify_tag(custom.assignment_params_and_context, 'assignment_params_and_context') 145 146 def test_assignment_tag_missing_context(self): 147 # That the 'context' parameter must be present when takes_context is True 148 def an_assignment_tag_without_parameters(arg): 149 """Expected __doc__""" 150 return "Expected result" 151 152 register = template.Library() 153 decorator = register.assignment_tag(takes_context=True) 154 155 with self.assertRaises(template.TemplateSyntaxError) as context_manager: 156 decorator(an_assignment_tag_without_parameters) 157 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 8db113a..2e281f7 100644
a b def use_l10n(context): 84 84 @register.inclusion_tag('test_incl_tag_use_l10n.html', takes_context=True) 85 85 def inclusion_tag_use_l10n(context): 86 86 return {} 87 88 @register.assignment_tag 89 def assignment_no_params(): 90 """Expected assignment_no_params __doc__""" 91 return "assignment_no_params - Expected result" 92 assignment_no_params.anything = "Expected assignment_no_params __dict__" 93 94 @register.assignment_tag 95 def assignment_one_param(arg): 96 """Expected assignment_one_param __doc__""" 97 return "assignment_one_param - Expected result: %s" % arg 98 assignment_one_param.anything = "Expected assignment_one_param __dict__" 99 100 @register.assignment_tag(takes_context=False) 101 def assignment_explicit_no_context(arg): 102 """Expected assignment_explicit_no_context __doc__""" 103 return "assignment_explicit_no_context - Expected result: %s" % arg 104 assignment_explicit_no_context.anything = "Expected assignment_explicit_no_context __dict__" 105 106 @register.assignment_tag(takes_context=True) 107 def assignment_no_params_with_context(context): 108 """Expected assignment_no_params_with_context __doc__""" 109 return "assignment_no_params_with_context - Expected result (context value: %s)" % context['value'] 110 assignment_no_params_with_context.anything = "Expected assignment_no_params_with_context __dict__" 111 112 @register.assignment_tag(takes_context=True) 113 def assignment_params_and_context(context, arg): 114 """Expected assignment_params_and_context __doc__""" 115 return "assignment_params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) 116 assignment_params_and_context.anything = "Expected assignment_params_and_context __dict__"