django-form-builder’s documentation

A Django Framework application to build dynamic forms and define custom form fields types.

Github: https://github.com/UniversitaDellaCalabria/django-form-builder

Features:

  • Forms definitions via JSON object;

  • Save compiled form as JSON objects in model db and get its structure and contents with a simple model method call;

  • Override form constructor in order to add static common fields;

  • Create input fields using heritable classes, with customizable validation methods;

  • Manage Django Formset fields, with form insertion and removal via javascript;

  • Manage and verify digitally signed file fields (PDF and P7M) without a certification authority validation (TODO via third-party API).


Requirements and Setup

Install ``django-form-builder`

Only FileSignatureValidator library is required as system dependency, it is needed to verify digitally signed attachments. See also requirements for python requirements.

pip install git+https://github.com/peppelinux/FileSignatureValidator.git

In INSTALLED_APPS include django_form_builder app.

INSTALLED_APPS = (
    # other apps
    'django_form_builder',
)

Use single fields in your form

You can simply take single fields from this app and use them in you own form.
Just in your project include
from django_form_builder import dynamic_fields

and use every field as a normal form field

my_field = dynamic_fields.DynamicFieldClassName(params)

Every field has (or inherit) a raise_error() method that can be overrided to implement cleaning features and validation functions.

Fields methods and attributes

BaseCustomField is the base class for every custom field.
This class defines two attributes and trhee foundamental methods that make fields work well.

Attributes

  • is_complex (default False): if True, specifies that the field is composed by more elementar fields (like two DateFields);

  • is_formset (default False): if True, specifies that the field is a Django Formset.

Methods

  • def define_value(self, custom_value=None, **kwargs): it integrates the field initialization with custom configuration parameters defined by user (e.g. choices of a SelectBox);

  • get_fields(self): if field is_complex, it returns a Python list of child fields. Else, it returns [self];

  • def raise_error(self, name, cleaned_data, **kwargs):: it integrates clean() method to have a customizable behaviour processing cleaned_data.

Create your own fields

If you need to define your own fields inheriting an existing one, you can fastly create them by importing dynamic_fields

from django_form_builder import dynamic_fields

make an inheritance declaration

class MyCustomField(dynamic_fields.DynamicFieldClassName):
     # e.g. MyCustomField(BaseCustomField)
     ...

and override get_fields(), define_value() and raise_error() according to your needs.

Build dynamic forms

Now you can build your own form dynamically both in Django backend and frontend, just selecting the fields that you want, in total flexibility and easiness.

Every form can be saved in a configurable storage, in JSON format or simply defined in a Python Dictionary. Please see django_dynamic_form.dynamic_fields to see all the supported type.

from django_form_builder.forms import BaseDynamicForm
from collections import OrderedDict

constructor_dict = OrderedDict([('Telefono',  # field name
              ('CustomCharField',           # defines the FieldType
               {'label': 'Telefono',
                'required': True,
                'help_text': 'Fisso o Mobile',
                'pre_text': ''},            # a text to be rendered before the input field
               '')),
             ('Credenziali attive dal',
              ('BaseDateField',
               {'label': 'Credenziali attive dal',
                'required': True,
                'help_text': 'Data di attivazione delle credenziali',
                'pre_text': ''},
               '')),
             ('al',
              ('BaseDateField',
               {'label': 'al',
                'required': True,
                'help_text': 'data di scadenza delle credenziali.',
                'pre_text': ''},
               '')),
             ('Descrizione Attività',
              ('TextAreaField',
               {'label': 'Descrizione Attività',
                'required': True,
                'help_text': "Descrizione dell'attività per la quale si richiedono le credenziali",
                'pre_text': ''},
               '')),
             ('Richiede che le seguenti anagrafiche vengano attivate',
              ('CustomComplexTableField',  # a django fieldset
               {'label': 'Richiede che le seguenti anagrafiche vengano attivate',
                'required': True,
                'help_text': 'inserire almeno first_name, last_name e email',
                'pre_text': ''},
               'first_name#last_name#place_of_birth#date_of_birth#codice_fiscale#email#tel#valid_until'))])

form = BaseDynamicForm.get_form(#class_obj=YourCustomDynFormClass, # None by default, then use BaseDynamicForm
                                constructor_dict=constructor_dict,
                                custom_params=None,
                                #data=data,   # if there's some data to load
                                #files=files, # if there's some file attachments (handled separately)
                                remove_filefields=False,
                                remove_datafields=False)

Example of Dynamic Form built via frontend:

Preview of the builded form:

Create your DynamicFormClass and add static fields

If you need some static field in your form, than you can define a new Form Class, inheriting BaseDynamicForm

from django_form_builder import dynamic_fields
from django_form_builder.forms import BaseDynamicForm

class MyDynamicForm(BaseDynamicForm):
    def __init__(self,
                 constructor_dict={},
                 custom_params={},
                 *args,
                 **kwargs):
       # Add a custom static field common to all dynamic forms
       self.fields = {}
       my_static_field = dynamic_fields.format_field_name(choice_field_name)
       my_static_field_data = {'required' : True,
                               'label': choice_field_label,
                               'help_text': choice_field_helptext}
       my_static_field = getattr(dynamic_fields,
                                'CustomFieldClass')(**my_static_field_data)
       self.fields[my_static_field_id] = my_static_field

       # call super() constructor to build form
       super().__init__(# define it only if you
                        # define a custom field source,
                        # see "Create your own fields" paragraph.
                        # fields_source=dynamic_fields_integration,
                        initial_fields=self.fields,
                        constructor_dict=constructor_dict,
                        custom_params=custom_params,
                        *args, **kwargs)

    # if needed, override clean() method with your own params
    def clean(self, *args, **kwargs):
        cleaned_data = super().clean(own_param=own_value)

Configure your project to use dynamic forms

  • Step 1

    Every Dynamic Form needs a table to store the list of fields that compose it.

    Also, it has to be strictly linked to a project model entity to be rendered (e.g. what kind of object wil the form map? A Book, a Car or something else!?).

    In your project’s models, then, create a Model Class to store the list of fields, make it inherit class DynamicFieldMap and choose the ForeignKey that represents the form linked model entity.

    from django_form_builder.dynamic_fields import get_fields_types
    from django_form_builder.models import DynamicFieldMap
    
    class MyFieldsListModel(DynamicFieldMap):
        """
        This class represents every single form field, each one linked to a unique object
        """
    
        # if you want to integrate dynamic fields with your own,
        # define a new file that import all 'dynamic_fields' and defines others new and
        # then pass it as param to get_fields_types(class_name=my_custom_fields_file)
    
        my_entity = models.ForeignKey(MyEntityClass, on_delete=models.CASCADE)
        DynamicFieldMap._meta.get_field('field_type').choices = get_fields_types()
    
  • Step 2

    Every submitted dynamic form, if valid, save its content as a JSON. Once we have our fields model (step 1), we have to define a Model Class to save our compiled form JSON attribute.

    from django_form_builder.models import SavedFormContent
    
    class MyModelClass(SavedFormContent):
        """
        This class contains the JSON with all submitted form details
        """
        ...
    
  • Step 3

    In your views, use/override get_form() and compiled_form() methods to respectively build form structure from scratch (using your step 1 model class) and rebuild and fill it simply by the JSON field.

    from django_form_builder.models import DynamicFieldMap
    
    ...
    
    # the class used as foreign key in 'Step 1'
    class MyEntityClass(models.Model):
    
         ...
    
         def get_form(self,
                      data=None,
                      files=None,
                      remove_filefields=False,
                      remove_datafields=False,
                      **kwargs):
           """
           Returns the form (empty if data=None)
           if remove_filefields is not False, remove from form the passed FileFields
           if remove_datafields is True, remove all fields different from FileFields
           """
           # retrieve all the fields (the model class is in 'Step 1')
           form_fields_from_model = self.myfieldslistmodel.all().order_by('ordinamento')
           if not form_fields_from_model: return None
           # Static method of DynamicFieldMap that build the constructor dictionary
           constructor_dict = DynamicFieldMap.build_constructor_dict(form_fields_from_model)
    
           # more params to pass with 'data'
           custom_params = {'extra_1': value_1,
                            'extra_2': value_2}
           # the form retrieved by calling get_form() static method
           form = DynamicFieldMap.get_form(# define it only if you
                                           # need your custom form:
                                           # class_obj=MyDynamicForm,
                                           constructor_dict=constructor_dict,
                                           custom_params=custom_params,
                                           data=data,
                                           files=files,
                                           remove_filefields=remove_filefields,
                                           remove_datafields=remove_datafields)
    
           return form
    
    from django_form_builder.models import SavedFormContent
    
    ...
    
    # the class used in 'Step 2'
    class MyModelClass(SavedFormContent):
    
        ...
    
        def compiled_form(self, files=None, remove_filefields=True):
            """
            Returns the builded and filled form
            Integrates django_form_builder.models.SavedFormContent.compiled_form
            SavedFormContent.compiled_form uses DynamicFieldMap.get_form() filled
            """
            # set get_form() source class (step 1)
            form_source = self.my_entity
            # set data source class (inherited from 'SavedFormContent')
            data_source = self.modulo_compilato
    
            form = SavedFormContent.compiled_form(data_source=data_source,
                                                  files=files,
                                                  remove_filefields=remove_filefields,
                                                  form_source=form_source,
                                                  **other_extra_params)
    
            return form
    

Upload P7M and signed PDF files

Custom fields set provides a base class called CustomSignedFileField that via FileSignatureValidator library checks if an upload attachment is digitally signed.

Also, with get_cleaned_signature_params() method, it returns the sign details

  • Signature Validation

  • Signing Time

  • Signer full Distinguished Name

P7M file fields are built by CustomSignedP7MField(CustomSignedFileField) class.
Signed PDF file fields are built by CustomSignedPdfField(CustomSignedFileField) class.

Add/Remove Formset dynamically with javascript

Django Form Builder provides a particular type of field, CustomComplexTableField, that allows user to easily insert Django Formset Fields in his form.

The built-in javascript enables form inserting and removing via frontend, simply using the relative buttons!

To build a formset just define the CustomComplexTableField attribute valore setting columuns. Divide each one using # char and, for every column, define the field type with a dictionary, like in the example

column1({'type':'CustomSelectBoxField', 'choices': 'value1;value2;value3',})#column2({'type':'CustomRadioBoxField', 'choices': 'value1;value2',})#column3#column4({'type':'BaseDateField',})

Column with no params dict generate CustomCharField by default.