TurboGears "offline" processes (crons, command-line commands, etc)

TurboGears uses the Paste commands system to create command-line entry points that, for example, set up your database or start your server.  When you get to larger projects, however, you will often have other things you need to do "in the context of your application" from the command line, such as periodic imports of data, or cron'd database management tasks.

Paste's command system is well documented, but it can take quite a bit of poking around to find out how to get SQLAlchemy, and TurboGears configured so that code that looks in tg.config gets the right values, and SQLAlchemy has access to your models.

This little module (tgcommand.py) is my hacked-together attempt to make it easier to create new TG command-line commands.  The idea is that for each application you will create a BaseCommand class like this:

from adminhack.commands import tgcommand
class BaseCommand( tgcommand.TGCommand ):
def import_model( self ):
from adminhack import model
return model

This command-class would then serve as the base command for each of your "real" commands.  For example, a command that iterates through all users showing their user_name property:

class Hello(BaseCommand):
def db_command( self, engine ):
from adminhack.model import User,Group, DBSession
for user in DBSession.query( User ):
print 'User',user.user_name

As with regular Paste commands you have to register your TGCommands in your application's setup.py (in the setup() call), like so:

    entry_points="""
    [paste.paster_command]
    hellocommand = adminhack.commands.hello:Hello
    """,

(you will need to re-run setup.py develop to get the command to be available).  I can't claim that this is the perfect way to set up these commands, but it *should* work :) .

[Update] I've done a bit of clean-up work on the base tgcommand module.  The updated version is now linked above.

[Update2] A few more tweaks, one of which changes the interface so that you receive an engine pointer, rather than a DBSession pointer in db_command.  This allows for the flag "with_session" to be set on the class to create a command that can do its own DB session management (useful for database-modification and the like).

Comments

  1. tmu

    tmu on 10/06/2009 5:16 p.m. #

    Thanks a lot for this! I also needed to get i18n working, which meant some crude hacks. I am sure someone who knows TG better could do the 'right thing' instead.

    In get_session():
    <pre>
    ...
    model = self.import_model()

    # Hack to get package name
    self.config['pylons.package'] = model.__name__.split('.')[0]

    model.init_model( engine )
    ...
    </pre>

    and in command():

    <pre>
    ...
    DBSession = self.get_session( engine )

    # Get a translator up to make i18n work
    kw = {'pylons_config': { 'pylons.paths' : {'root': os.path.join(cfg['here'], self.config['pylons.package']) }, 'pylons.package': self.config['pylons.package']}}

    # This assumes that lang is set in the .ini
    pylons.translator._push_object(_get_translator(cfg['lang'], **kw))

    try:
    self.db_command( DBSession )
    ...
    </pre>

  2. Mike C. Fletcher

    Mike C. Fletcher on 10/21/2009 12:53 p.m. #

    Thanks tmu, have integrated your changes into the script.

Comments are closed.

Pingbacks

Pingbacks are closed.