I've had a review of the current and historical tickets, and I think that
do_not_call_in_templates is sufficient. A template tag to control such a
low level feature of the templating system seems a bit niche. If you are
worried about adding attributes with objects from third party libraries,
you can always proxy them with a custom wrapt.ObjectProxy before passing to
the template (docs: https://wrapt.readthedocs.io/en/latest/wrappers.html ),
for example:

In [1]: import wrapt

In [2]: class DoNotCallInTemplatesProxy(wrapt.ObjectProxy):
   ...:     do_not_call_in_templates = True
   ...:

In [3]: class Dog:
   ...:     def __call__(self):
   ...:         return 'foobar'
   ...:
   ...: third_party_callable = Dog()
   ...:

In [4]: x = DoNotCallInTemplatesProxy(third_party_callable)

In [5]: x.do_not_call_in_templates
Out[5]: True

In [6]: third_party_callable.do_not_call_in_templates
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-1ec2cadaa18a> in <module>
----> 1 third_party_callable.do_not_call_in_templates

AttributeError: 'Dog' object has no attribute 'do_not_call_in_templates'

On Sun, 24 Feb 2019 at 02:33, Alex Epshteyn <[email protected]>
wrote:

> 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
> <https://groups.google.com/d/msgid/django-developers/962bead6-e186-416b-a18a-3e0f2ad23da9%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
> For more options, visit https://groups.google.com/d/optout.
>


-- 
Adam

-- 
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/CAMyDDM1aPSqVuhhNKpjVvVzYp5hbpNnMddS0m%2BOuo5mLObmdpg%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to