diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 8182cd4..22777b7 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -83,10 +83,14 @@ class FirstOfNode(Node):
return u''
class ForNode(Node):
- def __init__(self, loopvars, sequence, is_reversed, nodelist_loop):
+ def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_default=None):
self.loopvars, self.sequence = loopvars, sequence
self.is_reversed = is_reversed
self.nodelist_loop = nodelist_loop
+ if nodelist_default is None:
+ self.nodelist_default = NodeList()
+ else:
+ self.nodelist_default = nodelist_default
def __repr__(self):
reversed_text = self.is_reversed and ' reversed' or ''
@@ -97,16 +101,18 @@ class ForNode(Node):
def __iter__(self):
for node in self.nodelist_loop:
yield node
+ for node in self.nodelist_default:
+ 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_default.get_nodes_by_type(nodetype))
return nodes
def render(self, context):
- nodelist = NodeList()
if 'forloop' in context:
parentloop = context['forloop']
else:
@@ -121,6 +127,9 @@ class ForNode(Node):
if not hasattr(values, '__len__'):
values = list(values)
len_values = len(values)
+ if len_values < 1:
+ return self.nodelist_default.render(context)
+ nodelist = NodeList()
if self.is_reversed:
values = reversed(values)
unpack = len(self.loopvars) > 1
@@ -610,6 +619,17 @@ def do_for(parser, token):
{{ key }}: {{ value }}
{% endfor %}
+ The ``for`` tag can take an optional ``{% default %}`` clause that will
+ be displayed if the given array is empty or could not be found::
+
+
+ {% for athlete in athlete_list %}
+ - {{ athlete.name }}
+ {% default %}
+ - Sorry, no athlete in this list!
+ {% endfor %}
+
+
The for loop sets a number of variables available within the loop:
========================== ================================================
@@ -646,9 +666,14 @@ def do_for(parser, token):
" %s" % token.contents)
sequence = parser.compile_filter(bits[in_index+1])
- nodelist_loop = parser.parse(('endfor',))
- parser.delete_first_token()
- return ForNode(loopvars, sequence, is_reversed, nodelist_loop)
+ nodelist_loop = parser.parse(('default', 'endfor',))
+ token = parser.next_token()
+ if token.contents == 'default':
+ nodelist_default = parser.parse(('endfor',))
+ parser.delete_first_token()
+ else:
+ nodelist_default = None
+ return ForNode(loopvars, sequence, is_reversed, nodelist_loop, nodelist_default)
do_for = register.tag("for", do_for)
def do_ifequal(parser, token, negate):
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index a28cf4f..f2c6953 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -192,6 +192,19 @@ would display the keys and values of the dictionary::
{{ key }}: {{ value }}
{% endfor %}
+.. versionadded:: 1.1
+
+The ``for`` tag can take an optional ``{% default %}`` clause that will be
+displayed if the given array is empty or could not be found::
+
+
+ {% for athlete in athlete_list %}
+ - {{ athlete.name }}
+ {% default %}
+ - Sorry, no athlete in this list!
+ {% endfor %}
+
+
The for loop sets a number of variables available within the loop:
========================== ================================================
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index c69e7cd..6d728e9 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -473,6 +473,9 @@ class Templates(unittest.TestCase):
'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/")),
'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/")),
'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/")),
+ 'for-tag-default01': ("{% for val in values %}{{ val }}{% default %}default text{% endfor %}", {"values": [1, 2, 3]}, "123"),
+ 'for-tag-default02': ("{% for val in values %}{{ val }}{% default %}values array empty{% endfor %}", {"values": []}, "values array empty"),
+ 'for-tag-default03': ("{% for val in values %}{{ val }}{% default %}values array not found{% endfor %}", {}, "values array not found"),
### IF TAG ################################################################
'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),