Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving nested forms in Django

I have a strange problem. I am trying to validate one form sent through POST request, and this form is in fact built on the foundation of four models.

The idea is to validate all the forms and then save them accordingly, in appropriate order, assigning foreign keys on the basis of recently saved model instances.

The problem occurs when I try to assign a value to the foreign key of the model that was properly validated through the form.is_valid() call. It throws however an IntegrityError saying that this specific foreign key "may not be NULL".

Below please find the code that should give you the idea on what I am doing:

Models & forms defitinions:

class Author(models.Model):
    name = models.CharField(blank=False, max_length=150)
    book = models.OneToOneField('Book')  # its important, one author has one book

class Book(models.Model):
    name = models.CharField(blank=False, max_length=150)

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        exclude = ('book',)

class BookForm(ModelForm):
    class Meta:
        model = Book

In one of the views:

if request.method == "POST":
    author_form = AuthorForm(request.POST, prefix='author')
    book_form = BookForm(request.POST, prefix='book')
    if author_form.is_valid() and book_form.is_valid():
        book = book_form.save()
        author_form.cleaned_data['book_id'] = book.id
        author.form.save()  # Error!

During author_form.save() I get error similar to this:

IntegrityError at /api/my_method
myapp_author.book_id may not be NULL

I am completely sure that book.id is an integer (int, I have checked it) with the ID of recently saved Book record.

Do you have any idea how could I set the foreign key having validated & unsaved form, so I can safely save it into the database?

like image 676
Tadeck Avatar asked Mar 29 '12 13:03

Tadeck


2 Answers

You've excluded book from the AuthorForm, so Django doesn't care what the value of cleaned_data['book_id'] is - it'll just ignore it.

Instead, you need to set the book property directly on the unsaved author instance:

    book = book_form.save()
    author = author.form.save(commit=False)
    author.book = book
    author.save()
like image 111
Daniel Roseman Avatar answered Nov 20 '22 13:11

Daniel Roseman


You could try:

if author_form.is_valid() and book_form.is_valid():
    book = book_form.save()
    author = author_form.save(commit=False)
    author.book = book
    author.save()
like image 35
Timmy O'Mahony Avatar answered Nov 20 '22 12:11

Timmy O'Mahony