TG2 and custom routes

99 views
Skip to first unread message

Carl

unread,
Mar 13, 2009, 6:23:04 AM3/13/09
to TurboGears
Hi,
I'm trying to do custom routes with TG2 and failing so far. The
instructions in http://turbogears.org/2.0/docs/main/RoutesIntegration.html
tell you to:

"you can easily do that, just by providing your own function to set up
the routes map. You can update the routes defaults by overriding the
setup_routes method of the base_config object in app_cfg.py."

Well, it's not that clear . setup routes() is a method of AppConfig.
If you just implement your own function in app_cfg.py it is never
called. You can subclass AppConfig and provide your own setup_routes()
method or you can create a custom setup_routes() function and assign
it to the original AppConfig before you instantiate base_config:

AppConfig.setup_routes = custom_setup_routes
base_config = AppConfig()

You will also need to import a bunch of stuff from tg.configuration
such as Mapper and config.

Once, I got that part right and my custom_setup_routes function was
actually called, it just did,'t work. I can't even get a simple hard-
coded path to work. Here is my custom_setup_routes() function:

def custom_setup_routes(self):
"""Setup the default TG2 routes

Override this and set up your own routes maps if you want to use
routes.
"""
#from dbgp.client import brk; brk(port=9011)
map = Mapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])

map.connect('prefix/foo', controller='root', action='foo')

# Setup a default route for the root of object dispatch
map.connect('*url', controller='root',
action='routes_placeholder')

config['routes.map'] = map


I expect that the /prefix/foo URL will result in a call to the foo()
method on my RootController, but it doesn't. The standard object
dospatch works just fine, so /foo does get to my foo() method.

Can someone tell me what I'm doing wrong and/or explain how to debug
routing and/or show a full-fledged example of custom routes in TG2?

Thanks, Carl

Mark Ramm

unread,
Mar 13, 2009, 10:40:22 AM3/13/09
to turbo...@googlegroups.com
Yea, those docs are missing some important details.

One think you do need to know is that the
TGController/ObjectDispatchController expects to be routed to for
object dispatch, not for a final page.

So you want to use DecoratedController when defining your own custom routes.

All of this should go on the RoutesIntegration page.

--Mark
--
Mark Ramm-Christensen
email: mark at compoundthinking dot com
blog: www.compoundthinking.com/blog

Carl

unread,
Mar 13, 2009, 1:01:36 PM3/13/09
to TurboGears
Mark,
Thanks for the quick response. It didn't work though. My
RootController now subclasses tg.controllers.DecoratedController and I
get the following error even on http://localhost:8080/ (the root):

