Form Designer - a simple form designer for Django¶
This form designer does not try to offer every last configuration possibility of Django’s forms, just through the administration interface instead of directly in Python code. Instead, it strives to be a tool which everyone can use right away, without the need for long explanations.
It offers a small set of predefined input fields such as:
Text fields (One line and multi line widgets possible)
E-mail address fields
Checkboxes
Dropdowns
Radio Buttons
Multiple selection checkboxes
Hidden input fields
Every field can optionally be declared mandatory, default values and help texts are available too. That’s it.
The default actions (which can be enabled individually) are to send the form data to a list of freely definable email addresses and to store the data in the database so that it can be exported later. An XLSX export of saved submissions is provided too. It is possible to add your own actions as well.
Aside: If you need more options you may want to check out feincms3-forms as well.
Installing the form designer¶
Install the package using pip:
$ pip install form-designer
Setting up the form designer¶
Add
"form_designer"and"admin_ordering"toINSTALLED_APPS.Run
./manage.py migrate form_designerGo into Django’s admin panel and add one or more forms with the fields you require. Also select at least one action in the configuration options selectbox, most often you’d want to select both the “E-mail” and the “Save form submission” option and fill in one ore more email addresses.
Choices for selection fields are split on , or alternatively |;
use pipe character when individual choices need to contain commas.
If you’re using the form designer with FeinCMS, the content type can be
imported from form_designer.contents.FormContent. Otherwise, your
code should use the following methods (the code would probably reside in
a view):
# Somehow fetch the form_designer.models.Form instance:
instance = get_object_or_404()
# Build the form class:
form_class = instance.form_class()
# Standard form processing:
if request.method == "POST":
form = form_class(request.POST)
if form.is_valid():
# Do what you want, or run the configured processors:
result = instance.process(form, request)
# Maybe there's something useful in here:
pprint(result)
return HttpResponseRedirect("thanks/")
else:
form = form_class()
return render(request, "form.html", {"form": form})
Adding custom actions¶
Custom actions can be added by appending them to
Form.CONFIG_OPTIONS:
from form_designer.models import Form
def do_thing(model_instance, form_instance, request, config, **kwargs):
pass
def do_validate(form_instance, data):
pass
Form.CONFIG_OPTIONS.append(
(
"do_thing",
{
"title": _("Do a thing"),
"form_fields": lambda form: [
("optional_form_field", forms.CharField(
label=_("Optional form field"),
required=False,
# validators...
# help_text...
)),
],
"process": do_thing,
"validate": do_validate,
},
)
)
The interesting part is the do_thing callable. It currently receives
four arguments, however you should also accept **kwargs to support
additional arguments added in the future:
model_instance: TheFormmodel instanceform_instance: The dynamically generated form instancerequest: The current HTTP requestconfig: The config options (keys and values defined throughform_fields; for example theemailaction defines anemailchar field, and accesses its value usingconfig["email"]).
ReCaptcha¶
To enable ReCaptcha install
django-recaptcha and add
django_recaptcha to your INSTALLED_APPS. This will automatically add a ReCaptcha
field to the form designer. For everything else read through the
django-recaptcha readme.
reCAPTCHA keys only work with either v2 or v3. Both field types are offered by
default; set FORM_DESIGNER_RECAPTCHA_VERSION to "v2" or "v3" to only
offer the one matching your keys:
FORM_DESIGNER_RECAPTCHA_VERSION = "v3"
Existing form fields using the other field type will crash, so change them first.
Name the field captcha to omit it from submission data, see
Omitted fields.
Simple Captcha¶
To enable django-simple-captcha install
django-simple-captcha and add
captcha to your INSTALLED_APPS as described in the documentation for
django-simple-captcha. This will automatically add a “Simple Captcha”
field to the form designer field types.
Name the field captcha to omit it from submission data, see
Omitted fields.
Omitted fields¶
Fields matching any of the glob patterns in FORM_DESIGNER_OMIT_FIELDS are
left out of the submission data and the XLSX export. The default omits
captcha and fields starting with _:
FORM_DESIGNER_OMIT_FIELDS = ("captcha", "_*")
Set it to () to include all fields, as in previous versions.
Email action¶
The email action sends submissions to a comma separated list of addresses.
The subject defaults to the form title, but can be set in the action config.
%(fieldname)s placeholders are replaced with submitted values
(unknown fields become empty), other % signs are kept as-is.
For testing and staging setups, set FORCE_MAIL_RECIPIENT in settings.py
to an address to send all submission mails there instead.
FORCE_MAIL_RECIPIENT = "test@nowhere.example"
Override field types¶
Define FORM_DESIGNER_FIELD_TYPES in your settings file like:
FORM_DESIGNER_FIELD_TYPES = "your_project.form_designer_config.FIELD_TYPES"
In your_project.form_designer_config.py something like:
from django import forms
from django.utils.translation import gettext_lazy as _
FIELD_TYPES = [
{"type": "text", "verbose_name": _("text"), "field": forms.CharField},
{"type": "email", "verbose_name": _("email address"), "field": forms.EmailField},
]
Visit these sites for more information¶
form_designer: https://github.com/feincms/form_designer
feincms3: https://feincms3.readthedocs.io/
Change log¶
Next version¶
0.28.1 (2026-10-28)¶
Security fix: Required xlsxdocument 1.4 so that submitted values starting with
=are exported as text, not as formulas.
0.28 (2026-10-27)¶
Dropped Python 3.9 and Django 5.1. Django 3.2 and 4.2 are still tested.
Added Python 3.14, 3.15 and Django 6.0, 6.1.
Added out of the box support for
mosparo-django. Note that you need both to add the form field AND activate the validation action in the admin, just adding the field doesn’t actually do anything.Added Catalan and Spanish translations.
XLSX exports are generated with a datestamp in the file name.
Behaviour change: Omitted noisy fields from
FormSubmission.formatted_data(notification emails, admin) and the XLSX export. UseFORM_DESIGNER_OMIT_FIELDSsetting if you need to change it from("captcha", "_*"). Set it to()to restore the previous behaviour.Field types with
"no-data": Trueare left out of the formatted data and the XLSX export.The email action accepts a configurable subject with
%(fieldname)splaceholders for submitted values.Added the
FORCE_MAIL_RECIPIENTsetting to redirect all submission mail.(Mis)use the
choicesfield for text fields as placeholder.Added a
numberfield type.Added a
statictextfield type, it renders the title as raw HTML in adiv.choicesare used for CSS classes,default_valuefor other wrapper tag.Fixed submitted values of fields whose name changes when slugified (e.g.
_private) showing up as removed fields.Added the
FORM_DESIGNER_RECAPTCHA_VERSIONsetting to only offer the reCAPTCHA field type matching the configured keys.
0.27¶
Avoided crashing when trying to prettify the data.
Limited the email field choices to email fields only when using the send emails to authors option.
Stopped slugifying choice values so that we properly support non-latin characters.
Added Python 3.13.
Added support for
django-simple-captcha.
0.26¶
Changed form submissions to convert choice values back to the unslugified variant.
Changed
FormSubmission.formatted_datato automatically comma-separate list values instead of returning the list representation.
0.25¶
Fixed an incorrect input type of the
author_email_fieldfield.Added Django 5.1, dropped Django 4.1.
0.24¶
Raised the minimum django-recaptcha version to 4.0.
0.23¶
Added Django 5.0, Python 3.12.
Forwarded
**kwargsto theprocesshandlers.
0.22¶
Changed the default ordering of forms in the admin change list to alphabetical.
Modernized the package, switched to ruff and hatchling.
Dropped Python 3.8 support.
Added Django 4.2 to the CI.
0.21¶
Started using real JSON fields for the configuration and submitted data instead of stringifying the JSON explicitly. A side effect of this change may be that the JSON object key order isn’t preserved but it was a bad idea to rely on this anyway. Please check carefully if the database migration preserves past form submissions and form configurations. It worked when I tested it but you probably want to check twice.
Added a date field type to the list to the default fields.
Rewrote the submissions export to export all available data (also from removed fields) in order.
Replaced
FormSubmission.sorted_dataetc. with a centralized facility on the form model which properly handles missing fields, titles, old names etc. and which doesn’t produce stupidly expensive 1+N queries when processing a list of submissions.
0.20¶
Added Django 4.1 to the CI matrix.
Extended the
emailaction with the optional ability to add the author of the sibmission to the Cc: of the sent email.Changed mails to prefer the titles of fields instead of their name.
Included field titles in the XLSX export when possible.
Removed our usage of
collections.OrderedDict, not necessary anymore.Changed submissions to save the full URL, not just the path. Cleaned up the form submissions model while at it.
0.19¶
Specified the default
AutoFieldfor the form designer to avoid migrations.Added a reCAPTCHA v3 field to the default field types.
Added Django 4.0a1 to the CI matrix.
Disallowed non-sluggy contents on the name field. The old field name is preserved for exporting form submissions etc. so the change should be backwards compatible but don’t rely on it too much.
Raised the minimum requirements to Python 3.8, Django 3.2.
0.18¶
Stopped hardcoding the admin base form class. Overriding the
formattribute on theModelAdminclass for forms now works as expected.Raised the minimum requirements to Python 3.6, Django 2.2.
Switched to a declarative setup.
Switched to GitHub actions.
0.17¶
Fixed a typo and changed e-mail to email. Removed the help text from config options which isn’t correct anymore.
Added code to avoid crashing the admin interface if form submissions cannot be deserialized and/or rendered.
Worked around changes in the initialization of change forms in the administration interface.
Added Django 3.1 and Python 3.8 to the Travis CI matrix.
Reordered the configuration options in the administration panel; moved activation of processors closer to their configuration.
Fixed a recurring bug where migrations would be created when changing field types.
Made the ordering field a bit wider so that the value is still visible even on small screens.
0.16¶
Fixed the config fieldsets code to work when using Django 3.0.
Added an unit test and docs for the
"validate"config option.Passed additional data to the
"validate"config option. Not accepting arbitrary keyword arguments is now deprecated.Changed the forms administration interface to show all form options from the beginning. This requires a change to the
form_fieldsconfiguration option: Instead of a list it has to be a callable accepting the form instance now.Added support for specifying a description for config options.
0.15¶
Restructured the
FIELD_TYPESdata structure to use a dictionary instead of a tuple to allow for future expansion.Dropped compatibility with Python 2.
0.14¶
Fixed the package to include static files and templates.
Raised the minimum django-recaptcha version to 2.0.
0.13¶
Added tox configuration for easily running linters and tests locally.
Reformatted the project using black
Made django-admin-ordering a dependency.
Replaced the CSV export with an XLSX export based on xlsxdocument. It just is a better format.
Improved the test coverage a bit and fixed an edge case where form field model validation would crash.
0.12¶
Changed
FormSubmission.sorted_data(and by extension alsoformatted_data(_html)and the CSV export) to use field names instead of field titles as keys. Field names are guaranteed to be unique, titles are not.
0.11¶
Moved form processing into
FormContent.process; this removes the need to pass the request toFormContent.render.renderis not expected to require a request parameter in FeinCMS content types.Added Django 1.11 to the test matrix. No changes were necessary for 1.11 support.
Added documentation for adding new actions.
Fixed a bug where activated config options were lost because of differences between
list()anddict_keys()objects.
0.10¶
Make the fields tabular inline a bit less wide.
Added czech translations.
Fixed the usage of
render_to_stringto actually work correctly with Django 1.10.
0.9¶
The form admin uses django-admin-ordering for fields if available.
Now supports sending notification mails to multiple addresses.
0.8¶
Moved the
FormContentto the new moduleform_designer.contentsto make the form designer usable without FeinCMS.Replaced
SortedDictwithcollections.OrderedDict.Fixed an XSS vulnerability in the administration.
Dropped compatibility with old Django versions (<1.8).
Replaced the horrible form submission serialization of
repr()andeval()with JSON.General packaging and code cleanups.
0.7¶
Avoid the deprecated
mimetypeargument to HTTP responses.Fixed infinite recursion in
jsonize.Made field type choices lazy so that changing available field types is easier resp. actually possible.
0.6¶
Improve code coverage, less warnings, less complaining.
0.5¶
Added an app config for a nicer app name.
0.4¶
Built-in support for Django 1.7-style migrations. If you’re using South, update to South 1.0 or better.
0.3¶
Support for Python 3.3, 2.7 and 2.6.
Support for overridding field types with
FORM_DESIGNER_FIELD_TYPES.