.. _topics-modules:

===============
Library Modules
===============

.. admonition:: About this document

   This document describes the functionality provided by the various python
   modules in Softwarefabrica Django Forms.

.. contents::
   :depth: 3

See also the `documentation index`_.

.. _`documentation index`: index.html

Modules
=======

``extended``
------------

The ``extended`` module provides backward-compatible replacements for the standard `django.forms`_ classes.

.. _`django.forms`: http://docs.djangoproject.com/en/dev/topics/forms/?from=olddocs

``Form``
~~~~~~~~

``softwarefabrica.django.forms.extended.Form`` is a backward-compatible
replacement for ``django.forms.Form``, supporting optional template-based
rendering of the form.

Template-based output is enabled only when the ``template_name`` is not empty or
none and ``enable_template`` is True. By default template output is enabled, and
the default template is ``forms/form.html``.

Optional arguments
^^^^^^^^^^^^^^^^^^

``template_name``
    Name of the template to use, or a list of template names (if a list is
    specified, the first template found is utilized). If ``None``, template
    output is disabled (default is ``forms/form.html``).

``template_loader``
    Template loader to use (defaults to ``django.template.loader``).

``enable_template``
    Enable template output (defaults to ``True``).

Default templates
^^^^^^^^^^^^^^^^^

A default template is provided as ``forms/form.html``. This template should be
adequate for most uses, and can be used as a starting point for further
customization.
A simplified version of the included ``forms/form.html`` templates is::

    {% for field in bound_fields %}
        {% include "forms/field.html" %}
    {% endfor %}

and ``forms/field.html`` is::

    {% spaceless %}
    <tr{% if field.errors %} class="errors" {% endif %}>
      <th>
        <label for="id_{{ field.name }}"{% if field.field.required %} class="required"{% endif %}>{{ field.label }}{% if field.field.required %} *{% endif %}:</label>
      </th>
      <td>
        {{ field }}
        {% if field.errors %}{{ field.errors }}{% endif %}
        {% if field.help_text %}<p class="helptext">({{ field.help_text }})</p>{% endif %}
      </td>
    </tr>
    {% endspaceless %}

Example usage::

    from softwarefabrica.django.forms.extended import *

    >>> class SimpleAuthorForm(Form):
    ...     name       = forms.CharField(max_length=50)
    ...     last_name  = forms.CharField(max_length=50)
    ...     birth_year = forms.IntegerField()
    ...     active     = forms.BooleanField()

    >>> form = SimpleAuthorForm()

    >>> print form
    ...

    Then we can enable template-based output by setting `template_name`.

    >>> form.template_name = "forms/form.html"

    >>> print form
    ...

    Or we can just directly create the form passing `template_name`.

    >>> form = SimpleAuthorForm(template_name = "forms/form.html")

    >>> print form
    ...

``ModelForm``
~~~~~~~~~~~~~

A compatible replacement for the standard `django.forms.ModelForm`_ class,
supporting extended features, like **field ordering**, **advanced widgets**,
**templated output**.

By default, advanced widgets are used when possible. These include the
``DateField`` and ``DateTimeField`` replacements with the Javascript calendar,
and the ``ModelChoiceField`` and ``ModelMultipleChoiceField`` replacements with
popup interface. These last two are only enabled when the respective model
``ForeignKey`` and ``ManyToManyField`` fields point to classes which provide the
adequate support. Please see the corresponding field documentation for more
information.

If you don't want to use the advanced widgets, use the ``ModelFormStdWidgets``
class instead.

The arguments described below are the arguments to the ``Form`` class built with
``ModelForm``, not to ``ModelForm`` itself.

Optional arguments
^^^^^^^^^^^^^^^^^^

``fieldorder``
    a list of field names to be used for field ordering when rendering
    the form.

``template_name``
    Name of the template to use, or a list of template names (if a list is
    specified, the first template found is utilized). If ``None``, template
    output is disabled (default is ``None``).

``template_loader``
    Template loader to use (defaults to ``django.template.loader``).