Module tg.controllers:125 in _perform_call
>> controller.decoration.run_hooks('before_validate', remainder,
AttributeError: 'function' object has no attribute 'decoration'


The code expects 'controller' to be an object with a 'decoration'
attribute, but in my case the controller is the __before__ method of
RootController:

>>> controller
<bound method RootController.__before__ of
<nine.controllers.root.RootController object at 0x99a3d0>>

Is it possible to post a fully working TG2 custom routes example? It
doesn't even have to be the entire project just the differences from a
standard quickstarted project.

Thanks, Carl







On Mar 13, 7:40 am, Mark Ramm <mark.mchristen...@gmail.com> wrote:
> Yea, those docs are missing some important details.
>
> One think you do need to know is that the
> TGController/ObjectDispatchController expects to be routed to for
> object dispatch, not for a final page.
>
> So you want to use DecoratedController when defining your own custom routes.
>
> All of this should go on the RoutesIntegration page.
>
> --Mark
>
>
>
> On Fri, Mar 13, 2009 at 6:23 AM, Carl <tg2.u...@gmail.com> wrote:
>
> > Hi,
> > I'm trying to do custom routes with TG2 and failing so far. The
> > instructions inhttp://turbogears.org/2.0/docs/main/RoutesIntegration.html

Mark Ramm

unread,
Mar 13, 2009, 1:49:45 PM3/13/09
to turbo...@googlegroups.com
I think there was a bug with __before__ and the decoration object
that has been fixed in the last beta, what version of tg2 are you
using (you can check with paster tginfo).

I have not tried making the RootController into a decorated
controller, I'd try making a new controller and routing to that. I
won't have time to make a doc this afternoon, but I can try to look at
it tomorrow if you don't get it working before then.

--Mark

Carl

unread,
Mar 13, 2009, 2:50:14 PM3/13/09
to TurboGears
I'm using the latest TurboGears2 2.0b7 (according to paster tginfo). I
also did:

easy_install -U -i http://www.turbogears.org/2.0/downloads/current/index
tg.devtools

and it told me I already have the latest. I'll try routing to a
different controller.

Thanks, Carl


On Mar 13, 10:49 am, Mark Ramm <mark.mchristen...@gmail.com> wrote:
> I think there was a bug with __before__  and the decoration object
> that has been fixed in the last beta, what version of tg2 are you
> using (you can check with paster tginfo).
>
> I have not tried making the RootController into a decorated
> controller, I'd try making a new controller and routing to that.   I
> won't have time to make a doc this afternoon, but I can try to look at
> it tomorrow if you don't get it working before then.
>
> --Mark
>
>
>
> On Fri, Mar 13, 2009 at 1:01 PM, Carl <tg2.u...@gmail.com> wrote:
>
> > Mark,
> > Thanks for the quick response. It didn't work though. My
> > RootController now subclasses tg.controllers.DecoratedController and I
> > get the following error even onhttp://localhost:8080/(the root):

Carl

unread,
Mar 13, 2009, 3:40:58 PM3/13/09
to TurboGears
Ok. I followed the code through the internals. I created a separate
controller derived from DecoratedController and updates the
setup_routes() function. The routing works just fine and returns
eventually my new controller. Then we get into the actuall object
dispatch and it looks like the TG piece and the Pylons piece don't
agree exactly on what's going on.

- In pylons/controllers/core.py the __call__() method of the
WSGIController is called
- The __call__() method calls _inspect_call() with the __before__
attribute, which is a function
- The _inspect_call() method after a lot of arg checks and other stuff
calls the _perform_call with the __before__ attribute

- The _inspect_call() method is implemented by TG proper in
tg.controllers.DecoratedController. This expects the passed in func
object to have a 'decoration' attribute, but the __before__ function
passed by Pylons have no such attribute, so we get AttributeError and
everything blows up.

Does it ring a bell? I can't figure out all the implicit assumptions
and expectations in the code. I can't even tell if there is a bug or
if I'm doing something wrong/missing some configuration step.

Thanks, Carl




On Mar 13, 11:50 am, Carl <tg2.u...@gmail.com> wrote:
> I'm using the latest TurboGears2 2.0b7 (according to paster tginfo). I
> also did:
>
> easy_install -U -ihttp://www.turbogears.org/2.0/downloads/current/index

Gustavo Narea

unread,
Mar 14, 2009, 12:07:48 AM3/14/09
to turbo...@googlegroups.com, Carl
Hello,

I've had no problem while using custom routes; attached is the app_cfg.py file
of a project which uses them. I didn't have to deal with DecoratedControllers
or something else, just what you see in the attached file.

Cheers.
--
Gustavo Narea <xri://=Gustavo>.
| Tech blog: =Gustavo/(+blog)/tech ~ About me: =Gustavo/about |
app_cfg.py

Carl

unread,
Mar 14, 2009, 11:49:17 AM3/14/09
to TurboGears
Hello Gustavo,
I tried it but it didn't work for me. Maybe I got the URL syntax
wrong. What URLs map to your custom routes? Also, from reading the
Routes manual I got the impression that variables are words with a
colon in the end (e.g 'prefix:'), but your routes look like regular
expressions.

Thanks, Carl
>  app_cfg.py
> 2KViewDownload

Carl

unread,
Mar 16, 2009, 12:19:51 PM3/16/09
to TurboGears
I have made a few more almost successful experiments. My
RootController now sub-classes DecoratedController as Mark suggested.
Before I do the subclassing I remove the __before__ and __after__
attributes from DecoratedController:

delattr(DecoratedController, '__before__')
delattr(DecoratedController, '__after__')

That almost works. The routing works and if my custom route is:

m.connect('prefix/{action}', controller='root')

Then it gets to the proper method on the RootController. The problem
is that now the pylons.request object has no response_type attribute.
It's not surprizing because the __before__ and __after__ methods are
probably there for a reason.

So, I renew my plea for a working example of custom routes in TG2.

Thanks, Carl




Gustavo Narea

unread,
Mar 16, 2009, 12:46:59 PM3/16/09
to turbo...@googlegroups.com, Carl
Hello, Carl.

On Saturday March 14, 2009 16:49:17 Carl wrote:
> I tried it but it didn't work for me. Maybe I got the URL syntax
> wrong. What URLs map to your custom routes?

Those whose paths look like: /<string>/<integer>

> Also, from reading the
> Routes manual I got the impression that variables are words with a
> colon in the end (e.g 'prefix:'), but your routes look like regular
> expressions.

Placeholders can be defined with {brackets} and :colons:, but the later is
deprecated.

In my case I just wanted to force an integer in one of the variables, hence I
used a regular expression. It's optional.

Cheers!

PS: What didn't exactly work?

Carl

unread,
Mar 16, 2009, 2:43:47 PM3/16/09
to TurboGears
Hello Gustavo,
What didn't work for me is that if my controller I just subclasses
the BaseController, then it looks like the routing in app_cfg.py is
ignored. TGController calls a _get_routing_info() function defined in
ObjectDispatchController that does just object dispatch.

Gigi

Mark Ramm

unread,
Mar 16, 2009, 4:09:56 PM3/16/09
to turbo...@googlegroups.com
A code sample, and traceback would help us help you better ;)

