Callable context variables do not get called during resolution.

19 views
Skip to first unread message

Peter Rowell

unread,
May 9, 2008, 8:12:52 PM5/9/08
to Django developers
Short version:

Callable context variables do not get called during resolution. If
invoked as a simple variable they return something like <function foo
at 0x8c454fc>. If used in a for loop, they blow up the template. I'm
running trunk:7374

Longer version:

In order to delay a *very* expensive DB operation, I decided to put it
in a function and pass the function as a context variable. The
template uses fragment caching with vary_on, so only if the template
fragment is stale would the function be called and invoke the heavy
lifting.

Sounded great to me ... but it blows up with a 500 error.

Simple example:
=========================
def some_view(request):
def foo():
return [1, 2, 3]
return render_to_response('some_template.html', {'foo': foo, })

some_template.html:
{{foo}} ==>> yields <function foo at 0x8c454fc>
{% for x in foo %}{{x}}{% endif %} ==>> 500 error

=========================

The problem seems to be in (or is related to)
template.__init__.Variable._resolve_lookup.

[...]
current = context
for bit in self.lookups:
try: # dictionary lookup
current = current[bit]
## the following fixed the problem (but needs error handling?)
if callable(current):
current = current()
##
except (TypeError, AttributeError, KeyError):
try: # attribute lookup
current = getattr(current, bit)
if callable(current):
if getattr(current, 'alters_data', False):
current =
settings.TEMPLATE_STRING_IF_INVALID
else:
try: # method call (assuming no args
required)
current = current()
[...]


Note there is a check for callable, but only if it's a method. Is
there some reason why callability is OK in some context variables/
expressions and not others?

Peter Rowell

unread,
May 9, 2008, 8:23:39 PM5/9/08
to Django developers
BTW, I tried to add a comment on this to http://code.djangoproject.com/ticket/7153,
but got "Internal Server Error (Submission rejected as potential
spam)", which struck me as rather rude. :-)

Ionut Ciocirlan

unread,
May 9, 2008, 8:43:40 PM5/9/08
to django-d...@googlegroups.com
I was just gonna point you to #7153.

I don't know why it was decided only object methods should be called. I'm
waiting for the review of the patch.

Regards

Waylan Limberg

unread,
May 9, 2008, 8:44:00 PM5/9/08
to django-d...@googlegroups.com
On Fri, May 9, 2008 at 8:12 PM, Peter Rowell <hedr...@gmail.com> wrote:
>
[snip]

> In order to delay a *very* expensive DB operation, I decided to put it
> in a function and pass the function as a context variable. The
> template uses fragment caching with vary_on, so only if the template
> fragment is stale would the function be called and invoke the heavy
> lifting.
>
[snip]

QuearySets are lazy and, as I understand it, don't get evaluated until
they are called in the template. Therefore, if a quearyset call is
wrapped in an if statement in the template that evaluates to false,
the query should never happen. What are you trying to accomplish that
is any different?

Well, spam littered tickets would be worse. It's the lesser of two
evils. You can register [1] for an account and bypass the spam filter
-- Or, if you don't what to register, create a session [2] to get the
spam filter to go easy on you.

[1]: http://www.djangoproject.com/accounts/register/
[2]: http://code.djangoproject.com/settings

--
----
Waylan Limberg
way...@gmail.com

Peter Rowell

unread,
May 9, 2008, 10:01:04 PM5/9/08
to Django developers
> QuerySets are lazy [...] What are you trying to accomplish that is any different?

I'm working around the fact that Django's templates can't take
arguments. So I'm calling formatting functions which *do* take
arguments, one of which is a QuerySet. Of course, I can't pass args to
a function in the template system, so to avoid premature evaluation of
the expensive large-query/format I'm currying the function and trying
to pass *that* as a context variable. And that's when it hit the fan.

<rant>

After this site I swear I'm going with either Jinja2 or Mako
templates. I'm all for not confusing non-programming designers with
nasty programming concepts, but my very good designer runs screaming
from the room when I try to teach her the simplest template things.
Therefore, I'm the one having to deal with the crippled templating
system. It's great to keep app-logic out of the templates, but Django
makes a lot of *presentation logic* almost impossible (or extremely
clumsy) to implement. "ifequal foo bar" instead of "if foo == bar"?
With a different end tag? Can't combine and's and or's in a single if?
Give me a break.

Now that Malcolm has subclassable models working, my number one
complaint is the templating system. Number two is single DB, but I can
work around that more easily.

</rant>

James Bennett

unread,
May 9, 2008, 10:05:06 PM5/9/08
to django-d...@googlegroups.com
On Fri, May 9, 2008 at 9:01 PM, Peter Rowell <hedr...@gmail.com> wrote:
> Now that Malcolm has subclassable models working, my number one
> complaint is the templating system. Number two is single DB, but I can
> work around that more easily.

You probably should take a look at the ORM code.


--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."

Peter Rowell

unread,
May 9, 2008, 10:27:41 PM5/9/08
to Django developers
> You probably should take a look at the ORM code.

OK, I'll bite. I kind of doubt he rewrote the templating system in the
ORM, so you must be inferring that multiple database support was part
of the qs-refactoring. I just checked out trunk and I'll admit I'm
working with a one-beer-handicap at the moment, but nothing jumped out
and said "MULTIPLE DATABASE SUPPORT!!!"

Please, make my day.

James Bennett

unread,
May 9, 2008, 10:47:15 PM5/9/08
to django-d...@googlegroups.com
On Fri, May 9, 2008 at 9:27 PM, Peter Rowell <hedr...@gmail.com> wrote:
> OK, I'll bite. I kind of doubt he rewrote the templating system in the
> ORM, so you must be inferring that multiple database support was part
> of the qs-refactoring. I just checked out trunk and I'll admit I'm
> working with a one-beer-handicap at the moment, but nothing jumped out
> and said "MULTIPLE DATABASE SUPPORT!!!"

Once again, there's no high-level support, but qsrf added some
infrastructure which will make an eventual high-level implementation
easier, and which makes writing a manual implementation right now a
bit less tedious. Look at the constructor of
django.db.models.sql.query.Query for the "connection" argument.

Peter Rowell

unread,
May 9, 2008, 11:39:52 PM5/9/08
to Django developers
> Look at the constructor of django.db.models.sql.query.Query for the "connection" argument.

You know, I grepped on that but didn't see anything interesting. Now
with a two-beer-handicap, I'll go with Blanche DuBois -- Tomorrow is
another day.

Reply all
Reply to author
Forward
0 new messages