YAFOWIL is based on a set of core ideas:
If you work with YAFOWIL for the first time it probably feels a bit different compared to other multiple class inheriting, schema based forms.
Instead YAFOWIL uses simple callables at several places. You never need to inherit any class from yafowil.* (and if you think you need to you don’t understand its architecture and should ask us to improve this documentation).
Callables are used for every extensible aspect of YAFOWIL. They are bundled as blueprints and used via the factory.
Explanation: Callables are simple functions oder instances of a class with a
__call__
method.
A YAFOWIL form consists of a tree of widgets. Everything you would probably call a “Field” in other form libraries is a widget instance. Widgets can have children. Thus, also fieldsets and even the form root itself is a widget.
The behavior of a widget is defined by a series of blueprints and properties as shown below in the referring sections.
The widget class is generic! It’s never instanciated directly. The factory is the only place where this happens.
At creation time of widgets you need to use it’s dict-like API for creating compounds of widgets like a form or a fieldset.
Attention possible confusion:
While one request to response cycle runs, widgets state is kept in a runtime data instance. It collects all information like values, request, errors happened, and the rendered html of a widget in the context of the current request.
The controller is responsible for form processing (extraction and validation), delegation of actions and form rendering (including error handling).
The controller is initialized with a form and request object and immediately
starts the processing. The rendered
instance attribute contains the
rendered form, while the attribute data
contains the extracted runtime data
tree.
Unlike most form frameworks YAFOWIL does not make a difference between
extraction of a value from the HTTP-request and validation. Both happens in one
processing step. If an extraction step fails it raises a
yafowil.base.ExtractionError
. This special Python Exception carries a human
readable message and the information if this error shall abort the extraction
chain or not. In either case the form has errors.
The factory knows of the available blueprints and is respsonsible to construct and configure widget instances. To construct a widget the factory gets called with the blueprint name as first parameter:
from yafowil.base import factory
widget = factory('text', ...)
The behavior of the callbacks in the different execution chains of the
blueprint can be configured with the props
dict. See blueprints reference
for a full list of accepted properties:
widget = factory('text', props={
'disabled': 'disabled',
})
For the root widget (most probably the form itself), the name attribute must be given to the factory:
form = factory('form', name='example_form', props={
'action': 'http://www.example.com/process_form',
})
Child widget names are set transparent using the child key
:
form['field_1'] = factory('text')
Usually we have some common widgets, e.g. a pure textarea, and then we need some label, description, display encountered errors, maybe a table cell or an encapsulating div and so on. And it can be very different depending on the framework used or the design we need to implement. But the core functionality is always the same. In other words: The input field and its behavior is stable, the eye-candy around it is not.
To solve the different needs, YAFOWIL supports chaining blueprints at factory
time, the so called factory chain
.
The blueprint chain is used by passing a colon seperated list of blueprint names as string to the factory as first argument. I.e. provide a text field inside a wrapper div with label, help text and error message if extraction failed, call factory like so:
form['field_1'] = factory('field:label:error:text', props={
'label': 'Field 1',
'help': 'Helptext for field 1',
'required': 'Field 1 must not be empty',
})
This causes the callable chains of each blueprint beeing executed in order. Extractors are executed from right to left while all others are executed left to right.
Now we may come up with the problem that several properties refer to more than one callable inside the execution chains. To address a property specific to a blueprint of the widget, you can prefix it with the blueprint name.
E.g., ‘label.class’ addresses the ‘class’ property of the ‘label’ blueprint only instead of effecting all blueprints:
form['field_1'] = factory('field:label:error:text', props={
'label': 'Field 1',
'label.class': 'label_css_class'
'help': 'Helptext for field 1',
'required': 'Field 1 must not be empty',
})
For the lazy people macros are provided. Macros expand to a factory chain of blueprints. Expansion happens at chain-lookup time before the widget is built.
Macros must be registered in the factory and can override property defaults:
factory.register_macro('field', 'field:label:error', 'props': {
'field.class': 'field',
'field.error_class': 'error',
'error.class': 'fieldErrorBox',
'error.render_empty': True,
'error.position': 'before',
})
Now the field
macro can be used inside the factory chain by name prefixed
with #
:
textfield = factory('#field:text')
For usecases where it’s not worth to write a generic widget for, it’s possible to inject custom blueprints.
Custom blueprints are passed to the factory either as 5-tuple containing chains of extractors, edit renderers, preprocessors, builders and display renderers, or as dictionary containing the chains at keys ‘extractors’, ‘edit_renderers’, ‘preprocessors’, ‘builders’ and ‘display_renderers’. Please read the section about blueprints below to get a deeper understanding of what happens.
Each chain contains callables as explained above. To tell the factory about usage of a custom blueprint, use the asterisk-prefix in the factory chain, like:
``field:label:*mycustom:textarea``
When using custom blueprints, the factory expects the custom
keyword argument, which is a dict with custom blueprint names as keys
(mycustom
in our example), and the custom blueprint configuration as
explained above.
Create custom callbacks:
def special_renderer(widget, data):
return u'<SPECIAL>%s</SPECIAL>' % data.rendered
def special_extractor(widget, data):
return data.extracted + ['extracted special']
Inject as dict:
widget = factory('outer:*special:inner', custom={
'special': {
'extractors': [special_extractor],
'edit_renderers': [special_renderer],
},
})
Inject as list:
widget = factory('outer:*special:inner', custom={
'special': ([special_extractor], [special_renderer], [], [], []),
})
Custom blueprints are great for easily injecting validation extractors.
Blueprints are a construction guides providing different behaviors on a widget: i.e. rendering a HTML input field, or extracting and validating input data or converting data received from the request.
This behaviors are organized as chains of callables. The behavior of the callables itself is controlled by properties. Each chain has different responsibilities. Chains are executed left-to-right.
Extractors are responsible to get, convert and validate the data of the current widget in the context of the current request. An extarctor is a callable expecting a widget instance and a runtime data instance as parameters.
Edit renderers are responsible to create html form output (unicode-strings) ready to be passed to the response. It is a callable expecting a widget instance and a runtime data instance as parameters. At this point the runtime data instance already passed the extraction chain and contains information about extracted values and errors. Edit renderers may utilize any templating language if desired. YAFOWIL has no preferences nor does it support any specific templating language out of the box. All internal rendering in YAFOWIL happens in pure python.
The edit renderer chain is executed if mode of widget is ‘edit’.
<input ..>
tag for the file upload. Next renderer creates some checkboxes with labels.
It has access to the string-output of the first renderer as part of
runtime-data. So some <checkbox ..>
tags can be prepended, wrapped
around or appended to the previous rendered <input ..>
. Both renderers
are reusable and may be used in other contexts, i.e. in an image blueprint
context.Display renderers are responsible to create html view output (unicode-strings) ready to be passed to the response.
The display renderer chain is executed if mode of widget is ‘display’. Like edit renderers it is a callable expecting widget and runtime data as parameters Like the edit renderer it is executed after extraction.
It is possible to mix edit and display renderers in one widget tree, each widget can have it own mode.
The preprocessor chain is executed once per request to response cycle directly after runtime data was created and before extraction happens. A preprocessor callable can be used to hook up framework specific requirements and gets widget and runtime data as parameters. There are global preprocessors running on every widget and widget specific pre-processors. Later are executed after the global preprocessors.
get
and __getitem__
. Further i18n support should be available
i.e. via zope.i18n
. A framework integration package now provides one
global preprocessor function wrapping the request if needed, and another
hooking up the i18n message factory and the translate function.This chain of callables is called only once right after the widget was created in the factory. A common use-case is to automatically populate a widget with child widgets. It expects widget and factory as parameters.