Example usage::

    >>> from softwarefabrica.django.forms.extended import *

    >>> class BookForm(ModelForm):
    ...     class Meta:
    ...         model = Book

    >>> BookForm.base_fields.keys()
    ['title', 'isbn', 'author', 'active']

    Let's instantiate the form and see which fields come from the form instance,
    and in which order.

    >>> form = BookForm()

    >>> form.fields.keys()
    ['title', 'isbn', 'author', 'active']

    Now let's try again, but specifying an ordering:

    >>> form = BookForm(fieldorder = ['active', 'title', 'author', 'isbn'])

    >>> form.fields.keys()
    ['active', 'title', 'author', 'isbn']

    Enable template output

    >>> form.template_name = "forms/form.html"

    >>> print form
    ...

    Alternatively...

    >>> form = BookForm(fieldorder = ['active', 'title', 'author', 'isbn'], template_name = "forms/form.html")

    >>> print form
    ...

.. _`django.forms.ModelForm`: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/?from=olddocs

``ModelFormStdWidgets``
~~~~~~~~~~~~~~~~~~~~~~~

Similar to ``ModelForm`` but not using advanced widgets, this is a compatible
replacement for the standard `django.forms.ModelForm`_ class, supporting
extended features, like **field ordering** and **templated output**.

If you want to use the advanced widgets, use the ``ModelForm``
class instead.

The arguments described below are the arguments to the ``Form`` class built with
``ModelFormStdWidgets``, not to ``ModelFormStdWidgets`` itself.

Optional arguments
^^^^^^^^^^^^^^^^^^

``fieldorder``
    a list of field names to be used for field ordering when rendering
    the form.

``template_name``
    Name of the template to use, or a list of template names (if a list is
    specified, the first template found is utilized). If ``None``, template
    output is disabled (default is ``None``).

``template_loader``
    Template loader to use (defaults to ``django.template.loader``).

.. _`django.forms.ModelForm`: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/?from=olddocs

``extended_formfield_cb``
~~~~~~~~~~~~~~~~~~~~~~~~~

This is a form field callback that enables advanced widgets for extended forms.
This function is used by the replacement ``ModelForm`` class to generate
advanced widgets, and can be used with the replacement ``modelform_factory``
function to customize the widgets (by passing additional arguments to the
callback).

Optional arguments
^^^^^^^^^^^^^^^^^^

``popup_models``
    a list of Django db model classes for which popup widgets should be used,
    when referenced by ``ForeignKey`` or ``ManyToManyField`` fields. If not
    specified, all model classes are candidates for the new widgets. Keep in
    mind that to actually use the extended widgets, the models must implement
    the proper "protocol" (a set of methods used by the widgets, see the
    relevant documentation for more information, e.g `SelectPopupField`_).

``not_relatedpopup``
    if True, don't use popup widgets for ``ForeignKey`` and ``ManyToManyField``
    fields.

``old_datewidgets``
    if True, don't use the new popup widgets for DateField and DateTimeField
    fields.

``fallback``
    an optional formfield callback to be used as a fallback. If not ``None``,
    the fallback is called with the same arguments received by
    ``extended_formfield_cb``, and its return value, if not ``None`` is passed
    back to the caller of ``extended_formfield_cb``.

``modelform_factory``
~~~~~~~~~~~~~~~~~~~~~

A replacement for ``django.forms.models.modelform_factory`` which uses the
extended ``ModelForm`` class and mechanism. This function generates dynamically
at run-time a form class using the ``ModelForm`` mechanism, in the same fashion
as would be done normally by the user when declaring in the source a form class
deriving from ``ModelForm``.

Optional arguments
^^^^^^^^^^^^^^^^^^

``form``
    Base form class to use. Defaults to
    ``softwarefabrica.django.forms.extended.ModelForm``.

``fields``
    If specified, the field list to use when creating forms.

``exclude``
    If specified, fields that should not appear in the generated forms.

