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')
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>
<py:for
each="field in form.children"
py:replace="widget(field)"
/>
</form>
</body>
</html>
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>
{%- for field in form.children %}
{{- widget(field) }}
{%- endfor %}
</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')