Log in |
FunctionTemplateThe ProblemYou are developing a Python Product. You have a DTML file which originally contained a little presentation logic, but is now stretching the limits. One option is to convert the DTML into a python function. This would solve your complexity problems, however you would loose the context-oriented approach that makes DTML suitable for Document Templates (that is, the ability to find parameters based on when the template is used, rather than where it is implemented). You could pass the DTML namespace manually, however everywhere that uses this template would have to be changed to use the more complicated calling convention. If you were developing a through-the-web product you might consider using an ExternalMethod. However these dont fit seamlessly into a Python Product because you want to write the python function in the body of your class. A SolutionThe class below borrows a little of DTMLs magic to allow a regular python function to make use of context-oriented parameters. It can be used to wrapper a python function, defined in the body of your class. class MyClass(Implicit,OtherClasses): ....much other stuff goes here.... def example(self,context): return 'A method of %s used by %s' % (self.absolute_url(), context['absolute_url']) example = FunctionTemplate(example) The python function must have two parameters. self is as usual, and the second (context) is the DTML namespace of the caller (referred to in DTML by the name _ You could use that name here too if you prefer). In use, it is exactly like a DTMLMethod. You can call that template through
the web as To call it from python you need to use the DTML calling convention:
FunctionTemplate.pyfrom DocumentTemplate.DT_Util import TemplateDict,InstanceDict from ExtensionClass import Base from Acquisition import Implicit class FunctionTemplate(Implicit): """A wrapper to use a python function as a Document Template. This is useful if you want to promote a DTMLMethod into a python function (to better handle complex logic) but to not want to loose context-oriented nature of DTML. """ isDocTemp=1 # Prevent acquisition from our container index_html=None # Templates masquerade as functions: class func_code: pass func_code=func_code() func_code.co_varnames='self','REQUEST' func_code.co_argcount=2 func_defaults=() # Only used for wrapping trusted content validate = None def __init__(self,fn,**globals): self._fn = fn self._globals = globals def __call__(self,client=None,context={},**extras): md = TemplateDict() md.validate = self.validate md._push(self._globals) if context: md._push(context) if client is not None: md._push(InstanceDict(client, md)) # Circ. Ref. 8-| if extras: md._push(extras) try: real_self = self.aq_inner.aq_parent fn = self._fn return fn(real_self,md) finally: while md: md._pop() |