Forms are an essential part of any web application, as they are the primary way in which a user will interact with your site. Poorly implemented forms can have a significant negative impact on the user experience. Enough to put a user off using your site at all – so it is important to do forms well.
It is possible to build forms from scratch with Moya, but the Moya Forms library will accelerate designing, rendering, and processing of forms – without restricting customization.
The Moya Forms library is built in to Moya and may be imported via its Python path as follows:
<import py="moya.libs.forms"/>
The above line should be in your <server> declaration.
You can install the library with the following:
<install name="forms" lib="moya.forms"/>
Note that the library isn't mounted anywhere, because no views are required to use forms. Installing the library makes a number of custom tags available (which we will cover here).
The forms library comes with a test view which show the various form controls you can use. These are not intended for production, but if you are interested, you can mount them by adding the following to your server.xml
:
<mount app="forms" url="/forms/" mountpoint="tests" />
You can now visit http://127.0.0.1:8000/forms/tests/ to have a look at the test forms.
Forms tags use the XML namespace http://moyaproject.com/forms
.
You will probably find it easiest to add xmlns="http://moyaproject.com/forms"
to your <form> declarations, so that you won't need a prefix for every field. For form tags not inside a forms
tag, you can add xmlns:forms="http://moyaprojects.com/forms"
to the top level <moya> tag, to enable the forms
namespace prefix.
The form
tag begins a form definition. This is where you tell the Moya Forms library what fields should be in your form, how to display the form, and also how to validate the input. Let's have a look at a simple login form:
<form libname="form.login" legend="Please login" xmlns="http://moyaproject.com/forms"><input name="username" label="Username" type="text" maxlength="30" required="yes"/><password name="password" label="Password" maxlength="30" required="yes"/><submit-button text="Login" /></form>
This form has the libname "form.login"
, which will be required when we come to process / render the form. The prefix of "form."
is a convention which helps to keep forms distinct from other elements. The optional legend
attribute is a human readable text displayed as a header in the form.
The tags inside the form
define the contents of the form. The form above has an input box for a username, a password input for the password, and a button to submit the form; but any combination of the supported fields and controls possible. The attributes of field tags are used to customize the field and how it is validated. In the input and password fields there is a maxlength
attribute which restricts the number of characters the user may type, and a boolean attribute called required
which can tell Moya that the user must enter something in order to submit the form.
There are a number of other field tags to create the common fields expected in web forms. See Field Tags for details.
Forms may be extended so that they inherit the fields from another form. You might want to do this if you have two forms that differ only by the text on the submit button (for example): Here's an example of a defining two forms extending from a common base:
<form libname="form.post.base"><input name="title" label="Title" required="yes"/><text name="text" required="yes"/></form><form libname="form.post.new" legend="New Post" extends="#form.post.base"><submit-button text="Create"/></form><form libname="form.post.edit" legend="Edit Post" extends="#form.post.base"><submit-button text="Edit"/></form>
The first form with libname "form.post.base"
isn't used directly. The following two forms both extend the base form and inherit the title
and text
fields. The forms doing the extending need only add the fields that are not in the base. In this case, they add an appropriate submit button.
The first step in working with a form, wether it's to process a POST request or to render the form HTML, is to get a form object for your <form>. You do this with <get> which takes the libname of the form in the form
attribute. Here is an example:
<forms:get form="#form.login" dst="login_form" />
The value login_form
now contains a form object. It is this form object you work with when you come to process or render the form.
It is important to note that the call to <get> invokes the code inside the <form> much like a macro – you may even put breakpoints inside a form and step through each field tag a line at a time. You can also pass parameters to the form in order to customize it. For example, an alternative to extending a base form in order to customize the submit button text might be to supply the button text as a parameter. The following code sets the submit button text to the value of a variable called button_text
.
<form libname="form.login" legend="Please login" xmlns="http://moyaproject.com/forms"><input name="username" label="Username" type="text" maxlength="30" required="yes"/><password name="password" label="Password" maxlength="30" required="yes"/><submit-button text="${button_text}" /></form>
Here's how you would supply the button text when you get the form object:
<forms:get form="#form.login" dst="form" let:button_text="'Much Login'"/>
Form parameters may be used for more advanced customization, as well as to change text. The following creates a form with a definable number of address fields:
<form libname="form.address" legend="Enter your address" xmlns="http://moyaproject.com/forms"><moya:for src="1...(lines)" dst="line_no"><input name="address${line_no}" maxlength="50"/></moya:for><submit-button text="Submit" /></form>
This form requires that the form parameter lines
has been passed to it. The <for> tag repeats the <input> the given number of times, adjusting the name
attribute each time to create fields called address1
, address2
etc.
For example, the following retrieves a form object with 4 address fields:
<forms:get form="#form.address" dst="form" let:lines="4"/>
Form objects are renderable (Moya knows how to turn them in to HTML). You can render a form object with the <render> tag in a <content> definition, or by using the {% render %}
template tag.
The following is an example of rendering a form in content:
<view libname="view.login" content="#content.login"><forms:get form="form.login" dst="login_form"/></view><content libname="content.login" template="login.html"><title>Log In</title><section name="body"><render src="login_form"/></section></content>
Rendering a form object in a template is similar:
<view libname="view.login" template="login.html"><forms:get form="form.login" dst="login_form"/></view>
<!-- login.html --><div id="body"><h1>Please fill in the following form</h1>{% render login_form %}</div>
Rendering forms in this way generates HTML from templates in the Moya Forms library. A number of different styles are supported, from a list of fields in a table to Bootstrap style forms. This is often all you'll need and makes generating the markup effortless, but you may also customize the HTML generation on a number of levels, or render the HTML form manually and use Moya Forms for processing only.
The <form> tag has an attribute, style
, which can be used to select one of number of styles for the the form markup. The <get> tag also has a style
attribute which will override the style
on the <form> tag.
The following is a list of the builtin styles:
basic
style renders the form with basic bootstrap markup.
horizontal
style renders the form with a bootstrap horizontal layout.
inline
style renders the form with a bootstrap inline layout.
paragraphs
style renders the form with a controls in <p>
tags.
simple
style renders the form as a sequence of labels and controls.
table
style renders the form as a table with two columns; the first column contains the label and the second column contains the control.
Moya uses templates to render forms and controls. These may be customized by overriding the templates in the usual way.
When Moya renders the form, it generates a path from /moya.forms/styles/{style}/form.html
, where {style}
is the style specified on the <form> or <get>. For example, if the style was basic
, Moya would render the form /moya.forms/styles/basic/form.html
.
The form template is passed the form object (simply called form
), which it uses to render the HTML markup. The form template will loop through the fields in the form and render them. Template paths for fields are generated from /moya.forms/styles/{style}/fields/{fieldname}.html
, where {style}
is the form style, and {fieldname}
is the name of the field. Field templates may also be overriden if you want to customize how a field is rendered.
You can see the default templates used by the forms library with the following command:
$ moya fs templates --tree /moya.forms/
If you override any of the templates, it will change how forms or fields are rendered across your entire application. You may also customize a form or field template individually, with the template
attribute (on the <form> or field tags) which takes a path to your custom template.
You are not limited to the builtin form styles. If you want to define a new style entirely, you can add the template(s) under the path /moya.forms/styles/{style}
(in templates fs) where {style}
is the name of your custom style. You can now set that style on your forms to use your custom templates.
See any of the existing form templates as a guide how to render a custom form style. You display any of the builtin form templates with the moya fs
command. Here's an example:
$ moya fs templates --cat /moya.forms/styles/basic/form.html
An alternative to using the built-in form templates is to set a custom template on the form object that does all the rendering. You may lose the ability to add and remove fields easily to the HTML, but form processing and validation remains the same.
To set a custom template, you may either set a template
attribute on the <form> object, or override it in the <get> tag. Then when Moya renders the form object, it will render the custom template with the form object (passed to the template as form
).
Here's an example of setting a custom template on a form object:
<forms:get form="form.login" template="custom_login_form.html"/>
You can then render the form object in a template:
<!-- custom_login_form.html --><form action="${form.action}"><input type="text" name="username" value="${form.fields.username.value}"/><!-- render other fields... --></form>
The custom emplate has access to all the parameters of the form object, which you can use to insert information defined in your <form>.
Moya forms are actually a specialized form of content; when you add a field to a form you are also adding associated content information. Because of this, non-form related content tags will also work inside a <form>. You can use this to insert additional widgets, text, and images, for example. Content tags may also contain form field tags, which will be rendered normally, without breaking form functionality.
For example, let's say we want to wrap the username and password fields from the login form in a HTML div with an additional style. We can put the two fields inside a <node> which references a template:
<form libname="form.login" legend="Please login" xmlns="http://moyaproject.com/forms"><moya:node template="highlight.html"><input name="username" label="Username" type="text" maxlength="30" required="yes"/><password name="password" label="Password" maxlength="30" required="yes"/></moya:node><submit-button text="Login" /></form>
When the above form is rendered in the usual way, it will render highlight.html
within the form markup. Here's that template:
<div class="highlight">{% children %}</div>
Note the use of the {% children %}
template tag which ensures that content and fields inside the <node> are also rendered.
Moya includes a standard array of form inputs (see Field Reference), but there may be situations where you want to create entirely custom field tags, either to change the look and feel or to provide functionality that plain old HTML doesn't support. In these situations you can use the <field> tag which registers the field with the form system (to enable validation etc.), but doesn't add any content of it's own. You can tell Moya how to render the field by adding the appropriate content tag(s) inside the <field>.
For instance, Moya comes with a builtin library to embed a rich text editor (supporting bold / italic / links etc), which may be used in place of text area fields (see text-area). To use it, add the following lines to your <server>:
<import py="moya.libs.wysihtml5" /><install name="wysihtml5" lib="moya.wysihtml5" />
Then add the XML namespace declaration xmlns:wysihtml5="http://moyaproject.com/wysihtml5"
to the file containing your form. Now you can replace <text-area> tags with the following:
<field name="content" label="Rich Content"><wysihtml5:editor/></field>
When the form is rendered it will display the rich editor containing markup from the form data (in form.content
). The user may edit the HTML without ever seeing markup.
This works because when the <field> tag is rendered it inserts a value called field
in to the context. The field
value which contains information about the field – such as field.name
. The editor widget imports the CSS and JS required to make the editor work, and writes HTML in to an input when the form is submitted. The field may validated as if it were one of the standard fields.
When you get a form object during a POST request, Moya will fill in the field information from the POST data. A form that has been filled in (or bound) can then be validated.
Validating a form runs any checks on the fields, and generates error messages for fields that fail a check. The error messages are displayed when the form object is rendered, along with styles to highlight errors.
To validate a form, use the <validate> tag, which accepts a single attribute, src
, which should be your form object. If the form validates successfully, Moya will execute the code within the <validate> tag. The following code demonstrates how you might handle a login form:
<view libname="view.login" content="#content.login"><forms:get form="#form.login" dst="form"/><forms:validate src="form"><auth:login username="form.data.username" password="form.data.password"/><redirect-to url="/"/></forms></view>
When a form validates successfully it will have the value ok
set to True
. A valid form will also contain a dictionary of the data in the data
attribute. We can see that the view above retrieves the username and password from the form with form.data.username
and form.data.password
.
If the form doesn't validate (i.e. contains errors) then the form object may be passed to the content and rendered normally. Error messages will be displayed next to the appropriate fields.
You can pre-populate a form with values by setting the src
attribute of <get>, which should refer to an object containing attributes with the same names as the form fields (such as a database object). The following code shows how we could prepopulate the login form with an existing username:
<forms:get form="#form.login" dst="form" src="{'username': 'john'}" />
When this form is rendered, the text of the username field will be populated with the value 'john'
.
By default <get> will pre-populate values based on the name
attribute of the field. If you want to prepopulate a differently named value from the src object, you can set an alternative name in the field's src
attribute.
For example, here is an alternative login form. Note the src
attribute on the first field:
<form libname="form.login" legend="Please login" xmlns="http://moyaproject.com/forms"><input name="username" src="alt_name" label="Username" type="text" maxlength="30" required="yes"/><password name="password" label="Password" maxlength="30" required="yes"/><submit-button text="${button_text}" /></form>
With the above form, the username field can be prepopulated with a value called alt_name
:
<forms:get form="#form.login" dst="form" src="{'alt_name': 'john'}" />
You can also tell moya to not prepopulate a field by setting the fields src
attribute to an empty string. For an example:
<password name="password" src="" label="Password" maxlength="30" required="yes"/>
This will prevent the password field from being pre-populated, even if value exists for password
.
Note that if a form is rendered after a POST request, it will be populated with data from the request, which will likely be the result of the user having filled in the form. Data from the POST will take precedence over data from the <get> tag.
Values set on the form with the src
attribute are converted to text when the template is rendered. Moya will do this automatically, but you may define a custom text representation of a src value by supplying a process
attribute on its field. This attribute should be an expression which takes the value from the src
(simply called value
) and returns a string. For instance, the default behavior would be equivalent to setting process="str:value"
on every field.
One use for processing fields, is to define what happens when a value in the src
object is None
, which may occur on a database object with a null field. The default behavior would be to convert the value None
to the string 'None'
, which is unlikely to be what you want in a form.
For instance, lets say we have a field count
in a database object which could be None, and we would like to show this as 0
in a form. We could define a process
attribute on the <input> as follows:
<forms:input type="number" name="count" src="count" process="isnone:value ? '0' : str:value"/>
Now when you render the form a value of None
for Count
will be converted to the text 0
.
Most forms will require some kind of validation of the user's input, beyond marking a field as required. This is done with the <validate-field> tag which should be inside the <form> definition.
The <validate-field> tag takes a single attribute, field
, which should be the name of the field you want to validate. When the form is validated (with <validate>), Moya calls the code inside any <validate-field> tags, which may report validation errors with <fail>. Here's an example which validates an email field:
<!-- inside a form --><form:validate-field field="email"><fail if="'@' not in value">Doesn't appear to be an email</fail></form:validate-field>
When the form containing the above code is validated, Moya invokes the <validate-field> tag with a parameter, value
, which contains the text entered by the user. If there is no @ symbol in the email field, then the <fail> tag will cause the validation to fail and set an error message on the field. The form will only validate if all <validate-field> run without any fails.
It is possible for field validation to refer to more than a single field. For example, in a user sign-up form it's common to have two email fields in order to confirm the user has entered their email correctly (if the fields don't match, the user must have made a typo). In these situations, you can refer to the other fields with the values
dictionary, which is also passed to <validate-field>. Here's an example:
<validate-field field="email2"><fail if="values.email != value">Email fields do not match</fail></validate-field>
The above code assumes there are two email fields, called email
and email2
. The <validate-field> tag applies to the second email field, and checks that its value (stored in value
) matches what the user entered in the first email input (stored in values.email
). Note that the expression values.email != values.email2
would work equally well.
You may set error information on the form outside of the usual validate process with the <error> tag, which takes the form as input, and uses the enclosed text as the error message. The <error> can set an error message that applies to the form (typically displayed above the controls) or error messages that apply to a particular field, with the field
attribute. For example, the following code would set error text for a login form:
<forms:error src="form">Much login fail</forms:error>
The following sets an error message that applies to the username
field:
<forms:error src="form" field="username">No such user!</forms:error>
The data in a successfully validated form object is stored as text, which may not be convenient if we need it in a different format. We can tell Moya to further process (or adapt successfully validated data with the adapt
attribute on field tags, which should be an expression which takes the field value (as value
) and returns the adapted value.
For example, if we have an input that accepts a number, we may want to convert the value in the form data to an integer (rather than leave it as a string). Here's an example that adapts a field called count
by converting it to an integer.
<forms:input type="number" name="count" src="count" adapt="int:value" />
You can also adapt form data with the <adapt-field> tag – if you need to do more work than can fit in to a simple expression. When Moya processes the adapt
attributes it also invokes any code inside <adapt-field>, and will replace the field data with the return value. As with the adapt
attribute, the field being adapted is available as value
. Additionally, the code in <adapt-field> is also passed a dictionary called values
(plural) which contains all the data from the form.
Here's an example form containing a field called title
and a field called slug
. The field adapter in the form automatically generates the slug field from the title if it is left blank:
<form libname="form.post" legend="New Post" xmlns="http://moyaproject.com/forms"><input class="input-xxlarge" name="title" label="Post Title" maxlength="60" required="yes"/><input class="input-xxlarge" name="slug" label="Slug" maxlength="60" required="no"inlinehelp="Leave blank to auto-generate a slug" /><adapt-field field="slug"><moya:return value="slug:values.title" if="not value"/></adapt-field></form>
A common use-case for forms is to create or edit database objects, where the names of the fields map to an attribute on a model. For example, lets say we have a Post model that contains the fields, title
, slug
and text
. We could design the following form to edit a single Post object:
<form libname="form.post.edit" xmlns="http://moyaproject.com/forms"><input name="title" maxlength="30" required="yes"/><input name="slug" maxlength="30" required="yes"/><text name="text" required="yes" /></form>
The following code demonstrates how we might copy data from a successfully validated form to a model object:
<!-- assumes we have retrieved a 'post' object from the database --><let post.title="form.data.title"post.slug="form.data.slug"post.text="form.data.text" />
The above code is a perfectly valid way of processing a form, but it can get unwieldy if the form has a large number of fields. There may also be a lot of repetition if we have several views to edit different aspects of the model.
To simplify this kind of form, we can add a dst
attribute to field elements, which tells Moya how to apply the form. The dst
attribute should be the name of an attribute on the object which will receive the form data. Here's how we would modify the post form to enable this feature:
<form libname="form.post.edit" xmlns="http://moyaproject.com/forms"><input name="title" maxlength="30" required="yes" dst="title"/><input name="slug" maxlength="30" required="yes" dst="slug"/><text name="text" required="yes" dst="text"/></form>
With the addition of the dst
attributes, we can now apply the form as follows:
<forms:apply src="form" dst="post" />
Cross Site Request Forgery (CSRF) is a type of exploit where a form is submitted on behalf of a logged in user, without their knowledge. This is typically done by luring a user to a webpage and tricking them in to clicking on something that sends a pre-defined request to your web-application. It can also be done in some cases without even requiring the user to click on something.
An attacker can use this exploit to do things such as submitting SPAM comments, or potentially gaining access to their account entirely.
Moya protects against the kind of exploit by adding a hidden field to forms. This field contains a special token which is generated when the form when it is first rendered. Moya can use this token to detect if the form wasn't generated by your web application.
The good news is that Moya does this automatically, and if you are using the forms system to generate your forms, you won't have to worry about it.
To generate the CSRF token, Moya needs three pieces of information; the user's session id, the name of the form, and a 'secret' key which is unique to the project. The secret key is taken from the setting called 'secret' in the [project] section of your settings, and should be a long string of random digits. If you used moya start project
to create your project, this will have been done for you.
When you render a form, Moya inserts a <hidden-input> with a name of _moya_csrf
containing then token. When you validate the form with <validate>, Moya checks this token. If it passes, the form is processed as normal (the csrf field is removed). If the token doesn't match, Moya will return a forbidden response, which will typically redirect the user to the login page.
If you have a form that doesn't change the state for a logged in user, then you can disable the CSRF check by setting csrf="no"
on the form. You can also disable the CSRF check when the form is validated with the csrf
attribute on the <validate> tag.
You can also check the CSRF token without validating the form. The attribute csrf_check
on a form object will be set to True
if the form orignated from your web site or False
if it did not. When there is no user logged in, this value is set to True
.
Form objects contain a number of parameters which you can access in Moya Code, or in a template.
Form objects contain the following fields:
The fields
attribute contains a list of field objects. Field objects have the following parameters:
name
attribute of a field tag such as <input>)
False
the field should not be rendered
The following is a list of field tags which you can enter in a form.
A check select fields is simlar to an HTML select control with the multiple
attribute set. Such multiple select controls allow the user to select any number of options by holding down the Alt key. They suffer from a usability point of view in that it is not obvious (without additional text) that you can select multiple options. It is also way to easy to de-select a number of items by forgetting to hold down the Alt key. Moya offers an alternative to a multiple selected with the <check-select> control which renders multiple options as check-boxes – but otherwise operates in the same way as a select.
The <checkbox> tag renders an input with type checkbox, which displays an icon that may be checked or unchecked. You can tell Moya what value a checkbox should set on the form data when selected with the on
attribute.
The <hidden-input> tag renders an input with type hidden
. A hidden input does not visibly display anything in the form, but can be used to associate information with the form that is sent back to the server when submitted.
The <input> tag renders a simple HTML input
.
The <password> tag renders an HTML input of type password
, which will hide what the users has entered with asterisks.
The <radio> rag renders an HTML radio button, which may be selected or not-selected. Radio buttons differ from check-boxes in that only one radio button in a group may be selected. A group is defined by using the same name
for the <radio> tags in the group. For example, the following code creates three radio buttons:
If the user selects a different radio button, the previously selected button will be de-selected. The value option
in the form data will be set to the on
attribute of the selected radio button.
The <radio-group> renders a group of radio buttons in a single field group in the form. Without a <radio-group>, each radio button will have it's own label and have the same margin / padding as other fields. Here's an example of a radio group:
<radio-group name="option" label="Radio Group" required="yes"><radio text="Option 1" on="1"/><radio text="Option 2" on="2"/><radio text="Option 3" on="3"/></radio-group>
Note, that when using a radio group, you need only set the name
of the group, the name
attribute of the <radio> tags will be ignored inside a group.
The <select> tag renders an HTML select control, which is a list of possible values that my be selected by the user. The values used in a select are set with <option> tags which appear inside the <select>. Here's an example of a select control with three possible values:
<select name="fruit" label="Pick a fruit"><option value="apples">Apples</option><option value="oranges">Oranges</option><option value="pears">Pears</option></select>
The text enclosed in the <option> tag is what is shown to the user. The value
attribute defines the value which will be set on the form data.
Options in a <select> may be organized in to distinct groups to make them easier to search for with the <opt-group> tag. Here's an example:
<select name="fruit_or_veg" label="Pick something"><opt-group label="Fruit"><option value="apples">Apples</option><option value="oranges">Oranges</option><option value="pears">Pears</option></opt-group><opt-group label="Vegetable"><option value="carrot">Carrot</option><option value="potato">Potato</option></opt-group></select>
The code above defines two opt-groups; one for the fruit and one for vegetables under an appropriate heading.
The <submit-button> tag renders a button that will submit the form. The text
attribute sets the text to be displayed on the button. This should be something that reflects what will happen when you click the button; 'Edit', 'Create' etc.
Submit buttons can also set a value on the form when they are clicked; if you supply a name
attribute then clicking the button will store the value given in the value
attribute (which may be empty).
If you have more than one submit button, you can enclose them in a <submit-button> tag which will render the buttons horizontally rather than one per row. Here's an example:
<actions><button name="action" value="edit" text="Edit Post"/><button name="action value="cancel> text="Cancel"/></actions>
This will render two submit buttons. The first one will set the value action
to 'edit'
, the second will set action
to 'cancel'
. The buttons will be displayed on a single row, which looks more natural than a button per row.
The <text-area> tag renders an HTML textarea, which is a multiple-line text input control. Text areas are better suited for situations where the user is required to enter several lines of text, such as in comments or post content.
The <upload> tag renders a control to upload a file. This is typically rendered by browsers with a button that opens a dialog for the user to select a file to upload.