Carl

unread,
Mar 16, 2009, 9:00:41 PM3/16/09
to TurboGears
Ok. This is on a new quickstarted project with my changes for custom
routes. Here is the traceback and the changes to app_cfg.py and
root.py are bellow. If it's helpful I can upload the entire project.

URL: http://localhost:8080/prefix/1/about
File '/Users/carl/tg2env/lib/python2.5/site-packages/WebError-0.10.1-
py2.5.egg/weberror/evalexception.py', line 431 in respond
app_iter = self.application(environ, detect_start_response)
File '/Users/carl/tg2env/lib/python2.5/site-packages/TurboGears2-2.0b7-
py2.5.egg/tg/configuration.py', line 631 in wrapper
return app(environ, start_response)
File '/Users/carl/tg2env/lib/python2.5/site-packages/TurboGears2-2.0b7-
py2.5.egg/tg/configuration.py', line 534 in remover
return app(environ, start_response)
File '/Users/carl/tg2env/lib/python2.5/site-packages/repoze.tm2-1.0a3-
py2.5.egg/repoze/tm/__init__.py', line 19 in __call__
result = self.application(environ, save_status_and_headers)
File '/Users/carl/tg2env/lib/python2.5/site-packages/
ToscaWidgets-0.9.5dev_20081026-py2.5.egg/tw/core/middleware.py', line
36 in __call__
return self.wsgi_app(environ, start_response)
File '/Users/carl/tg2env/lib/python2.5/site-packages/
ToscaWidgets-0.9.5dev_20081026-py2.5.egg/tw/core/middleware.py', line
59 in wsgi_app
resp = req.get_response(self.application)
File 'build/bdist.macosx-10.3-i386/egg/webob/__init__.py', line 1325
in get_response
File 'build/bdist.macosx-10.3-i386/egg/webob/__init__.py', line 1293
in call_application
File '/Users/carl/tg2env/lib/python2.5/site-packages/
ToscaWidgets-0.9.5dev_20081026-py2.5.egg/tw/core/
resource_injector.py', line 67 in _injector
resp = req.get_response(app)
File 'build/bdist.macosx-10.3-i386/egg/webob/__init__.py', line 1325
in get_response
File 'build/bdist.macosx-10.3-i386/egg/webob/__init__.py', line 1293
in call_application
File '/Users/carl/tg2env/lib/python2.5/site-packages/Beaker-1.2.2-
py2.5.egg/beaker/middleware.py', line 81 in __call__
return self.app(environ, start_response)
File '/Users/carl/tg2env/lib/python2.5/site-packages/Beaker-1.2.2-
py2.5.egg/beaker/middleware.py', line 160 in __call__
return self.wrap_app(environ, session_start_response)
File '/Users/carl/tg2env/lib/python2.5/site-packages/Routes-1.10.3-
py2.5.egg/routes/middleware.py', line 130 in __call__
response = self.app(environ, start_response)
File '/Users/carl/tg2env/lib/python2.5/site-packages/Pylons-0.9.7-
py2.5.egg/pylons/wsgiapp.py', line 125 in __call__
response = self.dispatch(controller, environ, start_response)
File '/Users/carl/tg2env/lib/python2.5/site-packages/Pylons-0.9.7-
py2.5.egg/pylons/wsgiapp.py', line 324 in dispatch
return controller(environ, start_response)
File '/Users/carl/tg2env/lib/python2.5/site-packages/Pylons-0.9.7-
py2.5.egg/pylons/controllers/core.py', line 217 in __call__
response = self._inspect_call(self.__before__)
File '/Users/carl/tg2env/lib/python2.5/site-packages/Pylons-0.9.7-
py2.5.egg/pylons/controllers/core.py', line 107 in _inspect_call
result = self._perform_call(func, args)
File '/Users/carl/tg2env/lib/python2.5/site-packages/TurboGears2-2.0b7-
py2.5.egg/tg/controllers.py', line 125 in _perform_call
controller.decoration.run_hooks('before_validate', remainder,
AttributeError: 'function' object has no attribute 'decoration'


Here is my app_cfg.py:

# -*- coding: utf-8 -*-
"""
Global configuration file for TG2-specific settings in custom-routes.

This file complements development/deployment.ini.

"""

from tg.configuration import AppConfig, Bunch
# Carl: needed for setup_custom_routes
from tg.configuration import Mapper, config

import custom_routes
from custom_routes import model
from custom_routes.lib import app_globals, helpers

# Carl: added custom routes function to override the default in
AppConfig
def custom_setup_routes(self):
"""Setup the default TG2 routes

Override this and set up your own routes maps if you want to use
routes.
"""
map = Mapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])

