[Zope] DTML calling explained

Evan Simpson evan@4-am.com
Wed, 2 May 2001 11:51:45 -0500


From: "Brad Clements" <bkc@murkworks.com>
> target = traverse_subpath[-1]
> if target == 'ProductInfo.htm':
>     container.Products(context,productid=traverse_subpath[0])

In this case, you probably want to use:

container.Products(container.Products, context.REQUEST,
productid=traverse_subpath[0])

..or..

req = context.REQUEST
req.set('productid', traverse_subpath[0])
container.Products(context, req)

Lecture time:

DTML Methods have the call signature:
 (client=None,
  REQUEST={},
  RESPONSE=None,
  **kw)

When a Method is the target of a URL, ZPublisher calls it like this:

method(context, REQUEST, RESPONSE)

The Method creates a namespace and pushes some standard shared data onto
it, followed by the REQUEST and the context object (the object on which
the method is being called).  RESPONSE is just used to set up headers
properly, and can be ignored; You shouldn't pass it yourself.

Now the context is on top of the namespace, so the Method has access to
its attributes (including acquired ones).  If a name is not found in the
context, the REQUEST is searched next.

When DTML calls a DTML Method using <dtml-var method>, it does so like
this:

method(None, _)

The trick, here, is that the namespace "_" already contains the client,
request, and keyword arguments that were passed to the *calling* DTML
Method.  The called Method notices that it has been given a valid
namespace, and adopts it.  There is no client, and no keyword arguments,
so nothing else gets pushed onto the namespace.  The called method has
access to the exact same set of names as the caller did when it
performed the call.

When you call a DTML Method from Python, whether it be filesystem code,
a Script, or an expression in DTML or a Page Template, you must decide
what information to make available to it.  If you have access to a
properly constructed namespace (it was passed to you, or bound by your
Script) you can simply pass it along:

method(None, namespace)

Otherwise, you will probably want to call it like ZPublisher does,
passing a client and the REQUEST.  A reasonable client to choose is the
object from which you are acquiring the method (assuming that you
haven't simply been handed the method), and you can probably acquire the
REQUEST from it as well (or from your container).  This gives you:

container.Products(container.Products, context.REQUEST)

If you want to pass additional data, you have two choices.  You can use
keyword arguments, in which case you might override names in the client
or REQUEST.  Second, you can add the data to the REQUEST using set(), in
which case it might be hidden by names in the client or REQUEST.

Cheers,

Evan @ digicool & 4-am