Skip to content

Commit 755a0f6

Browse files
committed
[1.1.X] Fixed #6510 -- Refactored the way child nodes are found in template nodes to avoid potential inconsistencies. Thanks to SmileyChris for the patch.
Backport of r12654 from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@12655 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent a622539 commit 755a0f6

File tree

4 files changed

+70
-18
lines changed

4 files changed

+70
-18
lines changed

django/template/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,7 @@ class Node(object):
760760
# Set this to True for nodes that must be first in the template (although
761761
# they can be preceded by text nodes.
762762
must_be_first = False
763+
child_nodelists = ('nodelist',)
763764

764765
def render(self, context):
765766
"Return the node rendered as a string"
@@ -773,8 +774,10 @@ def get_nodes_by_type(self, nodetype):
773774
nodes = []
774775
if isinstance(self, nodetype):
775776
nodes.append(self)
776-
if hasattr(self, 'nodelist'):
777-
nodes.extend(self.nodelist.get_nodes_by_type(nodetype))
777+
for attr in self.child_nodelists:
778+
nodelist = getattr(self, attr, None)
779+
if nodelist:
780+
nodes.extend(nodelist.get_nodes_by_type(nodetype))
778781
return nodes
779782

780783
class NodeList(list):

django/template/defaulttags.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ def render(self, context):
8585
return u''
8686

8787
class ForNode(Node):
88+
child_nodelists = ('nodelist_loop', 'nodelist_empty')
89+
8890
def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None):
8991
self.loopvars, self.sequence = loopvars, sequence
9092
self.is_reversed = is_reversed
@@ -106,14 +108,6 @@ def __iter__(self):
106108
for node in self.nodelist_empty:
107109
yield node
108110

109-
def get_nodes_by_type(self, nodetype):
110-
nodes = []
111-
if isinstance(self, nodetype):
112-
nodes.append(self)
113-
nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))
114-
nodes.extend(self.nodelist_empty.get_nodes_by_type(nodetype))
115-
return nodes
116-
117111
def render(self, context):
118112
if 'forloop' in context:
119113
parentloop = context['forloop']
@@ -169,6 +163,8 @@ def render(self, context):
169163
return nodelist.render(context)
170164

171165
class IfChangedNode(Node):
166+
child_nodelists = ('nodelist_true', 'nodelist_false')
167+
172168
def __init__(self, nodelist_true, nodelist_false, *varlist):
173169
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
174170
self._last_seen = None
@@ -199,6 +195,8 @@ def render(self, context):
199195
return ''
200196

201197
class IfEqualNode(Node):
198+
child_nodelists = ('nodelist_true', 'nodelist_false')
199+
202200
def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
203201
self.var1, self.var2 = var1, var2
204202
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
@@ -215,6 +213,8 @@ def render(self, context):
215213
return self.nodelist_false.render(context)
216214

217215
class IfNode(Node):
216+
child_nodelists = ('nodelist_true', 'nodelist_false')
217+
218218
def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type):
219219
self.bool_exprs = bool_exprs
220220
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
@@ -229,14 +229,6 @@ def __iter__(self):
229229
for node in self.nodelist_false:
230230
yield node
231231

232-
def get_nodes_by_type(self, nodetype):
233-
nodes = []
234-
if isinstance(self, nodetype):
235-
nodes.append(self)
236-
nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
237-
nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
238-
return nodes
239-
240232
def render(self, context):
241233
if self.link_type == IfNode.LinkTypes.or_:
242234
for ifnot, bool_expr in self.bool_exprs:
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from unittest import TestCase
2+
from django.template.loader import get_template_from_string
3+
from django.template import VariableNode
4+
5+
6+
class NodelistTest(TestCase):
7+
8+
def test_for(self):
9+
source = '{% for i in 1 %}{{ a }}{% endfor %}'
10+
template = get_template_from_string(source)
11+
vars = template.nodelist.get_nodes_by_type(VariableNode)
12+
self.assertEqual(len(vars), 1)
13+
14+
def test_if(self):
15+
source = '{% if x %}{{ a }}{% endif %}'
16+
template = get_template_from_string(source)
17+
vars = template.nodelist.get_nodes_by_type(VariableNode)
18+
self.assertEqual(len(vars), 1)
19+
20+
def test_ifequal(self):
21+
source = '{% ifequal x y %}{{ a }}{% endifequal %}'
22+
template = get_template_from_string(source)
23+
vars = template.nodelist.get_nodes_by_type(VariableNode)
24+
self.assertEqual(len(vars), 1)
25+
26+
def test_ifchanged(self):
27+
source = '{% ifchanged x %}{{ a }}{% endifchanged %}'
28+
template = get_template_from_string(source)
29+
vars = template.nodelist.get_nodes_by_type(VariableNode)
30+
self.assertEqual(len(vars), 1)

tests/regressiontests/templates/tests.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from custom import custom_filters
2525
from parser import token_parsing, filter_parsing, variable_parsing
2626
from unicode import unicode_tests
27+
from nodelist import NodelistTest
2728

2829
try:
2930
from loaders import *
@@ -804,6 +805,32 @@ def get_template_tests(self):
804805
# Inheritance from a template with a space in its name should work.
805806
'inheritance29': ("{% extends 'inheritance 28' %}", {}, '!'),
806807

808+
# Base template, putting block in a conditional {% if %} tag
809+
'inheritance30': ("1{% if optional %}{% block opt %}2{% endblock %}{% endif %}3", {'optional': True}, '123'),
810+
811+
# Inherit from a template with block wrapped in an {% if %} tag (in parent), still gets overridden
812+
'inheritance31': ("{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", {'optional': True}, '1two3'),
813+
'inheritance32': ("{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", {}, '13'),
814+
815+
# Base template, putting block in a conditional {% ifequal %} tag
816+
'inheritance33': ("1{% ifequal optional 1 %}{% block opt %}2{% endblock %}{% endifequal %}3", {'optional': 1}, '123'),
817+
818+
# Inherit from a template with block wrapped in an {% ifequal %} tag (in parent), still gets overridden
819+
'inheritance34': ("{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", {'optional': 1}, '1two3'),
820+
'inheritance35': ("{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", {'optional': 2}, '13'),
821+
822+
# Base template, putting block in a {% for %} tag
823+
'inheritance36': ("{% for n in numbers %}_{% block opt %}{{ n }}{% endblock %}{% endfor %}_", {'numbers': '123'}, '_1_2_3_'),
824+
825+
# Inherit from a template with block wrapped in an {% for %} tag (in parent), still gets overridden
826+
'inheritance37': ("{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", {'numbers': '123'}, '_X_X_X_'),
827+
'inheritance38': ("{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", {}, '_'),
828+
829+
# The super block will still be found.
830+
'inheritance39': ("{% extends 'inheritance30' %}{% block opt %}new{{ block.super }}{% endblock %}", {'optional': True}, '1new23'),
831+
'inheritance40': ("{% extends 'inheritance33' %}{% block opt %}new{{ block.super }}{% endblock %}", {'optional': 1}, '1new23'),
832+
'inheritance41': ("{% extends 'inheritance36' %}{% block opt %}new{{ block.super }}{% endblock %}", {'numbers': '123'}, '_new1_new2_new3_'),
833+
807834
### I18N ##################################################################
808835

809836
# {% spaceless %} tag

0 commit comments

Comments
 (0)