Especially, I wish we had more Webware releases. I suppose I could just do it myself, but I feel I don't really know enough yet.
-ww
On Mar 10, 2005, at 2:38 PM, Olivier FAVRE-SIMON wrote:
For what it's worth I'm happy to share my way to use Cheetah with Webware. Comments are welcome._________________________________________
Starting with some facts:
1. The only way to have powerfull but well-structured Cheetah templates is to use inheritance (#extends).
2. The only way to have FormKit (or any other form kit that rely on Webware actions) whitout manually tweaking original source code is to inherit from WebKit.Page
3. Cheetah.Template and WebKit.Page do not mix well (at least in current versions)
4. I don't like the "inheritance" approach described in the Cheetah User's Guide: I'm not a templar of the MVC church but a class hierarchy with logic and view classes being intermixed seems ugly and quite to me:
Template <--- SiteLogic.py <--- Site.tmpl/.py <--- PageLogic.py <--- Page.tmpl/.py <--- SecurePageLogic.py <--- LoginPageLogic.py <--- LoginPage.tmpl/.py
So I came to this design:
Since templates are single inheritance only (only one base class, with Template at the root), and I don't want intermixed logic/view classes, let's
*** have 2 class hierarchies: one for logic, one for views *** :
WebKit.Page <-------------------+---SitePage.py <------+------ Main.py
| |
MiscUtils.Configurable <--+ +------ ... other public pages
|
+------ SecurePage.py <-------+------ Login.py
|
+------ Music.py
|
+------ ... other 'login required' pages
SitePageView.tmpl <------+------ MainView.tmpl
|
+------ LoginView.tmpl
|
+------ MusicView.tmpl
Each Page in the site will instantiate the corresponding View as self.tmpl in its __init__() method.
The .py View has been previously generated from the .tmpl via "cheetah compile" and a Makefile.
The searchList is [self], so the template can access all attributes the same way it's done in the documented "inheritance approach".
The SitePage class defines 2 methods that subclasses may override:
- get_data() to gather all page data (i.e. the controler / business logic) : Obviously must be redefined because each page has a different content
- write_html() to generate page content (i.e. the view) : Most a the time the default is sufficient : write the HTML via str(self.tmpl)
Because all pages are Webkit pages rather than Cheetah templates it is possible to use FormKit for the Login page and let Webware actions handle the form.
====================================================
class SitePage(Page, Configurable):
def __init__(self):
Page.__init__(self)
Configurable.__init__(self)
from SitePageView import SitePageView
self.tmpl = SitePageView(searchList=[self])
def writeHTML(self):
self.get_data()
self.write_html()
def write_html(self):
self.writeln(str(self.tmpl))
### etc. other SitePage methods ###
====================================================
class SecurePage(SitePage):
def __init__(self):
SitePage.__init__(self)
def awake(self, trans):
SitePage.awake(self,trans)
# pseudo-code here: use session() + database query to check login data
if ## session invalid / expired## :
trans.response().sendRedirect('Login')
### etc. other SecurePage methods ###
====================================================
class Login(FormKitMixIn, SecurePage):
def __init__(self):
SecurePage.__init__(self)
f = self.form = Form.Form(validators=[PasswordValidator(self)])
self.addForm( f )
f.addField( Fields.TextField('username', [Validators.MinLength(5)]))
f.addField( Fields.PasswordField('password', [Validators.NotEmpty()]))
f.addField( Fields.WebKitSubmitButton(name="submit", label="Log in"))
from LoginView import LoginView
self.tmpl = LoginView(searchList=[self])
def get_data(self):
SecurePage.get_data(self)
self.login_form_html = self.form.dump() #login_form_html is a place holder for the whole form in LoginView.tmpl
if not self.form.isSuccessful() and self.form.error():
# add form error msg
self.login_form_html = self.login_form_html + '<p style="color:red;">%s</p>' % self.form.error()
def actions(self):
"""Form actions - standard WebKit stuff"""
return ['submit']
def submit(self):
"""action for submit button : save user info and redirect to requested page (default: Main) if login successful."""
if self.form.isSuccessful():
self.session().setValue('user', self.userid)
self.session().setValue('username', self.form.username)
self.response().sendRedirect(self.request().field('page', 'Main'))
### etc. other Login methods ###
====================================================
class Music(SecurePage):
def __init__(self):
SecurePage.__init__(self)
from MusicView import MusicView
self.tmpl = MusicView(searchList=[self])
def get_data(self):
'''A simple pusic page for the example: display all songs that starts with a given letter in a music files directory.'''
SecurePage.get_data(self)
self.letter = self.request().field('letter','A')
DIR = '/mnt/disk/music'
from string import upper
from path import path
d = path(DIR)
self.music_files = [i.name for i in d.listdir() if upper(i.name).startswith(upper(self.letter))]
#music_files is a place holder for MusicView.tmpl, wich will iterate (e.g. #for $f in $music_files) to display
### etc. other Music methods ###
To make it short what is needed for a new page is:
1. Instantiate the correct view in __init__()
2. Override get_data() to generate content
3. Provide the template for the view
On the negative side, the views must have been previously generated from the .tmpl via "cheetah compile"; I've not found a way to have a servlet instantiated template being dynamically compiled when it #extends another template.
With the help of a little Makefile:
VIEWS=SitePageView.py LoginView.py MusicView.py
.SUFFIXES: .py .tmpl
all: $(VIEWS)
LoginView.tmpl: SitePageView.py
Login.py: LoginView.py
MusicView.tmpl: SitePageView.py
Music.py: MusicView.py
clean:
rm -f *.pyc
rm -f *.py.bak
rm -f $(VIEWS)
.PHONY: all clean
.tmpl.py:
@cheetah compile $<
I've been using python for some time now, mainly for PyGTK desktop GUI's or XMLRPC/SOAP web services, but for pure web development I'm coming from a PHP/Smarty background.
PHP is good for rapid prototyping but on the long-term it's all spaghetti code with screens "pulling" the data from databases. Rails is really cool but it's Ruby ;-)
I'ts a pity for me to see that python (IMHO the best language whatever the domain you're working in) has 10 or so web frameworks with as many template engines. That's too many brainpower dissipated in duplicate endeavours. PHP and Rails are strong because the whole community agreed that choosing a "main" (in not single) framework was more important than any technical argument/point of view/limitation/whatever.
If we want python to *really* make it on web servers (I mean as an opensource initiative, not Zope), the only way to succeed is (by priority order):
1. Stop/refrain from developping new features when the existing codebase is so poorly documented. (Microsoft MFC and .NET are crap but the documentation is really impressing and make it possible to start from scratch in very little time)
2. Add many examples and a real tutorial. What I mean is that one must not have to copy/paste from html pages to have a running tutorial site. We should have say a webware-examples.tar.bz2: just unpack it in some /opt or /var/www directory, tweak httpd.conf a little and it must be running from scratch).
3. Clearly an non-ambiguously define which packages are prefered and which are deprecated: e.g. Many people advocate SQLObject for Rails-alike sites (which I agree), but nowhere is it clearly defined that one must choose SQLObject over MiddleKit, say for new sites. e.g. I completely failed to understand what UserKit is meant for and how to use it; googling about it did not return valuable info or a good example.
4. Release early release often: The only thing that stops me to actually put big Webware sites on the corporate servers in the company i'm working for is seeing that same 0.8.1 version for so long. I know that development is still going on: I'm regularly updating my development box from [EMAIL PROTECTED] webware module but any serious management will throw me away if I ever speak of a CVS version on a prod server (even a tiny one).
Trust matters, and the best way to ensure trust is to *show* that things evolve; knowing it is not sufficient.
What we need is JOINING FORCES (and not only in Webware+Cheetah; other web frameworks (heho Aquarium) have common needs/requirements: form handling, session management, etc.
Regards.
OFS.
winston wolff - (646) 827-2242 - http://www.stratolab.com - learning by creating