``formfield_callback``
    If specified, the form field callback to use when creating the form
    class. Default to
    ``softwarefabrica.django.forms.extended.extended_formfield_cb``. To
    customize the ``extended_formfield_cb`` behaviour, pass a `lambda` function
    that perform a custom call to ``extended_formfield_cb``, specifying the
    desired arguments. See the examples.

Example usage::

    >>> from softwarefabrica.django.forms.extended import *

    >>> formC = extended.modelform_factory(BookRent, form=ModelForm, formfield_callback=lambda f, cb=extended_formfield_cb: cb(f, old_datewidgets=True))

    >>> form = formC()

    >>> print form
    ...

.. _fields:

``fields``
----------

The ``fields`` module provides custom Form field classes for the advanced
widgets in ``softwarefabrica.django.forms.widgets``.

``DateField``
~~~~~~~~~~~~~

A backward-compatible replacement for `django.forms.DateField`_ which provides a
popup Javascript calendar.

Optional arguments
^^^^^^^^^^^^^^^^^^

``input_formats``
    list of input format that the field must accept. Defaults to
    ``django.forms.fields.DEFAULT_DATE_INPUT_FORMATS``.

To be able to use the popup calendar, you'll need the `jscalendar`_ Javascript
calendar. A copy is provided with this library in the file
``jscalendar-1.0.zip``. Extract a copy inside your static media javascript
directory.

You'll also need to call the calendar Javascript code from your template, for
example including the following code fragment inside the ``<head>...</head>``
portion::

    <!-- calendar -->
    <link rel="stylesheet" type="text/css" href="{{ js }}/jscalendar/calendar-win2k-cold-2.css" />
    <script type="text/javascript" src="{{ js }}/jscalendar/calendar.js"></script>
    <!-- this is translation file - choose your language here -->
    <script type="text/javascript" src="{{ js }}/jscalendar/lang/calendar-{% if request.LANGUAGE_CODE %}{{ request.LANGUAGE_CODE }}{% else %}en{% endif %}.js"></script>
    <script type="text/javascript" src="{{ js }}/jscalendar/calendar-setup.js"></script>
    <!-- /calendar -->

**NOTE**: we are using the ``{{ js }}`` template variable, provided by the
``softwarefabrica.django.utils.viewshelpers.context_vars`` context processor.

.. _`jscalendar`: http://prdownloads.sourceforge.net/jscalendar/jscalendar-1.0.zip?download
.. _`django.forms.DateField`: http://docs.djangoproject.com/en/dev/ref/forms/fields/#datefield

``DateTimeField``
~~~~~~~~~~~~~~~~~

A backward-compatible replacement for `django.forms.DateTimeField`_ which provides a
popup Javascript calendar.

Optional arguments
^^^^^^^^^^^^^^^^^^

``input_formats``
    list of input format that the field must accept. Defaults to
    ``django.forms.fields.DEFAULT_DATETIME_INPUT_FORMATS``.

The same considerations regarding the `jscalendar`_ library apply here, as done
for ``DateField``.

.. _`jscalendar`: http://prdownloads.sourceforge.net/jscalendar/jscalendar-1.0.zip?download
.. _`django.forms.DateTimeField`: http://docs.djangoproject.com/en/dev/ref/forms/fields/#datetimefield

``DateRangeField``
~~~~~~~~~~~~~~~~~~

A date range field, with popup Javascript calendars.

.. _select-popup-field:

.. _`SelectPopupField`:

``SelectPopupField``
~~~~~~~~~~~~~~~~~~~~

A backward-compatible replacements for ``ModelChoiceField`` which uses a **popup
interface** for selecting the related instance. This is usually used as the Form
field corresponding to a ``ForeignKey`` model field.

There are two conditions to actually use the popup interface:

1. the page template must load some Javascript support routines, provided by the
   library as a template (to ease inclusion) in ``forms/js/Related.js``. It's a
   matter of including the following code fragment inside the
   ``<head>...</head>`` portion::

    <!-- softwarefabrica.django.forms -->
    <script language="javascript" type="text/javascript">
    <!--
    {% include "forms/js/Related.js" %}
    // -->
    </script>
    <!-- /softwarefabrica.django.forms -->

