Adding a Stream Form

Stream Forms behave similar to the Wagtail Form Builder, and are just like any other Wagtail Page. The difference is: the form fields are defined in a StreamField. Because they are in a StreamField, any blocks can be mixed and matched together to give your editors exactly what they need.

In this tutorial, assume that your Django app is named home.

Step 1: Define Blocks

In home/models.py, let’s first define some blocks:

from wagtail import blocks
from wagtail.images.blocks import ImageBlock
from wagtail_flexible_forms import blocks as wff_blocks

# First, let's define the fields we'd like our form to contain, as blocks.
# StreamForms can contain *any* block, not just form fields!
STREAMFORM_FIELDS = [
    # Include form field blocks from wagtail_flexible_forms.
    ("sf_singleline", wff_blocks.CharFieldBlock(group="Fields")),
    ("sf_multiline", wff_blocks.TextFieldBlock(group="Fields")),
    ("sf_checkboxes", wff_blocks.CheckboxesFieldBlock(group="Fields")),
    ("sf_radios", wff_blocks.RadioButtonsFieldBlock(group="Fields")),
    ("sf_dropdown", wff_blocks.DropdownFieldBlock(group="Fields")),
    ("sf_checkbox", wff_blocks.CheckboxFieldBlock(group="Fields")),
    ("sf_date", wff_blocks.DateFieldBlock(group="Fields")),
    ("sf_time", wff_blocks.TimeFieldBlock(group="Fields")),
    ("sf_datetime", wff_blocks.DateTimeFieldBlock(group="Fields")),
    ("sf_image", wff_blocks.ImageFieldBlock(group="Fields")),
    ("sf_file", wff_blocks.FileFieldBlock(group="Fields")),
    # And content blocks from Wagtail!
    ("text", blocks.RichTextBlock(group="Content")),
    ("image", ImageBlock(group="Content")),
]

Step 2: Define Form Submissions

Since Stream Forms can have multiple steps (more on that later) they need a temporary session-based model to store the data as the user fills out the form. The default implementation is to delete them at the end, and create a finalized form submission similar to Wagtail contrib forms.

from wagtail_flexible_forms.models import AbstractSessionFormSubmission
from wagtail_flexible_forms.models import AbstractSubmissionRevision

class MySubmissionRevision(AbstractSubmissionRevision):
    pass

class MySessionFormSubmission(AbstractSessionFormSubmission):
    @staticmethod
    def get_revision_class():
        return MySubmissionRevision

Step 3: Define a Stream Form Page

from wagtail.admin.panels import FieldPanel
from wagtail.contrib.forms.models import FormSubmission
from wagtail.fields import RichTextField
from wagtail.fields import StreamField
from wagtail.models import Page
from wagtail_flexible_forms.models import StreamFormMixin

class StreamFormPage(StreamFormMixin, Page):
    template = "home/stream_form_page.html"
    landing_page_template = "home/form_page_landing.html"

    # Typical Wagtail field, like any other page.
    intro = RichTextField(blank=True)

    # Set ``form_fields`` to contain our Streamform fields.
    form_fields = StreamField(STREAMFORM_FIELDS)

    content_panels = Page.content_panels + [
        FieldPanel("intro"),
        FieldPanel("form_fields"),
    ]

    @staticmethod
    def get_submission_class():
        """
        Submission class is used to store the final form
        submission, after the user has finished their session.

        For simplicity, use Wagtail's default FormSubmission class.
        """
        return FormSubmission

    @staticmethod
    def get_session_submission_class():
        """
        Session submission class is used to store temporary
        data while the form is being filled out, i.e. for
        multi-step forms.

        You must return something that inherits from
        ``AbstractSessionFormSubmission``.
        """
        return MySessionFormSubmission

Step 4: Create HTML Templates

More robust templates are available in the testproject, however the examples below will get you started.

File: home/templates/home/stream_form_page.html

{% load static wagtailcore_tags %}
<html>
  <head>
    <title>{{ page.title }}</title>
  </head>
  <body>

    <h1>{{ page.title }}</h1>

    {{ page.intro | richtext }}

    <form action="{% pageurl self %}" method="POST" enctype="{{ form_enctype }}">
      {% csrf_token %}

      {% for item in markups_and_bound_fields %}
      <!-- render content blocks -->
      {% if item.type == "markup" %}
      {% include_block item.block %}
      <!-- render form fields -->
      {% elif item.type == "field" %}
      <div class="field">
        {{ item.field.errors }}
        {{ item.field.label_tag }} {{ item.field }}
      </div>
      {% endif %}
      {% endfor %}

      <button type="submit">Submit</button>

    </form>
  </body>
</html>

File: home/templates/home/form_page_landing.html

{% load static wagtailcore_tags %}
<html>
  <head>
    <title>{{ page.title }}</title>
  </head>
  <body>

    <h1>{{ page.title }}</h1>

    <h2>Thank you.</h2>
    <p>We have received your submission!</p>

  </body>
</html>

Step 5: Migrate

Finally, you’ll need to make and run migrations. Then, begin editing your new page in the Wagtail admin.

$ python manage.py makemigrations
$ python manage.py migrate