Previous topic

Next topic

How does TurboGears2 help you get development done quickly? We’ll show
you by developing a simple wiki application that should take you no
more than 20 minutes to complete. We’re going to do this without
explaining the steps in detail (that is what this book is for, after
all). As a result, you’ll see how easily you can make your own web
applications once you are up to speed on what TurboGears2 offers.

If you’re not familiar with the concept of a wiki you might want to
check out the Wikipedia entry.
Basically, a wiki is an easily-editable collaborative web content
system that makes it trivial to link to pages and create new pages.
Like other wiki systems, we are going to use CamelCase words to
designate links to pages.

If you have trouble with this tutorial ask for help on the TurboGears
discussion list, or on the IRC channel #turbogears. We’re a
friendly bunch and, depending what time of day you post, you’ll get
your answer in a few minutes to a few hours. If you search the mailing
list or the web in general you’ll probably get your answer even
faster. Please don’t post your problem reports as comments on this
or any of the following pages of the tutorial. Comments are for
suggestions for improvement of the docs, not for seeking support.

If you want to see the final version you can download a copy of the
wiki code.

The TurboGears2 Development Tools are a bunch of commands and extensions useful when
developing TurboGears2 applications. They provide the gearbox suite of commands
to create new full stack projects, quickly create controllers, templates, models and
the TurboGears debugbar.

TurboGears2 provides a suite of tools for working with projects by
adding several commands to the Python command line tool gearbox. A
few will be touched upon in this tutorial. (Check the
GearBox section for a full listing.) The first tool
you’ll need is quickstart, which initializes a TurboGears project.
Go to a command line window and run the following command:

(tgenv)$ gearbox quickstart wiki20

This will create a project called wiki20 with the default template engine and with authentication.
TurboGears2 projects usually share a common structure, which should look like:

We recommend you use the names given here: this documentation looks
for files in directories based on these names.

You need to update the dependencies in the file Wiki-20/setup.py.
Look for a list named install_requires and append the docutils
entry at the end. TurboGears2 does not require docutils,
but the wiki we are building does.

Now to be able to run the project you will need to install it and
its dependencies. This can be quickly achieved by running from
inside the wiki20 directory:

$ pip install -e .

Note

If you skip the pipinstall-e. command you might end up with an error that looks
like: pkg_resources.DistributionNotFound: tw2.forms: Not Found for: wiki20 (did you run python setup.py develop?)
This is because some of the dependencies your project depend on the options you choose while
quickstarting it.

You should now be able to start the newly create project with the gearboxserve command:

The --reload option makes the server restart whenever a file is changed, this greatly speeds
up the development process by avoiding to manually restart the server whenever we need to try
our changes.

Note

The --debug option provides full stacktrace in case the server was unable to start, this
is useful in case your server didn’t start due to a configuration error.

Pointing your browser to http://127.0.0.1:8080/ should open up the TurboGears2 welcome page.
By default newly quickstarted projects provide a bunch of pages to guide the user through
some of the foundations of TurboGears2 web applications.

Wiki-20/wiki20/controllers/root.py (see below) is the code that
causes the welcome page to be produced. After the imports the first
line of code creates our main controller class by inheriting from
TurboGears’ BaseController:

classRootController(BaseController):

The TurboGears 2 controller is a simple object publishing system; you
write controller methods and @expose() them to the web. In our
case, there’s a single controller method called index. As you
might guess, this name is not accidental; this becomes the default
page you’ll get if you go to this URL without specifying a particular
destination, just like you’ll end up at index.html on an ordinary
web server if you don’t give a specific file name. You’ll also go to
this page if you explicitly name it, with
http://localhost:8080/index. We’ll see other controller methods
later in the tutorial so this naming system will become clear.

The @expose() decorator tells TurboGears which template to use to
render the page. Our @expose() specifies:

@expose('wiki20.templates.index')

This gives TurboGears the template to use, including the path
information (the .xhtml extension is implied). We’ll look at this
file shortly.

Each controller method returns a dictionary, as you can see at the end
of the index method. TG takes the key:value pairs in this
dictionary and turns them into local variables that can be used in the
template.

Wiki-20/wiki20/templates/index.xhtml is the template
specified by the @expose() decorator, so it formats what you view
on the welcome screen. Look at the file; you’ll see that it’s standard
XHTML with some simple namespaced attributes. This makes it very
designer-friendly, and well-behaved design tools will respect all the
Kajiki Template Language attributes and tags. You can even open it directly in your
browser.

Kajiki directives are elements and/or attributes in the template that
are prefixed with py:. They can affect how the template is
rendered in a number of ways: Kajiki provides directives for
conditionals and looping, among others. We’ll see some simple Kajiki
directives in the sections on Editing pages and
Adding views.

The following is the content of a newly quickstarted TurboGears2 project
at 2.3 release time:

Now that our model is recognized by TurboGears we must create the table that it is going to use
to store its data. By default TurboGears will automatically create tables for each model it is aware of,
this is performed during the application setup phase.

The setup phase is managed by the Wiki-20/wiki20/websetup python module, we are just
going to add to``websetup/boostrap.py`` the lines required to create a FrontPage page for
our wiki, so it doesn’t start empty.

We need to update the file to create our FrontPage data just before
the DBSession.flush() command by adding:

Controllers are the code that figures out which page to display, what
data to grab from the model, how to process it, and finally hands off
that processed data to a template.

quickstart has already created some basic controller code for us
at Wiki-20/wiki20/controllers/root.py.