map.connect('prefix', 'prefix/{id}/{action}', controller='root')

# Setup a default route for the root of object dispatch
map.connect('*url', controller='root',
action='routes_placeholder')

config['routes.map'] = map

# Carl: assign the custom routes function to AppConfig.
# It's equivalent to subclassing AppConfig in this case
AppConfig.setup_routes = custom_setup_routes

base_config = AppConfig()
base_config.renderers = []

base_config.package = custom_routes

#Set the default renderer
base_config.default_renderer = 'genshi'
base_config.renderers.append('genshi')
# if you want raw speed and have installed chameleon.genshi
# you should try to use this renderer instead.
# warning: for the moment chameleon does not handle i18n translations
#base_config.renderers.append('chameleon_genshi')

#Configure the base SQLALchemy Setup
base_config.use_sqlalchemy = True
base_config.model = custom_routes.model

And here are my changes to the RootController:


# -*- coding: utf-8 -*-
"""Main Controller"""

from tg import expose, flash, require, url, request, redirect,
validate
from pylons.i18n import ugettext as _, lazy_ugettext as l_

from custom_routes.lib.base import BaseController
# Carl: DecoratedController will be the base class for the root
controller
from tg.controllers import DecoratedController


from custom_routes.model import DBSession, metadata
from custom_routes.controllers.error import ErrorController

__all__ = ['RootController']

class RootController(DecoratedController):
#class RootController(BaseController):
# The rest is the same

Raphael Slinckx

unread,
Mar 17, 2009, 4:59:09 AM3/17/09
to TurboGears

Carl

unread,
Mar 18, 2009, 2:12:04 PM3/18/09
to TurboGears
I found a workaround, but it's not pretty. The solution is to override
the _perform_call() method of DecoratedController in your controller:

def _perform_call(self, controller, params, remainder=None):
# If not a decorated method just call it (needed for
__before__ and __after__)
if not hasattr(controller, 'decoration'):
if remainder is None:
remainder = []

return controller(*remainder, **dict(params))
return DecoratedController._perform_call(self, controller,
params, remainder)


Carl

On Mar 17, 1:59 am, Raphael Slinckx <rslin...@gmail.com> wrote:
> See also this post from me from a while ago:http://groups.google.com/group/turbogears/browse_thread/thread/ab9c8a...

Carl

