mirror of https://github.com/django/django.git
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:
parent
803de60c08
commit
2000f375cd
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue