Loading django/template/base.py +30 −49 Original line number Diff line number Diff line Loading @@ -61,7 +61,8 @@ from django.apps import apps from django.template.context import (BaseContext, Context, RequestContext, # NOQA: imported for backwards compatibility ContextPopException) from django.utils import lru_cache from django.utils.deprecation import RemovedInDjango20Warning from django.utils.deprecation import (RemovedInDjango20Warning, RemovedInDjango21Warning) from django.utils.itercompat import is_iterable from django.utils.text import (smart_split, unescape_string_literal, get_text_list) Loading Loading @@ -1021,9 +1022,9 @@ def token_kwargs(bits, parser, support_legacy=False): def parse_bits(parser, bits, params, varargs, varkw, defaults, takes_context, name): """ Parses bits for template tag helpers (simple_tag, include_tag and assignment_tag), in particular by detecting syntax errors and by extracting positional and keyword arguments. Parses bits for template tag helpers simple_tag and inclusion_tag, in particular by detecting syntax errors and by extracting positional and keyword arguments. """ if takes_context: if params[0] == 'context': Loading Loading @@ -1099,9 +1100,9 @@ def generic_tag_compiler(parser, token, params, varargs, varkw, defaults, class TagHelperNode(Node): """ Base class for tag helper nodes such as SimpleNode, InclusionNode and AssignmentNode. Manages the positional and keyword arguments to be passed to the decorated function. Base class for tag helper nodes such as SimpleNode and InclusionNode. Manages the positional and keyword arguments to be passed to the decorated function. """ def __init__(self, takes_context, args, kwargs): Loading Loading @@ -1191,71 +1192,51 @@ class Library(object): params, varargs, varkw, defaults = getargspec(func) class SimpleNode(TagHelperNode): def render(self, context): resolved_args, resolved_kwargs = self.get_resolved_arguments(context) return func(*resolved_args, **resolved_kwargs) function_name = (name or getattr(func, '_decorated_function', func).__name__) compile_func = partial(generic_tag_compiler, params=params, varargs=varargs, varkw=varkw, defaults=defaults, name=function_name, takes_context=takes_context, node_class=SimpleNode) compile_func.__doc__ = func.__doc__ self.tag(function_name, compile_func) return func if func is None: # @register.simple_tag(...) return dec elif callable(func): # @register.simple_tag return dec(func) else: raise TemplateSyntaxError("Invalid arguments provided to simple_tag") def assignment_tag(self, func=None, takes_context=None, name=None): def dec(func): params, varargs, varkw, defaults = getargspec(func) class AssignmentNode(TagHelperNode): def __init__(self, takes_context, args, kwargs, target_var): super(AssignmentNode, self).__init__(takes_context, args, kwargs) super(SimpleNode, self).__init__(takes_context, args, kwargs) self.target_var = target_var def render(self, context): resolved_args, resolved_kwargs = self.get_resolved_arguments(context) context[self.target_var] = func(*resolved_args, **resolved_kwargs) output = func(*resolved_args, **resolved_kwargs) if self.target_var is not None: context[self.target_var] = output return '' return output function_name = (name or getattr(func, '_decorated_function', func).__name__) def compile_func(parser, token): bits = token.split_contents()[1:] if len(bits) < 2 or bits[-2] != 'as': raise TemplateSyntaxError( "'%s' tag takes at least 2 arguments and the " "second last argument must be 'as'" % function_name) target_var = None if len(bits) >= 2 and bits[-2] == 'as': target_var = bits[-1] bits = bits[:-2] args, kwargs = parse_bits(parser, bits, params, varargs, varkw, defaults, takes_context, function_name) return AssignmentNode(takes_context, args, kwargs, target_var) return SimpleNode(takes_context, args, kwargs, target_var) compile_func.__doc__ = func.__doc__ self.tag(function_name, compile_func) return func if func is None: # @register.assignment_tag(...) # @register.simple_tag(...) return dec elif callable(func): # @register.assignment_tag # @register.simple_tag return dec(func) else: raise TemplateSyntaxError("Invalid arguments provided to assignment_tag") raise TemplateSyntaxError("Invalid arguments provided to simple_tag") def assignment_tag(self, func=None, takes_context=None, name=None): warnings.warn( "assignment_tag() is deprecated. Use simple_tag() instead", RemovedInDjango21Warning, stacklevel=2, ) return self.simple_tag(func, takes_context, name) def inclusion_tag(self, file_name, takes_context=False, name=None): def dec(func): Loading docs/howto/custom-template-tags.txt +20 −36 Original line number Diff line number Diff line Loading @@ -388,7 +388,7 @@ Simple tags .. method:: django.template.Library.simple_tag() Many template tags take a number of arguments -- strings or template variables -- and return a result after doing some processing based solely on the input arguments and some external information. For example, a ``current_time`` tag might accept a format string and return the time as a string formatted accordingly. Loading Loading @@ -459,6 +459,18 @@ positional arguments. For example: {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %} .. versionadded:: 1.9 It's possible to store the tag results in a template variable rather than directly outputting it. This is done by using the ``as`` argument followed by the variable name. Doing so enables you to output the content yourself where you see fit: .. code-block:: html+django {% get_current_time "%Y-%m-%d %I:%M %p" as the_time %} <p>The time is {{ the_time }}.</p> .. _howto-custom-template-tags-inclusion-tags: Inclusion tags Loading Loading @@ -602,11 +614,15 @@ Assignment tags .. method:: django.template.Library.assignment_tag() .. deprecated:: 1.9 ``simple_tag`` can now store results in a template variable and should be used instead. To ease the creation of tags setting a variable in the context, Django provides a helper function, ``assignment_tag``. This function works the same way as :ref:`simple_tag<howto-custom-template-tags-simple-tags>`, except that it stores the tag's result in a specified context variable instead of directly outputting it. :meth:`~django.template.Library.simple_tag` except that it stores the tag's result in a specified context variable instead of directly outputting it. Our earlier ``current_time`` function could thus be written like this:: Loading @@ -622,38 +638,6 @@ followed by the variable name, and output it yourself where you see fit: {% get_current_time "%Y-%m-%d %I:%M %p" as the_time %} <p>The time is {{ the_time }}.</p> If your template tag needs to access the current context, you can use the ``takes_context`` argument when registering your tag:: @register.assignment_tag(takes_context=True) def get_current_time(context, format_string): timezone = context['timezone'] return your_get_current_time_method(timezone, format_string) Note that the first parameter to the function *must* be called ``context``. For more information on how the ``takes_context`` option works, see the section on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`. ``assignment_tag`` functions may accept any number of positional or keyword arguments. For example:: @register.assignment_tag def my_tag(a, b, *args, **kwargs): warning = kwargs['warning'] profile = kwargs['profile'] ... return ... Then in the template any number of arguments, separated by spaces, may be passed to the template tag. Like in Python, the values for keyword arguments are set using the equal sign ("``=``") and must be provided after the positional arguments. For example: .. code-block:: html+django {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile as the_result %} Advanced custom template tags ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Loading docs/internals/deprecation.txt +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ details on these changes. * The ``django.forms.extras`` package will be removed. * The ``assignment_tag`` helper will be removed. .. _deprecation-removed-in-2.0: 2.0 Loading docs/releases/1.9.txt +12 −1 Original line number Diff line number Diff line Loading @@ -137,7 +137,9 @@ Signals Templates ^^^^^^^^^ * ... * Template tags created with the :meth:`~django.template.Library.simple_tag` helper can now store results in a template variable by using the ``as`` argument. Requests and Responses ^^^^^^^^^^^^^^^^^^^^^^ Loading Loading @@ -194,6 +196,15 @@ Miscellaneous Features deprecated in 1.9 ========================== ``assignment_tag()`` ~~~~~~~~~~~~~~~~~~~~ Django 1.4 added the ``assignment_tag`` helper to ease the creation of template tags that store results in a template variable. The :meth:`~django.template.Library.simple_tag` helper has gained this same ability, making the ``assignment_tag`` obsolete. Tags that use ``assignment_tag`` should be updated to use ``simple_tag``. Miscellaneous ~~~~~~~~~~~~~ Loading tests/template_tests/templatetags/custom.py +13 −78 Original line number Diff line number Diff line import operator import warnings from django import template from django.template.defaultfilters import stringfilter Loading Loading @@ -125,81 +126,15 @@ def minustwo_overridden_name(value): register.simple_tag(lambda x: x - 1, name='minusone') with warnings.catch_warnings(): warnings.simplefilter('ignore') @register.assignment_tag def assignment_no_params(): """Expected assignment_no_params __doc__""" return "assignment_no_params - Expected result" assignment_no_params.anything = "Expected assignment_no_params __dict__" @register.assignment_tag def assignment_one_param(arg): """Expected assignment_one_param __doc__""" return "assignment_one_param - Expected result: %s" % arg assignment_one_param.anything = "Expected assignment_one_param __dict__" @register.assignment_tag(takes_context=False) def assignment_explicit_no_context(arg): """Expected assignment_explicit_no_context __doc__""" return "assignment_explicit_no_context - Expected result: %s" % arg assignment_explicit_no_context.anything = "Expected assignment_explicit_no_context __dict__" @register.assignment_tag(takes_context=True) def assignment_no_params_with_context(context): """Expected assignment_no_params_with_context __doc__""" return "assignment_no_params_with_context - Expected result (context value: %s)" % context['value'] assignment_no_params_with_context.anything = "Expected assignment_no_params_with_context __dict__" @register.assignment_tag(takes_context=True) def assignment_params_and_context(context, arg): """Expected assignment_params_and_context __doc__""" return "assignment_params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) assignment_params_and_context.anything = "Expected assignment_params_and_context __dict__" @register.assignment_tag def assignment_two_params(one, two): """Expected assignment_two_params __doc__""" return "assignment_two_params - Expected result: %s, %s" % (one, two) assignment_two_params.anything = "Expected assignment_two_params __dict__" @register.assignment_tag def assignment_one_default(one, two='hi'): """Expected assignment_one_default __doc__""" return "assignment_one_default - Expected result: %s, %s" % (one, two) assignment_one_default.anything = "Expected assignment_one_default __dict__" @register.assignment_tag def assignment_unlimited_args(one, two='hi', *args): """Expected assignment_unlimited_args __doc__""" return "assignment_unlimited_args - Expected result: %s" % (', '.join(six.text_type(arg) for arg in [one, two] + list(args))) assignment_unlimited_args.anything = "Expected assignment_unlimited_args __dict__" @register.assignment_tag def assignment_only_unlimited_args(*args): """Expected assignment_only_unlimited_args __doc__""" return "assignment_only_unlimited_args - Expected result: %s" % ', '.join(six.text_type(arg) for arg in args) assignment_only_unlimited_args.anything = "Expected assignment_only_unlimited_args __dict__" @register.assignment_tag def assignment_unlimited_args_kwargs(one, two='hi', *args, **kwargs): """Expected assignment_unlimited_args_kwargs __doc__""" # Sort the dictionary by key to guarantee the order for testing. sorted_kwarg = sorted(six.iteritems(kwargs), key=operator.itemgetter(0)) return "assignment_unlimited_args_kwargs - Expected result: %s / %s" % ( ', '.join(six.text_type(arg) for arg in [one, two] + list(args)), ', '.join('%s=%s' % (k, v) for (k, v) in sorted_kwarg) ) assignment_unlimited_args_kwargs.anything = "Expected assignment_unlimited_args_kwargs __dict__" @register.assignment_tag(takes_context=True) def assignment_tag_without_context_parameter(arg): """Expected assignment_tag_without_context_parameter __doc__""" Loading Loading
django/template/base.py +30 −49 Original line number Diff line number Diff line Loading @@ -61,7 +61,8 @@ from django.apps import apps from django.template.context import (BaseContext, Context, RequestContext, # NOQA: imported for backwards compatibility ContextPopException) from django.utils import lru_cache from django.utils.deprecation import RemovedInDjango20Warning from django.utils.deprecation import (RemovedInDjango20Warning, RemovedInDjango21Warning) from django.utils.itercompat import is_iterable from django.utils.text import (smart_split, unescape_string_literal, get_text_list) Loading Loading @@ -1021,9 +1022,9 @@ def token_kwargs(bits, parser, support_legacy=False): def parse_bits(parser, bits, params, varargs, varkw, defaults, takes_context, name): """ Parses bits for template tag helpers (simple_tag, include_tag and assignment_tag), in particular by detecting syntax errors and by extracting positional and keyword arguments. Parses bits for template tag helpers simple_tag and inclusion_tag, in particular by detecting syntax errors and by extracting positional and keyword arguments. """ if takes_context: if params[0] == 'context': Loading Loading @@ -1099,9 +1100,9 @@ def generic_tag_compiler(parser, token, params, varargs, varkw, defaults, class TagHelperNode(Node): """ Base class for tag helper nodes such as SimpleNode, InclusionNode and AssignmentNode. Manages the positional and keyword arguments to be passed to the decorated function. Base class for tag helper nodes such as SimpleNode and InclusionNode. Manages the positional and keyword arguments to be passed to the decorated function. """ def __init__(self, takes_context, args, kwargs): Loading Loading @@ -1191,71 +1192,51 @@ class Library(object): params, varargs, varkw, defaults = getargspec(func) class SimpleNode(TagHelperNode): def render(self, context): resolved_args, resolved_kwargs = self.get_resolved_arguments(context) return func(*resolved_args, **resolved_kwargs) function_name = (name or getattr(func, '_decorated_function', func).__name__) compile_func = partial(generic_tag_compiler, params=params, varargs=varargs, varkw=varkw, defaults=defaults, name=function_name, takes_context=takes_context, node_class=SimpleNode) compile_func.__doc__ = func.__doc__ self.tag(function_name, compile_func) return func if func is None: # @register.simple_tag(...) return dec elif callable(func): # @register.simple_tag return dec(func) else: raise TemplateSyntaxError("Invalid arguments provided to simple_tag") def assignment_tag(self, func=None, takes_context=None, name=None): def dec(func): params, varargs, varkw, defaults = getargspec(func) class AssignmentNode(TagHelperNode): def __init__(self, takes_context, args, kwargs, target_var): super(AssignmentNode, self).__init__(takes_context, args, kwargs) super(SimpleNode, self).__init__(takes_context, args, kwargs) self.target_var = target_var def render(self, context): resolved_args, resolved_kwargs = self.get_resolved_arguments(context) context[self.target_var] = func(*resolved_args, **resolved_kwargs) output = func(*resolved_args, **resolved_kwargs) if self.target_var is not None: context[self.target_var] = output return '' return output function_name = (name or getattr(func, '_decorated_function', func).__name__) def compile_func(parser, token): bits = token.split_contents()[1:] if len(bits) < 2 or bits[-2] != 'as': raise TemplateSyntaxError( "'%s' tag takes at least 2 arguments and the " "second last argument must be 'as'" % function_name) target_var = None if len(bits) >= 2 and bits[-2] == 'as': target_var = bits[-1] bits = bits[:-2] args, kwargs = parse_bits(parser, bits, params, varargs, varkw, defaults, takes_context, function_name) return AssignmentNode(takes_context, args, kwargs, target_var) return SimpleNode(takes_context, args, kwargs, target_var) compile_func.__doc__ = func.__doc__ self.tag(function_name, compile_func) return func if func is None: # @register.assignment_tag(...) # @register.simple_tag(...) return dec elif callable(func): # @register.assignment_tag # @register.simple_tag return dec(func) else: raise TemplateSyntaxError("Invalid arguments provided to assignment_tag") raise TemplateSyntaxError("Invalid arguments provided to simple_tag") def assignment_tag(self, func=None, takes_context=None, name=None): warnings.warn( "assignment_tag() is deprecated. Use simple_tag() instead", RemovedInDjango21Warning, stacklevel=2, ) return self.simple_tag(func, takes_context, name) def inclusion_tag(self, file_name, takes_context=False, name=None): def dec(func): Loading
docs/howto/custom-template-tags.txt +20 −36 Original line number Diff line number Diff line Loading @@ -388,7 +388,7 @@ Simple tags .. method:: django.template.Library.simple_tag() Many template tags take a number of arguments -- strings or template variables -- and return a result after doing some processing based solely on the input arguments and some external information. For example, a ``current_time`` tag might accept a format string and return the time as a string formatted accordingly. Loading Loading @@ -459,6 +459,18 @@ positional arguments. For example: {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %} .. versionadded:: 1.9 It's possible to store the tag results in a template variable rather than directly outputting it. This is done by using the ``as`` argument followed by the variable name. Doing so enables you to output the content yourself where you see fit: .. code-block:: html+django {% get_current_time "%Y-%m-%d %I:%M %p" as the_time %} <p>The time is {{ the_time }}.</p> .. _howto-custom-template-tags-inclusion-tags: Inclusion tags Loading Loading @@ -602,11 +614,15 @@ Assignment tags .. method:: django.template.Library.assignment_tag() .. deprecated:: 1.9 ``simple_tag`` can now store results in a template variable and should be used instead. To ease the creation of tags setting a variable in the context, Django provides a helper function, ``assignment_tag``. This function works the same way as :ref:`simple_tag<howto-custom-template-tags-simple-tags>`, except that it stores the tag's result in a specified context variable instead of directly outputting it. :meth:`~django.template.Library.simple_tag` except that it stores the tag's result in a specified context variable instead of directly outputting it. Our earlier ``current_time`` function could thus be written like this:: Loading @@ -622,38 +638,6 @@ followed by the variable name, and output it yourself where you see fit: {% get_current_time "%Y-%m-%d %I:%M %p" as the_time %} <p>The time is {{ the_time }}.</p> If your template tag needs to access the current context, you can use the ``takes_context`` argument when registering your tag:: @register.assignment_tag(takes_context=True) def get_current_time(context, format_string): timezone = context['timezone'] return your_get_current_time_method(timezone, format_string) Note that the first parameter to the function *must* be called ``context``. For more information on how the ``takes_context`` option works, see the section on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`. ``assignment_tag`` functions may accept any number of positional or keyword arguments. For example:: @register.assignment_tag def my_tag(a, b, *args, **kwargs): warning = kwargs['warning'] profile = kwargs['profile'] ... return ... Then in the template any number of arguments, separated by spaces, may be passed to the template tag. Like in Python, the values for keyword arguments are set using the equal sign ("``=``") and must be provided after the positional arguments. For example: .. code-block:: html+django {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile as the_result %} Advanced custom template tags ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Loading
docs/internals/deprecation.txt +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ details on these changes. * The ``django.forms.extras`` package will be removed. * The ``assignment_tag`` helper will be removed. .. _deprecation-removed-in-2.0: 2.0 Loading
docs/releases/1.9.txt +12 −1 Original line number Diff line number Diff line Loading @@ -137,7 +137,9 @@ Signals Templates ^^^^^^^^^ * ... * Template tags created with the :meth:`~django.template.Library.simple_tag` helper can now store results in a template variable by using the ``as`` argument. Requests and Responses ^^^^^^^^^^^^^^^^^^^^^^ Loading Loading @@ -194,6 +196,15 @@ Miscellaneous Features deprecated in 1.9 ========================== ``assignment_tag()`` ~~~~~~~~~~~~~~~~~~~~ Django 1.4 added the ``assignment_tag`` helper to ease the creation of template tags that store results in a template variable. The :meth:`~django.template.Library.simple_tag` helper has gained this same ability, making the ``assignment_tag`` obsolete. Tags that use ``assignment_tag`` should be updated to use ``simple_tag``. Miscellaneous ~~~~~~~~~~~~~ Loading
tests/template_tests/templatetags/custom.py +13 −78 Original line number Diff line number Diff line import operator import warnings from django import template from django.template.defaultfilters import stringfilter Loading Loading @@ -125,81 +126,15 @@ def minustwo_overridden_name(value): register.simple_tag(lambda x: x - 1, name='minusone') with warnings.catch_warnings(): warnings.simplefilter('ignore') @register.assignment_tag def assignment_no_params(): """Expected assignment_no_params __doc__""" return "assignment_no_params - Expected result" assignment_no_params.anything = "Expected assignment_no_params __dict__" @register.assignment_tag def assignment_one_param(arg): """Expected assignment_one_param __doc__""" return "assignment_one_param - Expected result: %s" % arg assignment_one_param.anything = "Expected assignment_one_param __dict__" @register.assignment_tag(takes_context=False) def assignment_explicit_no_context(arg): """Expected assignment_explicit_no_context __doc__""" return "assignment_explicit_no_context - Expected result: %s" % arg assignment_explicit_no_context.anything = "Expected assignment_explicit_no_context __dict__" @register.assignment_tag(takes_context=True) def assignment_no_params_with_context(context): """Expected assignment_no_params_with_context __doc__""" return "assignment_no_params_with_context - Expected result (context value: %s)" % context['value'] assignment_no_params_with_context.anything = "Expected assignment_no_params_with_context __dict__" @register.assignment_tag(takes_context=True) def assignment_params_and_context(context, arg): """Expected assignment_params_and_context __doc__""" return "assignment_params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) assignment_params_and_context.anything = "Expected assignment_params_and_context __dict__" @register.assignment_tag def assignment_two_params(one, two): """Expected assignment_two_params __doc__""" return "assignment_two_params - Expected result: %s, %s" % (one, two) assignment_two_params.anything = "Expected assignment_two_params __dict__" @register.assignment_tag def assignment_one_default(one, two='hi'): """Expected assignment_one_default __doc__""" return "assignment_one_default - Expected result: %s, %s" % (one, two) assignment_one_default.anything = "Expected assignment_one_default __dict__" @register.assignment_tag def assignment_unlimited_args(one, two='hi', *args): """Expected assignment_unlimited_args __doc__""" return "assignment_unlimited_args - Expected result: %s" % (', '.join(six.text_type(arg) for arg in [one, two] + list(args))) assignment_unlimited_args.anything = "Expected assignment_unlimited_args __dict__" @register.assignment_tag def assignment_only_unlimited_args(*args): """Expected assignment_only_unlimited_args __doc__""" return "assignment_only_unlimited_args - Expected result: %s" % ', '.join(six.text_type(arg) for arg in args) assignment_only_unlimited_args.anything = "Expected assignment_only_unlimited_args __dict__" @register.assignment_tag def assignment_unlimited_args_kwargs(one, two='hi', *args, **kwargs): """Expected assignment_unlimited_args_kwargs __doc__""" # Sort the dictionary by key to guarantee the order for testing. sorted_kwarg = sorted(six.iteritems(kwargs), key=operator.itemgetter(0)) return "assignment_unlimited_args_kwargs - Expected result: %s / %s" % ( ', '.join(six.text_type(arg) for arg in [one, two] + list(args)), ', '.join('%s=%s' % (k, v) for (k, v) in sorted_kwarg) ) assignment_unlimited_args_kwargs.anything = "Expected assignment_unlimited_args_kwargs __dict__" @register.assignment_tag(takes_context=True) def assignment_tag_without_context_parameter(arg): """Expected assignment_tag_without_context_parameter __doc__""" Loading