Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #17675 -- Changed the implementation of the {% regroup %} templ…

…ate 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
  • Loading branch information...
commit 2000f375cde88358cdc66f775f4de14df1234f72 1 parent 803de60
@aaugustin aaugustin authored
View
22 django/template/defaulttags.py
@@ -10,7 +10,7 @@
TemplateSyntaxError, VariableDoesNotExist, InvalidTemplateLibrary,
BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_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.defaultfilters import date
from django.utils.encoding import smart_str, smart_unicode
@@ -287,6 +287,12 @@ def __init__(self, target, expression, var_name):
self.target, self.expression = target, expression
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):
obj_list = self.target.resolve(context, True)
if obj_list == None:
@@ -298,7 +304,7 @@ def render(self, context):
context[self.var_name] = [
{'grouper': key, 'list': list(val)}
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 ''
@@ -1112,10 +1118,16 @@ def regroup(parser, token):
if lastbits_reversed[1][::-1] != 'as':
raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
" be 'as'")
-
- expression = parser.compile_filter(lastbits_reversed[2][::-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)
@register.tag
View
29 tests/regressiontests/templates/tests.py
@@ -8,7 +8,7 @@
# before importing 'template'.
settings.configure()
-from datetime import datetime, timedelta
+from datetime import date, datetime, timedelta
import time
import os
import sys
@@ -1376,6 +1376,33 @@ def get_template_tests(self):
'{% 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 ########################################################
# Test normal behavior
Please sign in to comment.
Something went wrong with that request. Please try again.