Widgets
Here is a list of available web2py widgets:
SQLFORM.widgets.string.widget
SQLFORM.widgets.text.widget
SQLFORM.widgets.password.widget
SQLFORM.widgets.integer.widget
SQLFORM.widgets.double.widget
SQLFORM.widgets.time.widget
SQLFORM.widgets.date.widget
SQLFORM.widgets.datetime.widget
SQLFORM.widgets.upload.widget
SQLFORM.widgets.boolean.widget
SQLFORM.widgets.options.widget
SQLFORM.widgets.multiple.widget
SQLFORM.widgets.radio.widget
SQLFORM.widgets.checkboxes.widget
SQLFORM.widgets.autocomplete
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:
Field('comment', 'string', widget=SQLFORM.widgets.text.widget)
Widgets can also be assigned to fields a posteriori:
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
db.mytable.myfield.widget = lambda field, value: \
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:
def my_string_widget(field, value):
return INPUT(_name=field.name,
_id="%s_%s" % (field.tablename, field.name),
_class=field.type,
_value=value,
requires=field.requires)
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:
db.define_table('category', Field('name'))
db.define_table('product', Field('name'), Field('category'))
db.product.category.widget = SQLFORM.widgets.autocomplete(
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)
andmin_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:
db.define_table('category', Field('name'))
db.define_table('product', Field('name'), Field('category', 'reference category'))
db.product.category.widget = SQLFORM.widgets.autocomplete(
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:
db.product.category.widget = SQLFORM.widgets.autocomplete(
request, db.category.name, id_field=db.category.id,
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
andat_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.