I've been contemplating some python web development. This is mainly for the following reasons:
-
I'd like my blog to have features for threading subjects based on keywords, a kind of related articles list. This would be dynamically updated, old articles could link to newer articles. This is possible in Drupal but the taxonomy system is laborious to configure through the web interface. There is a new keyword feature (folksonomies) but it appears to be a library for other modules to use. There are some modules that promise this kind of thing but they are not what I am looking for. The most important thing is that old articles are displayed with links to new articles, which involves messing around with themeing to add links to a cached page or figuring out how to flush the cache and put articles through a filter that would add the links.
-
I'd rather spend my limited programming time being creative with technologies I am already familiar with. I don't like php enough to want to learn it any more. I know and like python.
-
I don't want to spend my time trying to figure out how the Drupal code base works. Ok, I'm lazy and I have a short attention span and I'm not very interested in how it works.
-
If I do this as a spare time project I want to enjoy it, I don't want it to feel like a maintenance exercise. I can do that at work.
-
I have found that using librarys etc written by third partys can give you 95% of what you want, then you spend weeks trying to hack them to get the extra 5%. The only exception to this rule I can think of is the python standard library which is way cool.
So I looked at a few web application frameworks. This is a little lightweight and short on detail, I did most of it a few weeks ago and I didn't take notes.
cherrypy: Requires python 2.3 and my hosting service will only give me python 2.2 with cgi. I think generator functions are way cool and I'd love to use them. Do I stand any chance of getting my hosting service to upgrade? I'll email them and ask.
quixote: got the demo going, although I had to fix some of the urls within it as they appeared to be broken. It worked easily enough with cgi, the 1.3 version worked with python 2.2. However, again they have just released version 2 which has a couple of yield statements in it, limiting it python 2.3.
webware: I don't think I could see the advantage of 'servlets' and their web site (presumably powered by webware but it only says 'python powered') is not an impressive display of it's capabilities.
zope/plone: I've toyed with zope in the past (version 2) and found it's learning curve to be almighty. It may be very capable but you have to learn the zope way to do everything. The developers seem to be concentrating on inventing new object-orientated paradigms rather than keeping things simple and I hate huge complex object models. I don't like the web-based source editor (what, no syntax highlighting?: although I could use mozex and edit in vim). Also, non-cgi.
I was most interested in quixote until I came to the python 2.3 stumbling block with the new version 2. I don't want to settle for an old version. This led me on to think about what these frameworks actually give me:
-
map urls to class/method calls
-
session management
-
do I need it for my simple purposes?
-
templating systems
-
interface to cgi/mod_python/fastcgi/whatever
-
I can only have cgi anyway
-
easy form generation
-
classes to build the contents of a form. How many forms will I need? Can't templates do this?
-
already used on the internet and exposed to hackers
-
lets not be too ambitious with what we do. Keep drupal in the background for data entry.
-
learning curves
Which has led me to... straight cgi. Run my own scripts from cgi, using the cgi module in the standard python library to get the url parameters and generate a nice error page when things go wrong. I can get it going easily enough and below, with just a few hours hacking, is something that can display nodes from the drupal database. The output is themed through the Cheetah template engine and gives me the same look as my drupal theme. Given that it is only cgi it is not massively scalable, but I understand every line of it and that means a lot in terms of development time. Given that it generates pages that look like the drupal pages, I can chop and change, leaving some bits to drupal (data entry) and others in python (presentation). It's a lash up but so what?
The main challenge is porting my wilki module to python, particularly the geshi syntax highlighting.
I am not planning to rewrite Drupal and I probably won't release Yet Another Python Web Framework. I may not even finish it. It is something for me to play with.
1
2
3 import Cheetah.Template
4 import cgi
5 import cgitb; cgitb.enable()
6 import time
7 import datetime
8 import os
9 import pdo
10
11 def ThemeAndOutput( strPageTitle, strContent, oParams={}):
12 "Theme the site"
13
14 strSideBar = """
15 <div class="block">
16 <h2>Reports</h2>
17 <ul>
18 <li><a href="Intranet.cgi">Drupal Emulator</a></li>
19 </ul>
20 </div>"""
21
22 oDict = {
23 'Root': '/SMB',
24 'Url': '/Intranet/Intranet.cgi',
25 'Title': 'Drupal Emulator',
26 'Slogan': 'There are no Problems, only Challenges',
27 'PrimaryLinks': [ '<a href="http://intranet.org">Intranet</a>',
28 '<a href="http://www.google.co.uk">Google</a>'],
29 'PageTitle': strPageTitle,
30 'SideBar': strSideBar,
31 'Footer': 'Copyright 2005 Peter Wilkinson'
32 }
33
34
35
36
37 strTemplate = open( 'PageTemplate.html').read() % { 'Content': strContent}
38
39
40
41
42
43 oHtml = Cheetah.Template.Template( strTemplate, [oDict, oParams])
44
45 print str(oHtml)
46
47 def MainPage( oForm):
48 "Main welcome page: tell user what is going on"
49
50
51
52
53 strFrom = oForm.getfirst( 'from')
54 if strFrom == None:
55 strLimit = ' LIMIT 10'
56 else:
57 try:
58 nLimit = int(strFrom)
59 strLimit = ' LIMIT %d, 10' % nLimit
60 except:
61 strLimit = ' LIMIT 10'
62
63 c = pdo.connect( 'Module=MySQLdb;user=drupal;passwd=secret;db=drupal')
64 rs = c.open( 'SELECT * FROM node ORDER BY created DESC %s' % strLimit)
65
66 strTitles = []
67 while rs.next():
68 strTitles.append( '<a href="Intranet.cgi?node=%s">%s</a>' %
69 (rs.fields['nid'].value, rs.fields['title'].value))
70
71 strPage = """
72 <ul>
73 #for $strTitle in $titles
74 <li>$strTitle</li>
75 #end for
76 </ul>
77 """
78
79 ThemeAndOutput( "Welcome", strPage, { 'titles': strTitles})
80
81 def NodePage( oForm):
82 "Display a node"
83
84 strNode = oForm.getfirst( 'node')
85 if strNode == None:
86 ErrorPage()
87 return
88 else:
89 try:
90 nNode = int(strNode)
91 except:
92 ErrorPage()
93 return
94
95 c = pdo.connect( 'Module=MySQLdb;user=drupal;passwd=secret;db=drupal')
96 rs = c.open( 'SELECT * FROM node WHERE nid = %d' % nNode)
97
98 if rs.next():
99 strTitle = rs.fields['title'].value
100 strBody = rs.fields['body'].value
101 else:
102 ErrorPage()
103 return
104
105 strPage = """
106 <h2>$strTitle</h2>
107
108 <pre>
109 $strBody
110 </pre>
111 """
112
113 ThemeAndOutput( strTitle, strPage,
114 { 'strTitle': strTitle, 'strBody': strBody})
115
116 def ErrorPage():
117 strPage = """<p>There has been some kind of navigation error, the link
118 you just clicked is broken</p>
119 <p>Better <a href="mailto:someone@somewhere.com">complain about it</a>.</p>
120 """
121
122 ThemeAndOutput( "Drupal emulator", strPage)
123
124 oForm=cgi.FieldStorage()
125
126 node = oForm.getfirst( 'node')
127 if node:
128 NodePage( oForm)
129 else:
130 MainPage( oForm)
The page template is kept in a seperate file. It is based on the phptemplate of my drupal theme and was ported over in less than an hour. Theming engines? A specialisation of templates.
1 Content-Type: text/html
2
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
6 <head>
7 <title>$Title</title>
8 <meta http-equiv="Content-Style-Type" content="text/css"/>
9 <style type="text/css">@import url(style.css);</style>
10 </head>
11 <body>
12 <div id="headerleft">
13 </div>
14 <div id="headermain">
15 <div id="ie6hack">
16 </div>
17 </div>
18 <div id="headertitle">
19 <h1>
20 <a href="$Url" title="$Title">
21 $Title</a>
22 </h1>
23 </div>
24 <div class="slogan">
25 $Slogan
26 </div>
27 <div id="logo">
28 <a href="$Url" title="$Title">
29 <img src="ITL.gif"/>
30 </a>
31 </div>
32 <div id="headerright">
33 </div>
34 #if len($PrimaryLinks) > 0
35 <div id="primarylinks">
36 <ul>
37 #for $PrimaryLink in $PrimaryLinks
38 <li>$PrimaryLink</li>
39 #end for
40 </ul>
41 </div>
42 #end if
43
44 <div id="ie5forcemargininsidehack">
45 <div id="main">
46 <div class="tab">
47 <div class="tabbackground">
48 <div class="tableft">
49 <h3>
50 $PageTitle
51 </h3>
52 </div>
53 </div>
54 </div>
55 <div class="tabbody">
56 <div class="ieisbuggy">
57 </div>
58 <div id="content">
59 %(Content)s
60 </div>
61 <div class="ieisbuggy">
62 </div>
63 </div>
64 </div>
65 <div id="footer">
66 <div class="footerleft">
67 <p>
68 $Footer
69 </p>
70 </div>
71 </div>
72 </div>
73 <div id="sidebarright">
74 <div id="sidebar">
75 <div class="sidebarbox">
76 $SideBar
77 </div>
78 </div>
79 <div id="sidebarbottom">
80 </div>
81 </div>
82 </body>