Extending

It is possible to add custom Field and Handler models to your application, therefore allowing you to extend the basic functionality provided by the OmniForms library. Once you have done so, the omniforms library should automatically find these models and make it possible to create and associate these fields and handlers with your OmniForm model instances.

Fields

It is possible to add custom field types to your application that can be used by the omniforms library. Doing so should be as simple as adding a model class that subclasses omniforms.models.OmniField and provides a few class attributes used by the omniforms library.

At the very minimum, a custom Field model might look something like this:

from omniforms.models import OmniField

class MyCustomOmniField(OmniField):
    """
    Custom OmniField model
    """
    FIELD_CLASS = 'django.forms.CharField'
    FORM_WIDGETS = (
        'django.forms.widgets.TextInput',
        'django.forms.widgets.Textarea',
        'django.forms.widgets.PasswordInput',
        'myapp.widgets.MyCustomFormWidget',
    )

Of course, it is also possible to implement more complex OmniField models. If you are interested in doing so it may be wise to look at omniforms.models.OmniChoiceField and omniforms.models.OmniMultipleChoiceField.

The FIELD_CLASS attribute must be a python dotted import path to the form field class that this OmniField will use in the generated form. An OmniField modelo may only have one FIELD_CLASS (i.e. you cannot assign it a list or tuple of field classes) for the user to pick from.

The FORM_WIDGETS attribute must be a list or tuple containing a series of python dotted import paths to potential form widget classes that this OmniField could use in generated forms. This list or tuple must contain at least one potential widget but could have many. If there is more than one widget listed for a given OmniField model, the administrator should be able to select the type of widget they would like to use for a given field at the point of creation within the admin environment. If only one type of widget is provided, this option is effectively removed from the form and pre-selected for the admin user.

Handlers

It is possible to add custom form handler types to your application that can be used by the omniforms library. Doing so should be as simple as adding a model class that subclasses omniforms.models.OmniFormHandler and implements a handle method.

The handle method should accept one positional argument, form, which will be the valid form instance.

At the very minimum, a custom Handler model might look something like this:

from omniforms.models import OmniFormHandler

class MyCustomOmniFormHandler(OmniFormHandler):
    """
    Custom OmniFormHandler model
    """
    def handle(self, form):
        """
        Method for handling the valid form action

        :param form: The validated form instance this handler is attached to
        """
        do_something_with(form.cleaned_data)

It is worth noting that you should never call the forms handle or save (for model forms) methods within the OmniFormHandler.handle method. Doing so will cause the forms handlers to be run repeatedly until python reaches its recursion limit.

Omniforms ships with a handler - OmniFormSaveInstanceHandler - (to only be used with OmniModelForm instances) for saving model instances. This handler does not call the forms save method directly. Instead it calls the django.forms.models.save_instance function which ensures that the form data is persisted to the database correctly, but avoids the issue of the forms handlers being run repeatedly.

This approach allows us to set up a series of form handlers that will run one after the other when the forms handle or save method is called. For example, it is theoretically possible to implement and configure handlers to do the following:

  • Create a NewsLetterSubscription (model instance);
  • Send an email to the marketing department containing the subscribers data;
  • Post the users data to a CRM application;
  • Send an email to the subscriber confirming their subscription;