You can customize auth but it's not totally clear that you need to at this point: http://web2py.com/book/default/chapter/08#Customizing-Auth
Depending on your needs, I could envision a many-to-many with "list: type". http://web2py.com/book/default/chapter/06#Many-to-Many,-list:<type>,-and-contains Or with tables: http://web2py.com/book/default/chapter/06#Many-to-Many The two table approach might not work since you'll likely have one tag on two or more different posts, hence the 3-table many-to-many approach. So perhaps: db.define_table('post', Field('title'), Field('body', 'text'), Field('author', db.auth_user, default=auth.user_id), Field('karma', 'integer', default=1), Field('created_on', 'datetime', default=request.now)) db.define_table('tag', Field('name')) db.define_table('tag_to_post', Field('post', db.post), Field('tag', db.tag)) Since this complicates things, you can create convenience functions such as: tag_cloud = (db.post.id==db.tag_to_post.post) & (db.tag.id==db.tag_to_post.tag) Or: def get_tags(post): return db(db.tag.post==post & db.tag.id==db.tag_to_post.tag).select() And then a controller and view: def post(): post = db((db.post.id==request.args(0)) & (tag_cloud)).select() return dict(post=post) {{extend 'layout.html'}} <h1>{{=post[0].post.title}}</h1> <p>{{=post[0].post.body}}<p> <p>Tags: {{for tag in post:}}{{=tag.tag.name}}, {{pass}}<p> These leaves a lot to code, particularly the inserts which may have you insert into 3 tables in one controller.