:mod:`formalchemy.fields` -- `Fields` and `Renderers`
=====================================================

.. automodule:: formalchemy.fields

.. Commented imports

   >>> from formalchemy.fields import *
   >>> from formalchemy.tests import *
   >>> from datetime import datetime

Fields
------

.. autoclass:: AbstractField
   :members:

.. autoclass:: Field
   :members:

.. autoclass:: AttributeField
   :members:

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
*************

.. autoclass:: FieldRenderer
   :members:

TextFieldRenderer
*****************

.. autoclass:: TextFieldRenderer
   :members:

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
********************

.. autoclass:: IntegerFieldRenderer
   :members:

PasswordFieldRenderer
*********************

.. autoclass:: PasswordFieldRenderer
   :members:

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
*********************

.. autoclass:: TextAreaFieldRenderer
   :members:

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
*******************

.. autoclass:: HiddenFieldRenderer
   :members:

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()
    <BLANKLINE>

HiddenFieldRendererFactory
***************************

.. autofunction:: HiddenFieldRendererFactory

CheckBoxFieldRenderer
*********************

.. autoclass:: CheckBoxFieldRenderer
   :members:

FileFieldRenderer
*****************

.. autoclass:: FileFieldRenderer
   :members:

DateFieldRenderer
*****************

.. autoclass:: DateFieldRenderer
   :members:

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

TimeFieldRenderer
*****************

.. autoclass:: TimeFieldRenderer
   :members:

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

DateTimeFieldRenderer
*********************

.. autoclass:: DateTimeFieldRenderer
   :members:

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

HiddenDateFieldRenderer
***********************

.. autoclass:: HiddenDateFieldRenderer

HiddenTimeFieldRenderer
***********************

.. autoclass:: HiddenTimeFieldRenderer

HiddenDateTimeFieldRenderer
***************************

.. autoclass:: HiddenDateTimeFieldRenderer

RadioSet
********

.. autoclass:: RadioSet
   :members:

CheckBoxSet
***********

.. autoclass:: CheckBoxSet
   :members:

SelectFieldRenderer
*******************

.. autoclass:: SelectFieldRenderer
   :members:

EscapingReadonlyRenderer
************************

.. autoclass:: EscapingReadonlyRenderer
   :members:


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::

.. literalinclude:: ../formalchemy/forms.py
   :pyobject: DefaultRenderers

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 <http://docs.jquery.com/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()

    Link

And for read only mode::

  >>> fs.readonly = True
  >>> print fs.render()

          Link:

      http://www.formalchemy.org

