building web pages with python editing a note
Editing a Note
The Editing Process
The next step is to be able to Edit -- or revise -- a Note. When a Note is edited, all that happens is that a new Revision is added to the revision table. The previous contents are still stored in the database. This allows the history of the Note to be viewed.
The steps to Edit a note are incredibly simple. In fact, it's almost exactly like adding a note except for two features: the values of the Note are shown in the form and instead of an INSERT function, an UPDATE function is used in the model.
In fact, Adding and Editing are so similar, the two will just be combined together.
Modifying the Add Form
First, change the Add form. The only change will be the addition of a hidden field for the note_id:
<form name="add" method="post" action=".">
<br/>
<input type="hidden" name="note_id" />
<label for="category_category_id">Category</label>
<select name="category_category_id">
{% for c in cats %}
<option value="{{ c.category_id }}">{{ c.category_category }}</option>
{% endfor %}
</select>
<br/>
<label for="note_title">Title</label><br/>
<input type="text" name="note_title" id="note_title" size="45"/>
<br/>
<label for="revision_body">Body</label><br/>
<textarea rows="23" cols="75" name="revision_body" id="revision_body"></textarea>
<br/>
<br/>
<input type="Submit" name="Submit" />
</form>
Modifying the Model
addNote() will now be changed to addEditNote():
def addEditNote(formData):
try:
c = db.connect()
transaction = c.begin()
if formData.has_key('note_id'):
formData['note_note_id'] = formData['note_id']
c.execute(note.update(note.c.note_id==formData['note_id']), formData)
else:
c.execute(note.insert(), formData)
newNote = c.execute(note.select(order_by=[desc(note.c.note_id)], limit=1)).fetchone()
formData['note_note_id'] = newNote['note_id']
c.execute(revision.insert(), formData)
session.flush()
transaction.commit()
c.close()
return formData['note_note_id']
except:
transaction.rollback()
c.close()
raise
Notice the changes. If formData is passed with a note_id key, the function makes an UPDATE to the database. If there is no note_id, an INSERT is performed.
The UPDATE function works a bit differently than INSERT. Update expects a paramter of which Note it will be updating. It also expects a dictionary with the updated values. In this case, it's formData.
Modifying the Controller
Finally, modify the add controller:
class note(baseWebpy):
def GET(self, note_id=""):
if note_id and noteExists(note_id):
note = makeWritable(getNotes(note_id)[0])
else:
note = {}
return sendData('addNote', {
'form': createAddNoteForm(note)
})
def POST(self, note_id=""):
i = formData(self.request.form)
eForm = validateAddNoteForm(i)
if eForm:
return sendData('addNote', {
'form': eForm
})
else:
try:
note_id = addEditNote(i)
return redirect('/view/note/%s' % str(note_id))
except Exception, e:
return HttpResponse('uh oh!')
The GET method now checks for the existence of a note_id in the URL. If there is not one, a blank form will be presented. However, if there is, the note_id is retrieved and the values are added to the form.
The POST method does the exact same thing as before. It relies on the addEditNote() function to determine if this was an Add or Edit.
There's a new function, too: redirect(). This will simply redirect us to another page. In this case, the user will be sent to the view controller of the note they just worked on.
It might be more descriptive if the add.py controller was renamed to addEdit.py. Don't forget to modify __init__.py to accommodate for this change.
Adding the New Function
To create the new redirect() function, add this to utils.py:
def redirect(url):
return HttpResponse('bye', [('Location', url)], 302)
This function relies on the Location HTTP header to send the user elsewhere.
Making the new URL Entries
Finally, some new entries will need added to the urls map:
(r'^add/note/$', controllers.addEdit.note),
(r'^edit/note/(\d*)/$', controllers.addEdit.note),
Make sure you remove the previous add entry.
Editing Notes
Everything is now set to be able to edit Notes. Load up the Colubrid server, view a Note, then click on the Edit link. You'll notice a new revision has been made.
Checkpoint
controllers/__init__.py
import view
import addEdit
controllers/addEdit.py
from base import *
class note(baseWebpy):
def GET(self, note_id=""):
if note_id and noteExists(note_id):
note = makeWritable(getNotes(note_id)[0])
else:
note = {}
return sendData('addNote', {
'form': createAddNoteForm(note)
})
def POST(self, note_id=""):
i = formData(self.request.form)
eForm = validateAddNoteForm(i)
if eForm:
return sendData('addNote', {
'form': eForm
})
else:
try:
note_id = addEditNote(i)
return redirect('/view/note/%s' % str(note_id))
except Exception, e:
return HttpResponse('uh oh!')
utils.py
from jinja import Template, Context, FileSystemLoader
from colubrid import HttpResponse
def tplLoad(tpl, vars={}):
t = Template(tpl, FileSystemLoader('tpl'))
c = Context(vars)
return t.render(c)
def sendData(tpl, vars, ctype='text/html', code=200):
page = tplLoad(tpl, vars)
return HttpResponse(page, [('Content-Type', ctype)], code)
def formData(dirty):
if dirty:
clean = {}
for k in dirty.keys():
clean[k] = dirty.getlist(k)[0]
return clean
def makeWritable(r):
w = {}
for x in r.keys():
w[x] = r[x]
return w
def redirect(url):
return HttpResponse('bye', [('Location', url)], 302)
app.py
from colubrid import WebpyApplication, Request, execute, HttpResponse
from colubrid.server import StaticExports
import controllers
class Notebook(WebpyApplication):
urls = [
(r'^$', controllers.view.index),
(r'^view/note/(\d*)/$', controllers.view.note),
(r'^view/note/(\d*)/(\d*)/$', controllers.view.note),
(r'^add/note/$', controllers.addEdit.note),
(r'^edit/note/(\d*)/$', controllers.addEdit.note),
]
slash_append = True
app = Notebook
app = StaticExports(app, {
'/static': './static'
})
if __name__ == '__main__':
execute(reload=True, debug=False)
models.py
from sqlalchemy import *
db = create_engine('sqlite:///sql/notebook.db')
metadata = BoundMetaData(db)
category = Table('category', metadata,
Column('category_id', Integer, primary_key=True),
Column('category_category', String(255)),
)
note = Table('note', metadata,
Column('note_id', Integer, primary_key=True),
Column('note_title', String(255)),
Column('category_category_id', Integer, ForeignKey('category.category_id')),
)
revision = Table('revision', metadata,
Column('revision_id', Integer, primary_key=True),
Column('note_note_id', Integer, ForeignKey('note.note_id')),
Column('revision_body', String),
Column('revision_time', DateTime, default=func.current_timestamp()),
)
session = create_session()
def getCategories(category_id=""):
r = select([category])
return r.execute().fetchall()
def getNotes(note_id="", revision_id=""):
r = select([note,category],
and_(note.c.category_category_id==category.c.category_id))
if note_id:
r = select([note,category,revision],
and_(note.c.category_category_id==category.c.category_id,
note.c.note_id==revision.c.note_note_id,
note.c.note_id==note_id),
order_by=[desc(revision.c.revision_time)], limit=1,)
if revision_id:
r.append_whereclause(revision.c.revision_id==revision_id)
return r.execute().fetchall()
def addEditNote(formData):
try:
c = db.connect()
transaction = c.begin()
if formData.has_key('note_id'):
formData['note_note_id'] = formData['note_id']
c.execute(note.update(note.c.note_id==formData['note_id']), formData)
else:
c.execute(note.insert(), formData)
newNote = c.execute(note.select(order_by=[desc(note.c.note_id)], limit=1)).fetchone()
formData['note_note_id'] = newNote['note_id']
c.execute(revision.insert(), formData)
session.flush()
transaction.commit()
c.close()
return formData['note_note_id']
except:
transaction.rollback()
c.close()
raise
def noteExists(note_id):
n = note.select(note.c.note_id==note_id).execute().fetchall()
if n:
return True
else:
return False
def revisionExists(revision_id):
r = revision.select(revision.c.revision_id==revision_id).execute().fetchall()
if r:
return True
else:
return False
def categoryExists(category_id):
c = category.select(category.c.category_id==category_id).execute().fetchall()
if c:
return True
else:
return False
def getRevision(note_id=""):
if note_id:
return select([revision],
revision.c.note_note_id==note_id,
order_by=[desc(revision.c.revision_time)]).execute().fetchall()
tpl/addNoteForm.html
`<form name="add" method="post" action=".">
<br/>
<input type="hidden" name="note_id" />
<label for="category_category_id">Category</label>
<select name="category_category_id">
{% for c in cats %}
<option value="{{ c.category_id }}">{{ c.category_category }}</option>
{% endfor %}
</select>
<br/>
<label for="note_title">Title</label><br/>
<input type="text" name="note_title" id="note_title" size="45"/>
<br/>
<label for="revision_body">Body</label><br/>
<textarea rows="23" cols="75" name="revision_body" id="revision_body"></textarea>
<br/>
<br/>
<input type="Submit" name="Submit" />
</form>`
