{"id":2928,"library":"django-formtools","title":"django-formtools","description":"django-formtools is a collection of high-level abstractions designed to simplify complex form use cases in Django applications. It primarily offers utilities for form previews and multi-step form wizards. Originally a part of Django's core (`django.contrib.formtools`), it was extracted into a standalone package in Django 1.8 to facilitate independent maintenance and trim the framework's core. The library is actively maintained by the Jazzband community, with releases typically following new Django versions to ensure compatibility.","status":"active","version":"2.5.1","language":"python","source_language":"en","source_url":"https://github.com/jazzband/django-formtools","tags":["django","forms","wizard","multi-step","form-preview","jazzband"],"install":[{"cmd":"pip install django-formtools","lang":"bash","label":"Install via pip"}],"dependencies":[{"reason":"Core framework dependency, requires Django>=4.2 for recent versions.","package":"Django","optional":false}],"imports":[{"note":"The `formtools` package was moved out of `django.contrib` in Django 1.8. Old import paths are no longer valid.","wrong":"from django.contrib.formtools.wizard.views import WizardView","symbol":"SessionWizardView","correct":"from formtools.wizard.views import SessionWizardView"},{"note":"The `formtools` package was moved out of `django.contrib` in Django 1.8. Old import paths are no longer valid.","wrong":"from django.contrib.formtools.wizard.views import CookieWizardView","symbol":"CookieWizardView","correct":"from formtools.wizard.views import CookieWizardView"},{"note":"The `formtools` package was moved out of `django.contrib` in Django 1.8. Old import paths are no longer valid.","wrong":"from django.contrib.formtools.wizard.views import NamedUrlWizardView","symbol":"NamedUrlWizardView","correct":"from formtools.wizard.views import NamedUrlWizardView"},{"note":"The `formtools` package was moved out of `django.contrib` in Django 1.8. Old import paths are no longer valid.","wrong":"from django.contrib.formtools.preview import FormPreview","symbol":"FormPreview","correct":"from formtools.preview import FormPreview"}],"quickstart":{"code":"import os\nfrom django import forms\nfrom django.shortcuts import render\nfrom django.urls import path\nfrom formtools.wizard.views import SessionWizardView\n\n# forms.py (example)\nclass ContactForm1(forms.Form):\n    name = forms.CharField(max_length=100)\n    email = forms.EmailField()\n\nclass ContactForm2(forms.Form):\n    subject = forms.CharField(max_length=100)\n    message = forms.CharField(widget=forms.Textarea)\n\n# views.py (example)\nFORMS = [(\"step1\", ContactForm1), (\"step2\", ContactForm2)]\nTEMPLATES = {\"step1\": \"wizard_form.html\", \"step2\": \"wizard_form.html\"}\n\nclass ContactWizard(SessionWizardView):\n    def get_template_names(self):\n        # Dynamically selects template based on current step\n        return [TEMPLATES[self.steps.current]]\n\n    def done(self, form_list, **kwargs):\n        # Process the cleaned data from all forms after the wizard is complete\n        cleaned_data = [form.cleaned_data for form in form_list]\n        return render(self.request, 'done.html', {'form_data': cleaned_data})\n\n# urls.py (example)\n# urlpatterns = [\n#     path('contact/', ContactWizard.as_view(FORMS)),\n# ]\n\n# To make this runnable as a standalone quickstart for illustration (requires a Django project setup)\n# In a real Django project, you'd add 'formtools' to INSTALLED_APPS and set up templates.\n# For testing, you can manually run the view logic (not a full server setup).\n\n# Example of what your templates/wizard_form.html might look like:\n# <h1>Step {{ wizard.steps.current }} of {{ wizard.steps.total }}</h1>\n# <form action=\"\" method=\"post\">{% csrf_token %}\n#     {{ wizard.management_form }}\n#     {{ wizard.form.as_p }}\n#     <input type=\"submit\" value=\"{% if wizard.steps.next %}Next{% else %}Submit{% endif %}\">\n# </form>\n\n# Example of what your templates/done.html might look like:\n# <h1>Wizard Complete!</h1>\n# <p>Form Data:</p>\n# <ul>\n#     {% for form_data in form_data %}\n#         <li>{{ form_data }}</li>\n#     {% endfor %}\n# </ul>\n","lang":"python","description":"To use `django-formtools`, define your form classes, then create a `WizardView` subclass (e.g., `SessionWizardView`) that specifies the forms and implements a `done` method to handle the final submission. The view needs to be hooked into your `urls.py`. Remember to add `'formtools'` to your `INSTALLED_APPS` in `settings.py` for templates and translations to work correctly."},"warnings":[{"fix":"Update import statements from `from django.contrib.formtools...` to `from formtools...`.","message":"The `django-formtools` package was moved from `django.contrib.formtools` to its own standalone package `formtools` in Django 1.8. All import paths changed accordingly.","severity":"breaking","affected_versions":"Django 1.8 and higher, django-formtools 1.0 and higher"},{"fix":"Add `'formtools'` to your `INSTALLED_APPS` list.","message":"You must add `'formtools'` to your `INSTALLED_APPS` setting in `settings.py` for its templates (like `wizard_form.html`) and internationalization (translations) to be discovered and used correctly.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Avoid using `FormPreview` with forms that include `FileField`. For wizards, refer to the 'Handling files' section in the `WizardView` documentation.","message":"`FormPreview` does not support file uploads. If your forms contain `FileField`s, you should implement the preview logic manually or use `WizardView` which supports file handling.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure your project uses Python >= 3.7 (preferably >= 3.8) and Django >= 3.2.","message":"In `django-formtools` 2.4.1, support for Python 3.6 and Django versions older than 3.2 was dropped.","severity":"breaking","affected_versions":"django-formtools 2.4.1 and higher"},{"fix":"For persistent session data across user visits, override the `get()` method in your `SessionWizardView` subclass to load saved data. Be aware of the `CookieWizardView`'s new behavior regarding invalid cookies.","message":"By default, `SessionWizardView` and `CookieWizardView` manage state across steps, but they do not inherently provide mechanisms to restore a user's progress if they leave the site and return later. Additionally, `CookieWizardView` now restarts the wizard from the first step if an invalid cookie is detected, instead of raising a `SuspiciousOperation` error.","severity":"gotcha","affected_versions":"`SessionWizardView` all versions; `CookieWizardView` 2.4 and higher for new cookie behavior."}],"env_vars":null,"search_vec":"'1.8':55 'abstract':14 'activ':69 'applic':24 'case':21 'collect':9 'communiti':74 'compat':84 'complex':18 'core':44,65 'design':15 'django':2,5,23,42,54,80,85 'django-formtool':1,4 'django.contrib.formtools':45 'ensur':83 'extract':48 'facilit':57 'follow':78 'form':19,30,36,86,92 'form-preview':91 'formtool':3,6 'framework':63 'high':12 'high-level':11 'independ':58 'jazzband':73,94 'level':13 'librari':67 'maintain':70 'mainten':59 'multi':34,89 'multi-step':33,88 'new':79 'offer':27 'origin':38 'packag':52 'part':40 'preview':31,93 'primarili':26 'releas':76 'simplifi':17 'standalon':51 'step':35,90 'trim':61 'typic':77 'use':20 'util':28 'version':81 'wizard':37,87","created_at":"2026-04-11T09:13:37.291513+00:00","updated_at":"2026-04-16T14:30:40.374615+00:00","problems":[{"fix":"First, ensure `django-formtools` is installed: `pip install django-formtools`. Then, update all import statements from `from django.contrib.formtools...` to `from formtools...` in your code.","cause":"The `formtools` application was moved from `django.contrib.formtools` to a standalone package `django-formtools` in Django 1.8. This error occurs if you are using an older import path or have not installed the standalone `django-formtools` package.","error":"ModuleNotFoundError: No module named 'formtools'"},{"fix":"Ensure that `{{ wizard.management_form }}` is present within the `<form>` tags of all your wizard step templates. Also, avoid manually manipulating hidden fields that `django-formtools` uses to manage state.","cause":"This error typically occurs in a `SessionWizardView` if the hidden `{{ wizard.management_form }}` field is missing from one of your wizard's templates, or if the form's management data is somehow altered or corrupted between steps.","error":"SuspiciousOperation: ManagementForm data is missing or has been tampered with."},{"fix":"Verify that all forms in the wizard (from the first to the last step) are validating correctly. If forms are failing validation, the wizard will re-render the current step with errors instead of proceeding to `done()`. Use the `process_step()` method if you need to perform actions after each successful step.","cause":"The `done()` method in a `SessionWizardView` subclass is only executed after the *final* step of the multi-step form wizard has been successfully submitted and all forms have passed validation. If an intermediate step's form validation fails, or if the wizard's state is somehow reset, the `done()` method will not be reached.","error":"Django formtools 'done' method is not getting called on form submission"},{"fix":"Add `'formtools'` to your `INSTALLED_APPS` list in your `settings.py` file. Ensure your `TEMPLATES` setting includes the `django.template.loaders.app_directories.Loader` or explicitly lists the `formtools/templates` directory in the `DIRS` option.","cause":"Django cannot locate the default templates provided by `django-formtools` for rendering the wizard or preview pages. This often happens if `'formtools'` is not added to your `INSTALLED_APPS` setting in `settings.py`, or if your template loaders are not configured to discover app templates.","error":"TemplateDoesNotExist at /my-wizard-url/ formtools/wizard/wizard_form.html"}],"ecosystem":"pypi","meta_description":null,"install_score":null,"quickstart_score":null,"quickstart_tag":null,"pypi_latest":"2.7","cli_name":"","cli_version":null,"type":"library","homepage":"https://django-formtools.readthedocs.io/en/latest/","github":"https://github.com/jazzband/django-formtools","docs":null,"changelog":null,"pypi":"https://pypi.org/project/django-formtools/","npm":null,"openapi_spec":null,"status_page":null,"smithery":null,"categories":["web-framework"],"base_url":null,"auth_type":null,"provenance":{"verified_status":"passing","verified_at":"2026-06-28","last_verified":"2026-08-28","next_check":"2026-07-28","install_tag":null}}