@jsonify.when('isinstance(obj, User)')
def jsonify_user(obj):
result = jsonify_sqlobject( obj )
del result['password']
result["groups"] = [g.group_name for g in obj.groups]
result["permissions"] = [p.permission_name for p in obj.permissions]
return result
The first line lets the default JSONifier rules handle the object; after that, it removes the "password" field for security reasons, and then adds support for fields that the default rules can't handle (like joins). The @jsonify.when decorator handles mapping the default jsonify() function to the type-specific version, so when you want to return a User object converted to JSON, you just return "jsonify(myUser)" and you're done.
This approach can be used for other purposes. For example, in one project, I kept running across is the need to render references to objects as links to view that object. For example, say you have a app that renders the text
Last updated at 12:00 by Joe
with the template snippet:
<p>Last updated at ${thing.last_update_time} by ${thing.update_user.display_name}</p>
Easy and straightforward. But if you want to link "Joe" to Joe's user profile page, then every time you want to do this, you end up writing something like:
<p>Last updated at ${thing.last_update_time} by
<a href="${'/users/%d' % thing.update_user.id}"
title="User profile for ${thing.update_user.display_name}"
${thing.update_user.display_name}
</a>
</p>
Then hours later you kick yourself because you find one place out of 20 where you made a typo in this monstrosity (see if you can find the one in the example above!).
So I "borrowed" jsonify's approach and created linkify.py for the project:
import dispatch
import model
import types
from elementtree import ElementTree
# Linkify generic methods... modeled after jsonify
@dispatch.generic()
def linkify(obj):
raise NotImplementedError
@linkify.when('isinstance(obj, model.User)')
def linkify_user(user):
link = ElementTree.Element('a',
href='/user/%d' % user.id,
title='User Profile for "%s"' % user.display_name)
link.text = user.display_name
return link
Then, in your controllers.py, you can make this available to templates:
# Add linkify to tg namespace
import linkify
def provide_linkify(vars):
vars['linkify'] = linkify.linkify
turbogears.view.variable_providers.append(provide_linkify)
And now, in your template, you just write:
<p>Last updated at ${thing.last_update_time} by ${tg.linkify(thing.update_user)}</p>
Much, much nicer.