Opened 13 years ago

Closed 13 years ago

Last modified 13 years ago

#16389 closed Bug (invalid)

In Django 1.3, dictionary lookup now calls the value if callable making classes unusable

Reported by: Domen Kožar Owned by: nobody
Component: Template system Version: 1.3
Severity: Normal Keywords: template lookup dictionary callable
Cc: domen@… Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I used to have context processor that returned a dict of all registered model classes. This used to work in Django 1.2.x very well and removed the need of redundant imports.

Let's refer to documentation:

If any part of the variable is callable, the template system will try calling it.

And the change:

Changed in Django 1.3: Previously, only variables that originated with an attribute lookup would be called by the template system. This change was made for consistency across lookup types.

This is a pretty big design change, I wonder what "consistency" implies here. For example, you can not do anymore in the templates:

    {{ User.objects.all }}

Because your User will become and User instance, since it's looked up in Context, which is an dictionary. As a side effect, there is no way you can use class in the template anymore.

Change History (2)

comment:1 by Luke Plant, 13 years ago

Resolution: invalid
Status: newclosed

That's correct. Yes, it was a significant change, but not a change in the philosophy of the template at all - merely the conclusion of decisions we had already made. #7153 has some background, and this discussion: https://groups.google.com/d/topic/django-developers/rgkNIu4XBKI/discussion

There were various consistency problems. The main one was that passing plain callables (e.g. a lambda or bound method) into a Context did not work as expected, when the same bound method arrived at as part of lookup done inside the template would get called. Another inconsistency is the fact that if you get to a class via an attribute lookup, it would get called (e.g. {{ some_model_form.model }} would result in 'model' being called), but if that class was passed in as a top level variable it would not get called. This was confusing and unnecessary.

Using classes directly is a strange thing to do from the point of view of a template author - what exactly is a 'class'? There is also an easy work-around for your problem - create a simple dict/class with the attributes of each model that you need e.g. instead of:

    def all_models(request):
        return dict([(klass.__name__, klass) for klass in get_models()])

do:

    def all_models(request):
        return dict([(klass.__name__, {'objects': klass.objects}) for klass in get_models()])

comment:2 by Domen Kožar, 13 years ago

This change will break quite some production ecosystems and it took me quite some time to debug (since templates eats all error messages). Currently there is no way to pass classes to templating system, I guess Django is more and more leaning to the point you can only pass variables, list of variables or dict of variables to it. Everything else bites you back.

This approach forces you to use refactored views to utility functions, which is rather writing bolierplate code because your templating engine does some magic calling leaving you grounded.

My proposition is to explicitly add documentation as examples that used to work Django 1.2, because the Django 1.3 change line might not be enough to trigger association of using classes and instances in templates.

Note: See TracTickets for help on using tickets.
Back to Top