Fixes #2637 -- Clarified handling of TEMPLATE_STRING_IF_INVALID, especially with regards to filtering of invalid values. Modified unit tests to test both empty and non-empty values for TEMPLATE_STRING_IF_INVALID. Thanks to SmileyChris for identifying and helping to resolve this bug.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@3714 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
3a919dbe2e
commit
cfe77946d7
3
AUTHORS
3
AUTHORS
|
@ -104,7 +104,6 @@ answer newbie questions, and generally made Django that much better:
|
||||||
mattycakes@gmail.com
|
mattycakes@gmail.com
|
||||||
Jason McBrayer <http://www.carcosa.net/jason/>
|
Jason McBrayer <http://www.carcosa.net/jason/>
|
||||||
michael.mcewan@gmail.com
|
michael.mcewan@gmail.com
|
||||||
mir@noris.de
|
|
||||||
mmarshall
|
mmarshall
|
||||||
Eric Moritz <http://eric.themoritzfamily.com/>
|
Eric Moritz <http://eric.themoritzfamily.com/>
|
||||||
Robin Munn <http://www.geekforgod.com/>
|
Robin Munn <http://www.geekforgod.com/>
|
||||||
|
@ -121,12 +120,14 @@ answer newbie questions, and generally made Django that much better:
|
||||||
plisk
|
plisk
|
||||||
Daniel Poelzleithner <http://poelzi.org/>
|
Daniel Poelzleithner <http://poelzi.org/>
|
||||||
J. Rademaker
|
J. Rademaker
|
||||||
|
Michael Radziej <mir@noris.de>
|
||||||
Brian Ray <http://brianray.chipy.org/>
|
Brian Ray <http://brianray.chipy.org/>
|
||||||
rhettg@gmail.com
|
rhettg@gmail.com
|
||||||
Oliver Rutherfurd <http://rutherfurd.net/>
|
Oliver Rutherfurd <http://rutherfurd.net/>
|
||||||
Ivan Sagalaev (Maniac) <http://www.softwaremaniacs.org/>
|
Ivan Sagalaev (Maniac) <http://www.softwaremaniacs.org/>
|
||||||
David Schein
|
David Schein
|
||||||
Pete Shinners <pete@shinners.org>
|
Pete Shinners <pete@shinners.org>
|
||||||
|
SmileyChris <smileychris@gmail.com>
|
||||||
sopel
|
sopel
|
||||||
Thomas Steinacher <tom@eggdrop.ch>
|
Thomas Steinacher <tom@eggdrop.ch>
|
||||||
Radek Švarz <http://www.svarz.cz/translate/>
|
Radek Švarz <http://www.svarz.cz/translate/>
|
||||||
|
|
|
@ -549,9 +549,12 @@ class FilterExpression(object):
|
||||||
obj = resolve_variable(self.var, context)
|
obj = resolve_variable(self.var, context)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
if ignore_failures:
|
if ignore_failures:
|
||||||
return None
|
obj = None
|
||||||
else:
|
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:
|
for func, args in self.filters:
|
||||||
arg_vals = []
|
arg_vals = []
|
||||||
for lookup, arg in args:
|
for lookup, arg in args:
|
||||||
|
|
|
@ -86,7 +86,7 @@ class ForNode(Node):
|
||||||
parentloop = {}
|
parentloop = {}
|
||||||
context.push()
|
context.push()
|
||||||
try:
|
try:
|
||||||
values = self.sequence.resolve(context)
|
values = self.sequence.resolve(context, True)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
values = []
|
values = []
|
||||||
if values is None:
|
if values is None:
|
||||||
|
@ -212,13 +212,13 @@ class RegroupNode(Node):
|
||||||
self.var_name = var_name
|
self.var_name = var_name
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
obj_list = self.target.resolve(context)
|
obj_list = self.target.resolve(context, True)
|
||||||
if obj_list == '': # target_var wasn't found in context; fail silently
|
if obj_list == None: # target_var wasn't found in context; fail silently
|
||||||
context[self.var_name] = []
|
context[self.var_name] = []
|
||||||
return ''
|
return ''
|
||||||
output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
|
output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
|
||||||
for obj in obj_list:
|
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?
|
# TODO: Is this a sensible way to determine equality?
|
||||||
if output and repr(output[-1]['grouper']) == repr(grouper):
|
if output and repr(output[-1]['grouper']) == repr(grouper):
|
||||||
output[-1]['list'].append(obj)
|
output[-1]['list'].append(obj)
|
||||||
|
|
|
@ -198,9 +198,19 @@ some things to keep in mind:
|
||||||
How invalid variables are handled
|
How invalid variables are handled
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
If a variable doesn't exist, the template system inserts the value of the
|
Generally, if a variable doesn't exist, the template system inserts the
|
||||||
``TEMPLATE_STRING_IF_INVALID`` setting, which is set to ``''`` (the empty
|
value of the ``TEMPLATE_STRING_IF_INVALID`` setting, which is set to ``''``
|
||||||
string) by default.
|
(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
|
Playing with Context objects
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
|
@ -84,7 +84,7 @@ class Templates(unittest.TestCase):
|
||||||
'basic-syntax03': ("{{ first }} --- {{ second }}", {"first" : 1, "second" : 2}, "1 --- 2"),
|
'basic-syntax03': ("{{ first }} --- {{ second }}", {"first" : 1, "second" : 2}, "1 --- 2"),
|
||||||
|
|
||||||
# Fail silently when a variable is not found in the current context
|
# 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
|
# A variable may not contain more than one word
|
||||||
'basic-syntax06': ("{{ multi word variable }}", {}, template.TemplateSyntaxError),
|
'basic-syntax06': ("{{ multi word variable }}", {}, template.TemplateSyntaxError),
|
||||||
|
@ -100,7 +100,7 @@ class Templates(unittest.TestCase):
|
||||||
'basic-syntax10': ("{{ var.otherclass.method }}", {"var": SomeClass()}, "OtherClass.method"),
|
'basic-syntax10': ("{{ var.otherclass.method }}", {"var": SomeClass()}, "OtherClass.method"),
|
||||||
|
|
||||||
# Fail silently when a variable's attribute isn't found
|
# 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
|
# Raise TemplateSyntaxError when trying to access a variable beginning with an underscore
|
||||||
'basic-syntax12': ("{{ var.__dict__ }}", {"var": SomeClass()}, template.TemplateSyntaxError),
|
'basic-syntax12': ("{{ var.__dict__ }}", {"var": SomeClass()}, template.TemplateSyntaxError),
|
||||||
|
@ -116,10 +116,10 @@ class Templates(unittest.TestCase):
|
||||||
'basic-syntax18': ("{{ foo.bar }}", {"foo" : {"bar" : "baz"}}, "baz"),
|
'basic-syntax18': ("{{ foo.bar }}", {"foo" : {"bar" : "baz"}}, "baz"),
|
||||||
|
|
||||||
# Fail silently when a variable's dictionary key isn't found
|
# 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
|
# 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 filter usage
|
||||||
'basic-syntax21': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"),
|
'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'),
|
'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
|
# 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,
|
# In methods that raise an exception without a "silent_variable_attribute" set to True,
|
||||||
# the exception propogates
|
# the exception propogates
|
||||||
|
@ -464,6 +464,14 @@ class Templates(unittest.TestCase):
|
||||||
# translation of a constant string
|
# translation of a constant string
|
||||||
'i18n13': ('{{ _("Page not found") }}', {'LANGUAGE_CODE': 'de'}, 'Seite nicht gefunden'),
|
'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 #############################################################
|
### MULTILINE #############################################################
|
||||||
|
|
||||||
'multiline01': ("""
|
'multiline01': ("""
|
||||||
|
@ -507,7 +515,7 @@ class Templates(unittest.TestCase):
|
||||||
'{{ item.foo }}' + \
|
'{{ item.foo }}' + \
|
||||||
'{% endfor %},' + \
|
'{% endfor %},' + \
|
||||||
'{% endfor %}',
|
'{% endfor %}',
|
||||||
{}, 'INVALID:INVALIDINVALIDINVALIDINVALIDINVALIDINVALIDINVALID,'),
|
{}, ''),
|
||||||
|
|
||||||
### TEMPLATETAG TAG #######################################################
|
### TEMPLATETAG TAG #######################################################
|
||||||
'templatetag01': ('{% templatetag openblock %}', {}, '{%'),
|
'templatetag01': ('{% templatetag openblock %}', {}, '{%'),
|
||||||
|
@ -592,30 +600,44 @@ class Templates(unittest.TestCase):
|
||||||
old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
|
old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
|
||||||
|
|
||||||
# Set TEMPLATE_STRING_IF_INVALID to a known string
|
# 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:
|
for name, vals in tests:
|
||||||
install()
|
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]:
|
if 'LANGUAGE_CODE' in vals[1]:
|
||||||
activate(vals[1]['LANGUAGE_CODE'])
|
activate(vals[1]['LANGUAGE_CODE'])
|
||||||
else:
|
else:
|
||||||
activate('en-us')
|
activate('en-us')
|
||||||
try:
|
|
||||||
output = loader.get_template(name).render(template.Context(vals[1]))
|
for invalid_str, result in [('', normal_string_result),
|
||||||
except Exception, e:
|
('INVALID', invalid_string_result)]:
|
||||||
if e.__class__ != vals[2]:
|
settings.TEMPLATE_STRING_IF_INVALID = invalid_str
|
||||||
failures.append("Template test: %s -- FAILED. Got %s, exception: %s" % (name, e.__class__, e))
|
try:
|
||||||
continue
|
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]:
|
if 'LANGUAGE_CODE' in vals[1]:
|
||||||
deactivate()
|
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
|
loader.template_source_loaders = old_template_loaders
|
||||||
deactivate()
|
deactivate()
|
||||||
settings.TEMPLATE_DEBUG = old_td
|
settings.TEMPLATE_DEBUG = old_td
|
||||||
settings.TEMPLATE_STRING_IF_INVALID = old_invalid
|
settings.TEMPLATE_STRING_IF_INVALID = old_invalid
|
||||||
|
|
||||||
self.assertEqual(failures, [])
|
self.assertEqual(failures, [], '\n'.join(failures))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue