Editing pages

Edit Template

One of the fundamental features of a wiki is the ability to edit the page just by clicking “Edit This Page,” so we’ll create a template for editing. First, make a copy of page.html in wiki20/templates:

$ cp page.html edit.html

We need to replace the content with an editing form and ensure people know this is an editing page. Here is the revised edit.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:py="http://genshi.edgewall.org/"
      xmlns:xi="http://www.w3.org/2001/XInclude">

    <xi:include href="master.html" />

<head>
        <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
        <title>Editing: ${wikipage.pagename}</title>
</head>

<body>
    <div class="main_content">
        <div style="float:right; width: 10em;"> Viewing
            <span py:replace="wikipage.pagename">Page Name Goes Here</span> <br/>
            You can return to the <a href="/">FrontPage</a>.
        </div>
        <div>
            <form action="/save" method="post">
                <input type="hidden" name="pagename" value="${wikipage.pagename}"/>
                <textarea name="data" py:content="wikipage.data" rows="10" cols="60"/>
                <input type="submit" name="submit" value="Save"/>
            </form>
        </div>
    </div>
</body>
</html>

11: The title in the header reflects that we are editing the page:

<head>
  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
  <title>Editing: ${wikipage.pagename}</title>
</head>

21: Change the div that displays the page:

<div py:replace="wikipage.data">Page text goes here.</div>

with a div that contains a standard HTML form:

<div>
  <form action="/save" method="post">
    <input type="hidden" name="pagename" value="${wikipage.pagename}"/>
    <textarea name="data" py:content="wikipage.data" rows="10" cols="60"/>
    <input type="submit" name="submit" value="Save"/>
  </form>
</div>

Edit Controller

Now that we have our view, we need to update our controller in order to display the form and handle the form submission. For displaying the form, we’ll add an edit method to our controller in wiki20/controllers/root.py. The new root.py file looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
"""Main Controller"""
from wiki20.lib.base import BaseController
from tg import expose, flash, require, url, request, redirect
from pylons.i18n import ugettext as _, lazy_ugettext as l_
#from tg import redirect, validate
from wiki20.model import DBSession, metadata
from wiki20.controllers.error import ErrorController
from wiki20.model.page import Page

class RootController(BaseController):
    error = ErrorController()

    @expose('wiki20.templates.page')
    def index(self, pagename="FrontPage"):
        page = DBSession.query(Page).filter_by(pagename=pagename).one()
        return dict(wikipage=page)

    @expose("wiki20.templates.edit")
    def edit(self, pagename):
        page = DBSession.query(Page).filter_by(pagename=pagename).one()
        return dict(wikipage=page)

    @expose('wiki20.templates.about')
    def about(self):
        return dict(page='about')

16: For now, the new method is identical to the index method; the only difference is that the resulting dictionary is handed to the edit template. To see it work, go to http://localhost:8080/edit/FrontPage. However, this only works because FrontPage already exists in our database; if you try to edit a new page with a different name it will fail, which we’ll fix in a later section.

Don’t click that save button yet! We still need to write that method.

Saving edits - controller

When we displayed our wiki’s edit form in the last section, the form’s action was /save (see line 22 of edit.html). So we need to make a method called save in the Root class of our controller.

However, we’re also going to make another important change. Our index method is only called when you either go to / or /index. If we change the index method to the special method default, then default will be automatically called whenever nothing else matches. default will take the rest of the URL and turn it into positional parameters.

Here’s our new version of root.py which includes both default and save:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
"""Main Controller"""
from wiki20.lib.base import BaseController
from tg import expose, flash, require, url, request, redirect
from pylons.i18n import ugettext as _, lazy_ugettext as l_
from tg import redirect, validate
from wiki20.model import DBSession, metadata
from wiki20.controllers.error import ErrorController
from wiki20.model.page import Page


class RootController(BaseController):
    error = ErrorController()

    @expose('wiki20.templates.page')
    def default(self, pagename="FrontPage"):
        page = DBSession.query(Page).filter_by(pagename=pagename).one()
        return dict(wikipage=page)

    @expose(template="wiki20.templates.edit")
    def edit(self, pagename):
        page = DBSession.query(Page).filter_by(pagename=pagename).one()
        return dict(wikipage=page)

    @expose('wiki20.templates.about')
    def about(self):
        return dict(page='about')

    @expose()
    def save(self, pagename, data, submit):
        page = DBSession.query(Page).filter_by(pagename=pagename).one()
        page.data = data
        redirect("/" + pagename)

5: Unlike the previous methods we’ve used, save just uses a plain @expose() without any template specified. That’s because we’re only redirecting the user back to the viewing page. We use tg.redirect to handle this redirect, so uncomment line 5 to import it.

28-32: Although the page.data = data statement tells SQLAlchemy that you intend to store the page data in the database, nothing happens until the DBSession.flush() method is called. This is commonly referred to as the “unit of work” pattern, and it’s an important structure for database developers because it allows SQLAlchemy to combine many operations into a single database update (or a minimized number of updates if some changes depend upon earlier changes) and thus be much more efficient in the database resources used.

SQLAlchemy also provides a DBSession.commit() method that flushes and commits any changes you’ve made in a transaction. TurboGears 2 provides a flexible transaction management system that automates this process wrapping each web request in its own transaction and automatically rolling back that transaction if you get a python exception or return an HTTP error code as your response.

You don’t have to do anything to use this transaction management system, it should just work. So, you can now make changes and save the page we were editing, just like a real wiki.

Table Of Contents

Previous topic

Adding Wiki Pages

Next topic

What about WikiWords?

This Page