Skip to content

Commit

Permalink
Fixes #2637 -- Clarified handling of TEMPLATE_STRING_IF_INVALID, espe…
Browse files Browse the repository at this point in the history
…cially 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
  • Loading branch information
freakboy3742 committed Sep 4, 2006
1 parent 3a919db commit cfe7794
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 26 deletions.
3 changes: 2 additions & 1 deletion AUTHORS
Expand Up @@ -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/>
Expand All @@ -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/>
Expand Down
7 changes: 5 additions & 2 deletions django/template/__init__.py
Expand Up @@ -549,9 +549,12 @@ def resolve(self, context, ignore_failures=False):
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:
Expand Down
8 changes: 4 additions & 4 deletions django/template/defaulttags.py
Expand Up @@ -86,7 +86,7 @@ def render(self, context):
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:
Expand Down Expand Up @@ -212,13 +212,13 @@ def __init__(self, target, expression, var_name):
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)
Expand Down
16 changes: 13 additions & 3 deletions docs/templates_python.txt
Expand Up @@ -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
---------------------------- ----------------------------
Expand Down
54 changes: 38 additions & 16 deletions tests/regressiontests/templates/tests.py
Expand Up @@ -84,7 +84,7 @@ def test_templates(self):
'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),
Expand All @@ -100,7 +100,7 @@ def test_templates(self):
'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),
Expand All @@ -116,10 +116,10 @@ def test_templates(self):
'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!"),
Expand Down Expand Up @@ -158,7 +158,7 @@ def test_templates(self):
'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
Expand Down Expand Up @@ -464,6 +464,14 @@ def test_templates(self):
# 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': ("""
Expand Down Expand Up @@ -507,7 +515,7 @@ def test_templates(self):
'{{ item.foo }}' + \ '{{ item.foo }}' + \
'{% endfor %},' + \ '{% endfor %},' + \
'{% endfor %}', '{% endfor %}',
{}, 'INVALID:INVALIDINVALIDINVALIDINVALIDINVALIDINVALIDINVALID,'), {}, ''),


### TEMPLATETAG TAG ####################################################### ### TEMPLATETAG TAG #######################################################
'templatetag01': ('{% templatetag openblock %}', {}, '{%'), 'templatetag01': ('{% templatetag openblock %}', {}, '{%'),
Expand Down Expand Up @@ -592,30 +600,44 @@ def test_template_loader(template_name, template_dirs=None):
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()

0 comments on commit cfe7794

Please sign in to comment.