2. the target model must implement a special "protocol", a set of methods used
   by the corresponding widget to obtain the necessary informations (essentially URLs).

In particular, the target models must implement:

- the standard method ``get_absolute_url``
- the class method ``get_create_url``, returning the URL for creating a new instance
- the method ``get_edit_url``, returning the URL to edit the instance
- the class method ``get_list_url``, returning the URL for the list view of the
  instances (typically a generic ``object_list`` view, or its equivalent provided
  by `softwarefabrica.django.crud`_).

Let's see a simple example of a class that can support the popup interface when
referenced by a ``ForeignKey`` or ``ManyToManyField``::

    class Author(models.Model):
        name       = models.CharField(max_length=50)
        last_name  = models.CharField(max_length=50)
        birth_year = models.IntegerField()
        active     = models.BooleanField(default=True)

        def __unicode__(self):
            return u'%s %s' % (self.name, self.last_name)

        class Meta:
            ordering = ['last_name', 'name']

        def get_absolute_url(self):
            return "/authors/%s/" % self.id

        def get_create_url(cls):
            return "/authors/new/"
        get_create_url = classmethod(get_create_url)

        def get_edit_url(self):
            return "/authors/%s/edit" % self.id

        def get_list_url(cls):
            return "/authors/"
        get_list_url = classmethod(get_list_url)

Note that it would be preferable to use `named URL patterns`_ and `permalink`_ decorators::

    def get_absolute_url(self):
        return ('library-author-detail', (), {'object_id': self.id})
    get_absolute_url = permalink(get_absolute_url)

    def get_create_url(cls):
        return ('library-author-create', (), {})
    get_create_url = permalink(get_create_url)
    get_create_url = classmethod(get_create_url)

    def get_edit_url(self):
        return ('library-author-edit', (), {'object_id': self.id})
    get_edit_url = permalink(get_edit_url)

    def get_list_url(cls):
        return ('library-author-list', (), {})
    get_list_url = permalink(get_list_url)
    get_list_url = classmethod(get_list_url)


.. _`softwarefabrica.django.crud`: http://pypi.python.org/pypi/softwarefabrica.django.crud
.. _`named URL patterns`: http://docs.djangoproject.com/en/dev/topics/http/urls/#id2
.. _`permalink`: http://docs.djangoproject.com/en/dev/ref/models/instances/#the-permalink-decorator

``SelectMultiplePopupField``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A backward-compatible replacements for ``ModelMultipleChoiceField`` which uses a
**popup interface** for selecting the related instances. This is usually used as
the Form field corresponding to a ``ManyToManyField`` model field.

To actually use the popup interface, the target model must implement the same
special "protocol" described for `SelectPopupField`_ above, and include the same
Javascript support routines.

``RelatedItemField``
~~~~~~~~~~~~~~~~~~~~

An alternative field to `SelectPopupField`_, using a different widget. This is
usually used as the Form field corresponding to a ``ForeignKey`` model field.

Target models for this field need to implement only the ``get_list_url`` and
``get_create_url`` methods, since the edit button is not provided.

This field uses the same Javascript routines required by ``SelectPopupField``.

``widgets``
-----------

The ``widgets`` module provides the advanced Form widgets used by the custom
Form field classes in ``softwarefabrica.django.forms.fields``.

``DateWidget``
~~~~~~~~~~~~~~

Widget used by ``DateField``.

``DateTimeWidget``
~~~~~~~~~~~~~~~~~~

Widget used by ``DateTimeField``.

``DateRangeWidget``
~~~~~~~~~~~~~~~~~~~

Widget used by ``DateRangeField``.

``SelectPopupWidget``
~~~~~~~~~~~~~~~~~~~~~

Widget used by ``SelectPopupField``.

``SelectMultiplePopupWidget``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Widget used by ``SelectMultiplePopupField``.

``RelatedItemWidget``
~~~~~~~~~~~~~~~~~~~~~

Widget used by ``RelatedItemField``.
