TurboGears: Getting Started

At this point, you've probably seen a tutorial and read some of the "about TurboGears" pages. If not, that's okay. This guide is a fine starting point as well.

This guide sits between a tutorial and reference documentation. A tutorial covers a specific topic, providing either a quick solution or a quick view of something, glossing over the background in the process. Reference documentation gets into the minutiae of working with a particular element.

This Getting Started guide provides background information giving a broad brush view of TurboGears without the focus of a tutorial or the attention to detail of reference documentation.

TurboGears installation is not covered at all here, as the download page will walk you through that process. This guide does not cover Python at all, given that there are plenty of good books and resources on Python.

tg-admin

Ultimately, the final product in a webapp appears in the form of executable code. Because of this, code makes a good frame of reference when talking about a programming tool. The quickest way to have some code with TurboGears is using the tg-admin tool.

tg-admin is a command line companion for the TurboGears library. It works through subcommands that let you: create a new project, manipulate your database, and interact in an object-oriented way with your database.

The reference for tg-admin provides the details on the commands. If you just run tg-admin without specifying any commands, you'll see the list of what's available.

For now, we'll use the "quickstart" command, so that we have some code to use as a frame of reference:

tg-admin quickstart

If you add the "--help" option to the commands, you can see what options each command takes. For quickstart, you can specify everything on the command line or be prompted for the information needed to get going. Odds are that you won't run quickstart that often. You never know, though. It's really easy to get going!

We'll just use "gs" (for "getting started") as our project and package names.

Enter project name: gs
Enter package name [gs]: 
creating directory gs
generating gs/dev.cfg
generating gs/prod.cfg
generating gs/gs-start.py
generating gs/setup.py
creating directory gs/gs
generating gs/gs/__init__.py
generating gs/gs/controllers.py
generating gs/gs/model.py
creating directory gs/gs/sqlobject-history
creating directory gs/gs/static/css
creating directory gs/gs/static/images
creating directory gs/gs/static/javascript
creating directory gs/gs/templates
generating gs/gs/templates/__init__.py
generating gs/gs/templates/master.kid
generating gs/gs/templates/welcome.kid
creating directory gs/gs.egg-info
generating gs/gs.egg-info/sqlobject.txt
running egg_info
writing requirements to ./gs.egg-info/requires.txt
writing ./gs.egg-info/PKG-INFO
writing top-level names to ./gs.egg-info/top_level.txt

This command gave us a clean-slate web application, ready to start filling in with real code.

Starting up the server

As soon as you run the quickstart, you're ready to fire up CherryPy's built-in server and get moving! To run the server for development use:

  1. Switch to the top level directory of your newly created project (in the example here, "cd gs")
  2. Run "python PROJECTNAME-start.py" ("python gs-start.py", in the example)

You'll see logging messages telling you that the server has started up. By default, it runs on port 8080, so you should be able to see the base welcome page by pointing your browser at http://localhost:8080/. On the Mac, you'll be able to see your development site in the Bonjour bookmarks.

The server watches for changes to files in your project and will automatically restart if it sees any. You can stop the server at any time by pressing Control-C.

Configuration

The quickstart created two configuration files (dev.cfg and prod.cfg) for you. The startup script looks to see if setup.py is present in your current directory and, if it is, assumes you're running in development mode and uses dev.cfg. Otherwise, it will look for prod.cfg in your current directory. You can tell the startup script which configuration file to use on the command line.

The Big Picture

That's a lot of boxes and arrows! Actually, it's not as bad as it may seem. This diagram shows all of the parts that make up a TurboGears application. A great deal of it comes from TurboGears itself. Your application consists of the parts in the light purple boxes. Let's look at each part:

You have these three areas to populate with code in order to make your application. TurboGears provides help at each point.

CherryPy makes it easy for your controller to get information from the web and string together all of the pieces that comprise your website. SQLObject makes it easy to define and work with your model. Kid makes it easy to generate good HTML for the browser. MochiKit makes it easy to implement complex in-the-browser behavior and can even be used to format output in AJAX requests that skip over Kid.

CherryPy Exposed

In CherryPy, your website is represented by a tree of objects and their methods. The way applications are set up in CherryPy, it's easy to follow how a request makes it through your system to reach its destination. How does CherryPy know where to start with a request? It starts a cherrypy.root.

If you look at the gs-start.py script, you'll see this (among other things):

By convention, your root controller class is called "Root" and it comes from the "controllers.py" module in your project's package. In a small application, one module of controllers is all you need. In a larger application, you will likely want to break your application up into separate packages. You'd want to use divisions that make sense for you. You can still import the controllers from your various packages into your controllers module, if you'd like them all available in one place.

As long as you make sure that yourpackage.controllers.Root is the root object you want for your webapp, you're all set.

Let's get a nicely contrived set of classes together to see URL lookup and controller traversal works:

With this setup, we'll be able to easily see how URL traversal works in TurboGears. These same rules apply to CherryPy even without the rest of TurboGears.

URL pathWhat you seeNotes
/WelcomeIf nothing else is given, the index method is run
/fun...and yet startlingly productiveIf a method matches, it's run directly
/sevenRedirect to /seven/Subobjects are treated like directories, so you get redirected with a slash on the end. This is a good thing, because it makes sure that your relative URLs will work.
/seven/7Just running the index method of the subobject
/add/cantgohereError!This method is not exposed. CherryPy will only navigate to exposed methods.
/add/twoplustwofourJust running the method of the subobject
/foobarThe next parts were ('foobar')If no match is found for the remaining URL parts, the default method is called.
/foobar/baz/borkThe next parts were ('foobar', 'baz', 'bork')The remaining parts of the URL are split up and sent into the default methods as arguments.

Between constructing your object tree however you need to and the default method, you have very sophisticated means to determine what happens when a URL is hit.

I wanted an argument!

One of the features that makes CherryPy (and TurboGears by association) so much fun to use is that parameters coming in from a web request are automatically turned into parameters for your method. This makes handling web requests about as natural as possible.

Consider a root that looks like this:

You probably won't be surprised to learn that access "/" will return "I am Arthur, King of the Britons". Default arguments work exactly as you'd expect them to. In fact, now that you're ready for it, I'm guessing that request parameters will work exactly as you expect them to. A request to "/?name=Lancelot" would return "I am Lancelot, King of the Britons".

Just as in Python itself, calling "/?foobar=baz" will raise an exception because it will try to call root.index(foobar="baz"). And, if you don't know exactly what to expect on the way in, you can always do this:

Just as in standard Python, kw would be a dictionary holding the rest of the request arguments.

Testing, 1... 2... 3...

Test-driven development is a good practice. By writing your tests before the code, you solidify in your mind what the code really needs to do and whether it's structured to do what you want.

Even if you don't write tests first, having automated tests can be a lifesaver when it comes time to change things around later on.

When you install TurboGears, you also get TestGears for free. TestGears is a simple addition to the standard Python unittest module that eliminates the need to manually write test suites.

A separate document will cover testing in detail. Here's a sample controllers.py:

TestGears looks for modules that start with test_. By convention, test modules go into separate packages called "tests" located beneath the package they are testing, though TestGears does not require this. Here is a simple test module for the class above:

Notice that these tests are just functions, and not unittest.TestCases. You can freely use unittest.TestCases also, if you prefer. Test functions must begin with "test". The docstring provides nicer output in the testrunner.

The template test assumes that the welcome template contains something like: "The new value is ${newvalue}."

To run the tests, you just run:

python setup.py testgears

The template test goes through CherryPy's request handling machinery, so you can test everything from URL traversal to filters to template processing.

Validating and converting arguments

TurboGears provides a simple means to use FormEncode validators to check whether incoming method arguments are valid and to convert them from strings to appropriate Python types. Here's a simple example:

This dictionary passed to turbogears.expose is saying to run the value argument through the Int validator. You are assured that if the index method is called, value will be an int.

If the validation fails, a method called validation_error (if present) will get called instead of the original target method. The first argument is a string with the name of the method that failed validation. You can use this to pick an appropriate template to render, for example. The second argument is the dictionary of arguments that was passed into the original method. The final argument is a list of Invalid exceptions that were raised.

Invalid exception objects can provide a user-friendly error message. These objects and the original arguments allow you to repopulate an entry form that failed validation.

What should the validation_error method do? It is free to do whatever the original method could do. It can return a dictionary or string. It can raise a cherrypy.HTTPRedirect to take the user to another page. It should be easy to do whatever makes sense for your app.

All of the validators are imported into the turbogears.validators module. You can see the validators that are available by looking at the FormEncode validators module and the TurboGears validators module.

You can do more advanced validation using a FormEncode validation schema. The validators parameter to expose allows you to pass either a schema or a dictionary as in the example above. For more information about schemas, refer to the FormEncode Validator documentation.

How a view template is chosen

There have already been some references to an "html" parameter to turbogears.expose, but now all will be made clear.

As with CherryPy alone, you can return a string and have that string presented to the browser. That's nice but, more often than not, you want to present a whole web page. With TurboGears, you can easily populate a Kid template by returning a dictionary of variables rather than a string, and by passing that "html" parameter to turbogears.expose.

The html parameter gives the full Python package path to get to the kid template. In a quickstarted project, your templates will be in "packagename.templates". The files all end in ".kid", but you can leave that out when specifying the template in the html parameter. So, if you have a template called "welcome", you can say html="packagename.templates.welcome".

What goes into the dictionary? Anything you want the template to have access to. If you return dict(newvalue=5), then ${newvalue} in the template will get the int 5.

Sometimes, in the processing of a request you find that you need a different template than the one you knew about initially. If the dictionary you return has a "tg_template" key, the template you specify there will be used instead. return dict(tg_template="packagename.templates.lancelot") will render the output using the lancelot template, rather than the one in the html parameter.

Returning XML instead of HTML

First and foremost, Kid is an XML template language. With the HTML serializer, it produces good HTML. But, with other serializers, you can also produce XHTML or other XML formats.

The expose function lets you specify a format (which can be "json" or one of Kid's serializers) and a content_type. Let's say you wanted to return RSS, for example. You could set up your expose call like this:

@turbogears.expose(template="project.templates.rss",
    format="xml", content_type="text/xml+rss")

The expose function defaults to HTML, but it's easy to change to a different format as needs require. Note that expose lets you pass in the template as the "template" parameter or as the "html" parameter.

Brief introduction to Kid templates

Kid templates can be any XML document with namespaced attributes that tells Kid how to process the template. In practice, your templates will be XHTML documents that will be processed and transformed into valid HTML documents.

This example (straight from Kid's documentation) shows what Kid is like:

Important points about Kid templates:

One of the great things about Kid is that everything you know about Python applies here. Kid templates get compiled to Python code this makes it easier to predict how things will behave. For example, that py:for="fruit in fruits" behaves just like "for fruit in fruits:" in Python.

The variables that you defined in your dictionary are all available for your use. In any of the "py" attributes, just use the variable from the dictionary as you would a local variable in Python. In the py:for="fruit in fruits" example, "fruits" would be some kind of iterable object passed in via the dictionary.

When variables are dropped in to your template, Kid will automatically escape them. You don't even need to think about running into problems with values that contain <, for example. The time when you do need to care is if you actually have XHTML itself to drop in place. If you do, wrap your substitution value in XML(). For example, let's say we had an XHTML fragment called "header". You could just say ${XML(header)}, and the header would be dropped in without being escaped.

Rather than reproduce it here, a quick read that is well worth it is the reference to Kid's attribute language.

Being Designer-Friendly

Since they are standard XHTML, you can load up Firefox and open your Kid templates directly. If it's important or useful for you, you can make the templates look great all by themselves when loaded in the browser.

What do you need to watch for? Stylesheets, JavaScript and variable substitutions.

Sometimes, it's inconvenient or impossible to have a stylesheet href that works properly when viewing the template directly and after the template is rendered. To get around this, you can use href to handle viewing the template in your browser and py:attrs to handle the rendered page. For example:

When you're viewing the template in your browser, your browser's only going to look at the href attribute, so your stylesheet will be loaded properly. When the template is rendered for the final view, Kid will substitute the value that's in py:attrs, so that the stylesheet works properly on the web. When dealing with JavaScript, this same approach will work for the src attribute on the script tag.

Application-wide headers and footers

Kid offers a number of ways to do application-wide headers and footers. Let's focus on a couple particular approaches: the cool approach and the fast approach.

Kid has a really, really useful command called py:match. What makes it so useful is that you can write individual page templates that know nothing about the site-wide styling that will be applied. The quickstart command starts you off with this kind of setup.

Let's start with a page template that we want to have headers/footers applied to (this is based on gs/templates/welcome.kid):

The only thing in this template that is necessary to get headers and footers applied is the py:extends up in the HTML tag. Kid allows one template to extend or subclass another template. When you do this, your template inherits the parent's py:match and py:def blocks. py:extends works with a string that looks for a file relative to the current template, or a template module object that you have imported.

What does the master template look like?

This master template can be viewed in a browser, just as individual page templates can. The py:match attribute on the body looks for the body of the child template (note the use of XML namespaces). In the py:match statement, "item" gives you access to the element at that point in the document. You can look at the tag or the attributes (through dictionary-style lookup on item.attrib) of the element to see if you're looking at one you care about. In this case, we're just going to take over the body element.

When a match is found, the body of the child (individual page) template is replaced by the one in the master template. How do you get your content in there? At the bottom of this master template, there is a div that is replaced by "item[:]". In this particular example, that says to put everything under the body of the page template into this spot in the master template. Usually, you'll want all of the elements, so item[:] is what you'll use.

py:match is not the fastest way to work, but premature optimization is the root of all evil. If you've got a fairly straightforward app without thousands of users, py:match may work just great for you. py:match has to look compare each element with any match expressions you have defined. If you have very large pages and tons of simultaneous users, you may want something faster.

Kid offers a faster approach: template functions. These work just like normal template functions and they are inherited from the master template. There is a simple one in the example above: phraseOfTheDay.

phraseOfTheDay is defined in the master by py:def on a div. In the page template, it shows up as a py:replace in a div. So, the div in the child page template will just get replaced by the div from the master. This lets you include headers, footers, search boxes and the like easily.

Template functions can also take parameters just like normal Python functions. You could, for instance, have a title parameter that gets passed in from the child template in order to format a heading.

Since template functions are normal Python functions and don't affect any other processing of the template, using this styling method is very fast.

Template variables you get for free

All templates that get rendered by TurboGears have access to some basic variables automatically. The variables are defined in the stdvars function in turbogears.view. They provide conveniences for identifying the user's browser, plus common template tasks. See the stdvars documentation for the complete list.

In order to avoid name collisions with your own template variables, all of the predefined variables are found in "std". For example, the user agent is found in std.useragent.

One in particular that's worth noting is std.ipeek, which lets you look at the first item of an iterator without actually consuming it from the iterator. This comes in handy when you have an iterator that might be empty. If it is empty, you may not display a whole block from your template. Here's an example:

When you have a list, you don't need to use std.ipeek. But, when all you have is an iterator, this is a handy tool.

If you have site-wide variables that you would like available in your templates, you can add a callable to turbogears.view.variableProviders. This will be called with a dictionary that is already set up with the TurboGears standard variables. If you want the current time available in all of your templates, you can just add this to a module:

Using URLs

CherryPy makes defining what happens for a given URL quite easy: you just give your Python objects the methods you need. In your templates and your code, you can just use the URLs based on how you've defined them for CherryPy.

Sometimes, however, the URLs you use are affected by the environment they're in. For example, if your whole TurboGears site is running at http://yoursite.com/~yourusername/tg, you'll need to be sure that absolute URLs take that into account.

TurboGears provides a convenient url function to create these URLs. Let's say you want to redirect to /view?id=5&page=10. You can call turbogears.url("/view", id=5, page=10) to get a URL that takes into account the top of the site. How does it know where the top of the site is? The server.webpath configuration variable will tell it where the top of your site is.

The url function is also available in templates as "std.url".

Future versions of TurboGears will also make it easy to wire up multiple TurboGears applications in one site. The url function already understands the concept of an application root and will generate absolute URLs relative to the application root. Note: in order for the url function to recognize the application root, your root must extend turbogears.controllers.Root.

JSON output

By returning a dictionary from your controller methods, you make it possible for TurboGears to dynamically decide how the data should be presented to the browser. Most often, you'll want to return HTML.

When doing modern, interactive web applications ("AJAX"), you often want to send data to the browser in a form that is readily usable by JavaScript. While modern browsers all have some level of support for XML, that support varies in its depth and speed. For this reason, the lightweight JSON format is a good one. The format is native to JavaScript, so it works in all browsers and is very fast for the browser to deal with.

If a request comes in with a parameter of "tg_format=json", TurboGears will convert your Python dictionary into the equivalent JSON. If you eval this in the browser, you'll have a hash table of your data, ready to use!

This means that your application can instantly get a usable API in addition to its browser-based interface!

One important caveat as of TurboGears 0.8: only the main Python types are supported by the current JSON implementation. SQLObject-based objects cannot yet be directly serialized as JSON. In the future, there will likely be a way to plug in your own code to take your objects and produce sane JSON versions of them.

Brief introduction to MochiKit

While TurboGears is a Python-based megaframework, a modern webapp has to use JavaScript to provide a good user experience. JavaScript includes some of the conveniences that we, as Python programmers, are used to: built-in dictionaries (hashes) and listish arrays, regular expressions, functions as objects that can be passed around, etc. JavaScript also lacks a number of the tools that we're used to. The basic JavaScript library also does not include everything you'd need for a complete app.

MochiKit fills in a lot of the gaps in a way that is both natural for Python and JavaScript. It is broken down into several packages, but you can have immediate access to all of its functionality by adding one line to your page's <HEAD>:

<script src="${std.tg_js}/MochiKit.js"/>

Your templates automatically get the "std.tg_js" variable, and MochiKit is automatically made available as a static file from the server. When you load MochiKit this way, you're getting everything from the MochiKit modules. MochiKit includes:

MochiKit includes so much functionality, that this module breakdown makes it much easier to follow (and work with, if you're making improvements to it).

MochiKit's documentation is very good, so there's no reason to duplicate it here. To get an idea what each module has to offer, take a look at the introductory material at the top of each module's documentation. It is worth skimming through the function reference as well, because each module has features that will make your life easier. For example, you know from the synopsis that MochiKit.Base offers features like map and repr... but did you know that it also has functions for putting together and parsing HTTP query strings?

Defining your model

Your model represents whatever data your application works with. TurboGears models are typically represented using SQLObject objects. SQLObject provides a bridge between Python objects and a relational database. It's not designed to hide away the database 100%, but it does go a long way toward letting you write Python and not worry about SQL.

Before TurboGears can access your database, you need to tell it where to find the database. You do this in the dev.cfg file (for development) or the prod.cfg file (for production use). The config parameter "sqlobject.dburi" controls where the database is stored, and uses a typical URI scheme.

You can also provide options via "query parameters" on the connection URI. A couple of useful options are debug and debugOutput. If you add ?debug=1 to your URI, each query will be output as it is run. If you add &debugOutput=1, you'll also see the result of the query displayed.

You define your data model in the model.py module in your project's package. If you have more model code than will comfortably fit in one module, you can always break it up into multiple modules and import into the model module. SQLObject provides two different ways to define your database: you can define it in SQL directly in your database, or you can define it in Python. For clarity in your code, it is often easier to define your model in Python terms. If you do want your Python classes to be based on your database, you just do this:

Note that this only works with some databases. Check the SQLObject documentation to be certain this style of working.

Defining your model in Python requires more typing of Python code, but saves you from having to write SQL to create your database. Here's an extended book example written in Python code:

To create this database, you just need to run:

tg-admin sql create

This will create three tables in your database: one for books, one for authors, and a join table for the many-to-many relationship between them.

Though SQLObject cannot model every database, it does well against a wide variety of databases. Many aspects of how the database is used, such as column names, table names and join table names can be customized as needed to fit existing databases.

Your model objects need not (and should not!) be dumb data containers. They are full Python objects and can have methods of their own to perform more complex calculations and operations.

There is quite a bit more information about defining your data model with SQLObject in the SQLObject documentation. Note that as of this writing, the SQLObject documentation is for version 0.6.1 and TurboGears uses version 0.7. THe most user-visible part of the change is that the metadata used by SQLObject in managing your database has moved into a class called sqlmeta. The SQLObject wiki includes more information about using sqlmeta (for things like fromDatabase, cache control, the table name and the id column name).

Using your model

SQLObject makes accessing your database seem very much like using any other Python objects. Behind the scenes, SQL queries are being run as you do things. To insert a new row into your database, just instantiate an object from the appropriate class. Here's an example with debug logging (?debug=1&debugOutput=1) turned on:

>>> Book(isbn="1234567890", title="A Fistful of Yen", description="An evocative look at Japanese currency surrounded by hands.")
1/QueryIns: INSERT INTO book (isbn, description, title) VALUES ('1234567890', 'An evocative look at Japanese currency surrounded by hands.', 'A Fistful of Yen')
1/QueryIns-> 1
1/COMMIT : auto
1/QueryOne: SELECT isbn, title, description FROM book WHERE id = 1
1/QueryR : SELECT isbn, title, description FROM book WHERE id = 1
; 1/QueryOne-> (u'1234567890', u'A Fistful of Yen', 'An evocative look at Japanese currency surrounded by hands.')
1/COMMIT : auto
<Book 1 isbn='1234567890' title='A Fistful of Yen' description="'An evocative loo...'">

Though there are ways to have keys handled differently, SQLObject works best in its default setup where each table has an integer primary key. You can get at any SQLObject's primary key through the "id" attribute. SQLObject makes it really easy to retrieve by ID.

>>> Book.get(1)
<Book 1 isbn='1234567890' title='A Fistful of Yen' description="'An evocative loo...'">

When you specify that a column is an "alternateID", as we did for the "isbn", SQLObject automatically creates a classmethod so that you can use to search on those values:

>>> Book.byIsbn("1234567890")
1/QueryOne: SELECT id, isbn, title, description FROM book WHERE isbn = '1234567890'
1/QueryR : SELECT id, isbn, title, description FROM book WHERE isbn = '1234567890'
1/QueryOne-> (1, u'1234567890', u'A Fistful of Yen', 'An evocative look at Japanese currency surrounded by hands.')
1/COMMIT : auto
<Book 1 isbn='1234567890' title='A Fistful of Yen' description="'An evocative loo...'">

Of course, there are plenty of times when we need to do searches beyond just simple ID lookups. SQLObject provides a "select" classmethod that lets you specify many queries in more Python-like terms. Your class has a special "q" attribute that gives you access to a placeholder for a real attribute to use in queries. For example, to query on the isbn column, you would use Book.q.isbn. Here's a sample query:

>>> list(Book.select(AND(LIKE(Book.q.title, "%Fistful%"), Book.q.isbn=="1234567890")))
1/Select : SELECT book.id, book.isbn, book.title, book.description FROM book WHERE ((book.title LIKE '%Fistful%') AND (book.isbn = '1234567890'))
1/QueryR : SELECT book.id, book.isbn, book.title, book.description FROM book WHERE ((book.title LIKE '%Fistful%') AND (book.isbn = '1234567890'))
1/COMMIT : auto
[<Book 1 isbn='1234567890' title='A Fistful of Yen' description="'An evocative loo...'">]

In the example above, you'll note the call to "list" around the Book.select call. The select classmethod returns a SelectResults object. The neat thing about SelectResults is that until you start pulling data out of it, it's just a placeholder for the results. Rather than converting the results to a list, we could have added .count() to the end of the select call in order to just retrieve the number of matching rows.

Updates are very easy: just change the attribute! Every time you change an attribute, SQLObject will run an UPDATE SQL statement. Sometimes, though, you may need to change several columns at once and don't want to run individual updates for each. Your instances have a "set" method that lets you set them all at once. Here are examples of both styles:

>>> book.title = "A Fistful of Foobar"
1/Query : UPDATE book SET title = 'A Fistful of Foobar' WHERE id = 1
1/QueryR : UPDATE book SET title = 'A Fistful of Foobar' WHERE id = 1
1/COMMIT : auto
>>> book.set(title="A Fistful of Yen 2: Electric Boogaloo", isbn="37")
1/Query : UPDATE book SET isbn = '37', title = 'A Fistful of Yen 2: Electric Boogaloo' WHERE id = 1
1/QueryR : UPDATE book SET isbn = '37', title = 'A Fistful of Yen 2: Electric Boogaloo' WHERE id = 1
1/COMMIT : auto

TurboGears makes it easy to use transactions, via its "connection hub". The connection hub automatically connects to the database as needed, and also gives you methods to begin, commit, rollback or end transactions. Here's an example of transactions at work:

>>> book.title
'A Fistful of Yen 2: Electric Boogaloo'
>>> hub.begin()
>>> book.title = "A Fistful of Yen 3: The Sequel That Shouldn't Be"
1/Query : UPDATE book SET title = 'A Fistful of Yen 3: The Sequel That Shouldn''t Be' WHERE id = 1
1/QueryR : UPDATE book SET title = 'A Fistful of Yen 3: The Sequel That Shouldn''t Be' WHERE id = 1
>>> hub.rollback()
1/ROLLBACK:
>>> hub.end()
>>> book.title
"A Fistful of Yen 3: The Sequel That Shouldn't Be"
>>> book.sync()
1/QueryOne: SELECT isbn, title, description FROM book WHERE id = 1
1/QueryR : SELECT isbn, title, description FROM book WHERE id = 1
1/QueryOne-> (u'37', u'A Fistful of Yen 2: Electric Boogaloo', 'An evocative look at Japanese currency surrounded by hands.')
1/COMMIT : auto
>>> book.title
'A Fistful of Yen 2: Electric Boogaloo'

Notice that, unlike in the previous examples, there was no COMMIT : auto for these queries. That's because we turned on transactions, so autocommit was automatically turned off. You can also specify that you don't want autocommit by adding an autoCommit=0 parameter to your connection URI.

It is also worth noting that the book object that we had in memory did not revert to its database state automatically on rollback. By calling sync(), the values are reloaded from the database.

Once you've had enough of "A Fistful of Yen 2", you can delete it from the database by using the destroySelf method.

Further Reading

SQLObject and many of the other topics touched upon here are too large for a brief introductory guide. Take a look at the documentation page for links to further resources. Documentation suggestions and patches are welcomed.

Download

TurboGears 0.8.9

Features: easy-to-use, database-driven, easy Ajax and designer- and programmer-friendly.

Download now button Upgrade instructions button

1.0 is in alpha! Tons of new features! Check it out

Download development version from SVN

Get involved


High-traffic discussion about using and making the most of TurboGears.

Low-traffic way to stay up-to-date.

Order the DVD and more!

The TurboGears Ultimate DVD is now shipping! More than 5 hours of video!
We also have the marble rolling mini-framework you've been looking for.

Hosting Options

TurboGears can be hosted in many places, but we've got 4 of the best choices picked out. Super simple TurboGears use from WebFaction starting at $11.50 a month or, if you need more control, dedicated servers from ServerPronto starting at just $29.95 a month. Check it out
last updated: May 25, 2006
Comment on this page