Django Extra Views
Django Extra Views provides a collection of useful, advanced class-based views for Django, extending the built-in generic views. It's particularly strong for handling complex formsets and inlines. The current version is 0.16.0, and releases are typically feature-driven, providing new views or compatibility updates for Django.
Common errors
-
ModuleNotFoundError: No module named 'extra_views.mixins'
cause Attempting to import from old submodule paths. In recent versions, most components are available directly under the `extra_views` namespace.fixChange your import statement from `from extra_views.mixins import SearchableListMixin` to `from extra_views import SearchableListMixin`. -
ImproperlyConfigured: One of 'form_class', 'model' should be set for FormSetFactory.
cause You are using `InlineFormSetFactory` (or an older `FormSetFactory`) but have not specified either the `model` attribute (for model-based formsets) or `form_class` (for custom forms).fixEnsure your `InlineFormSetFactory` class explicitly defines `model = YourRelatedModel` or `form_class = YourCustomForm`. -
django.core.exceptions.ImproperlyConfigured: ListView is missing a QuerySet. Define .model, .queryset, or override .get_queryset().
cause This error can occur with list-based views (e.g., `SortableListMixin` or `SearchableListMixin` when combined with a `ListView`) if you forget to specify the base queryset or model for the view.fixEnsure your view class has `model = YourModel` or `queryset = YourModel.objects.all()` defined, or that you override `get_queryset` correctly.
Warnings
- breaking Django 2.x support was dropped in version 0.13.0. Projects using older Django versions (2.x) will need to upgrade Django to >=3.2 or stick to an older `django-extra-views` release (<0.13.0).
- breaking The `SortableListMixin.get_queryset` method signature changed in version 0.10.0, adding an `ordering` parameter. If you override this method in your custom views, your existing implementation will break.
- deprecated The `FormSetFactory` class was removed in version 0.9.0. If you were directly using `FormSetFactory` to create formsets, you will need to refactor your code.
- gotcha When using `SearchableListMixin`, `SearchableList` or `SortableListMixin`, `django-filter` must be installed. For `SearchableListMixin`, make sure to also define `search_fields`.
Install
-
pip install django-extra-views
Imports
- InlineFormSetView
from extra_views import InlineFormSetView
- CreateWithInlinesView
from extra_views import CreateWithInlinesView
- UpdateWithInlinesView
from extra_views import UpdateWithInlinesView
- ModelFormSetView
from extra_views import ModelFormSetView
- SearchableListMixin
from extra_views import SearchableListMixin
Quickstart
from django.db import models
from django.views.generic import ListView
from extra_views import CreateWithInlinesView, InlineFormSetFactory
# models.py example
class Parent(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Child(models.Model):
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
item = models.CharField(max_length=100)
def __str__(self):
return f'{self.item} ({self.parent.name})'
# views.py example
class ChildInline(InlineFormSetFactory):
model = Child
fields = ['item']
factory_kwargs = {'extra': 1}
class CreateParentWithChildrenView(CreateWithInlinesView):
model = Parent
fields = ['name']
inlines = [ChildInline]
template_name = 'parent_create_with_children.html'
success_url = '/parents/' # Or reverse_lazy('parent-list')
# urls.py example
# from django.urls import path
# from .views import CreateParentWithChildrenView
#
# urlpatterns = [
# path('parents/create/', CreateParentWithChildrenView.as_view(), name='parent-create-with-children'),
# ]
# parent_create_with_children.html (simplified fragment)
# <form method="post">
# {% csrf_token %}
# {{ form.as_p }}
# <h3>Children</h3>
# {{ inlines.childinline.management_form }}
# {% for formset in inlines.childinline %}
# {{ formset.as_p }}
# {% endfor %}
# <button type="submit">Save</button>
# </form>
# To make this runnable, we need a minimal Django setup.
# For testing purposes, you could try:
# from django.conf import settings
# if not settings.configured:
# settings.configure(DEBUG=True, INSTALLED_APPS=['django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.admin', 'django.contrib.messages'], DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}})
# import django
# django.setup()
# # Then define Parent and Child models and run migrations implicitly.
# from django.core.management import call_command
# call_command('makemigrations', 'your_app_name', interactive=False)
# call_command('migrate', interactive=False)
# # This example is primarily for demonstrating the view setup.