The reason for this suggestion is over the years, while writing 
templates, I've had to spend a lot of time trying to figure out why I was 
getting an unexpected value (often an empty string) substituted for a 
template variable when that variable clearly had a different value.  

The reason that happens, of course, is that the 
​django.template.base.Variable._resolve_lookup 
<https://github.com/django/django/blob/21ff23bfeb4014bceaa3df27677fb68409c0634d/django/template/base.py#L851>
 
method tries to invoke every callable value in a variable's resolution 
path, including the variable itself.  If that callable is a function with 
parameters, for example, the invocation raises an exception and you end up 
with an empty string (or whatever value you use for the string_if_invalid 
setting) as a result.  If it's a different kind of callable, like a class 
or a regular object that implements __call__ 
<https://code.djangoproject.com/ticket/15791> for whatever reason, you just 
end up with something other than what you wanted.

While automatically invoking callables makes sense when the current "bit" 
in the resolution path is a method and the previous "bit" was an instance, 
it is very surprising when the "callable" value is not actually a method 
(e.g. it's a class or a standalone function), in which case I don't think 
it makes sense to call it automatically.  In fact, this goes against the 
fundamental Python (and Django) philosophy of *"Explicit is better than 
implicit".*

There are legitimate cases where you might want to pass something like a 
class object to a template and to not have it mysteriously replaced by an 
arbitrary instance of that class (see #30197 
<https://code.djangoproject.com/ticket/30197>). I also think that the 
existing workaround of setting a do_not_call_in_templates 
<https://code.djangoproject.com/ticket/15791> attribute on such object is 
insufficient to cover all such cases (e.g. when the object is a class or 
function that comes from some library and you don't want to risk messing 
with it).

My comment on #30197 <https://code.djangoproject.com/ticket/30197#comment:5> 
suggested 
replacing this implicit behavior with an explicit tag like {% call foo %} 
instead 
of {{ foo }} (this example assumes that foo is callable).  Although that 
would be a breaking change, I think it might be worth considering because 
it would be in the spirit of upholding the *"Explicit is better than 
implicit"* principle, and prevent a frequently-occurring problem for 
template authors (which is evidenced by the prevalence of tickets and 
StackOverflow questions about this topic; e.g. 
https://stackoverflow.com/questions/6861601/cannot-resolve-callable-context-variable
)

However, in #30205 <https://code.djangoproject.com/ticket/30205>, I am 
proposing a *non-breaking *change to solve this problem -- a new template 
tag similar to autoescap 
<https://django.readthedocs.io/en/stable/ref/templates/builtins.html#autoescape>
e 
<https://django.readthedocs.io/en/stable/ref/templates/builtins.html#autoescape>,
 
which could be used like this:

{% callables off %}
  <div>The class name is {{ foo.bar|type_name }}</div>{% endcallables %}


(in the above example foo is an object containing an attribute named "bar" 
whose value is a class, and |type_name is a user-defined filter that 
returns the "__name__" attribute of its argument)

-- 
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/962bead6-e186-416b-a18a-3e0f2ad23da9%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to