Using YAFOWIL form library in Plone
YAFOWIL is a framework agnostic Python library that aims at simplifying forms' building.
It's a creature of the wonderful Blue Dynamic Alliance and despite the funny name ("Yet Another FOrm WIdget Library")it's a flexible and powerful tool for getting forms done.
In this brief example we'll appreciate its simplicity for creating a custom Plone form.
The first thing to note about the form is that its design is really pythonic. The form it's essentially a dictionary where every key represent an item of the form, be it a field or a button.
Note that I said 'item' and not 'field' since you can create potentially any kind oh html element (see blueprints reference).
Let's create a simple form in Plone
First we load some component:
import yafowil.plone
import yafowil.loader
from yafowil.base import factory
from yafowil.plone import form
and we define the view by inheriting from `yafowil.plone.form.BaseForm` that will give us some pre-cooked stuff, leaving you the only task to define a `prepare` method that will have to create the form:
class MyForm(form.BaseForm):
""" this is a yafowil form
"""
then we define some handlers to be used by the form:
@property
def _form_action(self):
return self.context.absolute_url() + '/' + self.__name__
def _form_handler(self, widget, data):
self._do_something(data)
def prepare(self):
form = factory('form',
name='theform',
props={
'action': self._form_action,
'class': 'horizontal'
})
form['file'] = factory(
'field:label:error:file',
props={
'label': _(u'File'),
'field.class': 'field',
})
# add submit button
form['submit'] = factory(
'field:submit',
props={
'label': _(u'Do something'),
'submit.class': 'myCustomClass',
'handler': self._form_handler,
})
self.form = formWe can have different handler per-button, so that we could add another button in the same form
form['submit2'] = factory(
'field:submit',
props={
'label': _(u'Do something 2'),
'submit.class': 'myCustomClass',
'handler': self._form_handler2,
})
Form items are built using blueprint chains. Take the file field for instance:
field:label:error:file
We are saying that this is a field, with a label, an error box and the field type is 'file'.
Note that we can also override attributes as simple as:
'field.class': 'field',
'input.class': 'my-input',
meaning that the wrapper of the field will have the class 'field' and the input item will have the class 'my-input'.
What about validation?
We can easily define a validator like this:
from yafowil.base import ExtractionError
def myvalidator(widget, data):
if not data.extracted['file']['file'].filename=='foo.txt':
raise ExtractionError('this field is not valid')
return data.extracted
and then we redefine our field like this:
form['file'] = factory(
'field:label:error:*myvalidation:file',
props={
'field.class': 'field',
'label': _(u'File'),
},
custom={
'myvalidation': dict(
extractors=[myvalidator],
)
},
)Use the form in a browser view
The last thing to do for getting our form, is to call its renderer from the our view's template like this:
<div tal:content="structure python:view.render_form()">form</div>
This little tour is finished. There is a lot more to say about this lib so we are going to blog more about it. In the meantime, if you want to know more about it, here are a couple of references:
Follow us!