Added basic dynamic form config #18
|
@ -42,6 +42,7 @@ INSTALLED_APPS = [
|
|||
"mailings",
|
||||
"blog",
|
||||
"search",
|
||||
"dynamic_forms",
|
||||
"setup",
|
||||
"wagtail_localize",
|
||||
"wagtail_localize.locales",
|
||||
|
@ -57,6 +58,7 @@ INSTALLED_APPS = [
|
|||
"wagtail.search",
|
||||
"wagtail.admin",
|
||||
'wagtail.contrib.modeladmin',
|
||||
"wagtail.contrib.settings",
|
||||
"wagtail",
|
||||
"wagtailmenus",
|
||||
"modelcluster",
|
||||
|
@ -71,7 +73,7 @@ INSTALLED_APPS = [
|
|||
"phonenumber_field",
|
||||
"django_celery_results",
|
||||
"django_celery_beat",
|
||||
"easy_thumbnails",
|
||||
"easy_thumbnails"
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
<div class="col-md-11 mt-5 ml-2"></div>
|
||||
<div class="col-md-1 mt-5 ml-2">
|
||||
|
||||
{% if page %}
|
||||
{% if page and page.get_translations.live %}
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary btn-lg" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<img src="{% static 'images/icons/globe.svg' %}">
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
from wagtail.contrib.modeladmin.options import (
|
||||
ModelAdmin,
|
||||
ModelAdminGroup,
|
||||
modeladmin_register
|
||||
)
|
||||
|
||||
from dynamic_forms import models
|
||||
|
||||
class CustomEmailFormAdmin(ModelAdmin):
|
||||
model = models.CustomEmailForm
|
||||
menu_label = "Email Forms"
|
||||
menu_icon = 'mail'
|
||||
menu_order = 100
|
||||
add_to_settings_menu = False
|
||||
exclude_from_explorer = False
|
||||
list_display = (
|
||||
"title",
|
||||
"slug",
|
||||
)
|
||||
search_fields = (
|
||||
"title",
|
||||
"slug",
|
||||
)
|
||||
list_filter = (
|
||||
"title",
|
||||
"slug",
|
||||
)
|
||||
form_fields = (
|
||||
"slug",
|
||||
"intro",
|
||||
"thank_you_text",
|
||||
"from_address",
|
||||
"to_address",
|
||||
"subject",
|
||||
)
|
||||
|
||||
class CustomFormGroup(ModelAdminGroup):
|
||||
menu_label = "Custom Forms"
|
||||
menu_icon = 'tasks'
|
||||
menu_order = 100
|
||||
items = (
|
||||
CustomEmailFormAdmin,
|
||||
)
|
||||
|
||||
modeladmin_register(CustomFormGroup)
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DynamicFormsConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "dynamic_forms"
|
|
@ -0,0 +1,58 @@
|
|||
from django import forms
|
||||
|
||||
|
||||
class MultipleFileInput(forms.ClearableFileInput):
|
||||
allow_multiple_selected = True
|
||||
|
||||
|
||||
class MultipleFileField(forms.FileField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault("widget", MultipleFileInput())
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, data, initial=None):
|
||||
single_file_clean = super().clean
|
||||
if isinstance(data, (list, tuple)):
|
||||
result = [single_file_clean(d, initial) for d in data]
|
||||
else:
|
||||
result = single_file_clean(data, initial)
|
||||
return result
|
||||
|
||||
|
||||
class DynamicForm(forms.Form):
|
||||
|
||||
FIELD_TYPE_MAPPING = {
|
||||
"singleline": forms.CharField(max_length=50, widget=forms.TextInput(attrs={"class": "form-control"})),
|
||||
"multiline": forms.CharField(max_length=255, widget=forms.Textarea(attrs={"class": "form-control"})),
|
||||
"email": forms.EmailField(max_length=255, widget=forms.EmailInput(attrs={"class": "form-control"})),
|
||||
"number": forms.IntegerField(widget=forms.NumberInput(attrs={"class": "form-control"})),
|
||||
"url": forms.URLField(max_length=255, widget=forms.URLInput(attrs={"class": "form-control"})),
|
||||
"checkbox": forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={"class": "form-control"})),
|
||||
"checkboxes": forms.MultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple(attrs={"class": "form-control"})),
|
||||
"dropdown": forms.ChoiceField(widget=forms.Select(attrs={"class": "form-control"})),
|
||||
"multiselect": forms.MultipleChoiceField(widget=forms.SelectMultiple(attrs={"class": "form-control"})),
|
||||
"radio": forms.ChoiceField(widget=forms.RadioSelect(attrs={"class": "form-control"})),
|
||||
"date": forms.DateField(widget=forms.DateInput(attrs={"class": "form-control"})),
|
||||
"datetime": forms.DateTimeField(widget=forms.DateTimeInput(attrs={"class": "form-control"})),
|
||||
"hidden": forms.CharField(widget=forms.HiddenInput()),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
kwargs.pop("page", "")
|
||||
kwargs.pop("user", "")
|
||||
field_list = kwargs.pop("field_list")
|
||||
file_uploads = kwargs.pop("file_uploads", False)
|
||||
super().__init__(*args, **kwargs)
|
||||
for field in field_list:
|
||||
f = self.FIELD_TYPE_MAPPING[field.field_type]
|
||||
f.label = field.label
|
||||
if hasattr(f, "choices"):
|
||||
f.choices = [(v, v) for v in field.choices.split(",")]
|
||||
f.required = field.required
|
||||
self.fields[field.clean_name] = f
|
||||
if file_uploads:
|
||||
self.fields["attachments"] = MultipleFileField(
|
||||
required=True, widget=MultipleFileInput(
|
||||
attrs={"class": "form-control"}
|
||||
)
|
||||
)
|
|
@ -0,0 +1,132 @@
|
|||
# Generated by Django 4.1.11 on 2023-10-12 17:23
|
||||
|
||||
import django.core.serializers.json
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import modelcluster.fields
|
||||
import wagtail.contrib.forms.models
|
||||
import wagtail.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("wagtailcore", "0083_workflowcontenttype"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="CustomEmailForm",
|
||||
fields=[
|
||||
(
|
||||
"page_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="wagtailcore.page",
|
||||
),
|
||||
),
|
||||
("intro", wagtail.fields.RichTextField(blank=True)),
|
||||
("thank_you_text", wagtail.fields.RichTextField(blank=True)),
|
||||
("allow_attachments", models.BooleanField(default=False)),
|
||||
("from_address", models.EmailField(blank=True, help_text="Sender email address", max_length=254)),
|
||||
("to_address", models.CharField(help_text="Comma separated list of recipients", max_length=255)),
|
||||
("subject", models.CharField(help_text="Subject of the email with data", max_length=255)),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=(wagtail.contrib.forms.models.FormMixin, "wagtailcore.page"),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="EmailFormSubmission",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("form_data", models.JSONField(encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||
("submit_time", models.DateTimeField(auto_now_add=True, verbose_name="submit time")),
|
||||
("page", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="wagtailcore.page")),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "form submission",
|
||||
"verbose_name_plural": "form submissions",
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="EmailFormField",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("sort_order", models.IntegerField(blank=True, editable=False, null=True)),
|
||||
(
|
||||
"clean_name",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
default="",
|
||||
help_text="Safe name of the form field, the label converted to ascii_snake_case",
|
||||
max_length=255,
|
||||
verbose_name="name",
|
||||
),
|
||||
),
|
||||
(
|
||||
"label",
|
||||
models.CharField(help_text="The label of the form field", max_length=255, verbose_name="label"),
|
||||
),
|
||||
(
|
||||
"field_type",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("singleline", "Single line text"),
|
||||
("multiline", "Multi-line text"),
|
||||
("email", "Email"),
|
||||
("number", "Number"),
|
||||
("url", "URL"),
|
||||
("checkbox", "Checkbox"),
|
||||
("checkboxes", "Checkboxes"),
|
||||
("dropdown", "Drop down"),
|
||||
("multiselect", "Multiple select"),
|
||||
("radio", "Radio buttons"),
|
||||
("date", "Date"),
|
||||
("datetime", "Date/time"),
|
||||
("hidden", "Hidden field"),
|
||||
],
|
||||
max_length=16,
|
||||
verbose_name="field type",
|
||||
),
|
||||
),
|
||||
("required", models.BooleanField(default=True, verbose_name="required")),
|
||||
(
|
||||
"choices",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text="Comma or new line separated list of choices. Only applicable in checkboxes, radio and dropdown.",
|
||||
verbose_name="choices",
|
||||
),
|
||||
),
|
||||
(
|
||||
"default_value",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text="Default value. Comma or new line separated values supported for checkboxes.",
|
||||
verbose_name="default value",
|
||||
),
|
||||
),
|
||||
("help_text", models.CharField(blank=True, max_length=255, verbose_name="help text")),
|
||||
(
|
||||
"form",
|
||||
modelcluster.fields.ParentalKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="form_fields",
|
||||
to="dynamic_forms.customemailform",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ["sort_order"],
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,123 @@
|
|||
import datetime
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils.formats import date_format
|
||||
|
||||
from modelcluster.fields import ParentalKey
|
||||
from wagtail.admin.panels import (
|
||||
FieldPanel, FieldRowPanel,
|
||||
InlinePanel, MultiFieldPanel
|
||||
)
|
||||
from wagtail.fields import RichTextField
|
||||
from wagtail.contrib.forms.models import (
|
||||
AbstractFormField,
|
||||
FormMixin,
|
||||
Page,
|
||||
AbstractFormSubmission
|
||||
)
|
||||
|
||||
from mailings.models import (
|
||||
OutgoingEmail,
|
||||
Attachment
|
||||
)
|
||||
from dynamic_forms.forms import DynamicForm
|
||||
|
||||
|
||||
class Form(FormMixin, Page):
|
||||
intro = RichTextField(blank=True)
|
||||
thank_you_text = RichTextField(blank=True)
|
||||
allow_attachments = models.BooleanField(default=False)
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
FieldPanel('intro'),
|
||||
InlinePanel('form_fields', label="Form fields"),
|
||||
FieldPanel('thank_you_text'),
|
||||
MultiFieldPanel([
|
||||
FieldRowPanel([
|
||||
FieldPanel('from_address', classname="col6"),
|
||||
FieldPanel('to_address', classname="col6"),
|
||||
]),
|
||||
FieldPanel('subject'),
|
||||
], "Email"),
|
||||
FieldPanel("allow_attachments")
|
||||
]
|
||||
|
||||
def get_form_class(self):
|
||||
return DynamicForm
|
||||
|
||||
def get_form(self, *args, **kwargs):
|
||||
form_class = self.get_form_class()
|
||||
form_params = self.get_form_parameters()
|
||||
form_params.update(kwargs)
|
||||
form_params["field_list"] = self.get_form_fields()
|
||||
form_params["file_uploads"] = self.allow_attachments
|
||||
return form_class(*args, **form_params)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class EmailFormSubmission(AbstractFormSubmission):
|
||||
|
||||
def send_mail(self, data):
|
||||
# modify this, get proper template
|
||||
to_addresses = data.pop("to_address").split(",")
|
||||
attachments = [
|
||||
Attachment(
|
||||
file.name, file.file.read(), file.content_type
|
||||
)
|
||||
for file in data.pop("attachments", [])
|
||||
]
|
||||
subject = data.get("subject")
|
||||
from_address = data.pop("from_address", settings.DEFAULT_FROM_EMAIL)
|
||||
for address in to_addresses:
|
||||
OutgoingEmail.objects.send(
|
||||
subject=subject,
|
||||
template_name="form_mail",
|
||||
recipient=address,
|
||||
sender=from_address,
|
||||
context=data,
|
||||
attachments=attachments
|
||||
)
|
||||
|
||||
|
||||
class CustomEmailForm(Form):
|
||||
from_address = models.EmailField(
|
||||
blank=True,
|
||||
help_text="Sender email address"
|
||||
)
|
||||
to_address = models.CharField(
|
||||
max_length=255,
|
||||
help_text="Comma separated list of recipients"
|
||||
)
|
||||
subject = models.CharField(
|
||||
max_length=255,
|
||||
help_text="Subject of the email with data"
|
||||
)
|
||||
|
||||
template = "forms/email_form_page.html"
|
||||
|
||||
def get_submission_class(self):
|
||||
return EmailFormSubmission
|
||||
|
||||
def process_form_submission(self, form):
|
||||
attachments = form.cleaned_data.pop("attachments", [])
|
||||
submission = self.get_submission_class().objects.create(
|
||||
form_data=form.cleaned_data,
|
||||
page=self,
|
||||
)
|
||||
mail_data = form.cleaned_data.copy()
|
||||
mail_data.update({
|
||||
"from_address": self.from_address,
|
||||
"to_address": self.to_address,
|
||||
"subject": self.subject,
|
||||
"attachments": attachments
|
||||
})
|
||||
submission.send_mail(data=mail_data)
|
||||
return submission
|
||||
|
||||
class EmailFormField(AbstractFormField):
|
||||
form = ParentalKey(
|
||||
"CustomEmailForm", related_name="form_fields", on_delete=models.CASCADE
|
||||
)
|
|
@ -0,0 +1,32 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% load wagtailcore_tags wagtailimages_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ page.title }}</h1>
|
||||
<p class="meta">{{ page.date }}</p>
|
||||
{% if form %}
|
||||
<div>{{ page.intro|richtext }}</div>
|
||||
<form enctype="multipart/form-data" action="{% pageurl page %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<div class="form-group mt-3">
|
||||
<label for="{{field.id}}" class="form-label">
|
||||
{% trans field.label %}
|
||||
</label>
|
||||
{{field}}
|
||||
<small id="emailHelp" class="form-text text-muted">
|
||||
{% trans field.help_text %}
|
||||
</small>
|
||||
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="text-end mt-3">
|
||||
<input class="btn btn-lg btn-success" type="submit" value='{% trans "Submit" %}'>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<div>You can fill in the from only one time.</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,25 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load wagtailcore_tags wagtailimages_tags %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<div class="card col-9 bg-white shadow-md p-5">
|
||||
<div class="mb-4 text-center">
|
||||
<svg class="text-success" width="75" height="75"
|
||||
fill="currentColor" class="bi bi-check-circle" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
||||
<path
|
||||
d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div>
|
||||
{{page.thank_you_text|richtext}}
|
||||
</div>
|
||||
<a class="btn btn-outline-primary mt-3" href="/">
|
||||
{% trans "Homepage" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,277 @@
|
|||
from wagtail.tests.utils import WagtailPageTests
|
||||
from django import forms
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
from dynamic_forms.models import (
|
||||
CustomEmailForm,
|
||||
EmailFormField
|
||||
)
|
||||
from dynamic_forms.forms import DynamicForm
|
||||
|
||||
|
||||
class CustomEmailFormTestCase(WagtailPageTests):
|
||||
def setUp(self):
|
||||
self.form = CustomEmailForm.objects.create(
|
||||
slug="test", title="Test Form", path="test",
|
||||
depth=0, numchild=0, live=True, has_unpublished_changes=False,
|
||||
from_address="comfy-test@egalitare.pl",
|
||||
to_address="comfy-dest@egalitare.pl",
|
||||
subject="Test Form", allow_attachments=False,
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Name",
|
||||
field_type="singleline",
|
||||
required=True,
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Message",
|
||||
field_type="multiline",
|
||||
required=True,
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Email",
|
||||
field_type="email",
|
||||
required=True,
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Number",
|
||||
field_type="number",
|
||||
required=True,
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="URL",
|
||||
field_type="url",
|
||||
required=True,
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Checkbox",
|
||||
field_type="checkbox",
|
||||
required=True,
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Checkboxes",
|
||||
field_type="checkboxes",
|
||||
required=True,
|
||||
choices="a,b,c",
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Dropdown",
|
||||
field_type="dropdown",
|
||||
required=True,
|
||||
choices="a,b,c",
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="MultiSelect",
|
||||
field_type="multiselect",
|
||||
required=True,
|
||||
choices="a,b,c",
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Radio",
|
||||
field_type="radio",
|
||||
required=True,
|
||||
choices="a,b,c",
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Date",
|
||||
field_type="date",
|
||||
required=True,
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="DateTime",
|
||||
field_type="datetime",
|
||||
required=True,
|
||||
form=self.form
|
||||
)
|
||||
EmailFormField.objects.create(
|
||||
label="Hidden",
|
||||
field_type="hidden",
|
||||
required=True,
|
||||
form=self.form
|
||||
)
|
||||
|
||||
def test_generate_html_form_from_model(self):
|
||||
html_form = self.form.get_form()
|
||||
self.assertIsInstance(html_form, DynamicForm)
|
||||
self.assertEqual(len(html_form.fields), 13)
|
||||
self.assertEqual(html_form.fields["name"].label, "Name")
|
||||
self.assertEqual(html_form.fields["name"].required, True)
|
||||
self.assertEqual(html_form.fields["name"].widget.attrs["class"], "form-control")
|
||||
self.assertIsInstance(
|
||||
html_form.fields["name"],
|
||||
forms.CharField
|
||||
)
|
||||
self.assertIsInstance(
|
||||
html_form.fields["message"],
|
||||
forms.CharField
|
||||
)
|
||||
self.assertIsInstance(
|
||||
html_form.fields["email"].widget,
|
||||
forms.EmailInput
|
||||
)
|
||||
self.assertIsInstance(
|
||||
html_form.fields["number"].widget,
|
||||
forms.NumberInput
|
||||
)
|
||||
|
||||
def test_create_form_submission_success_without_files(self):
|
||||
form_data = {
|
||||
# generate data for this class self.form.get_form()
|
||||
"name": "Test",
|
||||
"message": "Test message",
|
||||
"email": "test@test.com",
|
||||
"number": 1,
|
||||
"url": "http://example.com",
|
||||
"checkbox": True,
|
||||
"checkboxes": ["a", "b"],
|
||||
"dropdown": "a",
|
||||
"multiselect": ["a", "b"],
|
||||
"radio": "a",
|
||||
"date": "2020-01-01",
|
||||
"datetime": "2020-01-01 00:00:00",
|
||||
"hidden": "hidden",
|
||||
}
|
||||
form = self.form.get_form(form_data)
|
||||
self.assertTrue(form.is_valid())
|
||||
# change field not to be required
|
||||
field = EmailFormField.objects.get(
|
||||
label="Name", form=self.form
|
||||
)
|
||||
field.required = False
|
||||
field.save()
|
||||
form = self.form.get_form(form_data)
|
||||
self.assertTrue(form.is_valid())
|
||||
# it should also work without this field
|
||||
form_data.pop("name")
|
||||
form = self.form.get_form(form_data)
|
||||
self.assertTrue(form.is_valid())
|
||||
|
||||
def test_create_form_submission_success_with_files(self):
|
||||
self.form.allow_attachments = True
|
||||
self.form.save()
|
||||
form_data = {
|
||||
# generate data for this class self.form.get_form()
|
||||
"name": "Test",
|
||||
"message": "Test message",
|
||||
"email": "test@test.com",
|
||||
"number": 1,
|
||||
"url": "http://example.com",
|
||||
"checkbox": True,
|
||||
"checkboxes": ["a", "b"],
|
||||
"dropdown": "a",
|
||||
"multiselect": ["a", "b"],
|
||||
"radio": "a",
|
||||
"date": "2020-01-01",
|
||||
"datetime": "2020-01-01 00:00:00",
|
||||
"hidden": "hidden"
|
||||
}
|
||||
files = {
|
||||
"attachments": [SimpleUploadedFile("test.txt", b"test")],
|
||||
}
|
||||
form = self.form.get_form(form_data, files=files)
|
||||
self.assertTrue(form.is_valid())
|
||||
|
||||
def test_create_form_submission_failure_without_files_missing_data(self):
|
||||
form_data = {
|
||||
# generate data for this class self.form.get_form()
|
||||
"message": "Test message",
|
||||
"email": "test@test.com",
|
||||
"number": 1,
|
||||
"url": "http://example.com",
|
||||
"checkbox": True,
|
||||
"checkboxes": ["a", "b"],
|
||||
"dropdown": "a",
|
||||
"multiselect": ["a", "b"],
|
||||
"radio": "a",
|
||||
"date": "2020-01-01",
|
||||
"datetime": "2020-01-01 00:00:00",
|
||||
"hidden": "hidden",
|
||||
}
|
||||
form = self.form.get_form(form_data)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertEqual(len(form.errors), 1)
|
||||
self.assertEqual(form.errors["name"], ['This field is required.'])
|
||||
|
||||
form_data.pop("url")
|
||||
form = self.form.get_form(form_data)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertEqual(len(form.errors), 2)
|
||||
self.assertEqual(form.errors["name"], ['This field is required.'])
|
||||
self.assertEqual(form.errors["url"], ['This field is required.'])
|
||||
# make Field not required
|
||||
field = EmailFormField.objects.get(
|
||||
label="Hidden", form=self.form
|
||||
)
|
||||
field.required = False
|
||||
field.save()
|
||||
# it should also work without this field
|
||||
form_data.pop("hidden")
|
||||
form = self.form.get_form(form_data)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertEqual(len(form.errors), 2)
|
||||
self.assertEqual(form.errors["name"], ['This field is required.'])
|
||||
self.assertEqual(form.errors["url"], ['This field is required.'])
|
||||
|
||||
def test_create_form_submission_failure_with_files_missing_data(self):
|
||||
self.form.allow_attachments = True
|
||||
self.form.save()
|
||||
form_data = {
|
||||
# generate data for this class self.form.get_form()
|
||||
"message": "Test message",
|
||||
"email": "test@test.com",
|
||||
"number": 1,
|
||||
"url": "http://example.com",
|
||||
"checkbox": True,
|
||||
"checkboxes": ["a", "b"],
|
||||
"dropdown": "a",
|
||||
"multiselect": ["a", "b"],
|
||||
"radio": "a",
|
||||
"date": "2020-01-01",
|
||||
"datetime": "2020-01-01 00:00:00",
|
||||
"hidden": "hidden",
|
||||
}
|
||||
files = {
|
||||
"attachments": [SimpleUploadedFile("test.txt", b"test")],
|
||||
}
|
||||
form = self.form.get_form(form_data, files=files)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertEqual(len(form.errors), 1)
|
||||
self.assertEqual(form.errors["name"], ['This field is required.'])
|
||||
|
||||
form_data.pop("url")
|
||||
form = self.form.get_form(form_data, files=files)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertEqual(len(form.errors), 2)
|
||||
self.assertEqual(form.errors["name"], ['This field is required.'])
|
||||
self.assertEqual(form.errors["url"], ['This field is required.'])
|
||||
# make Field not required
|
||||
field = EmailFormField.objects.get(
|
||||
label="Hidden", form=self.form
|
||||
)
|
||||
field.required = False
|
||||
field.save()
|
||||
# it should also work without this field
|
||||
form_data.pop("hidden")
|
||||
form = self.form.get_form(form_data, files=files)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertEqual(len(form.errors), 2)
|
||||
self.assertEqual(form.errors["name"], ['This field is required.'])
|
||||
self.assertEqual(form.errors["url"], ['This field is required.'])
|
||||
# Now try without files
|
||||
form = self.form.get_form(form_data, files={})
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertEqual(len(form.errors), 3)
|
||||
self.assertEqual(form.errors["name"], ['This field is required.'])
|
||||
self.assertEqual(form.errors["url"], ['This field is required.'])
|
||||
self.assertEqual(form.errors["attachments"], ['This field is required.'])
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
|
@ -4,7 +4,7 @@
|
|||
<div class="d-flex justify-content-center align-items-center">
|
||||
<div class="card col-9 bg-white shadow-md p-5">
|
||||
<div class="mb-4 text-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="text-success" width="75" height="75"
|
||||
<svg class="text-success" width="75" height="75"
|
||||
fill="currentColor" class="bi bi-check-circle" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
||||
<path
|
||||
|
|
Ładowanie…
Reference in New Issue