Pylons and pre- and postprocess functions?

4 views
Skip to the first unread message

durumdara

unread,
2 May 2007, 14:33:0202/05/2007
to pylons-discuss
Hi!

I have a special question.

I want to centralize, and globalize some of the website's codes.

Commonly these codes are initialize the handlers (pre-phase: check the
rights to the selected object (page), load session datas, set global
variables, initialize databases); or finalize the result (post-phase:
format it, set the type of the output, and force the main template on
it).

In the modpy in the one of the working modes I can write one handler
procedure: this procedure is makes the pre-phase, call the page
specific functions, and later get the result and force the main skin
(template) on it.

This model is makes the coding easy.
Because I can write the common init/done functions in a global module,
and they are run without my procedure call.

Example:
...
class XController...:
def Page1(self):
init()
res = ... # makeresultofpage1()
done()

def Page2(self):
init()
res = ... # makeresultofpage1()
done()
...

With post and preprocess:

def Page1(self):
return ... (some result)

def Page2(self):
return ... (some result)

...
# global handler

def preprocess():
loadsession()
checkrights()
if not haverights:
makeerror(_no_rights_)
opendatabases()
loadformvariables()
getcurrentcontroller()
getcurrentpage()

def postprocess(result):
savesession()
closedatabases()
load_main_template()
rendres = render_to_main_template(result)
writeoutput(rendres)

# The main handler
preprocess()
if haverighttopage:
result = call(currentcontroller, currentpage)
postprocess(result)

So I want to ask that how to register these procedures in pylons, or
how to avoid the repeating of some codes (like page level
"init(); .... ; done()", or same codes that initialize or finalize the
handlers - see the example 1.)?


Thanks for your help:
dd

Ben Bangert

unread,
2 May 2007, 15:16:3602/05/2007
to pylons-...@googlegroups.com
On May 2, 2007, at 11:33 AM, durumdara wrote:

> Commonly these codes are initialize the handlers (pre-phase: check the
> rights to the selected object (page), load session datas, set global
> variables, initialize databases); or finalize the result (post-phase:
> format it, set the type of the output, and force the main template on
> it).

In Pylons, you'd want to probably set __before__ and __after__ blocks
in your main controller (in lib/base.py). These will be called before
the controllers action and afterwards. This is the common place to
setup:
- Global objects and database sessions

Setting the type of output is handled differently depending on your
output. For example, if you use jsonify it will set the proper
content-type, and the XMLRPC controller will set the proper content
type as well. Centralizing the output-type setting thus doesn't make
much sense as different controllers really need to handle that.

You can set __before__ and __after__ methods in individual
controllers too, which is likely where you'll want to declare
permissions required for individual actions or the whole controller.

Session data is loaded the first time you actually use it, this
avoids unnecessary filesystem hits until you actually use the session.

> In the modpy in the one of the working modes I can write one handler
> procedure: this procedure is makes the pre-phase, call the page
> specific functions, and later get the result and force the main skin
> (template) on it.

This is handled by a layout that is inherited, depending on the
template language you use. Myghty, Mako, and Genshi all support
template inheritance which is where you would setup the main skin.

> So I want to ask that how to register these procedures in pylons, or
> how to avoid the repeating of some codes (like page level
> "init(); .... ; done()", or same codes that initialize or finalize the
> handlers - see the example 1.)?

Essentially, as you're coming from Zope, the biggest hurdle will be
clearing your mind of the "Zope way" to accomplish the task. A recent
recipe on using the SOAP service for example:
http://docs.pythonweb.org/display/pylonscookbook/Making+a+SOAP
+Controller+with+Optio%27s+Soaplib

And the doc on using MoinMoin under Pylons:
http://docs.pythonweb.org/display/pylonscookbook/A+Pylons+controller
+with+MoinMoin+as+a+WSGI+callable

Both rely on the fact that you can just hook WSGI apps up under
Pylons, and Pylons *does not* force output-types or apply other
formatting to the output of the controller.

So in Pylons, the tasks you previously put in one place, are split-up
to sit closer to the actual work being done.

- Authorization will commonly be done at the controller level unless
you want to require some sort of permission across multiple
controllers, in which case putting that check in the lib/base.py
controller's __before__ would be prudent. The formatting is handled

- Formatting and site skins are handled by the template engines

- Global objects for a request can be setup in the controller, the
action, or the BaseController that is inherited by all your controllers

- Session data is loaded when first accessed

- Database sessions are commonly created at the BaseController level
for use where needed

