I haven't used Django in years, and am not actively maintaining this package. If anyone is interested in maintaining this package, either on this repo or as a fork, please let me know in a new issue!
dynamic-django-forms is a simple, reusable app that allows you to build (and respond to) dynamic forms, i.e. forms that have variable numbers and types of fields. A few examples of uses include:
Install via pip:
pip install dynamic-django-forms
Add to settings.INSTALLED_APPS:
INSTALLED_APPS = {
"...",
"dynamic_forms",
"..."
}
The main functionality of dynamic-django-forms
is contained within 2 model fields:
dynamic_forms.models.FormField
allows you to build and edit forms via a convenient UI, and stores them in JSON-Schema form. It is easy to use both through the admin panel and in any custom template webpage.
Example Setup:
from dynamic_forms.models import FormField
class Survey:
# Other Fields Here
form = FormField()
Please note that JSON data can saved into the model field as a python dict
or a valid JSON string. When the value is retrieved from the database, it will be provided as a list
containing dict
s for each dynamic form field.
dynamic_forms.models.ResponseField
allows you to render, and collect responses to, forms built with the Form Builder. It is currently only supported through custom views. All form responses are stored as a dict where the key is the question label, and the value is the user's input.
Example Setup:
Model Config:
from django.db import models
from dynamic_forms.models import ResponseField
from otherapp.models import Survey
class SurveyResponse:
# Other Fields Here
survey = models.ForeignKey(Survey, on_delete=models.CASCADE) # Optional
response = ResponseField()
Please note that including a ForeignKey link from the model containing responses to the model containing forms isnt technically required; however, it is highly recommended and will make linking the two much easier
You must provide a valid JSON Schema to ResponseField's associated FormField at runtime. This is best done in the view where the dynamic form will be used. Generally speaking, this means you should:
An example of how to do this can be found in the DynamicFormMixin explained in the next section:
from django.views.generic.edit import FormMixin
class DynamicFormMixin(FormMixin):
form_field = "form"
form_pk_url_kwarg = "pk"
response_form_fk_field = None
response_field = "response"
def _get_object_containing_form(self, pk):
return self.form_model.objects.get(pk=pk)
def get_form(self, *args, **kwargs):
form = super().get_form(*args, **kwargs)
# Get instance of model containing form used for this response. Save this object as an instance variable for use in form_valid method
form_instance_pk = self.kwargs[self.form_pk_url_kwarg]
self.form_instance = self._get_object_containing_form(form_instance_pk)
# Get json form configuration from form-containing object
json_data = getattr(self.form_instance, self.form_field)
# Add fields in JSON to dynamic form rendering field.
form.fields[self.response_field].add_fields(json_data)
return form
def form_valid(self, form):
action = form.save(commit=False)
action.survey = self.form_instance
action.save()
return super().form_valid(form)
The process of configuring ResponseFields with forms is somewhat complicated, so a shortcut is provided.
dynamic_forms.views.DynamicFormMixin
can be added to Class Based Views extending from django.views.generic.edit.CreateView
and django.views.generic.edit.UpdateView
, and will automatically complete configure the dynamic form provided that:
form_model
: The relevant model (not instance) containing a FormField with the wanted dynamic form configuration. I.e. which model is the survey defined in? Default: None
form_field
: The attribute of form_model
that contains the FormField. I.e. Which field in the Survey model contains the form? Default: form
form_pk_url_kwarg
The URL Keyword Argument containing the primary key of the instance of form_model
that contains the dynamic form we want? I.e. Which survey are we responding to? Default: pk
response_form_fk_field
The attribute of the model which contains the ResponseField that links via ForeignKey to the model containing the FormField. I.e. Which attribute of the Survey Response model links to the Survey model? Default: None
response_field
The attribute of the Response model which contains the ResponseField. I.e. which attribute of the Survey Response model contains the actual responses? Default: response
Example:
class RespondView(DynamicFormMixin, CreateView):
model = SurveyResponse
fields = ['response']
template_name = "example/respond.html"
form_model = Survey
form_field = "form"
form_pk_url_kwarg = "survey_id"
response_form_fk_field = "survey"
response_field = "response"
def get_success_url(self):
return reverse('survey_detail', kwargs={"survey_id": self.form_instance.pk})
If you are using Django Crispy Forms to make your forms look awesome, set use the following setting:
USE_CRISPY = True
(false by default)
Please note that you are responsible for importing any CSS/JS libraries needed by your chosen crispy template pack into the templates where (e.x. bootstrap, uni-form, foundation).
dynamic-django-forms
currently supports the following field types:
Description | JSON |
---|---|
Checkbox Group | checkbox-group |
Date Field | date |
Hidden Input | hidden |
Number | number |
Radio Group | radio-group |
Select | select |
Text Field | text |
Email Field | |
Text Area | textarea |
The only major limitation of dynamic-django-forms
, which is also one of its major features, is the dissociation of dynamic form questions and responses.
Pros:
Cons:
On settings.py
you can use a variable to inject custom JS code before the form builder is initialized. Note that the options
variable. Note that when this custom JS runs, the following variables are available:
textArea
: This is a hidden textarea input used to submit the JSON form schema.options
: This is a FormBuilder Options object that you can override and modify to change how the form is displayed.DYNAMIC_FORMS_CUSTOM_JS = 'console.log(1)'
To run an example site, run cd example && docker-compose up
. If you do not use docker, you can manually install the requirements with pip install -r example/requirements.txt
and run the site with python example/manage.py runserver
.
Huge thanks to Kevin Chappell & Team for developing the awesome open source Form Builder UI!