First, we must import the Page class from our model. At the end of
the import block, add this line:

fromwiki20.model.pageimportPage

Now we will change the template used to present the data, by changing
the @expose('wiki20.templates.index') line to:

@expose('wiki20.templates.page')

This requires us to create a new template named page.xhtml in the
wiki20/templates directory; we’ll do this in the next section.

Now we must specify which page we want to see. To do this, add a
parameter to the index() method. Change the line after the
@expose decorator to:

defindex(self,pagename="FrontPage"):

This tells the index() method to accept a parameter called
pagename, with a default value of "FrontPage".

Now let’s get that page from our data model. Put this line in the
body of index:

page=DBSession.query(Page).filter_by(pagename=pagename).one()

This line asks the SQLAlchemy database session object to run a query
for records with a pagename column equal to the value of the
pagename parameter passed to our controller method. The
.one() method assures that there is only one returned result;
normally a .query call returns a list of matching objects. We only
want one page, so we use .one().

Finally, we need to return a dictionary containing the page we
just looked up. When we say:

returndict(wikipage=page)

The returned dict will create a template variable called
wikipage that will evaluate to the page object that we looked
it up.

quickstart also created some templates for us in the
Wiki-20/wiki20/templates directory: master.xhtml and index.xhtml.
Back in our simple controller, we used @expose() to hand off a
dictionary of data to a template called 'wiki20.templates.index',
which corresponds to Wiki-20/wiki20/templates/index.xhtml.

Take a look at the following line in index.xhtml:

<htmlpy:extends="master.xhtml"py:strip="True">

This tells the index template to extend the master
template. Using inheritance lets you easily maintain a cohesive look and
feel throughout your site by having each page include a common master
template.

Copy the contents of index.xhtml into a new file called page.xhtml.
Now modify it for our purposes:

In the <title> tag, we substitute the name of the page, using
the pagename value of page. (Remember, wikipage is an
instance of our mapped Page class, which was passed in a
dictionary by our controller.):

<title>${wikipage.pagename} - The TurboGears 2 Wiki</title>

In the second <div> element, we substitute the page name again
with py:replace:

<spanpy:replace="wikipage.pagename">Page Name Goes Here</span>

In the third <div>, we put in the contents of our``wikipage``:

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

When you refresh the output web page you should see “initial data”
displayed on the page.

Note

py:replace replaces the entire tag (including start and
end tags) with the value of the variable provided.

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
Wiki-20/wiki20/controllers/root.py:

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.

When we displayed our wiki’s edit form in the last section, the form’s
action was /save. 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 you 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. This will cause the wiki to become
the default when possible.

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

Unlike the previous methods we’ve made, save just uses a plain
@expose() without any template specified. That’s because we’re
only redirecting the user back to the viewing page.

Although the page.data=data statement tells SQLAlchemy that you
intend to store the page data in the database, you would usually
need to flush the SQLAlchemy Unit of Work and commit the currently
running transaction, those are operations that TurboGears2
transaction management will automatically do for us.

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.

Our wiki doesn’t yet have a way to link pages. A typical wiki will
automatically create links for WikiWords when it finds them
(WikiWords have also been described as WordsSmashedTogether). This
sounds like a job for a regular expression.

Here’s the new version of our RootController._default method,
which will be explained afterwards:

We need some additional imports, including re for regular
expressions and a method called publish_parts from docutils.

A WikiWord is a word that starts with an uppercase letter, has a
collection of lowercase letters and numbers followed by another
uppercase letter and more letters and numbers. The wikiwords
regular expression describes a WikiWord.

In _default, the new lines begin with the use of publish_parts,
which is a utility that takes string input and returns a dictionary of
document parts after performing conversions; in our case, the
conversion is from Restructured Text to HTML. The input
(page.data) is in Restructured Text format, and the output format
(specified by writer_name="html") is in HTML. Selecting the
fragment part produces the document without the document title,
subtitle, docinfo, header, and footer.

You can configure TurboGears so that it doesn’t live at the root of a
site, so you can combine multiple TurboGears apps on a single
server. Using tg.url() creates relative links, so that your links
will continue to work regardless of how many apps you’re running.

The next line rewrites the content by finding any WikiWords and
substituting hyperlinks for those WikiWords. That way when you click
on a WikiWord, it will take you to that page. The r'string' means
‘raw string’, one that turns off escaping, which is mostly used in
regular expression strings to prevent you from having to double escape
slashes. The substitution may look a bit weird, but is more
understandable if you recognize that the %s gets substituted with
root, then the substitution is done which replaces the \1 with
the string matching the regex.

Note that _default() is now returning a dict containing an
additional key-value pair: content=content. This will not break
wiki20.templates.page because that page is only looking for
page in the dictionary, however if we want to do something
interesting with the new key-value pair we’ll need to edit
wiki20.templates.page:

Since content comes through as XML, we can strip it off using the
Markup() function to produce plain text (try removing the function
call to see what happens).

To test the new version of the system, edit the data in your front
page to include a WikiWord. When the page is displayed, you’ll see
that it’s now a link. You probably won’t be surprised to find that
clicking that link produces an error.

The highlighted section represents the template code of interest. You can
guess that the py:for is a python for loop, modified to fit
into Kajiki’s XML. It iterates through each of the pages (which
we’ll send in via the controller, using a modification you’ll see
next). For each one, PageNameHere is replaced by pagename,
as is the URL. You can learn more about the Kajiki Template Language.

We must also modify the RootController class to implement pagelist and to
create and pass pages to our template: