Outline

These are the steps we'll follow

  1. create a working environment of the tg1 project
  2. install tg2 on that environment
  3. upgrade the project structure
  4. upgrade the templates
  5. upgrade the model
  6. upgrading the controller
  7. run under tg2
  8. make it so that it will run on tg1 (maybe)

Use case

We'll use the excelent SpammCan pastebin-like app by Christopher Arndt

Project Structure

  • spammcan [1]
    • SpammCan [2]
    • tg1-2 [3]
      • SpammCan [4]
  1. simply a wrapper directory
  2. An svn checkout of the code from svn://chrisarndt.de/projects/SpammCan/trunk
  3. a virtualenv as in http://turbogears.org/2.0/docs/main/DownloadInstall.html#setting-up-a-virtual-environment
  4. This will be different things, first a tg2 quickstart then a link to our original code in [1]

Setting the Environment

I actually cheated and used bazaar but that is because I found this as a great oportunity to test bzr-svn

So lets start with

$ cd spammcan
$ bzr branch svn://chrisarndt.de/projects/SpammCan/trunk SpammCan
$ cd tg1-2
$ source bin/acticate
$ ln -s ../SpammCan/ .
$ cd SpammCan
$ python setup.py develop
there is a bug in the current SpammCan trunk that prevents this from working, see r27 from bazaar, it basically assume TG1 is installed

Testing our instalation

Now lets see if everything runs as expected::
$ bootstrap-spammcan $ start-spammcan $ firefox http://0.0.0.0:8080/

That should take you to http://0.0.0.0:8080/new and everything should work, go ahead and paste some spamm!

Installing TurboGears 2

So back to work, shut down cherrypy and let's install TG2 into the virtualenv::
$ easy_install -i http://www.turbogears.org/2.0/downloads/current/index tg.devtools
you may have to run::
$ easy_install -U setuptools
the tg1 project doesn't runs after installing tg2 running easy_install Myghty fixes, this is a bug we should tackle.

Upgrading our code structure

The steps we are going to follow are:
  • create a tg2 project by the same name
  • move all the files that are new
  • move all the files that have changed place or name
  • do changes to the files (imports and such)
  • Do complex changes to the files

Now we will temporally delete our SpammCan project from the virtualenv and create a tg2 project by the same name, The reason we are doing this is to preserve the package name:

$ rm lib/python2.5/site-packages/SpammCan.egg-link
$ vi lib/python2.5/site-packages/easy-install.pth

In the last file we need to delete the line that contains SpammCan on it, it's a little tricky but unfortunaly setuptools doesn't have a command for this. Anyway if it did it will delete all our tg1 dependencies which we don't want.

Now On to the TG2 project:

$ cd ..
$ rm SpammCan
$ paster quickstart
Enter project name: SpammCan
Enter package name [spammcan]:
Do you need authentication and authorization in this project? [yes]

We'll reply yes to auth even if that isn't used fully in SpammCan to provide a more complete example.

Upgrading the files

I'll use a very nice diff/merge tool for linux called meld, so you can diff the trees, on windows you could use winmerge

On Mac?

We'll diff so our original code is on the left and the tg2 stub is on the right:

$ meld ../SpammCan/ SpammCan/
Files to ignore
  • the .bzr, .svn directories
  • egg-info dir as this is rebuild by setuptools
Files to copy without changes (new files)
  • ez_setup , this will provide a bootstrap of your tg project
  • config/environment.py
  • config/middleware.py
  • i18n (new i18n package)
  • lib (a common place to but generic code)
  • tests/__init__.py (empty in tg1)
  • websetup.py
  • README.TXT
  • anything still revelant to your app from the templates directory (debug.html is interesting)
Files that changed names or location
  • tests/test_controllers.py -> tests/functional
  • tests/test_model.py -> tests/test_models.py
  • static -> public

You should rename your static dir public and diff again so you could take the advantage of the new icon set:

$ cp ../SpammCan/spammcan/static/ ../SpammCan/spammcan/public -r
$ meld ../SpammCan/spammcan/public/ SpammCan/spammcan/public/

.. note :: We need to upgrade the TG2 quickstart with the new CSS layout from 1.1 and then add a section here on why it's so great

Depending on your TG1 quickstart project (which is mostly everyone as tgbig was a big mistery) you need to upgrade your model.py and controllers.py files into packages, both packages are totally flexible but this is the recommended setup.

$ cd ../SpammCan/spammcan $ mkdir controllers model
model now contains 3 files controller contrains 5 files you should copy all those over::
$ meld . ../../tg1-2/SpammCan/spammcan/
Link to an explanation of what each file is from the TG2 docs
Files that changed completely
For now we'll just copy these over
  • config/app_cfg.py
  • development.ini
  • test.ini
Special attention
These two are tricky and you should do a merge of their content which is outside the scope of this tutorial
  • setup.cfg (if you havent changed this you could copy the TG2 file below the tg1 file)
  • setup.py (copy the first 6 lines of tg2's if you want ez_setup to work,

I suggest you copy the tg2 setup function to your setup.py I have renamed it setup_tg2 with the following little hack:

def setup_tg2(**kargs):
    pass

setup_tg2(...
)
We are now done with the files we can delete the TG2 quickstart and replace it with our original project::
$ cd ../../tg1-2/ $ rm -r SpammCan/ $ ln -s ../SpammCan/ .

Upgrading the imports

And lets see if everything still runs::
$ cd SpammCan $ python setup.py develop $ start-spammcan

Sadly this probably didn't work you need to fix your model and controller imports as the package takes precedence over the file. You can accomplish this by copying the file to the directory and adding the following imports:

$ cp model.py model/tg1.py
$ cp controllers.py controllers/tg1.py
$ vi controllers.__init__.py
    from controllers import *

$ vi model.__init__.py
    from model import *

Although you will probably want to give them better names

the model import must be done at the END of the file due to the way SQLAlchemy works with TurboGears
Now lets try again and our TG1.1 app should still work::
$ start-spammcan

Upgrading the template

This step is trivial if you are using genshi you just need to copy over the files. And your done

The only change you should probably do is master.html, which is documented below

what about the path vs dot notation?

Upgrading the model

We are going to split our model.py into two files auth.py and spamm.py, if you haven't done any changes to the auth code you can skip the auth.py part as that is 100% backwards compatible.

do not name your basic model package the same as your project, as this will cause a naming collion in model.__init__.py
make a reference to visit/identity -> repoze.what/who

As for the rest of the code if you are already using SQLAlchemy you should just change some imports

turbogears.database.* doesn't exists anymore so you will have to import SQLAlchemy directly except for the metadata defined in your model.__init__.py

again we can use model.template as a base for our diff

turbogears.database.mapper -> sqlalchemy.orm.mapper turbogears.database.metadata -> p.model.metadata

TODO add a comment about SAClass.query

link about SQLObject -> SQLAlchemy migration

Upgrading the controller

We are going to try to keep things running for both TG1 and TG2 therefore all our code imports will happen in __init__.py and we could switch this to tg1 or tg2 controllers

TIP: this process will be a lot simpler if you organize your imports in 3 groups, first the stdlib imports, then the Turbogears imports and last the project imports, this way you can just copy over the first and last group and translate the second group.
The process here will be:
  • copy the controller to a tg2 file (ticket #2075 provides a good template),
  • run paster serve , see what fails
  • fix the imports
  • run paster serve
  • fix other code

List of imports that changed

  • turbogears.controllers -> p.lib.base.BaseController
  • turbogears.expose -> tg.expose
  • turbogears.flash -> tg.flash
  • turbogears.redirect -> tg.redirect
  • turbogears.validate -> tg.validate
  • turbogears.url -> tg.url
  • turbogears.validators -> formencode.validators
  • turbogears.config -> turbogears.configuration
  • cherrypy.request -> tg.request
  • p.model.session -> p.model.DBSession

List of components that changed

  • @error_handler isn't a decorator anymore but a parameter to validate
  • identity -> repoze.who/what *
  • cherrypy.request -> tg.request
  • cherrypy.NotFound -> abort(404)
  • SAclass.query -> p.model.DBSession.query *
You could turn back on the SAclass.query syntax which is called "contextual mappers" by doing mapper=DBSession.mapper

for the running part we could trick setuptools like this:

def setup_tg2(**kargs):
   pass
def setup_tg1(**kargs):
   pass

#setup_tg2 = setup
setup_tg1 = setup

setup_tg2(...)

setup_tg1(...)

SpammCan Specific

By this point any standard TG project should work, below several features used by SpammCan and their ports, therefore every section below is optional

SQLite Database File

devdata.sqlite becomes devdata.db, this is simple but could confuse you if you never changed the default dburi

tg_css,tg_js,static_filter_dir

Have all been removed, use tg.url

static_filter_dir is now config.paths.static

due to the way config is initialized from the project you need to call it after your project is fully loaded, therefore you shouldn't catch it in global variables
variable providers

In TG1 we used to have a trick where we will stick aditional variables to all templates by providing a function and appending it's results to the 'variable_providers' for example:

.. code-block ::
 from turbogears.view import variable_providers
 def add_global_tmpl_vars(vars):
   vars['motd'] = message_of_the_day()
   vars['abs_url'] = absolute_url
   vars['tg_version'] = tg_version

 variable_providers.append(add_global_tmpl_vars)

in TG2 this behavior as been standarized in a config call, you will need a function that returns a dict or a Bunch and then add a line to your app_cfg.py for example in lib/helpers.py:

.. code-block ::
def add_global_tmpl_vars():
    return dict(
           motd = message_of_the_day(),
           abs_url = absolute_url,
           tg_version = tg_version
           )
in config/app_cfg.py::
from spammcan.lib import app_globals, helpers (note this line is the default import) base_config.variable_provider = helpers.add_global_tmpl_vars
Database Initialization Script
The bootstrap module is replaced with websetup.py which should be called as::
$ paster setup-app development.ini
util.py -> lib/helpers.py

in general helpers.py is a set of functions you could use anywhere on your code you could optionally add another module to lib, but helpers has the advantage of being autopopulated to the templates as h.function()

redirect no longer supports a list (see #2080)

In TG1 you could do this redirect(['/paste', guid]) and it will redirect to /paste/guid where guid is the variable of course

cherrpy.request -> webob.request

TODO see:http://pythonpaste.org/webob/differences.html

2.0/RoughDocs/1.1Migration (last edited 2009-01-20 03:11:14 by localhost)