Skip to content

Commit

Permalink
Fixed #6510 -- Refactored the way child nodes are found in template n…
Browse files Browse the repository at this point in the history
…odes to avoid potential inconsistencies. Thanks to SmileyChris for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12654 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
freakboy3742 committed Mar 2, 2010
1 parent ecb56ce commit f034c79
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 18 deletions.
7 changes: 5 additions & 2 deletions django/template/__init__.py
Expand Up @@ -770,6 +770,7 @@ class Node(object):
# Set this to True for nodes that must be first in the template (although # Set this to True for nodes that must be first in the template (although
# they can be preceded by text nodes. # they can be preceded by text nodes.
must_be_first = False must_be_first = False
child_nodelists = ('nodelist',)


def render(self, context): def render(self, context):
"Return the node rendered as a string" "Return the node rendered as a string"
Expand All @@ -783,8 +784,10 @@ def get_nodes_by_type(self, nodetype):
nodes = [] nodes = []
if isinstance(self, nodetype): if isinstance(self, nodetype):
nodes.append(self) nodes.append(self)
if hasattr(self, 'nodelist'): for attr in self.child_nodelists:
nodes.extend(self.nodelist.get_nodes_by_type(nodetype)) nodelist = getattr(self, attr, None)
if nodelist:
nodes.extend(nodelist.get_nodes_by_type(nodetype))
return nodes return nodes


class NodeList(list): class NodeList(list):
Expand Down
24 changes: 8 additions & 16 deletions django/template/defaulttags.py
Expand Up @@ -97,6 +97,8 @@ def render(self, context):
return u'' return u''


class ForNode(Node): class ForNode(Node):
child_nodelists = ('nodelist_loop', 'nodelist_empty')

def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None): def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None):
self.loopvars, self.sequence = loopvars, sequence self.loopvars, self.sequence = loopvars, sequence
self.is_reversed = is_reversed self.is_reversed = is_reversed
Expand All @@ -118,14 +120,6 @@ def __iter__(self):
for node in self.nodelist_empty: for node in self.nodelist_empty:
yield node yield node


def get_nodes_by_type(self, nodetype):
nodes = []
if isinstance(self, nodetype):
nodes.append(self)
nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))
nodes.extend(self.nodelist_empty.get_nodes_by_type(nodetype))
return nodes

def render(self, context): def render(self, context):
if 'forloop' in context: if 'forloop' in context:
parentloop = context['forloop'] parentloop = context['forloop']
Expand Down Expand Up @@ -181,6 +175,8 @@ def render(self, context):
return nodelist.render(context) return nodelist.render(context)


class IfChangedNode(Node): class IfChangedNode(Node):
child_nodelists = ('nodelist_true', 'nodelist_false')

def __init__(self, nodelist_true, nodelist_false, *varlist): def __init__(self, nodelist_true, nodelist_false, *varlist):
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
self._last_seen = None self._last_seen = None
Expand Down Expand Up @@ -211,6 +207,8 @@ def render(self, context):
return '' return ''


class IfEqualNode(Node): class IfEqualNode(Node):
child_nodelists = ('nodelist_true', 'nodelist_false')

def __init__(self, var1, var2, nodelist_true, nodelist_false, negate): def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
self.var1, self.var2 = var1, var2 self.var1, self.var2 = var1, var2
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
Expand All @@ -227,6 +225,8 @@ def render(self, context):
return self.nodelist_false.render(context) return self.nodelist_false.render(context)


class IfNode(Node): class IfNode(Node):
child_nodelists = ('nodelist_true', 'nodelist_false')

def __init__(self, var, nodelist_true, nodelist_false=None): def __init__(self, var, nodelist_true, nodelist_false=None):
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
self.var = var self.var = var
Expand All @@ -240,14 +240,6 @@ def __iter__(self):
for node in self.nodelist_false: for node in self.nodelist_false:
yield node yield node


def get_nodes_by_type(self, nodetype):
nodes = []
if isinstance(self, nodetype):
nodes.append(self)
nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
return nodes

