I currently have an error with writing UpdateView for double nested forms. My architecture is comprised of one template containing one form and two formsets. There's a large form for publisher. Someone can then add as many author formsets as they want and as many book formsets to each author as they want. So it looks like:
- Publisher -> Author1, Author2, Author3, ...
- Author1 -> Book1, Book2, Book3
- Author2 -> Book4
- Author3 -> Book5, Book6
Again, this is all listed in one create.html template. I wrote this using CreateView and it works fine. Now with writing UpdateView, I redirect to my html template for the creation of the forms and can correctly populate the form with data for publisher and author. However, when I try to populate the form with the book data, it populates every author instance with the same book instances rather than populate each author instance with their matching book instances. So it looks like:
- Publisher -> Author1, Author2, Author3, ...
- Author1 -> Book1, Book2, Book3
- Author2 -> Book1, Book2, Book3
- Author3 -> Book1, Book2, Book3
My models.py looks like :
class Publisher(models.Model):
name = models.CharField(max_length=255, unique=True)
class Author(models.Model):
name = models.CharField(max_length=255, unique=True)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
class Book(models.Model):
title = models.CharField(max_length=255, unique=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
My forms.py looks like:
AuthorInlineFormSet = inlineformset_factory(
Publisher,
Author,
fields="__all__",
extra=1,
can_delete=False,
)
BookInlineFormSet = inlineformset_factory(
Author,
Book,
fields="__all__",
extra=1,
can_delete=False,
)
My views.py looks like:
class FormCreateView(CreateView):
model = Publisher
template_name = 'reservations/create.html'
fields = "__all__"
def form_valid(self, form):
result = super(FormCreateView, self).form_valid(form)
authors_count = 0
authors_formset = AuthorInlineFormSet(form.data, instance=self.object, prefix='authors_formset')
if authors_formset.is_valid():
authors = authors_formset.save()
for author in authors:
books_formset = BookInlineFormSet(form.data, instance=author, prefix='books_formset_%s' % authors_count)
if books_formset.is_valid():
books_formset.save()
authors_count += 1
return result
def get_context_data(self, **kwargs):
context = super(FormCreateView, self).get_context_data(**kwargs)
context['authors_formset'] = AuthorInlineFormSet(prefix='authors_formset')
context['books_formset'] = BookInlineFormSet(prefix='books_formset_0')
return context
class FormUpdateView(UpdateView):
model = Publisher
template_name = 'reservations/create.html'
fields = "__all__"
def get_success_url(self):
return reverse_lazy('reservations:dashboard')
def get_object(self, *kargs, **kwargs):
request = get_object_or_404(Publisher, pk=self.kwargs['pk'])
return request
def get_context_data(self, **kwargs):
context = super(FormUpdateView, self).get_context_data(**kwargs)
context['authors_formset'] = AuthorInlineFormSet(instance=self.object, prefix='authors_formset')
authors_count = 0
authors_formset = AuthorInlineFormSet(instance=self.object, prefix='authors_formset')
authors_formset = authors_formset.queryset
for author in authors_formset:
context['books_formset'] = BookInlineFormSet(instance=author, prefix='books_formset_%s' % authors_count)
return context
Now if I were to print out the context['books_formset'] in the for loop of UpdateView's get_context_data, it correctly prints out the matching book formsets for each author, so it'll print out (Book5, Book6), (Book4), and then (Book1, Book2, Book3). So I know it's correctly finding the corresponding book instances for each author instance. However, it then writes all book instances to Book1, Book2, Book3 so each author has Book1, Book2, Book3. I'm not quite sure how to fix this, and would appreciate any help! Thank you so much.