Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
Aymeric Augustin authored February 14, 2012
22  django/template/defaulttags.py
@@ -10,7 +10,7 @@
10 10
     TemplateSyntaxError, VariableDoesNotExist, InvalidTemplateLibrary,
11 11
     BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END,
12 12
     SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END,
13  
-    get_library, token_kwargs, kwarg_re)
  13
+    VARIABLE_ATTRIBUTE_SEPARATOR, get_library, token_kwargs, kwarg_re)
14 14
 from django.template.smartif import IfParser, Literal
15 15
 from django.template.defaultfilters import date
16 16
 from django.utils.encoding import smart_str, smart_unicode
@@ -287,6 +287,12 @@ def __init__(self, target, expression, var_name):
287 287
         self.target, self.expression = target, expression
288 288
         self.var_name = var_name
289 289
 
  290
+    def resolve_expression(self, obj, context):
  291
+        # This method is called for each object in self.target. See regroup()
  292
+        # for the reason why we temporarily put the object in the context.
  293
+        context[self.var_name] = obj
  294
+        return self.expression.resolve(context, True)
  295
+
290 296
     def render(self, context):
291 297
         obj_list = self.target.resolve(context, True)
292 298
         if obj_list == None:
@@ -298,7 +304,7 @@ def render(self, context):
298 304
         context[self.var_name] = [
299 305
             {'grouper': key, 'list': list(val)}
300 306
             for key, val in
301  
-            groupby(obj_list, lambda v, f=self.expression.resolve: f(v, True))
  307
+            groupby(obj_list, lambda obj: self.resolve_expression(obj, context))
302 308
         ]
303 309
         return ''
304 310
 
@@ -1112,10 +1118,16 @@ def regroup(parser, token):
1112 1118
     if lastbits_reversed[1][::-1] != 'as':
1113 1119
         raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
1114 1120
                                   " be 'as'")
1115  
-
1116  
-    expression = parser.compile_filter(lastbits_reversed[2][::-1])
1117  
-
1118 1121
     var_name = lastbits_reversed[0][::-1]
  1122
+    # RegroupNode will take each item in 'target', put it in the context under
  1123
+    # 'var_name', evaluate 'var_name'.'expression' in the current context, and
  1124
+    # group by the resulting value. After all items are processed, it will
  1125
+    # save the final result in the context under 'var_name', thus clearing the
  1126
+    # temporary values. This hack is necessary because the template engine
  1127
+    # doesn't provide a context-aware equivalent of Python's getattr.
  1128
+    expression = parser.compile_filter(var_name +
  1129
+                                       VARIABLE_ATTRIBUTE_SEPARATOR +
  1130
+                                       lastbits_reversed[2][::-1])
1119 1131
     return RegroupNode(target, expression, var_name)
1120 1132
 
1121 1133
 @register.tag
29  tests/regressiontests/templates/tests.py
@@ -8,7 +8,7 @@
8 8
     # before importing 'template'.
9 9
     settings.configure()
10 10
 
11  
-from datetime import datetime, timedelta
  11
+from datetime import date, datetime, timedelta
12 12
 import time
13 13
 import os
14 14
 import sys
@@ -1376,6 +1376,33 @@ def get_template_tests(self):
1376 1376
                           '{% endfor %},'
1377 1377
                           '{% endfor %}',
1378 1378
                           {}, ''),
  1379
+
  1380
+            # Regression tests for #17675
  1381
+            # The date template filter has expects_localtime = True
  1382
+            'regroup03': ('{% regroup data by at|date:"m" as grouped %}'
  1383
+                          '{% for group in grouped %}'
  1384
+                          '{{ group.grouper }}:'
  1385
+                          '{% for item in group.list %}'
  1386
+                          '{{ item.at|date:"d" }}'
  1387
+                          '{% endfor %},'
  1388
+                          '{% endfor %}',
  1389
+                          {'data': [{'at': date(2012, 2, 14)},
  1390
+                                    {'at': date(2012, 2, 28)},
  1391
+                                    {'at': date(2012, 7, 4)}]},
  1392
+                          '02:1428,07:04,'),
  1393
+            # The join template filter has needs_autoescape = True
  1394
+            'regroup04': ('{% regroup data by bar|join:"" as grouped %}'
  1395
+                          '{% for group in grouped %}'
  1396
+                          '{{ group.grouper }}:'
  1397
+                          '{% for item in group.list %}'
  1398
+                          '{{ item.foo|first }}'
  1399
+                          '{% endfor %},'
  1400
+                          '{% endfor %}',
  1401
+                          {'data': [{'foo': 'x', 'bar': ['ab', 'c']},
  1402
+                                    {'foo': 'y', 'bar': ['a', 'bc']},
  1403
+                                    {'foo': 'z', 'bar': ['a', 'd']}]},
  1404
+                          'abc:xy,ad:z,'),
  1405
+
1379 1406
             ### SSI TAG ########################################################
1380 1407
 
1381 1408
             # Test normal behavior

0 notes on commit 2000f37

Please sign in to comment.
Something went wrong with that request. Please try again.