Advanced wtforms usage
WTForms is a powerful form validation and rendering library especially for web development. It’s framework agnostic but there are packages that integrate it with popular web frameworks (e.g. Flask WTF ). In this article I’m going to cover some advanced techniques which is not covered in documentation.
Creating custom fields
If you want to add some non-standard field to your form you can create a custom field. First you need to inherit from appropriate field to describe which type of value you want to get as a result (StringField
will fit in most cases) and then define a widget on it. Widget is a class returning an html string when called. This is the place where you define how it will be rendered on a page.
In the example below we will create a field for jquery on-off switch.
1 | from wtforms.widgets import HTMLString, CheckboxInput |
So our widget is just a class which implements __call__
method so when rendering it returns string representation of its html code. And as a result we get a nice switch on our page.
In case you do not want to create a new field and just need to slightly customize some of existing you can just pass different widget when instantiating.
1 | from wtforms.widgets import TextInput |
This way you will tell wtforms to render regular text input but with autofocus attribute on it.
Store your own properties on a field
Sometimes you want to store additional data on your field and be able to pass it during field initialization stage. So you cannot just inherit from a Field
class each time you want to customize a field. One way is to override __init__
method with a monkey patch. For example when rendering a field we want some flag telling us whether to display label for it or not. In the template it looks like this (assuming Jinja2-like syntax)
1 | <div class="form-field"> |
And to control visibility of a field’s label we need to provide show_label
boolean value when defining a field for our form. But first let’s allow a field to accept such a parameter.
1 | import wtforms |
Custom name for a field
You might want to customize default name for field that will be set on html input. Most of the time you should be fine with the default name (field variable named with underscores). But if you need hyphens or your front end framework requires different name convention you can provide your own name or specify a rule of how to generate a name for you. For this purpose we will dive a bit to a world of metaclasses. Basically we will provide our metaclass for a Form
which will responsible for binding declared fields to our form and setting desired name in the meanwhile.
1 | from wtforms.meta import DefaultMeta |
Just create a form as usually passing custom_name
keyword argument on any field you don’t want the default name to be set.
1 | class NameForm(Form): |
As you can see we specified a name with hyphens instead of default one with underscores. If that’s what you are looking for on regular basis you can just pass name
parameter with applied str.replace('_', '-')
method. They said WTForms 3.0 will support specifying a name for an input but for now we need to use this workaround
Create custom validators
Each field allows to store a list of validators (functions returning boolean value and checking field data to correspond your criteria). Some of them are InputRequired
, Length
, Optional
and you can easily implement most business requirements using Regexp
validator. When more control is needed you can implement your own validator. So validator is a class(callable) accepting form
and field
parameters and raising ValidationError
if data doesn’t fit your condition.
1 | from wtforms import IntegerField |
As expected we will get {'divisible': ['7 is not divisible by 3']}
message because form did not validate.
Passing regular dicts to a form constructor
When instantiating a form wtforms expects some sort of request-data wrapper which can get multiple parameters from the form input, e.g. a Werkzeug/Django MultiDict. So to create a form you need either to provide data
parameter or use a wrapper around your dictionary. Note that data
will be used only if you did not provide formdata
and obj
arguments when creating a form.
1 | class NameForm(Form): |
But in case you need to override default Form
behavior or will be implementing your own version of process
method you definitely will need to follow the approach below
1 | class MultiDictWrapper(dict): |
Custom validation for a form
One of the most useful features is the ability to validate data passed to the form. As we already created our custom version of a Form
it would be nice to enhance it with additional abilities. Imagine we want a form to be valid only when request is made with POST
method. Instead of checking request method every time in your view/handler we can implement that directly on a form and reuse it later as many times as we want.
1 | class Form(wtforms.Form): |
Exact implementation might be framework-dependent but general idea is to add any validation logic to a form itself. You can even update self._errors
attribute for a form to be more verbose about what happened wrong. And now instead of calling form.validate()
just invoke the newly defined method
1 | form = MyForm(data) |
Populating an object with form data
populate_obj
method allows a form to set attributes on some arbitrary object. Each field name on a from will correspond to an attribute name and value will be set to a field data. Sometimes you may want to set only a subset of fields or specify another target which differs from field name. We just need to override this method and implement custom logic within it. In the example below we will populate our objects according to specified target fields.
1 | import wtforms |
This way we will check a field for additional parameter defining target name and if none found follow the default behavior and use regular field name. Usage will look like the following
1 | class NameForm(Form): |
Be aware that your StringField
should support arbitrary keyword arguments (target_name
in our example) to work properly. You can accomplish this by subclassing a field and creating your own implementation that does support such a behavior or patch all the fields at once as mentioned in Creating custom fields section above. The simplest way is to create class like this
1 | class StringField(wtforms.StringField): |
But if you use more than one type of field (and in most cases you do) the better way is to modify all of them.
That’s mostly it. I did not describe password confirmation field and file upload fields because there are recipes on the web for that but will probably add another article on that later.
See you then.