TurboGears 2 Tutorial


The files created in this tutorial can be downloaded as a .zip file, a .tar file, or can be cloned from a github repository.


Various parts of this tutorial differ based on the version of TurboGears you are using. If you are unsure about the exact version, fire up a Python interpreter and type:

>>> import tg
>>> tg.__version__

Please also consult the accompanying TurboGears documentation for your version: TurboGears 2.2 or TurboGears 2.3.

Enabling ToscaWidgets

First, you need to create a TurboGears project. The full instructions are in the TurboGears documentation, briefly (assuming you have virtualenvwrapper installed):

$ mkvirtualenv --no-site-packages tw2-and-tg2
$ pip install tg.devtools
TurboGears<=2.2 TurboGears>=2.3
$ paster quickstart -s -n -m myapp
$ gearbox quickstart -s -n -m myapp

This command creates a new TurboGears project named myapp in the directory myapp using the SQLAlchemy object relational mapper (-s), no authentication (-n) and the Mako templating engine (-m).

Now change to the newly created directory:

$ cd myapp

Then open setup.py and add the following to the install_requires=[...] entry:




Once that’s done, install your dependencies by running:

$ pip install -e .

TurboGears 2.0

Edit myapp/config/middleware.py, add import tw2.core as twc to the top of the file, and replace the line:

app = make_base_app(global_conf, full_stack=True, **app_conf)

with the following two lines:

custom = lambda app : twc.make_middleware(app, default_engine='mako')
app = make_base_app(global_conf, wrap_app=custom, full_stack=True, **app_conf)

TurboGears 2.1

Edit myapp/config/app_cfg.py and add at the end:

base_config.use_toscawidgets2 = True

TurboGears 2.2 or greater

ToscaWidgets 2 is fully integrated and supported by these TurboGears versions, so no changes are needed.

Verifying the installation

To check whether the installation worked:

TurboGears<=2.2 TurboGears>=2.3
$ paster serve development.ini
$ gearbox serve

Building a Form

We’ll create a movie database as in the Standalone Tutorial example. First, let’s create a movie controller and mount it from our root controller.

Create a new file myapp/controllers/movie.py with the contents:

from tg import expose, request

from myapp.lib.base import BaseController
from myapp import model

__all__ = ['MovieController']

import tw2.core
import tw2.forms

class MovieForm(tw2.forms.FormPage):
    title = 'Movie'

    class child(tw2.forms.TableForm):
        title = tw2.forms.TextField(validator=tw2.core.Required)
        director = tw2.forms.TextField()
        genres = tw2.forms.CheckBoxList(options=['Action', 'Comedy', 'Romance', 'Sci-fi'])

        class cast(tw2.forms.GridLayout):
            extra_reps = 5
            character = tw2.forms.TextField()
            actor = tw2.forms.TextField()

class MovieController(BaseController):
    def movie(self, *args, **kw):
        w = MovieForm(redirect='/movie/').req()
        return dict(widget=w, page='movie')

Add another new file myapp/templates/widget.mak with the contents:

<%inherit file="local:templates.master"/>

<%def name="title()">
  TurboGears 2 and ToscaWidgets 2, like jelly and jam with no bread:  Great!

${widget.display() | n}

And open up the existing file myapp/controllers/root.py and add, just below the from myapp.controllers.error import ErrorController line:

from myapp.controllers.movie import MovieController

And just below the error = ErrorController() line:

movie = MovieController()

With those three file edits in place, you should be able to restart the application with paster serve development.ini/gearbox serve (there is a --reload option for convenience) and point your browser at http://localhost:8080/movie/movie.

The form does not look particularly appealing. To try to improve this, lets add some CSS. We’ll start with something simple; create myapp/public/css/myapp.css with the following:

