diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 6779b9b5f2..22babfd6c0 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -355,8 +355,8 @@ def unordered_list(value):
Recursively takes a self-nested list and returns an HTML unordered list --
WITHOUT opening and closing
tags.
- The list is assumed to be in the proper format. For example, if ``var`` contains
- ``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,
+ The list is assumed to be in the proper format. For example, if ``var``
+ contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``,
then ``{{ var|unordered_list }}`` would return::
- States
@@ -371,14 +371,61 @@ def unordered_list(value):
"""
- def _helper(value, tabs):
+ def convert_old_style_list(list_):
+ """
+ Converts old style lists to the new easier to understand format.
+
+ The old list format looked like:
+ ['Item 1', [['Item 1.1', []], ['Item 1.2', []]]
+
+ And it is converted to:
+ ['Item 1', ['Item 1.1', 'Item 1.2]]
+ """
+ if not isinstance(list_, (tuple, list)) or len(list_) != 2:
+ return list_, False
+ first_item, second_item = list_
+ if second_item == []:
+ return [first_item], True
+ old_style_list = True
+ new_second_item = []
+ for sublist in second_item:
+ item, old_style_list = convert_old_style_list(sublist)
+ if not old_style_list:
+ break
+ new_second_item.extend(item)
+ if old_style_list:
+ second_item = new_second_item
+ return [first_item, second_item], old_style_list
+ def _helper(list_, tabs=1):
indent = u'\t' * tabs
- if value[1]:
- return u'%s%s\n%s\n%s' % (indent, force_unicode(value[0]), indent,
- u'\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent)
- else:
- return u'%s%s' % (indent, force_unicode(value[0]))
- return _helper(value, 1)
+ output = []
+
+ list_length = len(list_)
+ i = 0
+ while i < list_length:
+ title = list_[i]
+ sublist = ''
+ sublist_item = None
+ if isinstance(title, (list, tuple)):
+ sublist_item = title
+ title = ''
+ elif i < list_length - 1:
+ next_item = list_[i+1]
+ if next_item and isinstance(next_item, (list, tuple)):
+ # The next item is a sub-list.
+ sublist_item = next_item
+ # We've processed the next item now too.
+ i += 1
+ if sublist_item:
+ sublist = _helper(sublist_item, tabs+1)
+ sublist = '\n%s\n%s' % (indent, sublist,
+ indent, indent)
+ output.append('%s%s%s' % (indent, force_unicode(title),
+ sublist))
+ i += 1
+ return '\n'.join(output)
+ value, converted = convert_old_style_list(value)
+ return _helper(value)
###################
# INTEGERS #
diff --git a/docs/templates.txt b/docs/templates.txt
index 6cebd3b7bd..8bfa40dc5f 100644
--- a/docs/templates.txt
+++ b/docs/templates.txt
@@ -1301,9 +1301,14 @@ unordered_list
Recursively takes a self-nested list and returns an HTML unordered list --
WITHOUT opening and closing tags.
+**Changed in Django development version**
+
+The format accepted by ``unordered_list`` has changed to an easier to
+understand format.
+
The list is assumed to be in the proper format. For example, if ``var`` contains
-``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,
-then ``{{ var|unordered_list }}`` would return::
+``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then
+``{{ var|unordered_list }}`` would return::
- States
@@ -1317,6 +1322,9 @@ then ``{{ var|unordered_list }}`` would return::
+Note: the previous more restrictive and verbose format is still supported:
+``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,
+
upper
~~~~~
diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py
index a1efae66f6..9482f1cc9f 100644
--- a/tests/regressiontests/defaultfilters/tests.py
+++ b/tests/regressiontests/defaultfilters/tests.py
@@ -266,6 +266,22 @@ u'bc'
>>> slice_(u'abcdefg', u'0::2')
u'aceg'
+>>> unordered_list([u'item 1', u'item 2'])
+u'\t- item 1
\n\t- item 2
'
+
+>>> unordered_list([u'item 1', [u'item 1.1']])
+u'\t- item 1\n\t\n\t
'
+
+>>> unordered_list([u'item 1', [u'item 1.1', u'item1.2'], u'item 2'])
+u'\t- item 1\n\t
\n\t\t- item 1.1
\n\t\t- item1.2
\n\t
\n\t \n\t- item 2
'
+
+>>> unordered_list([u'item 1', [u'item 1.1', [u'item 1.1.1', [u'item 1.1.1.1']]]])
+u'\t- item 1\n\t
\n\t\t- item 1.1\n\t\t
\n\t\t\t- item 1.1.1\n\t\t\t
\n\t\t\t\t- item 1.1.1.1
\n\t\t\t
\n\t\t\t \n\t\t
\n\t\t \n\t
\n\t '
+
+>>> unordered_list(['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']])
+u'\t- States\n\t
\n\t\t- Kansas\n\t\t
\n\t\t\t- Lawrence
\n\t\t\t- Topeka
\n\t\t
\n\t\t \n\t\t- Illinois
\n\t
\n\t '
+
+# Old format for unordered lists should still work
>>> unordered_list([u'item 1', []])
u'\t- item 1
'
@@ -275,6 +291,9 @@ u'\t- item 1\n\t\n\t
'
>>> unordered_list([u'item 1', [[u'item 1.1', []], [u'item 1.2', []]]])
u'\t- item 1\n\t
\n\t\t- item 1.1
\n\t\t- item 1.2
\n\t
\n\t '
+>>> unordered_list(['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]])
+u'\t- States\n\t
\n\t\t- Kansas\n\t\t
\n\t\t\t- Lawrence
\n\t\t\t- Topeka
\n\t\t
\n\t\t \n\t\t- Illinois
\n\t
\n\t '
+
>>> add(u'1', u'2')
3