diff --git a/AUTHORS b/AUTHORS
index e3db830b74..4d57503bf8 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -104,7 +104,6 @@ answer newbie questions, and generally made Django that much better:
mattycakes@gmail.com
Jason McBrayer
michael.mcewan@gmail.com
- mir@noris.de
mmarshall
Eric Moritz
Robin Munn
@@ -121,12 +120,14 @@ answer newbie questions, and generally made Django that much better:
plisk
Daniel Poelzleithner
J. Rademaker
+ Michael Radziej
Brian Ray
rhettg@gmail.com
Oliver Rutherfurd
Ivan Sagalaev (Maniac)
David Schein
Pete Shinners
+ SmileyChris
sopel
Thomas Steinacher
Radek Švarz
diff --git a/django/template/__init__.py b/django/template/__init__.py
index faf31c1e46..c1d6ca1497 100644
--- a/django/template/__init__.py
+++ b/django/template/__init__.py
@@ -549,9 +549,12 @@ class FilterExpression(object):
obj = resolve_variable(self.var, context)
except VariableDoesNotExist:
if ignore_failures:
- return None
+ obj = None
else:
- return settings.TEMPLATE_STRING_IF_INVALID
+ if settings.TEMPLATE_STRING_IF_INVALID:
+ return settings.TEMPLATE_STRING_IF_INVALID
+ else:
+ obj = settings.TEMPLATE_STRING_IF_INVALID
for func, args in self.filters:
arg_vals = []
for lookup, arg in args:
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index d111ddca89..691b40f332 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -86,7 +86,7 @@ class ForNode(Node):
parentloop = {}
context.push()
try:
- values = self.sequence.resolve(context)
+ values = self.sequence.resolve(context, True)
except VariableDoesNotExist:
values = []
if values is None:
@@ -212,13 +212,13 @@ class RegroupNode(Node):
self.var_name = var_name
def render(self, context):
- obj_list = self.target.resolve(context)
- if obj_list == '': # target_var wasn't found in context; fail silently
+ obj_list = self.target.resolve(context, True)
+ if obj_list == None: # target_var wasn't found in context; fail silently
context[self.var_name] = []
return ''
output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
for obj in obj_list:
- grouper = self.expression.resolve(Context({'var': obj}))
+ grouper = self.expression.resolve(Context({'var': obj}), True)
# TODO: Is this a sensible way to determine equality?
if output and repr(output[-1]['grouper']) == repr(grouper):
output[-1]['list'].append(obj)
diff --git a/docs/templates_python.txt b/docs/templates_python.txt
index aa7fa901b0..8ca68d87d3 100644
--- a/docs/templates_python.txt
+++ b/docs/templates_python.txt
@@ -198,9 +198,19 @@ some things to keep in mind:
How invalid variables are handled
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If a variable doesn't exist, the template system inserts the value of the
-``TEMPLATE_STRING_IF_INVALID`` setting, which is set to ``''`` (the empty
-string) by default.
+Generally, if a variable doesn't exist, the template system inserts the
+value of the ``TEMPLATE_STRING_IF_INVALID`` setting, which is set to ``''``
+(the empty string) by default.
+
+Filters that are applied to an invalid variable will only be applied if
+``TEMPLATE_STRING_IF_INVALID`` is set to ``''`` (the empty string). If
+``TEMPLATE_STRING_IF_INVALID`` is set to any other value, variable
+filters will be ignored.
+
+This behaviour is slightly different for the ``if``, ``for`` and ``regroup``
+template tags. If an invalid variable is provided to one of these template
+tags, the variable will be interpreted as ``None``. Filters are always
+applied to invalid variables within these template tags.
Playing with Context objects
----------------------------
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index 2d1ce192ef..5a8dd2d6a2 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -84,7 +84,7 @@ class Templates(unittest.TestCase):
'basic-syntax03': ("{{ first }} --- {{ second }}", {"first" : 1, "second" : 2}, "1 --- 2"),
# Fail silently when a variable is not found in the current context
- 'basic-syntax04': ("as{{ missing }}df", {}, "asINVALIDdf"),
+ 'basic-syntax04': ("as{{ missing }}df", {}, ("asdf","asINVALIDdf")),
# A variable may not contain more than one word
'basic-syntax06': ("{{ multi word variable }}", {}, template.TemplateSyntaxError),
@@ -100,7 +100,7 @@ class Templates(unittest.TestCase):
'basic-syntax10': ("{{ var.otherclass.method }}", {"var": SomeClass()}, "OtherClass.method"),
# Fail silently when a variable's attribute isn't found
- 'basic-syntax11': ("{{ var.blech }}", {"var": SomeClass()}, "INVALID"),
+ 'basic-syntax11': ("{{ var.blech }}", {"var": SomeClass()}, ("","INVALID")),
# Raise TemplateSyntaxError when trying to access a variable beginning with an underscore
'basic-syntax12': ("{{ var.__dict__ }}", {"var": SomeClass()}, template.TemplateSyntaxError),
@@ -116,10 +116,10 @@ class Templates(unittest.TestCase):
'basic-syntax18': ("{{ foo.bar }}", {"foo" : {"bar" : "baz"}}, "baz"),
# Fail silently when a variable's dictionary key isn't found
- 'basic-syntax19': ("{{ foo.spam }}", {"foo" : {"bar" : "baz"}}, "INVALID"),
+ 'basic-syntax19': ("{{ foo.spam }}", {"foo" : {"bar" : "baz"}}, ("","INVALID")),
# Fail silently when accessing a non-simple method
- 'basic-syntax20': ("{{ var.method2 }}", {"var": SomeClass()}, "INVALID"),
+ 'basic-syntax20': ("{{ var.method2 }}", {"var": SomeClass()}, ("","INVALID")),
# Basic filter usage
'basic-syntax21': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"),
@@ -158,7 +158,7 @@ class Templates(unittest.TestCase):
'basic-syntax32': (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'),
# Fail silently for methods that raise an exception with a "silent_variable_failure" attribute
- 'basic-syntax33': (r'1{{ var.method3 }}2', {"var": SomeClass()}, "1INVALID2"),
+ 'basic-syntax33': (r'1{{ var.method3 }}2', {"var": SomeClass()}, ("12", "1INVALID2")),
# In methods that raise an exception without a "silent_variable_attribute" set to True,
# the exception propogates
@@ -464,6 +464,14 @@ class Templates(unittest.TestCase):
# translation of a constant string
'i18n13': ('{{ _("Page not found") }}', {'LANGUAGE_CODE': 'de'}, 'Seite nicht gefunden'),
+ ### HANDLING OF TEMPLATE_TAG_IF_INVALID ###################################
+
+ 'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')),
+ 'invalidstr02': ('{{ var|default_if_none:"Foo" }}', {}, ('','INVALID')),
+ 'invalidstr03': ('{% for v in var %}({{ v }}){% endfor %}', {}, ''),
+ 'invalidstr04': ('{% if var %}Yes{% else %}No{% endif %}', {}, 'No'),
+ 'invalidstr04': ('{% if var|default:"Foo" %}Yes{% else %}No{% endif %}', {}, 'Yes'),
+
### MULTILINE #############################################################
'multiline01': ("""
@@ -507,7 +515,7 @@ class Templates(unittest.TestCase):
'{{ item.foo }}' + \
'{% endfor %},' + \
'{% endfor %}',
- {}, 'INVALID:INVALIDINVALIDINVALIDINVALIDINVALIDINVALIDINVALID,'),
+ {}, ''),
### TEMPLATETAG TAG #######################################################
'templatetag01': ('{% templatetag openblock %}', {}, '{%'),
@@ -592,30 +600,44 @@ class Templates(unittest.TestCase):
old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
# Set TEMPLATE_STRING_IF_INVALID to a known string
- old_invalid, settings.TEMPLATE_STRING_IF_INVALID = settings.TEMPLATE_STRING_IF_INVALID, 'INVALID'
+ old_invalid = settings.TEMPLATE_STRING_IF_INVALID
for name, vals in tests:
install()
+
+ if isinstance(vals[2], tuple):
+ normal_string_result = vals[2][0]
+ invalid_string_result = vals[2][1]
+ else:
+ normal_string_result = vals[2]
+ invalid_string_result = vals[2]
+
if 'LANGUAGE_CODE' in vals[1]:
activate(vals[1]['LANGUAGE_CODE'])
else:
activate('en-us')
- try:
- output = loader.get_template(name).render(template.Context(vals[1]))
- except Exception, e:
- if e.__class__ != vals[2]:
- failures.append("Template test: %s -- FAILED. Got %s, exception: %s" % (name, e.__class__, e))
- continue
+
+ for invalid_str, result in [('', normal_string_result),
+ ('INVALID', invalid_string_result)]:
+ settings.TEMPLATE_STRING_IF_INVALID = invalid_str
+ try:
+ output = loader.get_template(name).render(template.Context(vals[1]))
+ except Exception, e:
+ if e.__class__ != result:
+ failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e))
+ continue
+ if output != result:
+ failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output))
+
if 'LANGUAGE_CODE' in vals[1]:
deactivate()
- if output != vals[2]:
- failures.append("Template test: %s -- FAILED. Expected %r, got %r" % (name, vals[2], output))
+
loader.template_source_loaders = old_template_loaders
deactivate()
settings.TEMPLATE_DEBUG = old_td
settings.TEMPLATE_STRING_IF_INVALID = old_invalid
- self.assertEqual(failures, [])
+ self.assertEqual(failures, [], '\n'.join(failures))
if __name__ == "__main__":
unittest.main()