Code

Ticket #6398: ticket6398-r9032.diff

File ticket6398-r9032.diff, 5.4 KB (added by jezdez, 6 years ago)

Updated patch to Django r9032 (http://github.com/jezdez/django/tree/ticket6398)

Line 
1diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
2index 8182cd4..22777b7 100644
3--- a/django/template/defaulttags.py
4+++ b/django/template/defaulttags.py
5@@ -83,10 +83,14 @@ class FirstOfNode(Node):
6         return u''
7 
8 class ForNode(Node):
9-    def __init__(self, loopvars, sequence, is_reversed, nodelist_loop):
10+    def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_default=None):
11         self.loopvars, self.sequence = loopvars, sequence
12         self.is_reversed = is_reversed
13         self.nodelist_loop = nodelist_loop
14+        if nodelist_default is None:
15+            self.nodelist_default = NodeList()
16+        else:
17+            self.nodelist_default = nodelist_default
18 
19     def __repr__(self):
20         reversed_text = self.is_reversed and ' reversed' or ''
21@@ -97,16 +101,18 @@ class ForNode(Node):
22     def __iter__(self):
23         for node in self.nodelist_loop:
24             yield node
25+        for node in self.nodelist_default:
26+            yield node
27 
28     def get_nodes_by_type(self, nodetype):
29         nodes = []
30         if isinstance(self, nodetype):
31             nodes.append(self)
32         nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))
33+        nodes.extend(self.nodelist_default.get_nodes_by_type(nodetype))
34         return nodes
35 
36     def render(self, context):
37-        nodelist = NodeList()
38         if 'forloop' in context:
39             parentloop = context['forloop']
40         else:
41@@ -121,6 +127,9 @@ class ForNode(Node):
42         if not hasattr(values, '__len__'):
43             values = list(values)
44         len_values = len(values)
45+        if len_values < 1:
46+            return self.nodelist_default.render(context)
47+        nodelist = NodeList()
48         if self.is_reversed:
49             values = reversed(values)
50         unpack = len(self.loopvars) > 1
51@@ -610,6 +619,17 @@ def do_for(parser, token):
52             {{ key }}: {{ value }}
53         {% endfor %}
54 
55+    The ``for`` tag can take an optional ``{% default %}`` clause that will
56+    be displayed if the given array is empty or could not be found::
57+
58+        <ul>
59+        {% for athlete in athlete_list %}
60+            <li>{{ athlete.name }}</li>
61+        {% default %}
62+            <li>Sorry, no athlete in this list!</li>
63+        {% endfor %}
64+        <ul>
65+
66     The for loop sets a number of variables available within the loop:
67 
68         ==========================  ================================================
69@@ -646,9 +666,14 @@ def do_for(parser, token):
70                                       " %s" % token.contents)
71 
72     sequence = parser.compile_filter(bits[in_index+1])
73-    nodelist_loop = parser.parse(('endfor',))
74-    parser.delete_first_token()
75-    return ForNode(loopvars, sequence, is_reversed, nodelist_loop)
76+    nodelist_loop = parser.parse(('default', 'endfor',))
77+    token = parser.next_token()
78+    if token.contents == 'default':
79+        nodelist_default = parser.parse(('endfor',))
80+        parser.delete_first_token()
81+    else:
82+        nodelist_default = None
83+    return ForNode(loopvars, sequence, is_reversed, nodelist_loop, nodelist_default)
84 do_for = register.tag("for", do_for)
85 
86 def do_ifequal(parser, token, negate):
87diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
88index a28cf4f..f2c6953 100644
89--- a/docs/ref/templates/builtins.txt
90+++ b/docs/ref/templates/builtins.txt
91@@ -192,6 +192,19 @@ would display the keys and values of the dictionary::
92         {{ key }}: {{ value }}
93     {% endfor %}
94 
95+.. versionadded:: 1.1
96+
97+The ``for`` tag can take an optional ``{% default %}`` clause that will be
98+displayed if the given array is empty or could not be found::
99+
100+    <ul>
101+    {% for athlete in athlete_list %}
102+        <li>{{ athlete.name }}</li>
103+    {% default %}
104+        <li>Sorry, no athlete in this list!</li>
105+    {% endfor %}
106+    <ul>
107+
108 The for loop sets a number of variables available within the loop:
109 
110     ==========================  ================================================
111diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
112index c69e7cd..6d728e9 100644
113--- a/tests/regressiontests/templates/tests.py
114+++ b/tests/regressiontests/templates/tests.py
115@@ -473,6 +473,9 @@ class Templates(unittest.TestCase):
116             'for-tag-unpack11': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, ("one:1,/two:2,/", "one:1,INVALID/two:2,INVALID/")),
117             'for-tag-unpack12': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2))}, ("one:1,carrot/two:2,/", "one:1,carrot/two:2,INVALID/")),
118             'for-tag-unpack13': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'cheese'))}, ("one:1,carrot/two:2,cheese/", "one:1,carrot/two:2,cheese/")),
119+            'for-tag-default01': ("{% for val in values %}{{ val }}{% default %}default text{% endfor %}", {"values": [1, 2, 3]}, "123"),
120+            'for-tag-default02': ("{% for val in values %}{{ val }}{% default %}values array empty{% endfor %}", {"values": []}, "values array empty"),
121+            'for-tag-default03': ("{% for val in values %}{{ val }}{% default %}values array not found{% endfor %}", {}, "values array not found"),
122 
123             ### IF TAG ################################################################
124             'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),