Using YAFOWIL form library in Plone

An introduction on how to use YAFOWIL library for creating custom Plone forms.

logo yafowilYAFOWIL 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 = form

We 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:

Share this on

Share |

On same topics

Comments

comments powered by Disqus