Skip to content

Commit

Permalink
Fixed #17675 -- Changed the implementation of the {% regroup %} templ…
Browse files Browse the repository at this point in the history
…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
aaugustin committed Feb 14, 2012
1 parent 803de60 commit 2000f37
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 6 deletions.
22 changes: 17 additions & 5 deletions django/template/defaulttags.py
Expand Up @@ -10,7 +10,7 @@
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
Expand Down Expand Up @@ -287,6 +287,12 @@ def __init__(self, target, expression, var_name):
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:
Expand All @@ -298,7 +304,7 @@ def render(self, context):
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 ''


Expand Down Expand Up @@ -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
Expand Down
29 changes: 28 additions & 1 deletion tests/regressiontests/templates/tests.py
Expand Up @@ -8,7 +8,7 @@
# 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
Expand Down Expand Up @@ -1376,6 +1376,33 @@ def get_template_tests(self):
'{% 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
Expand Down

0 comments on commit 2000f37

Please sign in to comment.