building web pages with python deleting notes

Deleting Notes

Note Deletion

All good things must come to an end. Sometimes you'll want the ability to delete a Note and that's what we're going to add in this Part.

The procedure for deleting data is pretty simple: We give the delete function a note_id to delete. First, the Note is removed from the note table. Next, all revisions with that note_id as its Foreign Key are removed as well.

The Delete Template

The delete template will be named deleteNote.html. It'll contain a simple form that has a hidden field for the note_id and a Submit button to confirm the deletion.

{% extends "base" %}
{% marker "title" set "Delete a Note" %}

{% block "content" %}
<h2>Are you sure?</h2>
<p>Are you sure you want to delete note "{{ n.note_title }}"?</p>
<p>Doing so will remove all revisions as well.</p>

<form name="delete" method="post" action=".">
    <input type="hidden" name="note_id" value="{{ n.note_id }}">
    <input type="submit" name="Submit" value="Delete"/>
</form>
{% endblock %}

Since this is such a simple form with only one form element, FormEncode won't be used -- that'd just be too much overhead.

The Delete Function

In order to remove the data from the database, a deleteNote() function needs to be defined in our model.

def deleteNote(note_id):
    try:
        c = db.connect()
        transaction = c.begin()
        c.execute(note.delete(note.c.note_id==note_id))
        c.execute(revision.delete(revision.c.note_note_id==note_id))
        transaction.commit()
        c.close()
    except:
        transaction.rollback()
        c.close()
        raise

A very short and self-explanatory function. Just like what was described at the beginning of this Part, a note_id is specified, removed, and then all the subsequent Revisions are also removed. The function uses a transaction for easy rollbacks and is wrapped in a try / except block.

The Delete Controller

Deletion is a new action to this application, so we'll be creating a new controller in the controllers directory called delete.py.

from base import *

class note(baseWebpy):
    def GET(self, note_id):
        if note_id and noteExists(note_id):
            return sendData('deleteNote', {
                'n': getNotes(note_id)[0]
            })
        else:
            raise PageNotFound

    def POST(self, note_id=""):
        i = formData(self.request.form)
        if i['note_id'] and noteExists(i['note_id']):
            try:
                deleteNote(i['note_id'])
                return redirect('/')
            except Exception, e:
                return HttpResponse('uh oh!')
        else:
            raise PageNotFound

The GET method takes a note_id as a parameter and checks to see if it exists. If so, it renders the delete.html template with the value of the note_id in the hidden field.

The POST method processes the form data and passes it to the deleteNote() model function. If everything was deleted OK, the user is sent back to the index page.

Save this and add the entry to __init__.py.

Adding the Delete URL

The final step for the Delete support is to add its entry to the urls map:

(r'^delete/note/(\d*)/$', controllers.delete.note),

Deleting Notes

Everything should now be in place for the Delete support. Try deleting a note from the Colubrid server.

Checkpoint

controllers/__init__.py
import view
import addEdit
import compare
import delete
controllers/delete.py
from base import *

class note(baseWebpy):
    def GET(self, note_id):
        if note_id and noteExists(note_id):
            return sendData('deleteNote', {
                'n': getNotes(note_id)[0]
            })
        else:
            raise PageNotFound

    def POST(self, note_id=""):
        i = formData(self.request.form)
        if i['note_id'] and noteExists(i['note_id']):
            try:
                deleteNote(i['note_id'])
                return redirect('/')
            except Exception, e:
                return HttpResponse('uh oh!')
        else:
            raise PageNotFound
tpl/deleteNote.html
{% extends "base" %}
{% marker "title" set "Delete a Note" %}

{% block "content" %}
<h2>Are you sure?</h2>
<p>Are you sure you want to delete note "{{ n.note_title }}"?</p>
<p>Doing so will remove all revisions as well.</p>

<form name="delete" method="post" action=".">
    <input type="hidden" name="note_id" value="{{ n.note_id }}">
    <input type="submit" name="Submit" value="Delete"/>
</form>
{% endblock %}
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),
        (r'^compare/(\d*)/(\d*)/(\d*)/$', controllers.compare.note),
        (r'^view/txt/(\d*)/$', controllers.view.txt),
        (r'^view/txt/(\d*)/(\d*)/$', controllers.view.txt),
        (r'^delete/note/(\d*)/$', controllers.delete.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="", revision_id=""):
    if note_id:
        return select([revision],
            revision.c.note_note_id==note_id,
            order_by=[desc(revision.c.revision_time)]).execute().fetchall()
    if revision_id:
        return select([revision], revision.c.revision_id==revision_id).execute().fetchone()

def deleteNote(note_id):
    try:
        c = db.connect()
        transaction = c.begin()
        c.execute(note.delete(note.c.note_id==note_id))
        c.execute(revision.delete(revision.c.note_note_id==note_id))
        transaction.commit()
        c.close()
    except:
        transaction.rollback()
        c.close()
        raise