Skip to content

Instantly share code, notes, and snippets.

@philgyford
Created March 15, 2019 16:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philgyford/c6e7064207aeddf12b2791a3f86335af to your computer and use it in GitHub Desktop.
Save philgyford/c6e7064207aeddf12b2791a3f86335af to your computer and use it in GitHub Desktop.
Example of how to add a form to every instance of a type of Wagtail CMS Page, without having to manually build the page for each new page in the Admin
{% extends "base.html" %}
{% block content %}
<p>(Article content here.)</p>
<p>Send us your thoughts:</p>
<form action="{% pageurl page %}" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Send">
</form>
{% endblock content %}
{% extends "base.html" %}
{% block content %}
<p>Thanks for your thoughts!</p>
<p><a href="{{ page.url }}">Return to “{{ page.title }}”</a></p>
{% endblock content %}
from django.core.exceptions import ValidationError
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
# All of this is so that we can automatically generate a form on every
# ArticlePage, and don't have to create it manually in the Wagtail
# Admin for each one.
#
# This inspired by https://stackoverflow.com/a/47303659/250962
# The fields that will be on the form on each ArticlePage.
#
# field_type can only be one of the following:
#
# 'singleline', 'multiline', 'email', 'number', 'url', 'checkbox',
# 'checkboxes', 'dropdown', 'multiselect', 'radio', 'date',
# 'datetime', 'hidden'
#
# Important: Label MUST be unique in each form
#
# Should be in the correct order for the form:
ARTICLE_FORM_FIELDS = [
{
'label': 'Your comments',
'field_type': 'multiline',
'required': True,
},
{
'label': 'Your name',
'field_type': 'singleline',
'required': False,
},
{
'label': 'Your email',
'field_type': 'email',
'required': False,
},
]
RESERVED_LABELS = [field['label'] for field in ARTICLE_FORM_FIELDS]
def validate_label(value):
"Ensure we have no duplicates."
if value in RESERVED_LABELS:
raise ValidationError("'%s' is reserved." % value)
class ArticleFormField(AbstractFormField):
"""For creating the fields on a ArticlePage's form.
These are all created in ArticleEmailForm.get_form_fields(), rather
than in the Wagtail admin.
"""
page = ParentalKey('ArticlePage',
on_delete=models.CASCADE,
related_name='form_fields')
# Redefine 'label' field so we can ensure there will be no conflicts with
# constant fields
label = models.CharField(
verbose_name='label',
max_length=255,
help_text='The label of the form field, cannot be one of the following: %s.'
% ', '.join(RESERVED_LABELS),
validators=[validate_label]
)
class ArticleEmailForm(AbstractEmailForm):
"""Extending AbstractEmailForm to add our customm extra fields, so that
they don't have to be defined afresh when creating every ArticlePage.
ArticlePage should inherit from this.
We're putting all the form-related stuff here so that it's kept separate
from the more general Page-related stuff.
"""
# Redefine the to_address, from_address and subject so that they don't have
# to be entered for every new ArticlePage.
subject = "Comment about an Article"
@property
def to_address(self):
"""Get the email's 'to' address. You may want to set this somewhere
more useful, in a setting.
"""
return "bob@example.org"
@property
def from_address(self):
"""Get the email's 'from' address. You may want to set this somewhere
more useful, in a setting.
"""
return "bob@example.org"
def get_form_fields(self):
"""Add to the parent's get_foro_fields() to add our hard-coded
field
"""
fields = list(super().get_form_fields())
# Append instances of ArticleFormField - not actually stored in the db
# `insert(0` will prepend these items, so the last added will be first
for field in reversed(ARTICLE_FORM_FIELDS):
fields.insert(0,
ArticleFormField(
label=field['label'],
field_type=field['field_type'],
required=field['required']
)
)
return fields
class Meta:
abstract = True
from wagtail.core.models import Page
from forms import ArticleEmailForm
class ArticlePage(ArticleEmailForm, Page):
template = "article.html"
landing_page_template = "article_landing.html"
# More here...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment