Some of the information here may be outdated, please check the book instead


  • We really like TurboGears and web2py has taken inspiration from some TG features.

  • If we say anything wrong about TurboGears please let us know and we will correct it.

  • These are just some tips on how to move a TurboGears 1.0 project to web2py, it is not a comparison

  • Notice that web2py is not based on TurboGears although it has some similarities in the syntax

  • Many of the TurboGears examples below are from the TG documentation page

Features comparison

Click here for a feature comparison between web2py and other frameworks.

Shell or not shell

Both TG and web2py have a shell. Web2py also has an administrative interface that allows you create, import, delete, export, design/edit, debug, test, and administer you app, so you do not need to use the shell. Here is a demo web2py also provides an ajax web-based python shell to interact with the apps and their models.

You can start the text-based shell with

python -S admin

You can get the ajax shell and other ready made web2py apps from the repository of appliances

There is also a Ulipad Shell Plugin


This is an example of a TG model:

#import SQLObject, UnicodeCol, etc. etc.
class Page(SQLObject):
    pagename = UnicodeCol(alternateID=True, length=30)
    data = UnicodeCol()

This is the same in a web2py model:


Notice that:

  • In web2py you do not need to import the web2py stuff.
  • The connection URI is specified in the model, not in the configuration file because web2py has no configuration files. This enables you to connect to multiple databases within different apps and within the same app.
  • In web2py the few statements above are sufficient to generate an administrative interface for the app.
  • Unlike TG if you change the fields in the table, web2py will do a migration for you and no questions asked.
  • There are no shell commands to type. Just edit the models/view/controllers and things will happen.
  • web2py works with SQLite, MySQL, PostgreSQL, and Oracle.

You can use the interactive model builder to generate web2py models.

To insert records in TG

 p=Page(name='name', data="")

To insert records in web2py'name', data="")

Notice that:

  • In web2py there is no save. If the insert is executed in a controller the above statement is executed in a transaction. The transaction is committed if no exception is raised by the controller, else it is rolled back.
  • In web2py db.insert returns the id of the record that was just inserted.
  • Tables are attributes of the object representing the db connection. This is because in web2py you can have multiple connections. You can also do distributed transactions with postgresql.

To select some records in TG:, %am%"),orderBy=Page.q.pagename)

To select some records in web2py:

  • In web2py the argument of db(...) defines the set. You can use ()&() for AND and ()|() for OR and ~() for NOT.
  • In web2py the condition in db(...) can involve multiple tables and this will result in an automatic join.
  • In web2py you can pass arguments to .select(...) to select only some fields, sort them, group them and cache the select.


The following TG view:

import turbogears
from turbogears import controllers, expose
from wiki20.model import Page
from docutils.core import publish_parts

class Root(controllers.RootController):
    def index(self , pagename="FrontPage"):     
        page = Page.byPagename(pagename)       
        return dict(page=page)

In web2py would be

def index():
     return dict(page=page)

Notice that:

  • In web2py there is not expose. All functions in controller that do not take arguments and do not start with a double underscore are exposed.
  • In web2py you do not need to import web2py stuff nor models. If you defined them, web2py understand you plan to use them.
  • You do not need to specify a view. web2py assumes index() in will be rendered by controller/index.html. You can change this default behavior.
  • In TG you need to create the view. In web2py you should but, if you don't, it will render the variables anyway using a generic view.

In TG you redirect with

import turbogears
raise turbogears.redirect("/", pagename='index')

In web2py you redirect with


It redirects the visitor to the 'index' controller function within the same application/controller.

Notice that:

  • In TG uncaught exceptions result in a traceback shown to the visitor (in debug mode) or they are lost (in production)
  • In web2py all uncaught exceptions other than HTTP(...) result are logged and the administrator can browse them via the administrative interface. A ticket is issued to the visitor in order to track the cause of the exception. web2py never shows code to the visitor, not even by mistake.

Views (also known as templates)

In TG the view for the above controller function index() could look like

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns=""
<meta content="text/html; charset=utf-8"
      http-equiv="Content-Type" py:replace="''"/>
<title> ${page.pagename}</title>
    <div style="float:right; width: 10em">
        Viewing <span py:replace="page.pagename">Page Name Goes Here</span>
    <div py:replace="XML(data)">Page text goes here.</div>

In web2py the same output could be produced by

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<meta content="text/html; charset=utf-8"
      http-equiv="Content-Type" py:replace="''"/>
    <div style="float:right; width: 10em">
        Viewing {{=page.pagename}}

Notice that:

  • The TG example uses the Kid template language (there is a choice with TG)
  • web2py uses Python in {{ }} as template language without any limitation.
  • web2py uses "pass" to terminate blocks when it is not obvious.
  • You can build your own layout files as in TG but you can also use our automated layout builder

On request, response, session

In web2py form variables are in request.vars and they can be accessed by

request.var.myvariable or request.vars['myvariable']

