mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Fixed #17675 -- Changed the implementation of the {% regroup %} template tag to use the context properly when resolving expressions.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17522 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -10,7 +10,7 @@ from django.template.base import (Node, NodeList, Template, Library, | |||||||
|     TemplateSyntaxError, VariableDoesNotExist, InvalidTemplateLibrary, |     TemplateSyntaxError, VariableDoesNotExist, InvalidTemplateLibrary, | ||||||
|     BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, |     BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, | ||||||
|     SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END, |     SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END, | ||||||
|     get_library, token_kwargs, kwarg_re) |     VARIABLE_ATTRIBUTE_SEPARATOR, get_library, token_kwargs, kwarg_re) | ||||||
| from django.template.smartif import IfParser, Literal | from django.template.smartif import IfParser, Literal | ||||||
| from django.template.defaultfilters import date | from django.template.defaultfilters import date | ||||||
| from django.utils.encoding import smart_str, smart_unicode | from django.utils.encoding import smart_str, smart_unicode | ||||||
| @@ -287,6 +287,12 @@ class RegroupNode(Node): | |||||||
|         self.target, self.expression = target, expression |         self.target, self.expression = target, expression | ||||||
|         self.var_name = var_name |         self.var_name = var_name | ||||||
|  |  | ||||||
|  |     def resolve_expression(self, obj, context): | ||||||
|  |         # This method is called for each object in self.target. See regroup() | ||||||
|  |         # for the reason why we temporarily put the object in the context. | ||||||
|  |         context[self.var_name] = obj | ||||||
|  |         return self.expression.resolve(context, True) | ||||||
|  |  | ||||||
|     def render(self, context): |     def render(self, context): | ||||||
|         obj_list = self.target.resolve(context, True) |         obj_list = self.target.resolve(context, True) | ||||||
|         if obj_list == None: |         if obj_list == None: | ||||||
| @@ -298,7 +304,7 @@ class RegroupNode(Node): | |||||||
|         context[self.var_name] = [ |         context[self.var_name] = [ | ||||||
|             {'grouper': key, 'list': list(val)} |             {'grouper': key, 'list': list(val)} | ||||||
|             for key, val in |             for key, val in | ||||||
|             groupby(obj_list, lambda v, f=self.expression.resolve: f(v, True)) |             groupby(obj_list, lambda obj: self.resolve_expression(obj, context)) | ||||||
|         ] |         ] | ||||||
|         return '' |         return '' | ||||||
|  |  | ||||||
| @@ -1112,10 +1118,16 @@ def regroup(parser, token): | |||||||
|     if lastbits_reversed[1][::-1] != 'as': |     if lastbits_reversed[1][::-1] != 'as': | ||||||
|         raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must" |         raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must" | ||||||
|                                   " be 'as'") |                                   " be 'as'") | ||||||
|  |  | ||||||
|     expression = parser.compile_filter(lastbits_reversed[2][::-1]) |  | ||||||
|  |  | ||||||
|     var_name = lastbits_reversed[0][::-1] |     var_name = lastbits_reversed[0][::-1] | ||||||
|  |     # RegroupNode will take each item in 'target', put it in the context under | ||||||
|  |     # 'var_name', evaluate 'var_name'.'expression' in the current context, and | ||||||
|  |     # group by the resulting value. After all items are processed, it will | ||||||
|  |     # save the final result in the context under 'var_name', thus clearing the | ||||||
|  |     # temporary values. This hack is necessary because the template engine | ||||||
|  |     # doesn't provide a context-aware equivalent of Python's getattr. | ||||||
|  |     expression = parser.compile_filter(var_name + | ||||||
|  |                                        VARIABLE_ATTRIBUTE_SEPARATOR + | ||||||
|  |                                        lastbits_reversed[2][::-1]) | ||||||
|     return RegroupNode(target, expression, var_name) |     return RegroupNode(target, expression, var_name) | ||||||
|  |  | ||||||
| @register.tag | @register.tag | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ if __name__ == '__main__': | |||||||
|     # before importing 'template'. |     # before importing 'template'. | ||||||
|     settings.configure() |     settings.configure() | ||||||
|  |  | ||||||
| from datetime import datetime, timedelta | from datetime import date, datetime, timedelta | ||||||
| import time | import time | ||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
| @@ -1376,6 +1376,33 @@ class Templates(unittest.TestCase): | |||||||
|                           '{% endfor %},' |                           '{% endfor %},' | ||||||
|                           '{% endfor %}', |                           '{% endfor %}', | ||||||
|                           {}, ''), |                           {}, ''), | ||||||
|  |  | ||||||
|  |             # Regression tests for #17675 | ||||||
|  |             # The date template filter has expects_localtime = True | ||||||
|  |             'regroup03': ('{% regroup data by at|date:"m" as grouped %}' | ||||||
|  |                           '{% for group in grouped %}' | ||||||
|  |                           '{{ group.grouper }}:' | ||||||
|  |                           '{% for item in group.list %}' | ||||||
|  |                           '{{ item.at|date:"d" }}' | ||||||
|  |                           '{% endfor %},' | ||||||
|  |                           '{% endfor %}', | ||||||
|  |                           {'data': [{'at': date(2012, 2, 14)}, | ||||||
|  |                                     {'at': date(2012, 2, 28)}, | ||||||
|  |                                     {'at': date(2012, 7, 4)}]}, | ||||||
|  |                           '02:1428,07:04,'), | ||||||
|  |             # The join template filter has needs_autoescape = True | ||||||
|  |             'regroup04': ('{% regroup data by bar|join:"" as grouped %}' | ||||||
|  |                           '{% for group in grouped %}' | ||||||
|  |                           '{{ group.grouper }}:' | ||||||
|  |                           '{% for item in group.list %}' | ||||||
|  |                           '{{ item.foo|first }}' | ||||||
|  |                           '{% endfor %},' | ||||||
|  |                           '{% endfor %}', | ||||||
|  |                           {'data': [{'foo': 'x', 'bar': ['ab', 'c']}, | ||||||
|  |                                     {'foo': 'y', 'bar': ['a', 'bc']}, | ||||||
|  |                                     {'foo': 'z', 'bar': ['a', 'd']}]}, | ||||||
|  |                           'abc:xy,ad:z,'), | ||||||
|  |  | ||||||
|             ### SSI TAG ######################################################## |             ### SSI TAG ######################################################## | ||||||
|  |  | ||||||
|             # Test normal behavior |             # Test normal behavior | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user