# Django Dynamic Forms
A Django reusable App for dynamically designing and generating forms.
### Features
- Visual form builder interface
- Dynamic field addition and configuration
- Drag-and-drop field creation/arrangement
- Support for complex field types with sub-fields
- Multi-Page forms
- Live form preview
- Works with Bootstrap 5
- Support for light/dark themes
- Additional field types created in other django apps, are auto-detected and can be used in the form builder
- Supports custom validation and error handling
- Ability to associate rules with fields, allowing for conditional logic to hide or show fields based on user input on
other fields
### Installation
1. Install the package using pip:
```bash
pip install django-dynforms
```
2. Add `dynforms` and it's requirements to your `INSTALLED_APPS` in `settings.py`:
```python
INSTALLED_APPS = [
...
'crispy_forms', # for form rendering
'crispy_bootstrap5', # for Bootstrap 5 support
'crisp_modals', # for modal dialogs
'dynforms',
...
]
```
3. Include the app URLs in your project’s `urls.py`:
```python
from django.urls import path, include
urlpatterns = [
...
path('dynforms/', include('dynforms.urls')),
...
]
```
4. Create the necessary database tables by running the migrations:
```bash
python manage.py migrate
```
## Usage
- Access the form builder at `/dynforms/`.
- Create a new form by clicking "[+ Add]" button the off-canvas Form Types sidebar or select a form from the list to
edit it.
- The search bar allows you to filter the form list by searching through the titles and descriptions.
- Use the sidebar field panel to add fields. Clicking a field on the panel will append a field of that type to the
current page. Drag the field to rearrange it within the form.
- To edit for field click on it and edit the settings using the Field settings form on the left-sidebar. Many of the field parameters
will auto-apply and update the form in real-time.
- Undo/Redo capabilities are not yet implemented. All changes are currently saved automatically as you edit the form.
- To delete a field, click the trash icon on the field settings form.
- Fields can be rearranged by dragging within the form. Move a field from one page to another by dragging it onto the
desired page tab.
- The form parameters like title, description, number of pages, and action buttons can be edited in, the form settings
sidebar.
- To select a new form for editing withing the designer, click the "Select Form" button in the top-right corner of
the form designer. This will open the off-screen sidebar for adding new forms or selecting an existing form.
## Field Rules
Fields can have rules associated with them, allowing for conditional logic to hide or show fields based on user input.
Multiple rules can be defined for a field, and they will be evaluated in the order they are defined.
To add rules to a field, click on the "Rules" button in the field settings sidebar. Here you can define conditions that
determine what action to take based on the value of other fields. Each rule consists of
- An **action** that determines what the rule does
- A source **field** that the rule will be evaluated against. The rule will be triggered when the value of this
field changes. Any field defined in the form can be used as a source field.
- A **subfield** to go along with the source field, if applicable. This is useful for fields that have subfields, such as
Address.
- An **operator** that defines how the source field's value is compared to the test value.
- A test **value** that the source field's value is compared against. This can be a static value or a reference to
another field's value.
Supported actions include:
- **Show If**: Makes the field visible when the condition is met.
- **Hide If**: Hides the field when the condition is met.
- **Required If**: Makes the field required when the condition is met.
Currently supported operators include:
- **Less Than**: The source field's value is less than the test value.
- **Greater Than**: The source field's value is greater than the test value.
- **Equal or Less Than**: The source field's value is less than or equal to the test value.
- **Equal or Greater Than**: The source field's value is greater than or equal to the test value.
- **Exactly**: The source field's value is exactly equal to the test value.
- **Exactly (Any Case)**: The source field's value is exactly equal to the test value, ignoring case.
- **Not Equal to**: The source field's value is not equal to the test value.
- **Contained in**: The source field's value is contained in the test value.
- **Contains**: The source field's value contains the test value.
- **Not Contained in**: The source field's value is not contained in the test value.
- **Starts With**: The source field's value starts with the test value.
- **Ends With**: The source field's value ends with the test value.
- **Starts With (Any Case)**: The source field's value starts with the test value, ignoring case.
- **Ends With (Any Case)**: The source field's value ends with the test value, ignoring case.
- **Is Empty**: The source field's value is empty.
- **Is Not Empty**: The source field's value is not empty.
## Abstract Model Classes
Inherit from the abstract class `BaseFormModel`, to create a model which records data from a dynamic form. This class
provides
the necessary fields and methods to handle form data. The `BaseFormModel` class provides the following fields:
- `created`: DateTime field that records when the form was created.
- `modified`: DateTime field that records when the form was last modified.
- `form_type`: A Foreign key to the form.
- `details`: A JSON field that stores the form data submitted by the user.
- `is_complete`: A Boolean field that indicates whether the form data is complete. Partial submissions are allowed to be
saved, and the form can be completed later.
Additionally, the `BaseFormModel` class provides the following methods:
- `get_field_value(self, field_name, default=None)`: Returns the value of a specific field in the form.
- `validate(self, data=None)`: Sets the value of a specific field in the form. The validation login uses either the
provided data or the current saved form data.
## Views
The app provides a set of views to handle form display and submission.
- `DynFormView`: The main view for displaying and submitting forms. It handles the form rendering, validation, and
submission logic. Derive from this class to create a plain FormView not bound to a specific model. The `template_name`
attribute should be set to a template that includes `dynforms/form.html` to render the form as desired. Submitted
form can then be handled in the `form_valid` method, which can be overridden to perform custom actions. The view expects
a single `pk` kwarg that corresponds to the primary key of the form type being displayed.
- `DynCreateView`: A view for creating new forms. This is derived from `django.generic.edit.CreateView` and provides a
a view form for creating a new entries of models that derive from `BaseFormModel`. The `form_class` attribute should
be set to a form class that inherits from `DynModelForm`. As for the `DynFormView`, the `template_name` attribute
should be set to a template that includes `dynforms/form.html` to render the form as desired. The view expects a
`pk` kwarg that corresponds to the primary key of the form type being created.
- `DynUpdateView`: A view for updating `DynModelForm` entries. This is derived from `django.generic.edit.UpdateView` and
provides a view for updating entries of models that derive from `BaseFormModel`. The `form_class` attribute should
be set to a form class that inherits from `DynModelForm`. As for the `DynFormView`, the `template_name` attribute
should be set to a template that includes `dynforms/form.html` to render the form as desired. The view expects a
`pk` kwarg that corresponds to the primary key of the `DynModelForm` entry being updated.
## Adding custom fields
To add custom fields to the form builder from another app, add a `dynfields.py` module to your App's top-level
directory and define a new field inheriting from `dynforms.fields.FieldType`. Implement the necessary methods to handle
rendering, validation, and type conversion.
### Example Custom Field
```python
from dynforms.fields import FieldType
from django.utils.translation import gettext_lazy as _
class CoolField(FieldType):
section = _('My Apps Fields') # This will group the field under a custom section in the field menu
name = _('Cool Field')
... # other field attributes
def render(self, context):
# Custom rendering logic
return f'<input type="text" name="{self.name}" value="{value or ""}">'
def clean(self, value, multi=False, validate=True):
"""
Custom cleaning logic for the field value.
If `validate` is True, it will perform validation on the value.
If `multi` is True, it will handle multiple values (e.g., for checkboxes).
"""
if not value:
raise ValueError('This field cannot be empty.')
return value
```
### Field Attributes:
When defining a custom field, you can specify the following attributes:
- `name`: The name of the field.
- `section`: The section under which the field will be grouped in the field menu.
- `template_name`: The template used to render the field. If not provided, the field will try to find a template with
the same name as the slugified field class name. Alternatively, you can override the `render()` method to provide
custom HTML rendering.
- `options`: A list of options for the field, if applicable. They will be shown as checkboxes in the field form
Supported options include:
- "required": Whether the field is required.
- "unique": Whether the field is unique,
- "randomize": Randomize the choices, useful for multiple-choice fields.
- "hide": The field should be hidden by default, used together with rules to reveal fields based on conditions
- "inline": Use inline form fields
- "other": Add an "Other" option for checkboxes or radio choices,
- "repeat": The field can be repeatable.
- Any other custom options specific to the field type can be added as a string.
- `settings`: A list of settings for the field. By default, all fields will have the following settings:
- "label": The label for the field.
- "instructions": Help text for the field.
- "name": Placeholder text for the field.
- "default": Default value for the field.
- "choices": Choices for select fields, if applicable.
### Field Methods
#### `render(context)`
Renders the field in the given context. By default, the field is rendered using the template
specified with `template_name`. This attribute can be specified directly, or the `get_template_name` method can be
overridden to return the template name.
#### `clean(value, multi=False, validate=True)`
Cleans the value of the field. If `validate` is True, it will
perform validation on the value and raise a Validation error on failure. If `multi` is True, it will handle multiple
values (e.g., for checkboxes).
## Screenshots