th {
    vertical-align: top;
    text-align: left;
    font-weight: normal;
    padding: 3px;

ul {
    list-style-type: none;

.required th {
    font-weight: bold;

th label {
    font-weight: bold;

td label {
    display: inline;

Notice the use of the required class. TableForm applies this to rows that contain a field that is required.

Before TableForm will inject myapp.css into the page, we’ll have to add it to the list of resources. Add the following to the top of the MovieForm class definition in myapp/controllers/movie.py just above the line title = 'Movie':

resources = [tw2.core.CSSLink(link='/css/myapp.css')]

Restart paster/gearbox and browse to http://localhost:8080/movie/movie to see the new css in action.

Connecting to a Database


Be aware that the following describes a different approach (using than the one recommended in the TurboGears documentation!

The next step is to save movies to a database. To do this, we’ll use only SQLAlchemy (and not elixir as in the Standalone Tutorial tutorial). SQLAlchemy is built into TurboGears by default. Edit myapp/config/app_config.py and add near the top:

from tw2.core.middleware import ControllersApp as TW2ControllersApp

and add at the very bottom:

base_config.custom_tw2_config['controllers'] = TW2ControllersApp()
base_config.custom_tw2_config['controller_prefix'] = '/tw2_controllers/'
base_config.custom_tw2_config['serve_controllers'] = True

Next add a brand new file myapp/model/movie.py with the contents:

from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Unicode, Integer
from sqlalchemy.orm import relation, backref

from myapp.model import DeclarativeBase, metadata, DBSession

__all__ = ['Movie', 'Genre', 'Cast']

movie_genre_table = Table('movie_genre', metadata,
    Column('movie_id', Integer, ForeignKey('movies.id',
        onupdate='CASCADE', ondelete='CASCADE'), primary_key=True),
    Column('genre_id', Integer, ForeignKey('genres.id',
        onupdate='CASCADE', ondelete='CASCADE'), primary_key=True)

class Movie(DeclarativeBase):
    __tablename__ = 'movies'
    id = Column(Integer, primary_key=True)
    title = Column(Unicode(255))
    director = Column(Unicode(255))

class Genre(DeclarativeBase):
    __tablename__ = 'genres'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(255))
    movies = relation('Movie', secondary=movie_genre_table, backref='genres')

    def __unicode__(self):
        return unicode(self.name)

class Cast(DeclarativeBase):
    __tablename__ = 'casts'
    id = Column(Integer, primary_key=True)
    movie_id = Column(Integer, ForeignKey(Movie.id))
    movie = relation(Movie, backref=backref('cast'))
    character = Column(Unicode(255))
    actor = Column(Unicode(255))

Next edit myapp/model/__init__.py and uncomment the line that reads:

DeclarativeBase.query = DBSession.query_property()

and also add the following line to the very bottom of that file:

from myapp.model.movie import Movie, Genre, Cast

Edit myapp/websetup/bootstrap.py and add the following just inside the bootstrap function definition:

for name in ['Action', 'Comedy', 'Romance', 'Sci-fi']:

And finally, get your controller ready to redirect everything as necessary. Edit myapp/controllers/movie.py and add to the very top:

import tw2.sqla

As well, change class MovieForm(tw2.forms.FormPage): to instead read:

class MovieForm(tw2.sqla.DbFormPage):
    entity = model.Movie

Just inside the definition of the child class (right above the title = line) add:

action = '/tw2_controllers/movie_submit'
id = tw2.forms.HiddenField()

And the last for the MovieForm, change genres = tw2.forms.CheckBoxList( ... ) to:

genres = tw2.sqla.DbCheckBoxList(entity=model.Genre)

And (still in myapp/controllers/movie.py) inside the MovieController’s movie method, just below the line w = MovieForm(... add the three lines:

tw2.core.register_controller(w, 'movie_submit')

Now, in your command prompt run:

TurboGears<=2.2 TurboGears>=2.3
$ paster setup-app development.ini
$ gearbox setup-app

This will create and initialize your database in a sqlite DB.

We’re almost done, but not quite. Nonetheless, this is a good point to restart your app and test to see if any mistakes have cropped up. Restart paster/gearbox and visit http://localhost:8080/movie/movie. Submit your first entry. It should give you an Error 404, but don’t worry. Point your browser now to http://localhost:8080/movie/movie?id=1 and you should see the same movie entry that you just submitted.

Great – we can write to the database and read back an entry, now how about a list of entries?

Add a whole new class to myapp/controllers/movie.py:

class MovieIndex(tw2.sqla.DbListPage):
    entity = model.Movie
    title = 'Movies'
    newlink = tw2.forms.LinkField(link='/movie/movie', text='New', value=1)
    class child(tw2.forms.GridLayout):
        title = tw2.forms.LabelField()
        id = tw2.forms.LinkField(link='/movie/movie?id=$', text='Edit', label='Action')

And add the following method to your MovieController:

def index(self, **kw):
    w = MovieIndex.req()
    return dict(widget=w, page='movie')

Getting Fancy

And if we wanted to start getting fancy we could add:

<li class="${('', 'active')[page=='movie']}"><a href="${tg.url('/movie')}">Movies</a></li>

to the list of <ul id="mainmenu"> ... </ul> items in myapp/templates/master.mak.

We could also make things dynamic by editing myapp/controllers/movie.py and adding at the top:

import tw2.dynforms

replacing class child(tw2.forms.TableForm): with:

class child(tw2.dynforms.CustomisedTableForm):

and replacing:

class cast(tw2.forms.GridLayout):
    extra_reps = 5


class cast(tw2.dynforms.GrowingGridLayout):

Getting Fancier

There are a lot of non-core TW2 widget libraries out there, and just to give you a taste, we’ll use one to add one more view to our Movie app.

Edit myapp/controllers/movie.py and add the following to the top:

import tw2.jqplugins.jqgrid

Add the following class definition to the same file:

class GridWidget(tw2.jqplugins.jqgrid.SQLAjqGridWidget):
    id = 'grid_widget'
    entity = model.Movie
    excluded_columns = ['id']
    prmFilter = {'stringResult': True, 'searchOnEnter': False}
    pager_options = {'search': True, 'refresh': True, 'add': False, }
    options = {
        'url': '/tw2_controllers/db_jqgrid/',
        'rowNum': 15,
        'rowList': [15, 30, 50],
        'viewrecords': True,
        'imgpath': 'scripts/jqGrid/themes/green/images',
        'width': 900,
        'height': 'auto',

And add the following method to the MovieController class:

def grid(self, *args, **kw):
    tw2.core.register_controller(GridWidget, 'db_jqgrid')
    return dict(widget=GridWidget, page='movie')

Your template has already been loading the current jQuery library the whole time, but that would be causing us trouble now, since tw2.jquery also provides the library, even versioned. So you need to delete or comment the line that looks like follows from myapp/templates/master.mak and myapp/templates/master.html:

<script src="http://code.jquery.com/jquery.js"></script>

Redirect your browser to http://localhost:8080/movie/grid and you should see the sortable, searchable jQuery grid.