Custom forms
If a form is created with SQLFORM, SQLFORM.factory or CRUD, there are multiple ways it can be embedded in a view allowing multiple degrees of customization. Consider for example the following model:
db.define_table('image',
Field('name', requires=IS_NOT_EMPTY()),
Field('source', 'upload'))
and upload action
def upload_image():
return dict(form=SQLFORM(db.image).process())
The simplest way to embed the form in the view for upload_image
is
{{=form}}
This results in a standard table layout. If you wish to use a different layout, you can break the form into components
{{=form.custom.begin}}
Name: <div>{{=form.custom.widget.name}}</div>
File: <div>{{=form.custom.widget.source}}</div>
{{=form.custom.submit}}
{{=form.custom.end}}
where form.custom.widget[fieldname]
gets serialized into the proper widget for the field. If the form is submitted and it contains errors, they are appended below the widgets, as usual.
The above sample form is show in the image below.
A similar result could have been obtained without using a custom form:
SQLFORM(..., formstyle='table2cols')
or in case of CRUD forms with the following parameter:
crud.settings.formstyle='table2cols'
See description for formstyle
argument of SQLFORM for other possible values.
If you do not wish to use the widgets serialized by web2py, you can replace them with HTML. There are some variables that will be useful for this:
form.custom.label[fieldname]
contains the label for the field.form.custom.comment[fieldname]
contains the comment for the field.form.custom.dspval[fieldname]
form-type and field-type dependent display representation of the field.form.custom.inpval[fieldname]
form-type and field-type dependent values to be used in field code.
If your form has deletable=True
you should also insert
{{=form.custom.delete}}
to display the delete checkbox.
It is important to follow the conventions described below.
CSS conventions
Tags in forms generated by SQLFORM, SQLFORM.factory and CRUD follow a strict CSS naming convention that can be used to further customize the forms.
Given a table “mytable”, and a field “myfield” of type “string”, it is rendered by default by a
SQLFORM.widgets.string.widget
that looks like this:
<input type="text" name="myfield" id="mytable_myfield"
class="string" />
Notice that:
- the class of the INPUT tag is the same as the type of the field. This is very important for the jQuery code in “web2py_ajax.html” to work. It makes sure that you can only have numbers in “integer” and “double” fields, and that “time”, “date” and “datetime” fields display the popup calendar/datepicker.
- the id is the name of the class plus the name of the field, joined by one underscore. This allows you to uniquely refer to the field via, for example,
jQuery('#mytable_myfield')
and manipulate the stylesheet of the field or bind actions associated to the field events (focus, blur, keyup, etc.). - the name is, as you would expect, the field name.
Hide errors
Occasionally, you may want to disable the automatic error placement and display form error messages in some place other than the default. That can be done easily.
- In the case of FORM or SQLFORM, pass
hideerror=True
to theaccepts
method. - In the case of CRUD, set
crud.settings.hideerror=True
You may also want to modify the views to display the error (since they are no longer displayed automatically).
Here is an example where the errors are displayed above the form and not in the form.
{{if form.errors:}}
Your submitted form contains the following errors:
<ul>
{{for fieldname in form.errors:}}
<li>{{=fieldname}} error: {{=form.errors[fieldname]}}</li>
{{pass}}
</ul>
{{form.errors.clear()}}
{{pass}}
{{=form}}
The errors will displayed as in the image shown below.
This mechanism also works for custom forms.