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 render_opts
Return a copy of this Field, bound to a different parent
Render the field as a set of checkboxes.
Render the field as a HTML5 color input type.
Render the field as a HTML5 date input type.
Render the field as a HTML5 datetime input type.
Render the field as a HTML5 datetime-local input type.
Render the field as an HTML select field. (With the multiple option this is not really a ‘dropdown’.)
Render the field as a HTML5 email input type.
Render the field hidden. (Value only, no label.)
True iff this Field is in readonly mode
True iff this Field must be given a non-empty value
Get or set the label for the field. If a value is provided then change the label associated with this field. By default, the field name is used, modified for readability (e.g., ‘user_name’ -> ‘User name’).
return the <label /> tag for the field.
raw value from model, transformed if necessary for use as a form input value.
Render the field as a HTML5 month input type.
Render the field as a HTML5 number input type, starting at min_, ending at max_, with legal increments every step distance. The default is set by value.
Render the field as a password input, hiding its value.
Perform a query in the parent’s session
Render the field as a set of radio buttons.
Render the field as a HTML5 range input type, starting at min_, ending at max_, with legal increments every step distance. The default is set by value.
raw value from model. different from .model_value in SQLAlchemy fields, because for reference types, .model_value will return the foreign key ID. This will return the actual object referenced instead.
Render the field readonly.
Render this Field as HTML.
Render this Field as HTML for read only mode.
Convenience method for validate(validators.required). By default, NOT NULL columns are required. You can only add required-ness, not remove it.
Return the field with all configuration changes reverted.
Sets different properties on the Field object. In contrast to the other methods that tweak a Field, this one changes thing IN-PLACE, without creating a new object and returning it. This is the behavior for the other methods like readonly(), required(), with_html(), with_metadata, with_renderer(), with_null_as(), label(), hidden(), validate(), etc...
Allowed attributes are:
- validate - append one single validator
- validators - appends a list of validators
- renderer - sets the renderer used (.with_renderer(val) equiv.)
- hidden - marks a field as hidden (changes the renderer)
- required - adds the default ‘required’ validator to the field
- readonly - sets the readonly attribute (.readonly(val) equiv.)
- null_as - sets the ‘null_as’ attribute (.with_null_as(val) equiv.)
- label - sets the label (.label(val) equiv.)
- multiple - marks the field as a multi-select (used by some renderers)
- options - sets .render_opts[‘options’] (for selects and similar fields, used by some renderers)
- size - sets render_opts[‘size’] with this val (normally an attribute to textarea(), dropdown(), used by some renderers)
- instructions - shortcut to update metadata[‘instructions’]
- metadata - dictionary that updates the .metadata attribute
- html - dictionary that updates the .html_options attribute (.with_html() equiv.)
NOTE: everything in .render_opts, updated with everything in .html_options will be passed as keyword arguments to the render() function of the Renderer set for the field.
Example:
>>> field = Field('myfield')
>>> field.set(label='My field', renderer=SelectFieldRenderer,
... options=[('Value', 1)],
... validators=[lambda x: x, lambda y: y])
AttributeField(myfield)
>>> field.label_text
'My field'
>>> field.renderer
<SelectFieldRenderer for AttributeField(myfield)>
Render the field as a textarea. Size must be a string (“25x10”) or tuple (25, 10).
Render the field as a HTML5 time input type.
Render the field as a HTML5 url input type.
Add the validator function to the list of validation routines to run when the FieldSet‘s validate method is run. Validator functions take one parameter: the value to validate. This value will have already been turned into the appropriate data type for the given Field (string, int, float, etc.). It should raise ValidationError if validation fails with a message explaining the cause of failure.
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.
Render the field as a HTML5 week input type.
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.
Render null as the given option tuple of text, value.
Return a copy of this Field, with a different renderer. Used for one-off renderer changes; if you want to change the renderer for all instances of a Field type, modify FieldSet.default_renderers instead.
A manually-added form field
Create a new Field object.
field name
data type, from formalchemy.types (Integer, Float, String, LargeBinary, 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.
Set the attribute’s value in model to the value given in data
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
return the best information from SA’s Column.info
The type of object in the collection (e.g., User). Calling this is only valid when is_relation is True.
Set the attribute’s value in model to the value given in data
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).
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 received from the web can be accessed via self.params. This dict-like object usually accepts the getone() and getall() method calls.
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 will need to override this in a child Renderer object if you want to mangle the data from your web form, before it reaches your database model. For example, if your render() method displays a select box filled with items you got from a CSV file or another source, you will need to decide what to do with those values when it’s time to save them to the database – or is this field going to determine the hashing algorithm for your password ?.
This function should return the value that is going to be assigned to the model and used in the place of the model value if there was an error with the form.
Note
Note that this function will be called twice, once when the fieldset is .validate()‘d – with it’s value only tested, and a second time when the fieldset is .sync()‘d – and it’s value assigned to the model. Also note that deserialize() can also raise a ValidationError() exception if it finds some errors converting it’s values.
If calling this function twice poses a problem to your logic, for example, if you have heavy database queries, or temporary objects created in this function, consider using the deserialize_once decorator, provided using:
from formalchemy.fields import deserialize_once
@deserialize_once
def deserialize(self):
... my stuff ...
return calculated_only_once
Finally, you should only have to override this if you are using custom (e.g., Composite) types.
return a GNUTranslations object in the most convenient way
Name of rendered input element.
The fieldset_prefix is defined when instantiating the FieldSet object, by passing the prefix= keyword argument.
The ModelName is taken by introspection from the model passed in at that same moment.
The pk is the primary key of the object being edited. If you are creating a new object, then the pk is an empty string.
The fieldname is, well, the field name.
Note
This method as the direct consequence that you can not create two objects of the same class, using the same FieldSet, on the same page. You can however, create more than one object of a certain class, provided that you create multiple FieldSet instances and pass the prefix= keyword argument.
Otherwise, FormAlchemy deals very well with editing multiple existing objects of same/different types on the same page, without any name clash. Just be careful with multiple object creation.
When creating your own Renderer objects, use self.name to get the field’s name HTML attribute, both when rendering and deserializing.
This gives access to the POSTed data, as received from the web user. You should call .getone, or .getall to retrieve a single value or multiple values for a given key.
For example, when coding a renderer, you’d use:
vals = self.params.getall(self.name)
to catch all the values for the renderer’s form entry.
return fields field.raw_value (mean real objects, not ForeignKeys)
Render the field. Use self.name to get a unique name for the input element and id. self.value may also be useful if you are not rendering multiple input elements.
When rendering, you can verify self.errors to know if you are rendering a new form, or re-displaying a form with errors. Knowing that, you could select the data either from the model, or the web form submission.
render a string representation of the field value
Submitted value, or field value converted to string. Return value is always either None or a string.
render a field as a text field
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 an integer as a text field
Render a password field
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 field as a textarea
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 boolean value as checkbox field
Render a date field
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
Render a time field
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())
<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 date time field
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())
<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
In readonly mode, html-escapes the output of the default renderer for this field type. (Escaping is not performed by default because it is sometimes useful to have the renderer include raw html in its output. The FormAlchemy admin app extension for Pylons uses this, for instance.)
You can write your own FieldRenderer s to customize the widget (input element[s]) used to edit different types of fields...
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>