[Zope] avoiding nested DTMLs

Luciano Ramalho luciano@hiper.com.br
Tue, 20 Mar 2001 04:07:29 -0300


Timothy Wilson wrote:
> Cool. That worked. I need to read up on that _[] stuff. Thanks.

Knowing Python really helps in decrypting _['x']. 

First of all, _ is just a valid, albeit weird, variable name in Python.
_ does not have any special powers in Python (as $_ has in Perl). 

The syntax y['x'] means: fetch item 'x' from dictionary y. y is not a
real dictionary, but rather an object that behaves mostly like a
dicionary. So _['x'] just means, fetch item 'x' from the namespace. In a
dicionary, whatever goes inside the [] is called a "key". Now, what
happens if I write y[x] and not y['x']? That is an indirection, is is
discussed a few paragraphs below.

Now, although _ would be just a regular variable name in Python, in DTML
expressions it always refers to the namespace object which, like I said,
is not really an object but just pretends to be one. The symbol for the
namespace object could be 'namespace', 'super_dict', or '_ground'. But
it was called '_'. Zopistas I've met pronounce _ as "under" or
"namespace", depending on how formal they are.

Like a dicionary, _ has a has_key method, which you call like this:
"_.has_key('x')". Unlike a regular dictionary, it gives you access to
dozens of Python functions such as int "_.int(x)", and whole modules
such as string, DateTime and random: "_.random.randint(x,y)". On the
other hand, _ does not have some usual dicionary methods, like keys() or
items(), which are useful to retrieve the entire contents of a
dictionary.

WHAT _['x'] REALLY DOES

The strangest thing about _ is that when you access something in it,
like _['x'], it doesn't just fetch the x object for you. Whenever _
retrives an object, it checks whether the object is callable (most
things are callable objects in Zope). If the object is callable, _ calls
or invokes it, which means the object is executed as code, and then what
you get is not the object, but the result of executing it, whis is
normally a string containing HTML. This is the same behaviour of the
dtml-var "name" attribute. So:

(1) <dtml-var name="x"> 

gives the same result as

(2) <dtml-var expr="_['x']">

but is not the same as

(3) <dtml-var expr="x">.

For instance, if x is a DTML Method, (1) and (2) execute, or render it,
replacing DTML tags within x by their results, and returning plain HTML.
Then if you want to access some z attribute of x, you get the dreadful
"String object has no attribute z", which tells you that x is no longer
a rich object like a folder or a document, but is now a flat string
containing the HTML resulting from rendering the x object. 

With syntax (3) you just fetch it, which depending on context would
display the unrendered DTML code.

If you need to indirectly fetch an object without executing it, you have
to use another of _'s methods, getitem. The expression "_.getitem('x')"
returns a reference to the object called x, without invoking it. 

INDIRECTION

Notice difference between:

(2) <dtml-var expr="_['x']">

(4) <dtml-var expr="_[x]">

These mean COMPLETELY DIFFERENT things. In (2) you want to get the
object called 'x' from the namespace dictionary. The key you are using
is the string 'x'. 

In (4), you are getting the object whose name is stored in variable x.
The key, in this case, is whatever is referred by the variable x. If x
refers to the string 'ni', then _[x] means _['ni'], that is, fetch the
object called 'ni'. 

That is why _[x] is called an indirection: in our example you are not
fetching 'ni' directly, but indirectly through the x variable. The next
time _[x] is evaluated, x may refer to 'sikander', and then _[x] may
yield a totally different result.

The same rationale goes for 

(5) <dtml-var expr="_.getitem('x')">

(6) <dtml-var expr="_.getitem(x)">

The result of (5) is the same as

(3) <dtml-var expr="x">

so you would never use (5) in real code. 

But (6) is useful to fetch an object indirectly without executing it.


AN INTERESTING EXPERIMENT

Try this: within a folder, create two methods with ids 'method1' and
'method2'. Leave 'method2' with its default content, but replace the
content of 'method1' with this, then View it.

<dtml-var standard_html_header>

<dtml-var name="method2">
<hr>
<dtml-var expr="method2">
<hr>
<dtml-var expr="_['method2']">
<hr>
<dtml-var expr="_.getitem('method2')">

<dtml-var standard_html_footer>

If my explanations were any good, you should understand what is going on
now.

The whole _ issue is why, whenever I teach Zope, I include at least one
hour of "instrumental Python".
And while playing with the Python interpreter, I always make the
students create and do lots of operations with a dictionary called _.


--
Best regards,

Luciano