The first notation returns None if not request.vars.has_key('myvariable'). The second raises an exception. You can use request.post_vars to get only post vars and request.get_vars to get only get variables.

To store stuff in session you do


and you retrieve it with

  • Normally sesions are stored on disk and locked when in use.
  • It is also possible to store session on database.
  • sessions are saved automatically when web2py returns, unless specified otherwise.
  • It is possible for multiple apps to cooperate by sharing sessions and/or databases

The response object is used to stream data, create xmlrpc services and send some standard variables to the views.


In TG:

_('this is a message')

In web2py:

T('this is a message')

(and as usual no need to import anything since you are supposed to use T) You web2py you can also do

T('this is message %(name)s',dict(name='xxx'))
  • The expression is evaluated lazily when displayed in views.
  • In web2py, the web based administrative interface provides a translation page interface.
  • To create a language file in web2py just type its name (for example ) in the admin interface.


web2py also has helpers to create HTML for example


produces the same output as

<a href="{{=URL(r=request,f='index)}}">here</a>

Helpers can be nested as in

HTML(BODY(H1('title'),P('bla '*10,_id='main')))

and they can be used to build forms via FORM and INPUT.

Notice that web2py escapes all text displayed in views to prevent XSS. Only text marked by XML('...') is not escaped.


Here is how to create a create_page form in web2py

 def create_page():
     if form.accepts(request.vars): response.flash='page posted'
     elif: form.errors: response.flash='there are errors!'
     return dict(form=form)

Notice that in we2bpy:

  • SQLFORM can generate Create/Update/delete forms
  • SQLFORM.accepts does all the processing of input variables, validation and database IO. If the form is validated it performs the database insert or update. If it is not validated it alters the form and includes error messages that are displayed with the form.


web2py validators are objects which means they take parameters (for example the error message on failed validation)

db.mytable.myfield.requires=[IS_EMAIL(error_message="invalid email")]
  • You never need to call web2py validators since they are called automatically upon form submission.
  • The error messages are automatically inserted in forms.
  • Some validators like IS_IN_DB and IS_NOT_IN_DB check whether the values is or is not already in the database.
  • If a form field requires that it IS_IN_DB it is automatically rendered as a select/option input field.


Both TG and web2py can use doctests for testing.

In web2py, the administrative interface provides a button to run all the tests for you app and writes a report.


Sorry, I do not know how to cache in TG.

In web2py there is no configuration to do. There are two built-in caching mechanisms cache.ram and cache.disk and other plugins (memcache). You can use them as follows:

def index(): ...

The first argument of cache is the key to be used for caching the function, the second is the expiration time in seconds and the third is the cache type. You need to specify because you can use different caching mechanisms within the same ap. You can also use cache.ram and cache.disk to cache any function, select and view.

If you really like memcache you can define cache.memcache by doing

from gluon.contrib.memcache import MemcacheClient

and then use cache.memcache in place of cache.ram.


TG uses Dojo. web2py comes with jQuery base. But with any of them you can use any JS library.


In TG:

class Root(controllers.RootController):
def pagelist(self):
   pages = [ for page in]
   return dict(pages=pages)

In web2py

import gluon.contrib.simplejson as sj
def pagelist():
   pages = [ for page in db().select(,]
   return sj.dumps(dict(pages=pages))

What else?

  • web2py comes in one executable package including an SSL enabled web server, the sqlite database, a web administratve interface that allows you to create/edit/deploy and manage all your applications.

  • Exception are automatically caught by web2py which logs the traceback, the code causing the exception, and issues a ticket to the visitor that triggered the exception. No code is exposed to the visitor, not even by mistake, ever.

  • web2py includes libraries for generating CREATE/UPDATE/DELETE forms from your database tables.

  • web2py incudes libraries for handling AJAX, JSON, REST, RSS, ATOM, RTF, CSV, WIKI (markdown) and some more protocols.

  • web2py has helpers that help you build objects that can be serialized in HTML or XML. Any correct HTML/XML can be generated using exclusively helpers.

  • web2py code runs on the Google App Engine.

  • web2py packages everything you need in one binary file that you don't even need to install. I keep mine on a USB stick. You just click on it and it start web server, the sqlite database, fires the browser for access to the administrative interface.

  • web2py deals with static files for you, streams them when they are large (both in upload and download), and automatically supports IF_MODIFIED_SINCE and PARTIAL CONTENT. You can do streaming audio and video without any extra tools or settings.

  • web2py can map URLs using regular expressions so that you can use it to handle legacy URLs.

  • web2py has no configuration files. You just create an empty app via the shell or the web based admin interface and create/edit your models, controllers and views (using an editor or the web based admin interface).

There is a repository of free web2py apps here and an interactive FAQ there.

© 2008-2010 by Massimo Di Pierro - All rights reserved - Powered by web2py - design derived from a theme by the earlybird
The content of this book is released under the Artistic License 2.0 - Modified content cannot be reproduced.