*Field settings sidebar*

*Form Creation*

*List of forms*

*Form Builder interface showing the field menu and main designer area*

*Form editor showing multipage form support*
## Contributing
Pull requests and issues are welcome!
## License
[MIT License](LICENSE)
Raw data
{
"_id": null,
"home_page": "https://github.com/michel4j/django-dynforms",
"name": "django-dynforms",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": null,
"author": "Michel Fodje",
"author_email": "michel4j@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/7d/50/5bf90090d9393cb7bae7c2f313d0dec4ceb31af901015941dfd0a6d0d978/django_dynforms-2025.7.45.tar.gz",
"platform": null,
"description": "# Django Dynamic Forms\n\nA Django reusable App for dynamically designing and generating forms.\n\n### Features\n\n- Visual form builder interface\n- Dynamic field addition and configuration\n- Drag-and-drop field creation/arrangement\n- Support for complex field types with sub-fields\n- Multi-Page forms\n- Live form preview\n- Works with Bootstrap 5\n- Support for light/dark themes\n- Additional field types created in other django apps, are auto-detected and can be used in the form builder\n- Supports custom validation and error handling\n- Ability to associate rules with fields, allowing for conditional logic to hide or show fields based on user input on\n other fields\n\n### Installation\n\n1. Install the package using pip:\n ```bash\n pip install django-dynforms\n ```\n\n2. Add `dynforms` and it's requirements to your `INSTALLED_APPS` in `settings.py`:\n ```python\n INSTALLED_APPS = [\n ...\n 'crispy_forms', # for form rendering\n 'crispy_bootstrap5', # for Bootstrap 5 support \n 'crisp_modals', # for modal dialogs\n 'dynforms',\n ...\n ]\n ```\n\n3. Include the app URLs in your project\u2019s `urls.py`:\n\n ```python\n from django.urls import path, include\n\n urlpatterns = [\n ...\n path('dynforms/', include('dynforms.urls')),\n ...\n ]\n ```\n\n4. Create the necessary database tables by running the migrations:\n\n ```bash\n python manage.py migrate\n ```\n\n## Usage\n\n- Access the form builder at `/dynforms/`.\n- Create a new form by clicking \"[+ Add]\" button the off-canvas Form Types sidebar or select a form from the list to\n edit it.\n- The search bar allows you to filter the form list by searching through the titles and descriptions.\n- Use the sidebar field panel to add fields. Clicking a field on the panel will append a field of that type to the \n current page. Drag the field to rearrange it within the form.\n- To edit for field click on it and edit the settings using the Field settings form on the left-sidebar. Many of the field parameters\n will auto-apply and update the form in real-time.\n- Undo/Redo capabilities are not yet implemented. All changes are currently saved automatically as you edit the form.\n- To delete a field, click the trash icon on the field settings form.\n- Fields can be rearranged by dragging within the form. Move a field from one page to another by dragging it onto the\n desired page tab.\n- The form parameters like title, description, number of pages, and action buttons can be edited in, the form settings\n sidebar.\n- To select a new form for editing withing the designer, click the \"Select Form\" button in the top-right corner of\n the form designer. This will open the off-screen sidebar for adding new forms or selecting an existing form.\n\n## Field Rules\nFields can have rules associated with them, allowing for conditional logic to hide or show fields based on user input. \nMultiple rules can be defined for a field, and they will be evaluated in the order they are defined.\n\nTo add rules to a field, click on the \"Rules\" button in the field settings sidebar. Here you can define conditions that\ndetermine what action to take based on the value of other fields. Each rule consists of\n- An **action** that determines what the rule does\n- A source **field** that the rule will be evaluated against. The rule will be triggered when the value of this\n field changes. Any field defined in the form can be used as a source field.\n- A **subfield** to go along with the source field, if applicable. This is useful for fields that have subfields, such as\n Address. \n- An **operator** that defines how the source field's value is compared to the test value.\n- A test **value** that the source field's value is compared against. This can be a static value or a reference to\n another field's value.\n\nSupported actions include:\n- **Show If**: Makes the field visible when the condition is met.\n- **Hide If**: Hides the field when the condition is met.\n- **Required If**: Makes the field required when the condition is met.\n\nCurrently supported operators include:\n- **Less Than**: The source field's value is less than the test value.\n- **Greater Than**: The source field's value is greater than the test value.\n- **Equal or Less Than**: The source field's value is less than or equal to the test value.\n- **Equal or Greater Than**: The source field's value is greater than or equal to the test value.\n- **Exactly**: The source field's value is exactly equal to the test value.\n- **Exactly (Any Case)**: The source field's value is exactly equal to the test value, ignoring case.\n- **Not Equal to**: The source field's value is not equal to the test value.\n- **Contained in**: The source field's value is contained in the test value.\n- **Contains**: The source field's value contains the test value.\n- **Not Contained in**: The source field's value is not contained in the test value.\n- **Starts With**: The source field's value starts with the test value.\n- **Ends With**: The source field's value ends with the test value.\n- **Starts With (Any Case)**: The source field's value starts with the test value, ignoring case.\n- **Ends With (Any Case)**: The source field's value ends with the test value, ignoring case.\n- **Is Empty**: The source field's value is empty.\n- **Is Not Empty**: The source field's value is not empty.\n\n\n## Abstract Model Classes\n\nInherit from the abstract class `BaseFormModel`, to create a model which records data from a dynamic form. This class\nprovides\nthe necessary fields and methods to handle form data. The `BaseFormModel` class provides the following fields:\n\n- `created`: DateTime field that records when the form was created.\n- `modified`: DateTime field that records when the form was last modified.\n- `form_type`: A Foreign key to the form.\n- `details`: A JSON field that stores the form data submitted by the user.\n- `is_complete`: A Boolean field that indicates whether the form data is complete. Partial submissions are allowed to be\n saved, and the form can be completed later.\n\nAdditionally, the `BaseFormModel` class provides the following methods:\n\n- `get_field_value(self, field_name, default=None)`: Returns the value of a specific field in the form.\n- `validate(self, data=None)`: Sets the value of a specific field in the form. The validation login uses either the\n provided data or the current saved form data.\n\n\n## Views\nThe app provides a set of views to handle form display and submission.\n\n- `DynFormView`: The main view for displaying and submitting forms. It handles the form rendering, validation, and\n submission logic. Derive from this class to create a plain FormView not bound to a specific model. The `template_name`\n attribute should be set to a template that includes `dynforms/form.html` to render the form as desired. Submitted\n form can then be handled in the `form_valid` method, which can be overridden to perform custom actions. The view expects\n a single `pk` kwarg that corresponds to the primary key of the form type being displayed.\n- `DynCreateView`: A view for creating new forms. This is derived from `django.generic.edit.CreateView` and provides a\n a view form for creating a new entries of models that derive from `BaseFormModel`. The `form_class` attribute should\n be set to a form class that inherits from `DynModelForm`. As for the `DynFormView`, the `template_name` attribute\n should be set to a template that includes `dynforms/form.html` to render the form as desired. The view expects a\n `pk` kwarg that corresponds to the primary key of the form type being created.\n- `DynUpdateView`: A view for updating `DynModelForm` entries. This is derived from `django.generic.edit.UpdateView` and\n provides a view for updating entries of models that derive from `BaseFormModel`. The `form_class` attribute should\n be set to a form class that inherits from `DynModelForm`. As for the `DynFormView`, the `template_name` attribute\n should be set to a template that includes `dynforms/form.html` to render the form as desired. The view expects a\n `pk` kwarg that corresponds to the primary key of the `DynModelForm` entry being updated.\n\n\n## Adding custom fields\nTo add custom fields to the form builder from another app, add a `dynfields.py` module to your App's top-level\ndirectory and define a new field inheriting from `dynforms.fields.FieldType`. Implement the necessary methods to handle\nrendering, validation, and type conversion.\n\n### Example Custom Field\n\n```python\nfrom dynforms.fields import FieldType\nfrom django.utils.translation import gettext_lazy as _\n\n\nclass CoolField(FieldType):\n section = _('My Apps Fields') # This will group the field under a custom section in the field menu\n name = _('Cool Field')\n ... # other field attributes\n\n def render(self, context):\n # Custom rendering logic\n return f'<input type=\"text\" name=\"{self.name}\" value=\"{value or \"\"}\">'\n\n def clean(self, value, multi=False, validate=True):\n \"\"\" \n Custom cleaning logic for the field value.\n If `validate` is True, it will perform validation on the value.\n If `multi` is True, it will handle multiple values (e.g., for checkboxes).\n \"\"\"\n if not value:\n raise ValueError('This field cannot be empty.')\n return value\n```\n\n### Field Attributes:\n\nWhen defining a custom field, you can specify the following attributes:\n\n- `name`: The name of the field.\n- `section`: The section under which the field will be grouped in the field menu.\n- `template_name`: The template used to render the field. If not provided, the field will try to find a template with\n the same name as the slugified field class name. Alternatively, you can override the `render()` method to provide\n custom HTML rendering.\n- `options`: A list of options for the field, if applicable. They will be shown as checkboxes in the field form\n Supported options include:\n - \"required\": Whether the field is required.\n - \"unique\": Whether the field is unique,\n - \"randomize\": Randomize the choices, useful for multiple-choice fields.\n - \"hide\": The field should be hidden by default, used together with rules to reveal fields based on conditions\n - \"inline\": Use inline form fields\n - \"other\": Add an \"Other\" option for checkboxes or radio choices,\n - \"repeat\": The field can be repeatable.\n - Any other custom options specific to the field type can be added as a string.\n- `settings`: A list of settings for the field. By default, all fields will have the following settings:\n - \"label\": The label for the field.\n - \"instructions\": Help text for the field.\n - \"name\": Placeholder text for the field.\n - \"default\": Default value for the field.\n - \"choices\": Choices for select fields, if applicable.\n\n### Field Methods\n\n#### `render(context)`\nRenders the field in the given context. By default, the field is rendered using the template\nspecified with `template_name`. This attribute can be specified directly, or the `get_template_name` method can be\noverridden to return the template name.\n\n#### `clean(value, multi=False, validate=True)`\nCleans the value of the field. If `validate` is True, it will\nperform validation on the value and raise a Validation error on failure. If `multi` is True, it will handle multiple \nvalues (e.g., for checkboxes).\n\n## Screenshots\n\n\n*Field settings sidebar*\n\n\n*Form Creation*\n\n\n*List of forms*\n\n\n*Form Builder interface showing the field menu and main designer area*\n\n\n*Form editor showing multipage form support*\n\n## Contributing\n\nPull requests and issues are welcome!\n\n## License\n\n[MIT License](LICENSE)\n\n \n\n ",
"bugtrack_url": null,
"license": "MIT",
"summary": "A Django reusable App for dynamically designing and generating forms.",
"version": "2025.7.45",
"project_urls": {
"Homepage": "https://github.com/michel4j/django-dynforms",
"Issues": "https://github.com/michel4j/django-dynforms/issues"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "d64a5aba67dded86070fac008cd41590ad1ea0c103210e96e13d0218e2a0578e",
"md5": "71c231a8b80a9311a158956780c95637",
"sha256": "dc9afd68a471070815216ce9370d9b77b677456b0c3e0697af573f20d151dbd2"
},
"downloads": -1,
"filename": "django_dynforms-2025.7.45-py3-none-any.whl",
"has_sig": false,
"md5_digest": "71c231a8b80a9311a158956780c95637",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 218189,
"upload_time": "2025-08-10T18:03:55",
"upload_time_iso_8601": "2025-08-10T18:03:55.138928Z",
"url": "https://files.pythonhosted.org/packages/d6/4a/5aba67dded86070fac008cd41590ad1ea0c103210e96e13d0218e2a0578e/django_dynforms-2025.7.45-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "7d505bf90090d9393cb7bae7c2f313d0dec4ceb31af901015941dfd0a6d0d978",
"md5": "069cf7c1d878f506e2bb5b7d2bc87243",
"sha256": "231402e4cbe74fea00ca0e9053fb58d2d73c58caa04f839d431aa640061eb37f"
},
"downloads": -1,
"filename": "django_dynforms-2025.7.45.tar.gz",
"has_sig": false,
"md5_digest": "069cf7c1d878f506e2bb5b7d2bc87243",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 192579,
"upload_time": "2025-08-10T18:03:56",
"upload_time_iso_8601": "2025-08-10T18:03:56.794304Z",
"url": "https://files.pythonhosted.org/packages/7d/50/5bf90090d9393cb7bae7c2f313d0dec4ceb31af901015941dfd0a6d0d978/django_dynforms-2025.7.45.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-10 18:03:56",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "michel4j",
"github_project": "django-dynforms",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "django-dynforms"
}