[mod_python] Redirect and/or output regeneration with PSP files

Jim Gallacher jpg at jgassociates.ca
Tue May 30 14:31:10 EDT 2006


Anastasios Hatzis (Hatzis Edelstahlbearbeitung) wrote:
> Dear list members,
> 
> I'm still struggling with my PSP files. I like the embedding feature 
> very much, but there is one issue I don't know how to solve and couldn't 
> find a hint in the manual or web. It's about redirecting and/or 
> re-generation of the HTML output depending on object states. I 
> recognized that PSP obviously by default sends header/output directly to 
> the client.
> 
> My first approach: I built a web-layer consisting of PSP-files only. 
> They were called from a browser and each of them imported and accessed 
> appropriate Python modules of my back-end application. These PSP files 
> also included some other PSP files containing HTML snipplets. Structure 
> was simplified like this
> 
> examplePage.psp:
> a) include head.psp
> b) do something with the back-end (imported Python modules with 
> database-access) and build body
> c) include foot.psp
> 
> head.psp and foot.psp also contained some condition-dependent output.
> 
> When specified conditions are met during processing of the request, it 
> is necessary to either redirect to another URL - typical example would 
> be a redirect to a sign-on form - or to re-generate partially or totally 
> the HTML output. Unfortunatelly this can occur anywhere in a), b) or c), 
> where PSP already have been included. I learned that I neither can clear 
> and re-generate the already generated HTML output, nor use a 
> util.redirect to another (PSP) file. Latter leads to the following error 
> message:
> 
> IOError: Cannot redirect after headers have already been sent.
> 
> 
> My second approach was to use mod_python.publisher instead of psp, where 
> methods in py files are called, e.g.
> http://www.modpython.org/examplePage.py/view?objectID=5
> calling in examplePage.py the method view(req, objectID) which should 
> then call an appropriate py-file which is responsible for importing the 
> back-end modules and including PSP-files. For latter I used the template 
> suggestion as made in the modpython manual:
> 
>        template = psp.PSP(self.oReq(), filename='../src/psp/head.psp')
> 
>        template.run({'txtPageTitle':'hello world'})
> 
> 
> Unfortunatelly this doesn't help since PSP.run() is also directly 
> sending header to the browser.
> 
> So, I'm ending with the question if there are options or how-tos in 
> order to
> a) prevent PSP files from sending headers to the browser, so I can later 
> in the process do a full re-direct, like util.redirect, if necessary
> b) collecting PSP file outputs in objects and deciding myself which to 
> send and which not to send, something like:
> 
> template = psp.PSP(self.oReq(), filename='../src/psp/head.psp')
> 
> htmlHead = template.run({'txtPageTitle':'hello world'})
> 
> template = psp.PSP(self.oReq(), filename='../src/psp/ExampleBody.psp')
> 
> htmlBody = template.run({'txtPageTitle':'hello world'})
> 
> template = psp.PSP(self.oReq(), filename='../src/psp/foot.psp')
> 
> htmlFoot = template.run({'txtPageTitle':'hello world'})
> 
> if simpleHead:
> 
>    htmlHead = '<html><body>'
> 
> return htmlHead + htmlBody + htmlFoot
> 
> 
> A link to documentation or how-to would be great or any good idea is 
> appreciated.

No link, but perhaps a clarification. It's not really template.run() 
that is sending the headers.

Generating output with psp is a 2 step process.

1. Parse the psp template and generate python code. Any text outside the 
"<% %>" tag is wrapped in a req.write() call. Anything inside the tag is 
  treated as python code. Stuff inside "<%= %>" is treated as a python 
statement and wrapped by a req.write() call. All this happens in the 
psp.PSP() constructor.

2. Execute the code generated in step 1, with the vars copied into the 
global scope. This happens when run() is called. PSP does not concern 
itself with the response headers.

Ultimately it is the first call to req.write() that causes the headers 
to be flushed. You could still do a redirect in a psp template, but you 
need to make sure this happens *before* any output such as straight html 
or python code. This will not be a very robust solution however, since a 
single unwanted space or other character at the beginning of the 
template file will trigger a req.write() call.

You may find the following pattern more useful. Use publisher or some 
other handler to organize your back-end and page logic. Use a master 
template and pass sub-templates to the master in vars.

mptest.py
---------
def index(req):
     vars = {}
     if some_condition:
         vars['body'] = psp.PSP(req, filename='hi.tmpl')
     else:
         vars['body'] = psp.PSP(req, filename='bye.tmpl')

     master_tmpl = psp.PSP(req, filename='master.tmpl', vars=vars)
     return master_tmpl


master.tmpl
-----------
<html>
<body>
<%= body %>
</body>
</html>

hi.tmpl
---------
<p>Hello world</p>

bye.tmpl
--------
<p>Goodbye world</p>


When <%= body %> is encountered it's __str__ method is called. For a PSP 
instance this causes body.run() to be called. If you need access to vars 
in the sub-templates you'll need to pass vars to each in their constructors.

> Many thanks again and greetings from southern Germany (snowing here, 
> hurrrr)

Now *that* is just crazy. (The snow part, not the fact that you are from 
Southern Germany. ;) )

Jim


More information about the Mod_python mailing list