Benno was over, hacking on the LCA 2007 website with me yesterday, and
we hit two gotchas, both I knew about but when I explained them to
him they sounded silly.
c
considered harmful.
c
is a request-local global object that you can attach objects to,
which is useful as a way of passing data from the controller to the
template code – when you’re calling a parameterised template you
might not know at call time what the args the template wants are, but
you can pass them all in on c
. If you’re using some pattern like a
mixin CRUD class for generalising common data operations, then the
code that actually calls the template doesn’t know what the object is,
but the template it’s calling does.
c
has the magical property that it has overloaded __getattr__
to
return an empty string if the attribute is not found. This is a mixed
blessing; your templates can access an attribute that hasn’t been
attached and it’ll mostly cope with it. (Problems happen when you try
to access attributes of nonexistent attributes, and you get the
confusing message ‘str has no attribute X’.)
However, this means you hide bugs; you’ve forgotten to attach the
object you want to c
and then your code runs fine; it’s the users
who find the problem after deployment, not during development. Having
a __getattr__
that throws exceptions means you find out about these
problems a lot sooner.
I think both of these points show that c
in general is a bad idea;
you should make use of explicit args so that your template interface
is clearly defined – I haven’t yet found a nice way of doing it that
is as easy as or better than using c
though.
Myghty expressions that evaluate to False return empty strings.
We had a simple construct like so:
1
| Count: <% len(c.review_collection) %>
|
which has the interesting property of evaluating to ’’ when
c.review_collection
is empty; len()
returns 0 which is False.
This is pretty retarded; I suspect there’s a shortcut along the lines
of:
1
2
3
| content = evaluate_fragment("len(c.review_collection)")
if content:
write(content)
|
when these inline blocks are rendered; the if
block clearly will
fail to trigger when the inline block evaluates to 0
, False
, []
, or
{}
. I can’t think of a case where this is a good thing.
The workaround is to wrap the len()
call in str()
, so that the
fragment doesn’t evaluate to false.
1
| <% str(len(c.review_collection)) %>
|
More gotchas as they come to hand.