unread,
Mar 18, 2009, 7:29:54 PM3/18/09
to TurboGears
Correction :-(. It doesn't really work. The Pylons request object
doesn't have a response_type attribute. This line in the controller
method raises AttributeError for 'response_type'

if pylons.request.response_type == 'application/json':
...


I'll leave it to the experts to figure it out. Please do.

Hjhj

Danke schein, Carl

Mark Ramm

unread,
May 13, 2009, 9:43:09 PM5/13/09
to TurboGears
Oops, I thought this was fixed because of Gustavo's message, but it
seems that it wasn't -- so I'm committing a fix to the 2.0 branch
tonight.

Basically the fix is to do this:

#Special case __before__ and __after__ so don't need to be
@exposed
if controller.__name__ in ['__before__', '__after__']:
return controller(*remainder, **dict(params))

inside of _perform_call so that __before__ and __after__ don't need
decoration attributes since they really are special methods that you
want called on every request.

I'd very much apriciate it if someone could verify that routes work
from the hg.turbogears.org repo. The whole routing sytem is different
here, but I think we'll need to apply the same fix there. I think
this is a missing test that we ought to add, since using routes is
something we want to encourage.

--Mark Ramm

anthonyt

unread,
May 15, 2009, 2:14:52 PM5/15/09
to TurboGears
I haven't had a chance to check out the latest patch, but since the
thread of conversation has picked up again, I figured I'd share
something I wrote a couple days ago: http://simplestation.com/locomotion/routes-in-tg2/

I'm going to guess my solution is very similar to the solution in the
patch, but doesn't require updating TG itself.
> ...
>
> read more »

Anthony Theocharis

unread,
Jun 12, 2009, 8:24:38 PM6/12/09
to turbo...@googlegroups.com
I tested with the tg2.0 and tg2.1 code today.

I used checkouts:
svn co http://svn.turbogears.org/branches/2.0
hg clone http://bitbucket.org/mramm/tg-21

The "'function' object has no attribute 'decoration'" error is not
fixed in the latest 2.0 from SVN.

It is avoided entirely in 2.1, because you can't directly subclass
DecoratedController, since it's not a WSGIController anymore.

What isn't addressed, however, is that the _perform_call(self, func,
args) method in TGController (inherited from ObjectDispatchController
in 2.0 and from Dispatcher in 2.1) still ignores the 'action'
parameter of routing paths. It still dispatches the request according
to object dispatch, even if a route has specified the action to
render. (This happens in 2.0 and 2.1). This is the behaviour that the
parents in this thread were trying to avoid by subclassing
DecoratedController.

I continue to work around this problem by using my RoutingController
(subclassing DecoratedController in 2.0 and TGController in 2.1)
[code: http://simplestation.com/locomotion/routes-in-tg2]. Which,
essentially, implements TGController's _perform_call() method, but
removes all Object Dispatch logic.

I think a superior solution would be to have TGController's
_perform_call() method detect if a route was active, and if that
route specified an action side-step object dispatch routing. Perhaps
by checking the values in args['environ']['pylons.routes_dict'].

Perhaps this change could go into 2.1, and something like the
RoutingController could go into 2.0, to prevent breaking anybody's
apps that already rely on the current functionality.

Thoughts? Is anybody else out there using Routes in tg2?

-- Anthony

Anthony Theocharis

unread,
Mar 2, 2010, 5:20:50 PM3/2/10
to Mengu, turbo...@googlegroups.com
Turbogears 2.0.3 doesn't support Routes yet.
Turbogears 2.0.4 will (a release looks promising soon).

For now, we've got support for Routes in the mercurial repository and
you can install from there if you want support built in:
http://bitbucket.org/turbogears/tg-2_0/

Controllers you want to use Routes with should then extend
RoutingController.

If you want to stick with 2.0.3, you can make your own
RoutingController like I did here:
http://getmediacore.com/blog/routes-in-turbogears2/

But beware that if you stick with 2.0.3 there are some additional
bugs with pagination and positional url arguments that you might
encounter (these are fixed in the mercurial repository, and will be
fixed in 2.0.4).

-- Anthony

PS: I hope you don't mind that I've forwarded this message to the
turbogears mailing list--but I bet you're not the only person with
that question, and maybe we can help somebody else too.

On 2-Mar-10, at 8:30 AM, Mengu wrote:
> so, in order to be able to use custom routes, should the controller
> class extend DecoratedController? if so, i'm getting this error too.
> if not why i'm getting a 404? i have turbogears 2.0.3.

Reply all
Reply to author
Forward
0 new messages