Forms

The main purpose of this app is to handle Forms. Of course, the app provides an API to Create and Edit forms, but it’s not the only option: django-formidable also provides a full python builder in order to create forms. django-formidable also provides a method to retrieve a standard django form class which can then be used just like an ordinary django form.

Formidable object

The main class is formidable.models.Formidable. This class is a classic django model which defines a representation of a dynamic form.

This is the main object which is used to create or edit dynamic forms through the RESTful API or directly in Python/Django.

Django form class

One of the main feature is to provide a standard django form class built from the definition stored as Formidable object. The django form class is accessible throught the formidable.models.Formidable.get_django_form_class().

>>> formidable = Formidable.objects.get(pk=42)
>>> form_class = formidable.get_django_form_class()

This form class can be manipulated as all django form class, you can build an instance to validate data:

>>> form = form_class(data={'first_name': 'Obiwan'})
>>> form.is_valid()
False
>>> form.errors
{'last_name': ['This field is required.']}
>>> form = form_class(data={'first_name': 'Obiwan', 'last_name': 'Kenobi'})
>>> form.is_valid()
True

Or to render it:

{{ form.as_p }}

When a standard mechanism is implemented, you have a method to custom the final objec we get. django-formidable provides a way in order to custom the form class you get.

Each kind of field is built with an associated FieldBuilder:

slug Field / Widgets FieldBuilder
text CharField / TextInput formidable.forms.field_builder.TextFieldBuilder
paragraph CharField / TextArea formidable.forms.field_builder.ParagraphFieldBuilder
dropdown ChoiceField / Select formidable.forms.field_builder.DropdownFieldBuilder
checkbox ChoiceField / CheckboxInput formidable.forms.field_builder.CheckboxFieldBuilder
radios ChoiceField / RadioSelect formidable.forms.field_builder.RadioFieldBuilder
checkboxes ChoiceField / CheckboxSelectMultiple formidable.forms.field_builder.CheckboxesFieldBuilder
email EmailField / TextInput formidable.forms.field_builder.EmailFieldBuilder
date DateField formidable.forms.field_builder.DateFieldBuilder
number IntegerField formidable.forms.field_builder.IntegerFieldBuilder

