Widgets

Here is a list of available web2py widgets:

  1. SQLFORM.widgets.string.widget
  2. SQLFORM.widgets.text.widget
  3. SQLFORM.widgets.password.widget
  4. SQLFORM.widgets.integer.widget
  5. SQLFORM.widgets.double.widget
  6. SQLFORM.widgets.time.widget
  7. SQLFORM.widgets.date.widget
  8. SQLFORM.widgets.datetime.widget
  9. SQLFORM.widgets.upload.widget
  10. SQLFORM.widgets.boolean.widget
  11. SQLFORM.widgets.options.widget
  12. SQLFORM.widgets.multiple.widget
  13. SQLFORM.widgets.radio.widget
  14. SQLFORM.widgets.checkboxes.widget
  15. SQLFORM.widgets.autocomplete
  16. SQLFORM.widgets.list

The first ten of them plus “list” are the defaults for the corresponding field types. The “options” widget is used when a field’s requires is IS_IN_SET or IS_IN_DB with multiple=False (default behavior). The “multiple” widget is used when a field’s requires is IS_IN_SET or IS_IN_DB with multiple=True. The “radio” and “checkboxes” widgets are never used by default, but can be set manually. The “autocomplete” widget is special and discussed in its own section.

For example, to have a “string” field represented by a textarea:

  1. Field('comment', 'string', widget=SQLFORM.widgets.text.widget)

Widgets can also be assigned to fields a posteriori:

  1. db.mytable.myfield.widget = SQLFORM.widgets.string.widget

Sometimes widgets take additional arguments and one needs to specify their values. In this case one can use lambda

  1. db.mytable.myfield.widget = lambda field, value: \
  2. SQLFORM.widgets.string.widget(field, value, _style='color:blue')

Widgets are helper factories and their first two arguments are always field and value. The other arguments can include normal helper attributes such as _style, _class, etc. Some widgets also take special arguments. In particular SQLFORM.widgets.radio and SQLFORM.widgets.checkboxes take a style argument (not to be confused with _style) which can be set to “table”, “ul”, “divs” or whatever matches the formstyle of the containing form.

You can create new widgets or extend existing widgets.

SQLFORM.widgets[type] is a class and SQLFORM.widgets[type].widget is a static member function of the corresponding class. Each widget function takes two arguments: the field object, and the current value of that field. It returns a representation of the widget. As an example, the string widget could be re-coded as follows:

  1. def my_string_widget(field, value):
  2. return INPUT(_name=field.name,
  3. _id="%s_%s" % (field.tablename, field.name),
  4. _class=field.type,
  5. _value=value,
  6. requires=field.requires)
  7. Field('comment', 'string', widget=my_string_widget)

The id and class values must follow the convention described later in this chapter. A widget may contain its own validators, but it is good practice to associate the validators to the “requires” attribute of the field and have the widget get them from there.

Autocomplete widget

There are two possible uses for the autocomplete widget: to autocomplete a field that takes a value from a list or to autocomplete a reference field (where the string to be autocompleted is a representation of the reference which is implemented as an id).

The first case is easy:

  1. db.define_table('category', Field('name'))
  2. db.define_table('product', Field('name'), Field('category'))
  3. db.product.category.widget = SQLFORM.widgets.autocomplete(
  4. request, db.category.name, limitby=(0, 10), min_length=2)

Where limitby instructs the widget to display no more than 10 suggestions at the time, and min_length instructs the widget to perform an Ajax callback to fetch suggestions only after the user has typed at least 2 characters in the search box. When used this way you can avoid duplicate suggestions setting optional argument distinct=True.

Notice that both arguments limitby=(0, 10) and min_length=2 could be omitted in the example above because of their default values.

Notice that distinct does not work when autocompleting a virtual field (see Chapter 6 for virtual fields).

The second case is more complex:

  1. db.define_table('category', Field('name'))
  2. db.define_table('product', Field('name'), Field('category', 'reference category'))
  3. db.product.category.widget = SQLFORM.widgets.autocomplete(
  4. request, db.category.name, id_field=db.category.id)

In this case the value of id_field tells the widget that even if the value to be autocompleted is a db.category.name, the value to be stored is the corresponding db.category.id. An optional parameter is orderby that instructs the widget on how to sort the suggestions, for example to have them alphabetically sorted use:

  1. db.product.category.widget = SQLFORM.widgets.autocomplete(
  2. request, db.category.name, id_field=db.category.id,
  3. orderby=db.category.name)

You cannot use distinct=True to avoid duplicate suggestions in this case, because when autocompleting a reference field the id_field is selected too (see Chapter 6 for further details).

When looking for suggestion the widget normally matches at beginning of the field value, to let the widget matching everywhere (i.e. performing a search) set optional argument at_beginning=False.

Note that on Google App Engine both distinct=True and at_beginning=False do not work.

This widget works via Ajax. Where is the Ajax callback? Some magic is going on in this widget. The callback is a method of the widget object itself. How is it exposed? In web2py any piece of code can generate a response by raising an HTTP exception. This widget exploits this possibility in the following way: the widget sends the Ajax call to the same URL that generated the widget in the first place and puts a special token in the request.vars. Should the widget get instantiated again, it finds the token and raises an HTTP exception that responds to the request. All of this is done under the hood and hidden to the developer.

To enahnce security, the autocomplete widget can digitally sign Ajax callbacks, this is done through the user_signature and hash_vars arguments, see on Chapter 4 for an explanation of that.