Form builder customisation ========================== For a basic usage example see :ref:`form_builder_usage`. Custom ``related_name`` for form fields --------------------------------------- If you want to change ``related_name`` for form fields (by default ``AbstractForm`` and ``AbstractEmailForm`` expect ``form_fields`` to be defined), you will need to override the ``get_form_fields`` method. You can do this as shown below. .. code-block:: python from modelcluster.fields import ParentalKey from wagtail.wagtailadmin.edit_handlers import ( FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel ) from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField class FormField(AbstractFormField): page = ParentalKey('FormPage', related_name='custom_form_fields') class FormPage(AbstractEmailForm): intro = RichTextField(blank=True) thank_you_text = RichTextField(blank=True) content_panels = AbstractEmailForm.content_panels + [ FieldPanel('intro', classname="full"), InlinePanel('custom_form_fields', label="Form fields"), FieldPanel('thank_you_text', classname="full"), MultiFieldPanel([ FieldRowPanel([ FieldPanel('from_address', classname="col6"), FieldPanel('to_address', classname="col6"), ]), FieldPanel('subject'), ], "Email"), ] def get_form_fields(self): return self.custom_form_fields.all() Custom form submission model ---------------------------- If you need to save additional data, you can use a custom form submission model. To do this, you need to: * Define a model that extends ``wagtail.wagtailforms.models.AbstractFormSubmission``. * Override the ``get_submission_class`` and ``process_form_submission`` methods in your page model. Example: .. code-block:: python import json from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder from django.db import models from modelcluster.fields import ParentalKey from wagtail.wagtailadmin.edit_handlers import ( FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel ) from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField, AbstractFormSubmission class FormField(AbstractFormField): page = ParentalKey('FormPage', related_name='form_fields') class FormPage(AbstractEmailForm): intro = RichTextField(blank=True) thank_you_text = RichTextField(blank=True) content_panels = AbstractEmailForm.content_panels + [ FieldPanel('intro', classname="full"), InlinePanel('form_fields', label="Form fields"), FieldPanel('thank_you_text', classname="full"), MultiFieldPanel([ FieldRowPanel([ FieldPanel('from_address', classname="col6"), FieldPanel('to_address', classname="col6"), ]), FieldPanel('subject'), ], "Email"), ] def get_submission_class(self): return CustomFormSubmission def process_form_submission(self, form): self.get_submission_class().objects.create( form_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder), page=self, user=form.user ) class CustomFormSubmission(AbstractFormSubmission): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) Add custom data to CSV export ----------------------------- If you want to add custom data to the CSV export, you will need to: * Override the ``get_data_fields`` method in page model. * Override ``get_data`` in the submission model. The following example shows how to add a username to the CSV export: .. code-block:: python import json from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder from django.db import models from modelcluster.fields import ParentalKey from wagtail.wagtailadmin.edit_handlers import ( FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel ) from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField, AbstractFormSubmission class FormField(AbstractFormField): page = ParentalKey('FormPage', related_name='form_fields') class FormPage(AbstractEmailForm): intro = RichTextField(blank=True) thank_you_text = RichTextField(blank=True) content_panels = AbstractEmailForm.content_panels + [ FieldPanel('intro', classname="full"), InlinePanel('form_fields', label="Form fields"), FieldPanel('thank_you_text', classname="full"), MultiFieldPanel([ FieldRowPanel([ FieldPanel('from_address', classname="col6"), FieldPanel('to_address', classname="col6"), ]), FieldPanel('subject'), ], "Email"), ] def get_data_fields(self): data_fields = [ ('username', 'Username'), ] data_fields += super(FormPage, self).get_data_fields() return data_fields def get_submission_class(self): return CustomFormSubmission def process_form_submission(self, form): self.get_submission_class().objects.create( form_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder), page=self, user=form.user ) class CustomFormSubmission(AbstractFormSubmission): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) def get_data(self): form_data = super(CustomFormSubmission, self).get_data() form_data.update({ 'username': self.user.username, }) return form_data Note that this code also changes the submissions list view. Check that a submission already exists for a user ------------------------------------------------- If you want to prevent users from filling in a form more than once, you need to override the ``serve`` method in your page model. Example: .. code-block:: python import json from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder from django.db import models from django.shortcuts import render from modelcluster.fields import ParentalKey from wagtail.wagtailadmin.edit_handlers import ( FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel ) from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField, AbstractFormSubmission class FormField(AbstractFormField): page = ParentalKey('FormPage', related_name='form_fields') class FormPage(AbstractEmailForm): intro = RichTextField(blank=True) thank_you_text = RichTextField(blank=True) content_panels = AbstractEmailForm.content_panels + [ FieldPanel('intro', classname="full"), InlinePanel('form_fields', label="Form fields"), FieldPanel('thank_you_text', classname="full"), MultiFieldPanel([ FieldRowPanel([ FieldPanel('from_address', classname="col6"), FieldPanel('to_address', classname="col6"), ]), FieldPanel('subject'), ], "Email"), ] def serve(self, request, *args, **kwargs): if self.get_submission_class().objects.filter(page=self, user__pk=request.user.pk).exists(): return render( request, self.template, self.get_context(request) ) return super(FormPage, self).serve(request, *args, **kwargs) def get_submission_class(self): return CustomFormSubmission def process_form_submission(self, form): self.get_submission_class().objects.create( form_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder), page=self, user=form.user ) class CustomFormSubmission(AbstractFormSubmission): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) class Meta: unique_together = ('page', 'user') Your template should look like this: .. code-block:: django {% load wagtailcore_tags %}