Commit 755a0f6d authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

[1.1.X] Fixed #6510 -- Refactored the way child nodes are found in template...

[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
parent a6225393
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -760,6 +760,7 @@ class Node(object):
    # Set this to True for nodes that must be first in the template (although
    # they can be preceded by text nodes.
    must_be_first = False
    child_nodelists = ('nodelist',)

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

class NodeList(list):
+8 −16
Original line number Diff line number Diff line
@@ -85,6 +85,8 @@ class FirstOfNode(Node):
        return u''

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

    def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None):
        self.loopvars, self.sequence = loopvars, sequence
        self.is_reversed = is_reversed
@@ -106,14 +108,6 @@ class ForNode(Node):
        for node in self.nodelist_empty:
            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):
        if 'forloop' in context:
            parentloop = context['forloop']
@@ -169,6 +163,8 @@ class ForNode(Node):
        return nodelist.render(context)

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

    def __init__(self, nodelist_true, nodelist_false, *varlist):
        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
        self._last_seen = None
@@ -199,6 +195,8 @@ class IfChangedNode(Node):
        return ''

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

    def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
        self.var1, self.var2 = var1, var2
        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
@@ -215,6 +213,8 @@ class IfEqualNode(Node):
        return self.nodelist_false.render(context)

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

    def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type):
        self.bool_exprs = bool_exprs
        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
@@ -229,14 +229,6 @@ class IfNode(Node):
        for node in self.nodelist_false:
            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):
        if self.link_type == IfNode.LinkTypes.or_:
            for ifnot, bool_expr in self.bool_exprs:
+30 −0
Original line number Diff line number Diff line
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 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ from context import context_tests
from custom import custom_filters
from parser import token_parsing, filter_parsing, variable_parsing
from unicode import unicode_tests
from nodelist import NodelistTest

try:
    from loaders import *
@@ -804,6 +805,32 @@ class Templates(unittest.TestCase):
            # Inheritance from a template with a space in its name should work.
            '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 ##################################################################

            # {% spaceless %} tag