Mir gefällt, wie simpel ein einfaches "Hallo Welt" ist. Mir gefällt auch, dass es hinreichend komplett mit einem eigenen Server, einer Template-Lösung einer simplen Datenbank-Abstraktion daher kommt. Eine Session-Verwaltung fehlt allerdings bereits.
Es gibt zwar (ein sogar deutschsprachiges) Tutorial, aber ansonsten ist die Dokumentation außerordentlich schlecht. Nicht nur, dass es kaum irgendwelche Informationen auf der Webseite gibt, der Quelltext ist schwer verdaulich, weil kaum kommentiert und mit äußerst kurzen Variablen oder Funktionsnamen gesegnet. Ich erwarte als Doku außerdem nicht nur das API - das könnte ich im Quelltext nachlesen - sondern die Art und Weise, wie man das Ganze einsetzen soll.
Dennoch, vielleicht als Hilfe für andere, hier mein simples Beispiel in Tutorial-Form.
Das obligatorisches "Hallo Welt" zeigt, wie URLs auf Controller abgebildet werden und wie die Antwort per PRINT erzeugt wird. Die letzte Zeile startet einen Server auf Port 8080.
Code: Alles auswählen
import web
urls = ('/', 'index')
class index:
def GET():
print "Hallo, Welt"
web.run(urls, globals())
Code: Alles auswählen
create table pages (name string primary key, title string, content text);
insert into pages (name, title, content) values ('index', 'The Index', 'This is the index');
Code: Alles auswählen
web.config.db_parameters = dict(dbn='sqlite', db='code.db')
Code: Alles auswählen
urls = (
'/', 'index',
'/(\w+)', 'page',
'/(\w+)/edit', 'edit',
'/(\w+)/save', 'save',
)
Code: Alles auswählen
render = web.template.render('templates/', cache=False)
Code: Alles auswählen
class index:
def GET(self):
web.redirect('/index')
Code: Alles auswählen
class page:
def GET(self, name):
p = get_page(name)
if p:
print render.base(p.title, render.view(p))
else:
web.redirect('/%s/edit' % name)
Eine Seite laden macht die folgende Funktion. Hier bin ich das erste Mal ob der mangelhaften Dokumentation verzweifelt. Vielleicht gibt es einen besseren Weg, ein Objekt (web.py nutzt hier spezielle dicts) aus der Datenbank zu laden.
Code: Alles auswählen
def get_page(name):
for p in web.select('pages', where="name=$name", vars=locals(), limit=1):
return p
return None
Meine Templates bestehen aus einem gemeinsamen Basis-Template und je einem View-spezifischen. Ich habe mir das so aus einem Beispiel abgeschaut, Vererbung muss man explizit im Controller machen. Nun, ja. "render" hat jetzt für jede Datei ein magisches Attribut, darin eine Funktion, der ich die im Template definierten Parameter übergebe. Oh, man beachte das ":" vor body, was definiert, dass dieser Wert nicht automatisch escaped werden soll - das ansonsten automatisch < und & escaped wird, finde ich sehr gut.
Code: Alles auswählen
def with (title, body) "base.html"
<html>
<head>
<title>$title</title>
<style type="text/css" media="screen">
pre { margin: 1em; background: #eee; padding: .5em; }
</style>
</head>
<body>
<h1>Wiki</h1>
<div>$:body</div>
<h6>© 2007 by me</h6>
</body>
Code: Alles auswählen
$def with (page) "view.html"
<h2>$page.title</h2>
<div>$page.content</div>
<p><a href="/$page.name/edit">Edit this page</a></p>
Code: Alles auswählen
class edit:
def GET(self, name):
p = get_page(name)
if not p:
p = web.Storage(name=name, title=name, content="")
print render.base(p.title, render.edit(p))
Code: Alles auswählen
$def with (page) "edit.html"
<h2>Edit $page.title</h2>
<form method="post" action="/$page.name/save">
<input type="text" name="title" value="$page.title"/>
<br/>
<textarea cols="60" rows="20" name="content">$page.content</textarea>
<br/>
<input type="submit" value="Save"> or <a href="/$page.name">Cancel</a>
</form>
So weit, so gut. Nicht so schick der - wegen des kruden DB-Zugriffs - der verbleibende Controller, der eine Seite speichert:
Code: Alles auswählen
class save:
def POST(self, name):
i = web.input()
if get_page(name):
web.update('pages',
title=i.title, content=i.content,
where='name=$name', vars=locals())
else:
web.insert('pages',
seqname=False,
name=name, title=i.title, content=i.content)
web.redirect("/%s" % name)
Ok, aber ihr habt jetzt meinen Wiki gesehen, er funktioniert und wenn man noch dies macht, ist die Entwicklung auch nicht ganz so schmerzhaft (die Debug-Seite stammt übrigens von Django ;):
Code: Alles auswählen
web.webapi.internalerror = web.debugerror
web.run(urls, globals(), web.reloader)
Ergo: Leichtgewichtig, vielleicht zu leicht, denn alles was jetzt schnell geht, könnte sich bei umfangreicheren Projekten rächen. Der DB-Zugriff ist IMHO die schwächste Komponente von web.py. Das Template-Rahmenwerk scheint interessant, ist aber noch nicht fertig. Die Debug-Seite könnte mehr eigene Identität vertragen und es wäre nett, wenn man - wie bei Werkzeug - eine kleine Kommandozeile hätte. Formularverarbeitung braucht Dokumentation genau wie der URL-Dispatch und was und wie das mit der Middleware geht.
Stefan