So, as describe in django document (https://docs.djangoproject.com/en/1.9/topics/forms/media/#assets-as-a-static-definition), if you want add a CalendarWidget on the date field on your form, you can write your own field builder.

from django import forms

from formidable.forms.field_builder import DateFieldBuilder, FormFieldFactory

class CalendarWidget(forms.TextInput):

    class Media:
        css = {
            'all': ('pretty.css',)
        }
        js = ('animations.js', 'actions.js')


class CalendarDateFieldBuilder(DateFieldBuilder):
    widget_class = CalendarWidget


class MyFormFieldFactory(FormFieldFactory):
    field_map = FormFieldFactory.field_map.copy()
    field_map['date'] = CalendarDateFieldBuilder

With this definition you can call:

>>> formidable.get_django_form_class(field_factory=MyFormFieldFactory)

Roles and access-rights

Roles

One of the main features of formidable is to set up different access-rights for the same form. This way, you can create a form with certain fields that are only accessible to a specific group of users, for example.

For the moment, formidable is not designed to work without roles, so even if you don’t need to handle multiple roles or access-rights inside your application, you will still have to define a default role for formidable to work properly.

All roles must be declared through a formidable.accesses.AccessObject instance. This class must be instantiated with an id and a label. The id has to be unique, it’s up to you to maintain this constraint. The label serves as a human readable value. You can set this to any string you like.

from formidable.accesses import AccessObject

padawan = AccessObject(id='padawan', label='Padawan')
jedi = AccessObject(id='jedi', label='Jedi')
sith = AccessObject(id='sith', label='Bad Guy')

django-formidable needs to know how to get all declared instances. To do so, you will need to create a function which returns the correct instances:

def get_accesses():
    return [padawan, jedi, sith]

Once this function is defined, you will need to fill the settings key FORMIDABLE_ACCESS_RIGHTS_LOADER.

FORMIDABLE_ACCESS_RIGHTS_LOADER = 'myapp.accesses.get_accesses'

Once this is done, django-formidable will know which roles have been defined, so it can create or check access-rights as necessary.

Fetch context

Occasionally, django-formidable will require access to the web request’s context, e.g. to find out which kind of user is accessing the current form.

For this reason, you must define a function to fetch the context of the current request. The function takes as parameters the request object of the view (self.request) and the view kwargs (self.kwargs).

The function must return an access id which is defined in one of the AccessObject instances returned by the method configured in FORMIDABLE_ACCESS_RIGHTS_LOADER.

If the user’s role is defined as an attribute, you can just return it directly:

def fetch_context(request, kwargs):
    return request.user.role

Then, set FORMIDABLE_CONTEXT_LOADER in your settings:

FORMIDABLE_CONTEXT_LOADER = myapp.accesses.fetch_context

Available access-rights

For each field of a form, and for each role you have defined, you can define a specific access-right. There are four different available access-rights:

  • EDITABLE, the user may fill-in the field but there is no obligation to do so.
  • REQUIRED, the user must fill-in the field in order to submit the form.
  • READONLY, this will render the field as disabled, allowing the user to view but not modify its contents.
  • HIDDEN, the field will not be available to the user, preventing the user from either viewing or modifying its contents.

All the value are defined in formidable.constants

Conditions

Important

As of 1.4.0, it is allowed to have several conditional display rules that target a common field. In case of “conflict” between these rules, priority goes to the display, rather than the hide action.

e.g.:

  • Rule 1 says: “if checkbox-1 is checked, then display field X”
  • Rule 2 says: “if checkbox-2 is checked, then display field X and Y”

if only checkbox-1 is checked, field X will be displayed, even if checkbox-2 is unchecked, and vice-versa. If both are checked, fields X and Y will be displayed. If none is checked, fields X and Y will be hidden.

Types for conditional rules

At this moment, we can guarantee only the support of the checkboxes and dropdown lists, but normally you could use it for any type you want.

Also, you could specify types allowed for the conditions using the settings variable FORMIDABLE_CONDITION_FIELDS_ALLOWED_TYPES By default formidable will accept any type.

FORMIDABLE_CONDITION_FIELDS_ALLOWED_TYPES = []  # formidable will allow any type for the conditional rules
FORMIDABLE_CONDITION_FIELDS_ALLOWED_TYPES = ['checkbox']  # formidable will allow checkboxes only

In case you try to configure a conditional display based on a field that has been excluded from the allowed types, you’ll receive a ValidationError when trying to save the form.

Here is a list of all the available types:

available_types = [
    'title', 'helpText', 'fieldset', 'fieldsetTable', 'separation',
    'checkbox', 'checkboxes', 'dropdown', 'radios', 'radiosButtons',
    'text', 'paragraph', 'file', 'date', 'email', 'number'
]

Python builder

In some cases, you may want to build a formidable object without using the RESTful API (in tests for example). django-formidable provides a Python API in order to that. Take a look at formidable.forms.fields to discover all the fields that are available through this API.

The main class to use is formidable.forms.FormidableForm. Feel free to subclass this form and define your own form(s), just like any other django form.

For example, let’s say we need to build a form with a first name, last name and a description. We can use formidable.fields to accomplish this. Lets consider using the different roles defined in the installation part, jedi and padawan.

from formidable.forms import FormidableForm
from formidable.forms import fields


class MySubscriptionForm(FormidableForm):

    first_name = fields.CharField(label='Your First Name')
    last_name = fields.CharField(label='Your Last Name')
    description = fields.TextField(
        label='Description',
        help_text='Tell us about yourself.'
    )

Attributes like required should not be used as these will depend on the context when the form is built. If you want to define a field as required, it will need to be required for a specific role through the accesses argument. This argument is a dictionary containing the various access-rights for each role. By default, if you don’t specify any access-rights for a previously defined role, the field will be created as EDITABLE:

class MySubscriptionForm(FormidableForm):

    first_name = fields.CharField(
        label='Your First Name',
        accesses={'padawan': constants.REQUIRED, 'jedi': constants.READONLY}
    )
    last_name = fields.CharField(label='Your Last Name')
    description = fields.TextField(
        label='Description',
        help_text='Tell us about yourself.'
    )

When the form definition is complete, you can create a new formidable.models.Formidable object:

formidable = MySubscriptionForm.to_formidable(
    label='My Subscription Form',
    description='This form is for subscribing to the jedi order.')

This method will create the object in the database and return the complete instance:

>>> formidable.pk
42

You can also get the django form class from the formidable object:

>>> form_class = formidable.get_django_form_class(role='padawan')

For our ‘padawan’ role, the first_name is required:

>>> form = form_class(data={'last_name': 'Kenobi'})
>>> form.is_valid()
False
>>> form.errors
{'first_name': ['This field is required.']}

Available fields

This module provides custom form fields in order to define a Formidable object, as with a standard django form.

You can find most of the standard django fields :

class formidable.forms.fields.CharField(accesses=None, default=None, *args, **kwargs)
class formidable.forms.fields.BooleanField(accesses=None, default=None, *args, **kwargs)
widget

alias of formidable.forms.widgets.CheckboxInput

class formidable.forms.fields.NumberField(accesses=None, default=None, *args, **kwargs)
widget

alias of formidable.forms.widgets.NumberInput

class formidable.forms.fields.FileField(accesses=None, default=None, *args, **kwargs)
widget

alias of formidable.forms.widgets.ClearableFileInput

class formidable.forms.fields.DateField(accesses=None, default=None, *args, **kwargs)
widget

alias of formidable.forms.widgets.DateInput

class formidable.forms.fields.MultipleChoiceField(defaults=None, *args, **kwargs)
widget

alias of formidable.forms.widgets.SelectMultiple

class formidable.forms.fields.ChoiceField(defaults=None, *args, **kwargs)
widget

alias of formidable.forms.widgets.Select

Extra fields are allowed to use with FormidableForm, the Format Field. These kinds of fields allow us to define format inside the form:

class formidable.forms.fields.TitleField(accesses=None, default=None, *args, **kwargs)

Add a title inside the rendering.

class formidable.forms.fields.HelpTextField(text, *args, **kwargs)

The help text field is just a field to show some text formatted in markdown (implemented in the render widget). Due to the method Form._html_output(), we cannot override the mechanism to render the field correctly.

Add a description inside the form, to comment a block of inputs or just to add some text.

class formidable.forms.fields.SeparatorField(*args, **kwargs)

Add separators between fields to separate blocks of fields, for example.

Each field comes with its own widget class. Via these classes, the type of fields they deal with can be specified. If you want to override any with your own fields, please look at formidable.forms.widgets.

class formidable.forms.fields.FormatField(*args, **kwargs)

Handles display information inside the form.

The help_text attribute is removed automatically to handle display.

The get_bound_field is defined here (must be implemented in a child class). The bound field is used to return the correct value to display.

Normally, if help_text exists it’s rendered inside a <span> tag, and labels are rendered in <label> tags.

To avoid this, we override the label and the help_text attributes, setting them to blank values.

class formidable.forms.fields.HelpTextField(text, *args, **kwargs)

The help text field is just a field to show some text formatted in markdown (implemented in the render widget). Due to the method Form._html_output(), we cannot override the mechanism to render the field correctly.

get_bound_field(form, name)

Return a BoundField instance that will be used when accessing the form field in a template.

widget

alias of formidable.forms.widgets.HelpTextWidget

class formidable.forms.fields.SeparatorField(*args, **kwargs)
get_bound_field(form, name)

Return a BoundField instance that will be used when accessing the form field in a template.

widget

alias of formidable.forms.widgets.SeparatorWidget

Available Widgets

Most of Django’s widgets are defined here. These widgets are usable with any fields defined in formidable.forms.fields. These widgets are here in order to use with formidable.forms.FormidableForm declaration. Do not use them directly as normal widgets for rendering!

The main goal of the Widget class declaration is to provide a reliable type of associated field.

class formidable.forms.widgets.FormidableWidget(attrs=None)

Base Widget definition, implements the basic method to create a field in a database, associated with a form.

multiple = False

If true, the widget is multiple selection items

to_formidable(formidable, slug, label, help_text, order)

Create an association in database with the formidable reference object. slug, label, help_text come from the formidable.forms.fields.Field associated object.

type_id = None

define the type of the field

class formidable.forms.widgets.TextInput(attrs=None)
class formidable.forms.widgets.Textarea(attrs=None)
class formidable.forms.widgets.ItemsWidget(attrs=None)
class formidable.forms.widgets.ItemsWidgetMultiple(attrs=None)
class formidable.forms.widgets.Select(attrs=None, choices=())
class formidable.forms.widgets.RadioSelect(attrs=None, choices=())
class formidable.forms.widgets.SelectMultiple(attrs=None, choices=())
class formidable.forms.widgets.CheckboxInput(attrs=None, check_test=None)
class formidable.forms.widgets.CheckboxSelectMultiple(attrs=None, choices=())
class formidable.forms.widgets.DateInput(attrs=None, format=None)
class formidable.forms.widgets.NumberInput(attrs=None)
class formidable.forms.widgets.ClearableFileInput(attrs=None)
class formidable.forms.widgets.SeparatorWidget(attrs=None)
class formidable.forms.widgets.TitleWidget(tag='h4', *args, **kwargs)
class formidable.forms.widgets.HelpTextWidget(attrs=None)