Widgets using Templates and Schema Properties¶
Unlike utilities more directly focused on processing Web forms, Flatland does not include any concept of “widgets” that render a field. It is however easy enough to employ Flatland’s “properties” and markup generation support to build our own widget system. This also gives us complete control over the rendering.
from flatland import Form, String
Input = String.with_properties(widget='input', type='text')
Password = Input.with_properties(type='password')
class SignInForm(Form):
username = Input.using(label='Username')
password = Password.using(label='Password')
Rendering Widgets with Genshi¶
Macros via Genshi’s py:def
directive would be a good way to implement
the actual widgets. For example:
<html
xmlns:form="http://ns.discorporate.us/flatland/genshi"
xmlns:py="http://genshi.edgewall.org/"
py:strip=""
>
<py:def
function="widget(field)"
py:with="macro = value_of(field.properties.widget + '_widget')"
py:replace="macro(field)"
/>
<fieldset py:def="input_widget(field)">
<form:with
auto-domid="on"
auto-for="on"
>
<label
form:bind="field"
py:content="field.label"
/>
<input
form:bind="field"
type="${field.properties.type}"
/>
</form:with>
</fieldset>
</html>
Typically we would call the widget
macro manually for each field we
want rendered, and in the desired order, but for demonstrative purposes
we stub out widgets for each field in arbitrary order:
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
>
<xi:include href="widgets.html"/>
<body>
<form>
${widget(form['username'])}
${widget(form['password'])}
</form>
</body>
</html>
Rendering with Jinja¶
If you’re not using Genshi you can still benefit from Flatland’s schema-aware markup generating support. With Jinja we might implement the macros as something resembling this:
{% set html = form_generator %}
{% macro widget(field) %}
{%- set macro = {'input': input}[field.properties.widget] -%}
{{- macro(field) -}}
{% endmacro %}
{% macro input(field) %}
{%- do html.begin(auto_domid=true, auto_for=true) %}
<fieldset>
{{ html.label(field, contents=field.label) }}
{{ html.input(field, type=field.properties.type) }}
</fieldset>
{%- do html.end() %}
{% endmacro %}
Then we can simply import the widget
macro to form templates:
{% from 'widgets.html' import widget -%}
<html>
<body>
<form>
{{- widget(form['username']) }}
{{- widget(form['password']) }}
</form>
</body>
</html>
Make sure to add a markup generator to the globals of your Jinja environment:
from flatland.out.markup import Generator
jinja_env.globals['form_generator'] = Generator('html')