mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #14908 -- Added a 'takes_context' argument to simple_tag. Thanks to Julien Phalip for driving the issue and providing the final patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@14987 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -872,8 +872,14 @@ class Library(object): | ||||
|         self.filters[getattr(func, "_decorated_function", func).__name__] = func | ||||
|         return func | ||||
|  | ||||
|     def simple_tag(self,func): | ||||
|     def simple_tag(self, func=None, takes_context=None): | ||||
|         def dec(func): | ||||
|             params, xx, xxx, defaults = getargspec(func) | ||||
|             if takes_context: | ||||
|                 if params[0] == 'context': | ||||
|                     params = params[1:] | ||||
|                 else: | ||||
|                     raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") | ||||
|  | ||||
|             class SimpleNode(Node): | ||||
|                 def __init__(self, vars_to_resolve): | ||||
| @@ -881,13 +887,26 @@ class Library(object): | ||||
|  | ||||
|                 def render(self, context): | ||||
|                     resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] | ||||
|                 return func(*resolved_vars) | ||||
|                     if takes_context: | ||||
|                         func_args = [context] + resolved_vars | ||||
|                     else: | ||||
|                         func_args = resolved_vars | ||||
|                     return func(*func_args) | ||||
|  | ||||
|             compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode) | ||||
|             compile_func.__doc__ = func.__doc__ | ||||
|             self.tag(getattr(func, "_decorated_function", func).__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 inclusion_tag(self, file_name, context_class=Context, takes_context=False): | ||||
|         def dec(func): | ||||
|             params, xx, xxx, defaults = getargspec(func) | ||||
|   | ||||
| @@ -669,9 +669,27 @@ A couple of things to note about the ``simple_tag`` helper function: | ||||
|     * If the argument was a template variable, our function is passed the | ||||
|       current value of the variable, not the variable itself. | ||||
|  | ||||
| When your template tag does not need access to the current context, writing a | ||||
| function to work with the input values and using the ``simple_tag`` helper is | ||||
| the easiest way to create a new tag. | ||||
| .. versionadded:: 1.3 | ||||
|  | ||||
| If your template tag needs to access the current context, you can use the | ||||
| ``takes_context`` argument when registering your tag:: | ||||
|  | ||||
|     # The first argument *must* be called "context" here. | ||||
|     def current_time(context, format_string): | ||||
|         timezone = context['timezone'] | ||||
|         return your_get_current_time_method(timezone) | ||||
|  | ||||
|     register.simple_tag(takes_context=True)(current_time) | ||||
|  | ||||
| Or, using decorator syntax:: | ||||
|  | ||||
|     @register.simple_tag(takes_context=True) | ||||
|     def current_time(context, format_string): | ||||
|         timezone = context['timezone'] | ||||
|         return your_get_current_time_method(timezone) | ||||
|  | ||||
| For more information on how the ``takes_context`` option works, see the section | ||||
| on `inclusion tags`_. | ||||
|  | ||||
| .. _howto-custom-template-tags-inclusion-tags: | ||||
|  | ||||
|   | ||||
| @@ -184,12 +184,16 @@ requests. These include: | ||||
|  | ||||
|     * Support for _HTTPOnly cookies. | ||||
|  | ||||
|     * mail_admins() and mail_managers() now support easily attaching | ||||
|       HTML content to messages. | ||||
|     * :meth:`mail_admins()` and :meth:`mail_managers()` now support | ||||
|       easily attaching HTML content to messages. | ||||
|  | ||||
|     * Error emails now include more of the detail and formatting of | ||||
|       the debug server error page. | ||||
|  | ||||
|     * :meth:`simple_tag` now accepts a :attr:`takes_context` argument, | ||||
|       making it easier to write simple template tags that require | ||||
|       access to template context. | ||||
|  | ||||
| .. _HTTPOnly: http://www.owasp.org/index.php/HTTPOnly | ||||
|  | ||||
| .. _backwards-incompatible-changes-1.3: | ||||
|   | ||||
| @@ -1,11 +1,54 @@ | ||||
| from django import template | ||||
| from django.utils.unittest import TestCase | ||||
| from templatetags import custom | ||||
|  | ||||
|  | ||||
| class CustomTests(TestCase): | ||||
| class CustomFilterTests(TestCase): | ||||
|     def test_filter(self): | ||||
|         t = template.Template("{% load custom %}{{ string|trim:5 }}") | ||||
|         self.assertEqual( | ||||
|             t.render(template.Context({"string": "abcdefghijklmnopqrstuvwxyz"})), | ||||
|             u"abcde" | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class CustomTagTests(TestCase): | ||||
|     def verify_tag(self, tag, name): | ||||
|         self.assertEquals(tag.__name__, name) | ||||
|         self.assertEquals(tag.__doc__, 'Expected %s __doc__' % name) | ||||
|         self.assertEquals(tag.__dict__['anything'], 'Expected %s __dict__' % name) | ||||
|  | ||||
|     def test_simple_tags(self): | ||||
|         c = template.Context({'value': 42}) | ||||
|  | ||||
|         t = template.Template('{% load custom %}{% no_params %}') | ||||
|         self.assertEquals(t.render(c), u'no_params - Expected result') | ||||
|  | ||||
|         t = template.Template('{% load custom %}{% one_param 37 %}') | ||||
|         self.assertEquals(t.render(c), u'one_param - Expected result: 37') | ||||
|  | ||||
|         t = template.Template('{% load custom %}{% explicit_no_context 37 %}') | ||||
|         self.assertEquals(t.render(c), u'explicit_no_context - Expected result: 37') | ||||
|  | ||||
|         t = template.Template('{% load custom %}{% no_params_with_context %}') | ||||
|         self.assertEquals(t.render(c), u'no_params_with_context - Expected result (context value: 42)') | ||||
|  | ||||
|         t = template.Template('{% load custom %}{% params_and_context 37 %}') | ||||
|         self.assertEquals(t.render(c), u'params_and_context - Expected result (context value: 42): 37') | ||||
|  | ||||
|     def test_simple_tag_registration(self): | ||||
|         # Test that the decorators preserve the decorated function's docstring, name and attributes. | ||||
|         self.verify_tag(custom.no_params, 'no_params') | ||||
|         self.verify_tag(custom.one_param, 'one_param') | ||||
|         self.verify_tag(custom.explicit_no_context, 'explicit_no_context') | ||||
|         self.verify_tag(custom.no_params_with_context, 'no_params_with_context') | ||||
|         self.verify_tag(custom.params_and_context, 'params_and_context') | ||||
|  | ||||
|     def test_simple_tag_missing_context(self): | ||||
|         # That the 'context' parameter must be present when takes_context is True | ||||
|         def a_simple_tag_without_parameters(arg): | ||||
|             """Expected __doc__""" | ||||
|             return "Expected result" | ||||
|  | ||||
|         register = template.Library() | ||||
|         decorator = register.simple_tag(takes_context=True) | ||||
|         self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) | ||||
|   | ||||
| @@ -9,3 +9,33 @@ trim = stringfilter(trim) | ||||
|  | ||||
| register.filter(trim) | ||||
|  | ||||
| @register.simple_tag | ||||
| def no_params(): | ||||
|     """Expected no_params __doc__""" | ||||
|     return "no_params - Expected result" | ||||
| no_params.anything = "Expected no_params __dict__" | ||||
|  | ||||
| @register.simple_tag | ||||
| def one_param(arg): | ||||
|     """Expected one_param __doc__""" | ||||
|     return "one_param - Expected result: %s" % arg | ||||
| one_param.anything = "Expected one_param __dict__" | ||||
|  | ||||
| @register.simple_tag(takes_context=False) | ||||
| def explicit_no_context(arg): | ||||
|     """Expected explicit_no_context __doc__""" | ||||
|     return "explicit_no_context - Expected result: %s" % arg | ||||
| explicit_no_context.anything = "Expected explicit_no_context __dict__" | ||||
|  | ||||
| @register.simple_tag(takes_context=True) | ||||
| def no_params_with_context(context): | ||||
|     """Expected no_params_with_context __doc__""" | ||||
|     return "no_params_with_context - Expected result (context value: %s)" % context['value'] | ||||
| no_params_with_context.anything = "Expected no_params_with_context __dict__" | ||||
|  | ||||
| @register.simple_tag(takes_context=True) | ||||
| def params_and_context(context, arg): | ||||
|     """Expected params_and_context __doc__""" | ||||
|     return "params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) | ||||
| params_and_context.anything = "Expected params_and_context __dict__" | ||||
|  | ||||
|   | ||||
| @@ -23,7 +23,7 @@ from django.utils.safestring import mark_safe | ||||
| from django.utils.tzinfo import LocalTimezone | ||||
|  | ||||
| from context import ContextTests | ||||
| from custom import CustomTests | ||||
| from custom import CustomTagTests, CustomFilterTests | ||||
| from parser import ParserTests | ||||
| from unicode import UnicodeTests | ||||
| from nodelist import NodelistTest | ||||
|   | ||||
		Reference in New Issue
	
	Block a user