alias51
alias51

Reputation: 8618

How to save an instance and update a reverse M2M field in a modelForm

Lots of questions cover saving instances before M2M relationships, however I have a problem achieving this when saving via a form with a _set relationship, which does not appear to be covered in other answers.

I have two models with an M2M relationship:

class Author(models.Model):
   name = models.CharField()

class Publisher(models.Model):
   authors = models.ManyToManyField(Author)

When creating a new Author, I want to be able to set Publishers for that Author. Currently I handle this in the form:

class AuthorForm(forms.ModelForm):
    publishers = forms.ModelMultipleChoiceField(queryset=Publisher.objects.all())

    def __init__(self, *args, **kwargs):
        super(AuthorForm, self).__init__(*args, **kwargs)

        # If the form is used in a UpdateView, set the initial values
        if self.instance.id:
            self.fields['publishers'].initial = self.instance.publisher_set.all(
            ).values_list('id', flat=True)

    def save(self, *args, **kwargs):
        instance = super(AuthorForm, self).save(*args, **kwargs)

        # Update the M2M relationship
        instance.issuer_set.set(self.cleaned_data['issuers'])

        return instance

    class Meta:
        model = Author
        fields = ['name', 'publishers']

This works fine when updating an existing Author. However, when I use it to create a new one I get:

<Author: Sample Author> needs to have a value for field "id" before this many-to-many relationship can be used.

I understand this is because the instance needs to be saved first, however in def save() I am doing this (instance = super(AuthorForm, self).save(*args, **kwargs)).

Where am I going wrong?

Upvotes: 0

Views: 522

Answers (1)

ruddra
ruddra

Reputation: 51988

calling super does not guarantee that it will create an instance. Because, form's save(...) method has a argument called commit, if the method calling the save is passing commit=False(for example: form.save(commit=False)), then it will not save the instance. So you need to explicitly save it. Like this:

instance = super(AuthorForm, self).save(commit=True)

Upvotes: 1

Related Questions