def render(self, context): def render(self, context):
if self.var.eval(context): if self.var.eval(context):
return self.nodelist_true.render(context) return self.nodelist_true.render(context)
Expand Down
30 changes: 30 additions & 0 deletions tests/regressiontests/templates/nodelist.py
@@ -0,0 +1,30 @@
from unittest import TestCase
from django.template.loader import get_template_from_string
from django.template import VariableNode


class NodelistTest(TestCase):

def test_for(self):
source = '{% for i in 1 %}{{ a }}{% endfor %}'
template = get_template_from_string(source)
vars = template.nodelist.get_nodes_by_type(VariableNode)
self.assertEqual(len(vars), 1)

def test_if(self):
source = '{% if x %}{{ a }}{% endif %}'
template = get_template_from_string(source)
vars = template.nodelist.get_nodes_by_type(VariableNode)
self.assertEqual(len(vars), 1)

def test_ifequal(self):
source = '{% ifequal x y %}{{ a }}{% endifequal %}'
template = get_template_from_string(source)
vars = template.nodelist.get_nodes_by_type(VariableNode)
self.assertEqual(len(vars), 1)

def test_ifchanged(self):
source = '{% ifchanged x %}{{ a }}{% endifchanged %}'
template = get_template_from_string(source)
vars = template.nodelist.get_nodes_by_type(VariableNode)
self.assertEqual(len(vars), 1)
27 changes: 27 additions & 0 deletions tests/regressiontests/templates/tests.py
Expand Up @@ -24,6 +24,7 @@
from custom import custom_filters from custom import custom_filters
from parser import token_parsing, filter_parsing, variable_parsing from parser import token_parsing, filter_parsing, variable_parsing
from unicode import unicode_tests from unicode import unicode_tests
from nodelist import NodelistTest
from smartif import * from smartif import *


try: try:
Expand Down Expand Up @@ -886,6 +887,32 @@ def get_template_tests(self):
# Inheritance from a template with a space in its name should work. # Inheritance from a template with a space in its name should work.
'inheritance29': ("{% extends 'inheritance 28' %}", {}, '!'), 'inheritance29': ("{% extends 'inheritance 28' %}", {}, '!'),


# Base template, putting block in a conditional {% if %} tag
'inheritance30': ("1{% if optional %}{% block opt %}2{% endblock %}{% endif %}3", {'optional': True}, '123'),

# Inherit from a template with block wrapped in an {% if %} tag (in parent), still gets overridden
'inheritance31': ("{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", {'optional': True}, '1two3'),
'inheritance32': ("{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", {}, '13'),

# Base template, putting block in a conditional {% ifequal %} tag
'inheritance33': ("1{% ifequal optional 1 %}{% block opt %}2{% endblock %}{% endifequal %}3", {'optional': 1}, '123'),

# Inherit from a template with block wrapped in an {% ifequal %} tag (in parent), still gets overridden
'inheritance34': ("{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", {'optional': 1}, '1two3'),
'inheritance35': ("{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", {'optional': 2}, '13'),

# Base template, putting block in a {% for %} tag
'inheritance36': ("{% for n in numbers %}_{% block opt %}{{ n }}{% endblock %}{% endfor %}_", {'numbers': '123'}, '_1_2_3_'),

# Inherit from a template with block wrapped in an {% for %} tag (in parent), still gets overridden
'inheritance37': ("{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", {'numbers': '123'}, '_X_X_X_'),
'inheritance38': ("{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", {}, '_'),

# The super block will still be found.
'inheritance39': ("{% extends 'inheritance30' %}{% block opt %}new{{ block.super }}{% endblock %}", {'optional': True}, '1new23'),
'inheritance40': ("{% extends 'inheritance33' %}{% block opt %}new{{ block.super }}{% endblock %}", {'optional': 1}, '1new23'),
'inheritance41': ("{% extends 'inheritance36' %}{% block opt %}new{{ block.super }}{% endblock %}", {'numbers': '123'}, '_new1_new2_new3_'),

### I18N ################################################################## ### I18N ##################################################################


# {% spaceless %} tag # {% spaceless %} tag
Expand Down

0 comments on commit f034c79

Please sign in to comment.