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.
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 BookImages 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 Books belonging to a Publisher... and we pass it this argument: formset=BaseBooksWithImagesFormset so it knows how to handle each of the Books' BookImages.
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.txtfile:pip install -r requirements.txtOr using pipenv with the
Pipfiles:pipenv install -
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.
