If you benefit from web2py hope you feel encouraged to pay it forward by contributing back to society in whatever form you choose!

First, I state that I really don´t know if this will always work. For me it worked so here it goes!

You can put all the code in a file in models folder:

The validator:

class TAGS_LIST:
    def __init__(self, separator=',', error_message='This is not a valid list!'):
        self.separator = separator
        self.e = error_message

    def __call__(self,value):
            list = value.split(self.separator)
            return (list, None)
            return (value, self.e)

    def formatter(self, value):
        tags = ''
        for tag in value:
            tags += '%(tag)s%(sep)s ' % {'tag':tag,'sep':self.separator} 
        return tags

The widget:

def tags_widget(field, value, **attributes):
    id = "%s_%s" % (field._tablename, field.name)   # form field
    idu = id + '_u'       #user field
    idl = id + '_l'       #list display
    script = SCRIPT(
                    'function parse() {var s = ""; $("#%(idl)s > li").each(function(){s+=$(this).html()+",";});  $("#%(id)s").val(s)}' % {'id':id,'idl':idl},
                    'function addTag(tag){var item=$(document.createElement("li")); item.text(tag); item.click(function(){item.remove(); parse();}); $("#%(idl)s").append(item); }' % {'idl':idl},
                    'function init() {list = $("#%(id)s").val().split(","); list.pop(); $.each(list, function(index,item){addTag(item)}); }' % {'id':id},
                    '$(document).ready(function (){init();});')
    inp_user = INPUT(_id=idu,
                     _onkeyup='if (event.keyCode==32) {addTag($(this).val()); parse(); $(this).val("");}' % {'idu':idu})
    inp_hiden = INPUT(
    list = UL(_id=idl)  
    return DIV(script,inp_user,list,inp_hiden)

Now the only thing to do is set validator and widgets attributes of your field. db.define_table('table',Field('tags','list:string'))

db.table.tags_field.requires = TAGS_LIST() db.table.tags_field.widget = tags_widget

You can customize the list display with CSS. If you want a simpler thing you may use only the validator and it will parse the comma separated tags on the form validation.

Related slices

Comments (2)

  • Login to post

  • 0
    emilio-sanchez-sierra-11473 10 years ago

    I would like to add that if this is used in a form that uses an 'onvalidation' and the validation fails, it corrupts the data by adding artifacts (in the form of "'[" and related).   To fix this,  modify the method tags_widget with:


        inp_hiden = INPUT(
                     _value=value[0] if value else value,


    This happen even if you apply piemaster21 changes (which are awesome btw).


  • 1
    piemaster21 12 years ago
    This is great, yamandu! Solves the tag input problem nicely. A couple of points:
    • How could this be extended to use an autocomplete field, to encourage reuse of existing/common tags?
    • A tiny bit of sample CSS to render the list output as a horizontal list of 'bubbles' (as most sites with tags have) would make this even better. I'll have to have a play myself later.
    • I changed your key handling code a bit to more cleanly handle end-of-tag input (see below). When the space, comma or enter keys are pressed, it will add the current field contents as a new tag and reset the input field without ugly artefacts (e.g. when you hold down the tab key). Just replace the whole input_user field with (should only be 2 lines):
    inp_user = INPUT(_id=idu,
                     _onkeydown='if(event.keyCode in {32:1, 188:1, 13:1}){v=$(this).val();if(v.length) {addTag(v); parse();}$(this).val(""); return false;}')

Hosting graciously provided by:
Python Anywhere