New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed #24977 -- Made template tags treat undefined variables not equal to None. #7901
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,6 +117,32 @@ def __str__(self): | |
return self.msg % self.params | ||
|
||
|
||
class UndefinedVariable: | ||
""" | ||
This is the result of evaluating a template expression that | ||
contains an undefined variable. | ||
|
||
An instance of this class evaluates to False in a boolean | ||
context. All instances are treated as being equal to each other. | ||
""" | ||
|
||
def __init__(self, var_rep=""): | ||
""" | ||
`var_rep` is the string value that this expression should | ||
expand to when displaying in a template. | ||
""" | ||
self.var_rep = var_rep | ||
|
||
def __bool__(self): | ||
return False | ||
|
||
def __eq__(self, other): | ||
return isinstance(other, UndefinedVariable) | ||
|
||
def __str__(self): | ||
return self.var_rep | ||
|
||
|
||
class Origin: | ||
def __init__(self, name, template_name=None, loader=None): | ||
self.name = name | ||
|
@@ -672,22 +698,19 @@ def __init__(self, token, parser): | |
self.filters = filters | ||
self.var = var_obj | ||
|
||
def resolve(self, context, ignore_failures=False): | ||
def resolve(self, context): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was assuming this wasn't necessary since |
||
if isinstance(self.var, Variable): | ||
try: | ||
obj = self.var.resolve(context) | ||
except VariableDoesNotExist: | ||
if ignore_failures: | ||
obj = None | ||
else: | ||
string_if_invalid = context.template.engine.string_if_invalid | ||
if string_if_invalid: | ||
if '%s' in string_if_invalid: | ||
return string_if_invalid % self.var | ||
else: | ||
return string_if_invalid | ||
string_if_invalid = context.template.engine.string_if_invalid | ||
if string_if_invalid: | ||
if '%s' in string_if_invalid: | ||
obj = UndefinedVariable(string_if_invalid % self.var) | ||
else: | ||
obj = string_if_invalid | ||
obj = UndefinedVariable(string_if_invalid) | ||
else: | ||
obj = UndefinedVariable() | ||
else: | ||
obj = self.var | ||
for func, args in self.filters: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -271,6 +271,25 @@ If you wish to keep this restriction in the admin when editing users, set | |
admin.site.unregister(User) | ||
admin.site.register(User, MyUserAdmin) | ||
|
||
Undefined variables in templates are distinguished from `None` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. double backticks must be used for correct formatting of the rendered docs |
||
-------------------------------------------------------------- | ||
|
||
In templates, undefined values and non-existent attributes of objects | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nonexistent (no dash) |
||
are now treated as being instances of a special type that don't | ||
compare equal to any other Python object (including `None`). In older | ||
versions of Django, such a variable would be treated as having a value | ||
of `None`, meaning that comparisons could succeed unexpectedly due to | ||
a typo or refactoring:: | ||
|
||
{% if user.pk == product.product_owner_id %} | ||
If the product_owner_id field is renamed, this private data would | ||
have been rendered for users who are not logged in. | ||
{% endif %} | ||
|
||
Such code will now fail the comparison if one side contains an | ||
undefined value. You should avoid deliberately relying on undefined | ||
values in your templates. | ||
|
||
Miscellaneous | ||
------------- | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,12 +20,9 @@ def test_exception02(self): | |
""" | ||
Raise exception for invalid variable template name | ||
""" | ||
if self.engine.string_if_invalid: | ||
with self.assertRaises(TemplateDoesNotExist): | ||
self.engine.render_to_string('exception02') | ||
else: | ||
with self.assertRaises(TemplateSyntaxError): | ||
self.engine.render_to_string('exception02') | ||
with self.assertRaisesRegexp(TemplateSyntaxError, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use:
`assertRraisesMessage |
||
"Got this from the 'nonexistent' variable"): | ||
self.engine.render_to_string('exception02') | ||
|
||
@setup( | ||
{'exception03': "{% extends 'inheritance01' %}" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -543,10 +543,12 @@ def test_if_tag_badarg02(self): | |
output = self.engine.render_to_string('if-tag-badarg02', {'y': 0}) | ||
self.assertEqual(output, '') | ||
|
||
@setup({'if-tag-badarg03': '{% if x|default_if_none:y %}yes{% endif %}'}) | ||
@setup({'if-tag-badarg03': '{% if x|default_if_none:y %}yes{% else %}no{% endif %}'}) | ||
def test_if_tag_badarg03(self): | ||
# default_if_none is not triggered since undefined variables | ||
# are not None. | ||
output = self.engine.render_to_string('if-tag-badarg03', {'y': 1}) | ||
self.assertEqual(output, 'yes') | ||
self.assertEqual(output, 'no') | ||
|
||
@setup({'if-tag-badarg04': '{% if x|default_if_none:y %}yes{% else %}no{% endif %}'}) | ||
def test_if_tag_badarg04(self): | ||
|
@@ -577,7 +579,7 @@ def test_if_is_variable_missing(self): | |
@setup({'template': '{% if foo is bar %}yes{% else %}no{% endif %}'}) | ||
def test_if_is_both_variables_missing(self): | ||
output = self.engine.render_to_string('template', {}) | ||
self.assertEqual(output, 'yes') | ||
self.assertEqual(output, 'no') | ||
|
||
@setup({'template': '{% if foo is not None %}yes{% else %}no{% endif %}'}) | ||
def test_if_is_not_match(self): | ||
|
@@ -599,6 +601,14 @@ def test_if_is_not_variable_missing(self): | |
@setup({'template': '{% if foo is not bar %}yes{% else %}no{% endif %}'}) | ||
def test_if_is_not_both_variables_missing(self): | ||
output = self.engine.render_to_string('template', {}) | ||
self.assertEqual(output, 'yes') | ||
|
||
@setup({'if-tag-invalid-member': '{% if x.non_existent == None %}yes{% else %}no{% endif %}'}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. member -> attribute |
||
def test_if_tag_invalid_member(self): | ||
""" | ||
Invalid attributes should not be regarded as equal to None | ||
""" | ||
output = self.engine.render_to_string('if-tag-invalid-member', {'x': TestObj()}) | ||
self.assertEqual(output, 'no') | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think
var_repr
is more explanatory than justrep
. Also, use single quotes rather than double quotes unless the string contains a single quote.