{"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":"en","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,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}