Author: aaugustin
Date: 2012-02-14 13:29:50 -0800 (Tue, 14 Feb 2012)
New Revision: 17522

Modified:
   django/trunk/django/template/defaulttags.py
   django/trunk/tests/regressiontests/templates/tests.py
Log:
Fixed #17675 -- Changed the implementation of the {% regroup %} template tag to 
use the context properly when resolving expressions.


Modified: django/trunk/django/template/defaulttags.py
===================================================================
--- django/trunk/django/template/defaulttags.py 2012-02-14 09:55:09 UTC (rev 
17521)
+++ django/trunk/django/template/defaulttags.py 2012-02-14 21:29:50 UTC (rev 
17522)
@@ -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 @@
         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 @@
         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 @@
     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

Modified: django/trunk/tests/regressiontests/templates/tests.py
===================================================================
--- django/trunk/tests/regressiontests/templates/tests.py       2012-02-14 
09:55:09 UTC (rev 17521)
+++ django/trunk/tests/regressiontests/templates/tests.py       2012-02-14 
21:29:50 UTC (rev 17522)
@@ -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 @@
                           '{% 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

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-updates@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to