What about adding a filter |defined that returns True if a variable is 
defined, False otherwise? It may not solve any problems when it's left 
implicit, but at least it allows users the explicitly define the behaviour 
for non-existing template variables, and it makes the intention of the 
template code crystal clear. And of course it's fully backwards compatible. 
The drawback is perhaps the resulting verbosity of if statements:

    {% if user.role|defined and roles.staff|defined and user.role == 
roles.staff %}
        [sensitive information here]
    {% endif %}

Marten

On Monday, February 27, 2017 at 11:36:09 AM UTC+1, Luke Plant wrote:
>
>
> On 26/02/17 00:44, Karen Tracey wrote:
>
> On Sat, Feb 25, 2017 at 2:10 PM, Tim Graham <[email protected] 
> <javascript:>> wrote:
>
>> I think any use of undefined template variables should raise an 
>> exception. In the long run, keeping a setting to allow some other behavior 
>> seems confusing and, considering the case of templates that might be reused 
>> in different projects with different settings, even dangerous.
>>
>
> I think I'm confused...Django templates have allowed use of undefined 
> variables and documented their use as evaluating to the empty string for as 
> long as I recall. Wouldn't a change to instead raise exceptions be a major 
> backwards-incompatibility?
>
> https://docs.djangoproject.com/en/1.7/topics/templates/#variables said 
> "If you use a variable that doesn’t exist, the template system will insert 
> the value of the TEMPLATE_STRING_IF_INVALID setting, which is set to '' 
> (the empty string) by default."
>
>
> This behaviour applies only to what happens when the variable is rendered. 
> In the context of `if` tags, undefined variables get converted to `None`. 
> This behaviour is documented - 
> https://docs.djangoproject.com/en/dev/ref/templates/api/#how-invalid-variables-are-handled
>  
> but perhaps not in as much detail as necessary.
>
> The question is about changing this, especially due to templates like this:
>
>     {% if user.role == roles.staff %}
>         [sensitive information here]
>     {% endif %}
>
> If:
> 1) you forget to provide both user and role to template context or
> 2) 'role' or 'staff' are invalid attributes, or
> 3) 'role' returns None but 'staff' is an invalid attribute, for example,
> 4) various combinations of these
>
> then this results in the sensitive info being shown, because `None == 
> None`. The proposal is to introduce a value that doesn't not compare equal 
> to itself and avoid this kind of issue.
>
> Having thought about it more, I've realised that this really isn't going 
> to work at all.
>
> First attempt:
>
>     class Undefined1(object):
>         def __eq__(self, other):
>             return False
>     
> If we use this, then consider the following template code:
>
>     {% if user.role != roles.staff %}
>         This info is private, sorry
>     {% else %}
>         [sensitive information here]
>     {% endif %}
>
>
> The default implementation of != means we will again get the sensitive 
> data shown for some of the error situations given above (e.g. 1 and 2). 
> Second attempt:
>
>     class Undefined2(object):
>         def __eq__(self, other):
>             return False
>         def __new__(self, other):
>             return False
>
> This object is neither equals nor not-equals to itself or anything else. 
> But consider this template:
>
>
>     {% if user.role == roles.customer %}
>         This info is private, sorry
>     {% else %}
>         [sensitive information here]
>     {% endif %}
>
> With `Undefined2` being used for invalid attributes, we again get the 
> sensitive information being shown in the case of developer error and 
> missing attributes. Plus, we now have the situation that `{% if a != b %}` 
> is not the same as `{% if not a == b %}`, which is very confusing behaviour 
> for most people.
>
> In other words, we are getting into the territory of needing a value that 
> behaves like NULL in SQL. Even if there were no implementation issues (and 
> there will be lots, because operators like 'or' 'and' etc. would need to 
> cope with this, not to mention 3rd party code), and there were no backwards 
> compatibility issues (there are lots), this would be extremely dubious to 
> me, because ternary logic is extremely tricky.
>
> The only sensible alternative to current behaviour is to raise exceptions, 
> but huge backwards incompatibility issues I think rule this out. If I were 
> to start again, I think I would make the template system not silence any 
> errors you would normally get in Python, but just have some special syntax 
> for converting non-existent data or attributes into None. But I don't have 
> that time machine.
>
> Regards,
>
> Luke
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/07061e34-4982-4fcd-a015-7d33bb66904b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to