This Django project is purely to demonstrate an example of how to create a form that contains inline formsets that each contains its own inline formset.
It runs in Django 4.0 using Python 3.9.
I'm indebted to this blogpost by Ravi Kumar Gadila for helping me figure this out.
We have a model describing Publishers. Each Publisher can have a number of Books. Each Book can have a number of BookImages (e.g. its cover, back cover, illustrations, etc):
Publisher #1
|-Book
| |-BookImage
| |-BookImage
|
|-Book
|-BookImage
Publisher #2
|-Book
|
|-Book
See these in models.py
.
Using an inline formset we could display a single form that would let the user edit all of the Books belonging to a single Publisher.
Using another inline formset we could display another form that would let the user edit all of the BookImages belonging to a single Book.
It becomes trickier if we want to combine these two forms into one: displaying all of the Books for a Publisher, and for each Book, all of its BookImages.
You can see in forms.py
how we construct an inline formset, BookImageFormset
for editing the BookImage
s belonging to a single Book
.
And then we create a custom BaseBooksWithImagesFormset
that has a custom nested
property. This contains our BookImageFormset
. We add custom methods for is_valid()
and save()
to ensure the data in these nested formsets are validated and saved.
Finally we create our PublisherBooksWithImagesFormset
which is for editing all the Book
s belonging to a Publisher
... and we pass it this argument: formset=BaseBooksWithImagesFormset
so it knows how to handle each of the Book
s' BookImage
s.
See views.py
for how we use this in a class-based view to create the page. This expects the id
of a Publisher
. And see the books/publisher_books_update.html
template for how the outer form, and its Book
formsets, and their nested BookImage
formsets, are rendered.
Here's an image showing how that page looks:
If you want to get this project running to see how it works...
-
Download or clone the repository.
-
Install Django and Pillow (required for the
ImageField
). For example, using pip with therequirements.txt
file:pip install -r requirements.txt
Or using pipenv with the
Pipfile
s:pipenv install
(If using pipenv, enter the virtual environment before running the following commands, by doing
pipenv shell
) -
Run the migrations:
./manage.py migrate
-
Create a superuser if you want to use the Django Admin:
./manage.py createsuperuser
-
Run the development server:
./manage.py runserver
-
View the site at http://127.0.0.1:8000/ and add at least one Publisher.
-
You can then click the link to add some Books to your Publisher. You'll then be on a page like http://127.0.0.1:8000/publishers/1/books/edit/ which is the form with its inline formsets.