Ticket #14262: 14262.assignment_tag.2.diff
File 14262.assignment_tag.2.diff, 13.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 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 e86e070..41da2b5 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__"