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

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>

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 value="12" selected="selected">
       December
      </option>
     </select>
     <select id="One--date__day" name="One--date__day">
      <option value="DD">
       Day
      </option>
      <option value="1">
       1
      </option>
    ...
      <option value="31" selected="selected">
       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

.. 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 value="9" selected="selected">
       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 value="3" selected="selected">
       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 value="30" selected="selected">
       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 value="12" selected="selected">
       December
      </option>
     </select>
     <select id="One--datetime__day" name="One--datetime__day">
      <option value="DD">
       Day
      </option>
    ...
      <option value="31" selected="selected">
       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 value="9" selected="selected">
       9
      </option>
    ...
     </select>
     :
     <select id="One--datetime__minute" name="One--datetime__minute">
      <option value="MM">
       MM
      </option>
    ...
      <option value="3" selected="selected">
       3
      </option>
    ...
     </select>
     :
     <select id="One--datetime__second" name="One--datetime__second">
      <option value="SS">
       SS
      </option>
    ...
      <option value="30" selected="selected">
       30
      </option>
    ...
     </select>
    </span>

Read only mode::

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

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

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