Hope that helps, of course Pylons doesn't force any of this on you.
There's nothing forcing you to use the BaseController and inherit
from it, you could design some other system entirely.... I'd
recommend staying with this layout as it'll make it easier for other
people familiar with Pylons to maintain your project as too much
customization when not 'necessary' can create maintenance nightmares.

Cheers,
Ben

Ron Bickers

unread,
2 May 2007, 17:15:0802/05/2007
to pylons-discuss
On May 2, 3:16 pm, Ben Bangert <b...@groovie.org> wrote:

> In Pylons, you'd want to probably set __before__ and __after__ blocks
> in your main controller (in lib/base.py). These will be called before
> the controllers action and afterwards. This is the common place to
> setup:
> - Global objects and database sessions

> - Authorization will commonly be done at the controller level unless

> you want to require some sort of permission across multiple
> controllers, in which case putting that check in the lib/base.py
> controller's __before__ would be prudent. The formatting is handled

There's a note in the __call__ method of the lib/base.py
BaseController that says to put any per-request code there. How does
putting code in __call__ differ from putting it in __before__ ?

--
Ron

Ben Bangert

unread,
2 May 2007, 18:41:2302/05/2007
to pylons-...@googlegroups.com
On May 2, 2007, at 2:15 PM, Ron Bickers wrote:

> There's a note in the __call__ method of the lib/base.py
> BaseController that says to put any per-request code there. How does
> putting code in __call__ differ from putting it in __before__ ?

The WSGIController (that BaseController inherits from) will actually
run your __before__ methods, putting code in __call__ means you can
load even before that. Perhaps you have code that has to run before
some inherited controllers __before__ method, putting it in __call__
ensures that its called in advance.

Cheers,
Ben

Max Ischenko

unread,
3 May 2007, 08:13:1103/05/2007
to pylons-...@googlegroups.com
On 5/3/07, Ben Bangert <b...@groovie.org> wrote:
The WSGIController (that BaseController inherits from) will actually
run your __before__ methods, putting code in __call__ means you can
load even before that. Perhaps you have code that has to run before
some inherited controllers __before__ method, putting it in __call__
ensures that its called in advance.

There is  a caveat in using methods like __before__. The base class' implementation needs to be called explicitly (as with any super impl). Which means putting "common" logic into BaseController.__before__ poses the threat that subclass may failed to execute it.

I am interested in hearing how others deal with this issue. I ended up having an empty __before__ in BaseController, putting "common" logic into BaseController.__call__ and never overriding __call__ in subclasses.

Max.

Shannon -jj Behrens

unread,
3 May 2007, 14:35:4003/05/2007
to pylons-...@googlegroups.com

I don't have this problem, but I see where you're coming from. Just
shoving stuff in BaseController.__call__ does make sense. If you
really want to *force* the user to call BaseController's __before__
and __after__ methods, throw some assertion statements into
BaseController's __call__ method:

assert getattr(self, "_base_controller_before_called", False), \
"Dummy, you forgot to call BaseController.__before__!"

Then, in BaseController's __before__ method, you just make sure to set
that attribute.

I find that using assertions in this way is a great way to keep
subclasses "honest" ;) It's good policy to put assertions in places
where you know the user is likely to mess up.

Best Regards,
-jj

--
http://jjinux.blogspot.com/

Max Ischenko

unread,
3 May 2007, 23:37:2703/05/2007
to pylons-...@googlegroups.com
Hello,

On 5/3/07, Shannon -jj Behrens <jji...@gmail.com> wrote:
>  There is  a caveat in using methods like __before__. The base class'
> implementation needs to be called explicitly (as with any super impl). Which
> means putting "common" logic into BaseController.__before__ poses the threat
> that subclass may failed to execute it.
>
>  I am interested in hearing how others deal with this issue. I ended up
> having an empty __before__ in BaseController, putting "common" logic into
> BaseController.__call__ and never overriding __call__ in subclasses.

I don't have this problem, but I see where you're coming from.  Just
shoving stuff in BaseController.__call__ does make sense.  If you
really want to *force* the user to call BaseController's __before__
and __after__ methods, throw some assertion statements into
BaseController's __call__ method:

    assert getattr(self, "_base_controller_before_called", False), \
               "Dummy, you forgot to call BaseController.__before__!"

Then, in BaseController's __before__ method, you just make sure to set
that attribute.

I think Ben said __call__ is called earlier than __before__. Which  means  it won't work.

I find that using assertions in this way is a great way to keep
subclasses "honest" ;)  It's good policy to put assertions in places
where you know the user is likely to mess up.

Right. Asserts are very helpful. And easier to type then if  ...: raise RuntimeError

Max.


Reply all
Reply to author
Forward
0 new messages