formalchemy.fieldsFields and Renderers

Fields

Renderers

It is important to note that althought these objects are called renderers, they are also responsible for deserialization of data received from the web and insertion of those (possibly mangled) values back to the SQLALchemy object, if any.

They also have to take into consideration that the data used when displaying can come either from the self.params (the dict-like object received from the web) or from the model. The latter case happens when first displaying a form, and the former when validation triggered an error, and the form is to be re-displayed (and still contain the values you entered).

FieldRenderer

TextFieldRenderer

Render a string field:

>>> fs = FieldSet(One)
>>> fs.append(Field(name='text', type=types.String, value='a value'))

Edit mode:

>>> print(fs.text.render())
<input id="One--text" name="One--text" type="text" value="a value" />

Read only mode:

>>> print(fs.text.render_readonly())
a value

IntegerFieldRenderer

PasswordFieldRenderer

Render a string field:

>>> fs = FieldSet(One)
>>> fs.append(Field(name='passwd').with_renderer(PasswordFieldRenderer))

Edit mode:

>>> print(fs.passwd.render())
<input id="One--passwd" name="One--passwd" type="password" />

Read only mode:

>>> print(fs.passwd.render_readonly())
******

TextAreaFieldRenderer

Render a string field:

>>> fs = FieldSet(One)
>>> fs.append(Field(name='text',value='a value').with_renderer(TextAreaFieldRenderer))

Edit mode:

>>> print(fs.text.render())
<textarea id="One--text" name="One--text">a value</textarea>

Read only mode:

>>> print(fs.text.render_readonly())
a value

HiddenFieldRenderer

Render a string field:

>>> fs = FieldSet(One)
>>> fs.append(Field(name='text', value='h').with_renderer(HiddenFieldRenderer))

Edit mode:

>>> print(fs.render())
<input id="One--text" name="One--text" type="hidden" value="h" />

Read only mode:

>>> print(fs.text.render_readonly())

HiddenFieldRendererFactory

CheckBoxFieldRenderer

FileFieldRenderer

DateFieldRenderer

Render a date field:

>>> date = datetime(2000, 12, 31, 9, 00)
>>> fs = FieldSet(One)
>>> fs.append(Field(name='date', type=types.Date, value=date))

Edit mode:

>>> print(pretty_html(fs.date.render()))  
<span id="One--date">
 <select id="One--date__month" name="One--date__month">
  <option value="MM">
   Month
  </option>
  <option value="1">
   January
  </option>
...
  <option selected="selected" value="12">
   December
  </option>
 </select>
 <select id="One--date__day" name="One--date__day">
  <option value="DD">
   Day
  </option>
  <option value="1">
   1
  </option>
...
  <option selected="selected" value="31">
   31
  </option>
 </select>
 <input id="One--date__year" maxlength="4" name="One--date__year" size="4" type="text" value="2000" />
</span>

Read only mode:

>>> print(fs.date.render_readonly())
2000-12-31

TimeFieldRenderer

Render a time field:

>>> time = datetime(2000, 12, 31, 9, 3, 30).time()
>>> fs = FieldSet(One)
>>> fs.append(Field(name='time', type=types.Time, value=time))

Edit mode:

>>> print(pretty_html(fs.time.render()))  
<span id="One--time">
 <select id="One--time__hour" name="One--time__hour">
  <option value="HH">
   HH
  </option>
  <option value="0">
   0
  </option>
...
  <option selected="selected" value="9">
   9
  </option>
...
  <option value="23">
   23
  </option>
 </select>
 :
 <select id="One--time__minute" name="One--time__minute">
  <option value="MM">
   MM
  </option>
  <option value="0">
   0
  </option>
...
  <option selected="selected" value="3">
   3
  </option>
...
  <option value="59">
   59
  </option>
 </select>
 :
 <select id="One--time__second" name="One--time__second">
  <option value="SS">
   SS
  </option>
  <option value="0">
   0
  </option>
...
  <option selected="selected" value="30">
   30
  </option>
...
  <option value="59">
   59
  </option>
 </select>
</span>

Read only mode:

>>> print(fs.time.render_readonly())
09:03:30

DateTimeFieldRenderer

Render a datetime field:

>>> datetime = datetime(2000, 12, 31, 9, 3, 30)
>>> fs = FieldSet(One)
>>> fs.append(Field(name='datetime', type=types.DateTime, value=datetime))

Edit mode:

>>> print(pretty_html(fs.datetime.render()))  
<span id="One--datetime">
 <select id="One--datetime__month" name="One--datetime__month">
  <option value="MM">
   Month
  </option>
...
  <option selected="selected" value="12">
   December
  </option>
 </select>
 <select id="One--datetime__day" name="One--datetime__day">
  <option value="DD">
   Day
  </option>
...
  <option selected="selected" value="31">
   31
  </option>
 </select>
 <input id="One--datetime__year" maxlength="4" name="One--datetime__year" size="4" type="text" value="2000" />
 <select id="One--datetime__hour" name="One--datetime__hour">
  <option value="HH">
   HH
  </option>
...
  <option selected="selected" value="9">
   9
  </option>
...
 </select>
 :
 <select id="One--datetime__minute" name="One--datetime__minute">
  <option value="MM">
   MM
  </option>
...
  <option selected="selected" value="3">
   3
  </option>
...
 </select>
 :
 <select id="One--datetime__second" name="One--datetime__second">
  <option value="SS">
   SS
  </option>
...
  <option selected="selected" value="30">
   30
  </option>
...
 </select>
</span>

Read only mode:

>>> print(fs.datetime.render_readonly())
2000-12-31 09:03:30

HiddenDateFieldRenderer

HiddenTimeFieldRenderer

HiddenDateTimeFieldRenderer

RadioSet

CheckBoxSet

