Contains the information necessary to render (and modify the rendering of) a form field
Methods taking an options parameter will accept several ways of specifying those options:
Options can be “chained” indefinitely because each modification returns a new Field instance, so you can write:
>>> from formalchemy.tests import FieldSet, User
>>> fs = FieldSet(User)
>>> fs.append(Field('foo').dropdown(options=[('one', 1), ('two', 2)]).radio())
or:
>>> fs.configure(options=[fs.name.label('Username').readonly()])
Update field attributes in place. Allowed attributes are: validate, renderer, required, readonly, nul_as, label, multiple, options, size, instructions, metadata:
>>> field = Field('myfield')
>>> field.set(label='My field', renderer=SelectFieldRenderer,
... options=[('Value', 1)])
AttributeField(myfield)
>>> field.label_text
'My field'
>>> field.renderer
<SelectFieldRenderer for AttributeField(myfield)>
The value of this Field: use the corresponding value in the bound data, if any; otherwise, use the value in the bound model. For SQLAlchemy models, if there is still no value, use the default defined on the corresponding Column.
For SQLAlchemy collections, a list of the primary key values of the items in the collection is returned.
Invalid form data will cause an error to be raised. Controllers should thus validate first. Renderers should thus never access .value; use .model_value instead.
Give some HTML options to renderer.
Trailing underscore (_) characters will be stripped. For example, you might want to add a class attribute to your checkbox. You would need to specify .options(class_=’someclass’).
For WebHelpers-aware people: those parameters will be passed to the text_area(), password(), text(), etc.. webhelpers.
Attach some metadata attributes to the Field, to be used by conditions in templates.
Example usage:
>>> test = Field('test')
>>> field = test.with_metadata(instructions='use this widget this way')
...
And further in your templates you can verify:
>>> 'instructions' in field.metadata
True
and display the content in a <span> or something.
A manually-added form field
Create a new Field object.
field name
data type, from formalchemy.types (Integer, Float, String, Binary, Boolean, Date, DateTime, Time) or a custom type
default value. If value is a callable, it will be passed the current bound model instance when the value is read. This allows creating a Field whose value depends on the model once, then binding different instances to it later.
Field corresponding to an SQLAlchemy attribute.
>>> from formalchemy.tests import FieldSet, Order
>>> fs = FieldSet(Order)
>>> print fs.user.key
user
>>> print fs.user.name
user_id
This should be the super class of all Renderer classes.
Renderers generate the html corresponding to a single Field, and are also responsible for deserializing form data into Python objects.
Subclasses should override render and deserialize. See their docstrings for details.
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.
You should only have to override this if you are using custom (e.g., Composite) types.
Same as value, except returns a list of objects instead of primary keys, when working with ForeignKeys.
Use this when your deserialize or render functions manipulates ForeignKey objects; adding, removing or changing display according to their contents.
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
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()
******
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
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()) #doctest: +ELLIPSIS
<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
Render a time field:
>>> time = datetime(2000, 12, 31, 9, 03, 30).time()
>>> fs = FieldSet(One)
>>> fs.append(Field(name='time', type=types.Time, value=time))
Edit mode:
>>> print pretty_html(fs.time.render()) #doctest: +ELLIPSIS
<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
Render a datetime field:
>>> datetime = datetime(2000, 12, 31, 9, 03, 30)
>>> fs = FieldSet(One)
>>> fs.append(Field(name='datetime', type=types.DateTime, value=datetime))
Edit mode:
>>> print pretty_html(fs.datetime.render()) #doctest: +ELLIPSIS
<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
You can write your own FieldRenderer s to customize the widget (input element[s]) used to edit different types of fields...
Subclass FieldRenderer.
Update FieldSet.default_renderers. default_renderers is a dict of FieldRenderer subclasses. The default contents of default_renderers is:
default_renderers = {
types.String: fields.TextFieldRenderer,
types.Integer: fields.IntegerFieldRenderer,
types.Boolean: fields.BooleanFieldRenderer,
types.DateTime: fields.DateTimeFieldRendererRenderer,
types.Date: fields.DateFieldRenderer,
types.Time: fields.TimeFieldRenderer,
types.Binary: fields.FileFieldRenderer,
'dropdown': fields.SelectFieldRenderer,
'checkbox': fields.CheckBoxSet,
'radio': fields.RadioSet,
'password': fields.PasswordFieldRenderer,
'textarea': fields.TextAreaFieldRenderer,
}
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>