SelectFieldRenderer

EscapingReadonlyRenderer

Custom renderer

You can write your own FieldRenderer s to customize the widget (input element[s]) used to edit different types of fields...

  1. Subclass FieldRenderer.
    1. Override render to return a string containing the HTML input elements desired. Use self.name to get a unique name and id for the input element. self._value may also be useful if you are not rendering multiple input elements.
    2. If you are rendering a custom type (any class you defined yourself), you will need to override deserialize as well. render turns the user-submitted data into a Python value. (The raw data will be available in self.field.parent.data, or you can use _serialized_value if it is convenient.) For SQLAlchemy collections, return a list of primary keys, and FormAlchemy will take care of turning that into a list of objects. For manually added collections, return a list of values.
    3. If you are rendering a builtin type with multiple input elements, override _serialized_value to return a single string combining the multiple input pieces. See the source for DateFieldRenderer for an example.
  2. Update FieldSet.default_renderers. default_renderers is a dict of FieldRenderer subclasses. The default contents of default_renderers is:
class DefaultRenderers(object):

    default_renderers = {
        fatypes.String: fields.TextFieldRenderer,
        fatypes.Unicode: fields.TextFieldRenderer,
        fatypes.Text: fields.TextFieldRenderer,
        fatypes.Integer: fields.IntegerFieldRenderer,
        fatypes.Float: fields.FloatFieldRenderer,
        fatypes.Numeric: fields.FloatFieldRenderer,
        fatypes.Interval: fields.IntervalFieldRenderer,
        fatypes.Boolean: fields.CheckBoxFieldRenderer,
        fatypes.DateTime: fields.DateTimeFieldRenderer,
        fatypes.Date: fields.DateFieldRenderer,
        fatypes.Time: fields.TimeFieldRenderer,
        fatypes.LargeBinary: fields.FileFieldRenderer,
        fatypes.List: fields.SelectFieldRenderer,
        fatypes.Set: fields.SelectFieldRenderer,
        'dropdown': fields.SelectFieldRenderer,
        'checkbox': fields.CheckBoxSet,
        'radio': fields.RadioSet,
        'password': fields.PasswordFieldRenderer,
        'textarea': fields.TextAreaFieldRenderer,
        'email': fields.EmailFieldRenderer,
        fatypes.HTML5Url: fields.UrlFieldRenderer,
        'url': fields.UrlFieldRenderer,
        fatypes.HTML5Number: fields.NumberFieldRenderer,
        'number': fields.NumberFieldRenderer,
        'range': fields.RangeFieldRenderer,
        fatypes.HTML5Date: fields.HTML5DateFieldRenderer,
        'date': fields.HTML5DateFieldRenderer,
        fatypes.HTML5DateTime: fields.HTML5DateTimeFieldRenderer,
        'datetime': fields.HTML5DateTimeFieldRenderer,
        'datetime_local': fields.LocalDateTimeFieldRenderer,
        'month': fields.MonthFieldRender,
        'week': fields.WeekFieldRenderer,
        fatypes.HTML5Time: fields.HTML5TimeFieldRenderer,
        'time': fields.HTML5TimeFieldRenderer,
        fatypes.HTML5Color: fields.ColorFieldRenderer,
        'color': fields.ColorFieldRenderer,
    }

For instance, to make Boolean s render as select fields with Yes/No options by default, you could write:

>>> from formalchemy.fields import SelectFieldRenderer
>>> class BooleanSelectRenderer(SelectFieldRenderer):
...     def render(self, **kwargs):
...         kwargs['options'] = [('Yes', True), ('No', False)]
...         return SelectFieldRenderer.render(self, **kwargs)

>>> FieldSet.default_renderers[types.Boolean] = BooleanSelectRenderer

Of course, you can subclass FieldSet if you don’t want to change the defaults globally.

One more example, this one to use the JQuery UI DatePicker to render Date objects:

>>> from formalchemy.fields import FieldRenderer
>>> class DatePickerFieldRenderer(FieldRenderer):
...     def render(self):
...         value= self.value and self.value or ''
...         vars = dict(name=self.name, value=value)
...         return """
...            <input id="%(name)s" name="%(name)s"
...                   type="text" value="%(value)s">
...            <script type="text/javascript">
...              $('#%(name)s').datepicker({dateFormat: 'yy-mm-dd'})
...            </script>
...         """ % vars

(Obviously the page template will need to add references to the jquery library and css.)

Another example to render a link field:

>>> class LinkFieldRenderer(FieldRenderer):
...     def render(self, **kwargs):
...         """render html for edit mode"""
...         from formalchemy import helpers as h
...         return h.text_field(self.name, value=self._value, **kwargs)
...     def render_readonly(self, **kwargs):
...         """render html for read only mode"""
...         kwargs = {'value':self.field.raw_value}
...         return '<a href="%(value)s">%(value)s</a>' % kwargs

Then bind it to a specific field:

>>> from formalchemy.tests import *
>>> fs = FieldSet(One)
>>> fs.append(Field('link', value='http://www.formalchemy.org'))
>>> fs.configure(include=[fs.link.with_renderer(LinkFieldRenderer)])

Here is the result for edit mode:

>>> print(fs.render())
<div>
 <label class="field_opt" for="One--link">
  Link
 </label>
 <input id="One--link" name="One--link" type="text" value="http://www.formalchemy.org" />
</div>
<script type="text/javascript">
 //<![CDATA[
document.getElementById("One--link").focus();
//]]>
</script>

And for read only mode:

>>> fs.readonly = True
>>> print(fs.render())
<tbody>
 <tr>
  <td class="field_readonly">
   Link:
  </td>
  <td>
   <a href="http://www.formalchemy.org">
    http://www.formalchemy.org
   </a>
  </td>
 </tr>
</tbody>