简体   繁体   中英

CreateView doesn't save related objects

I have two models: Student and Group. A group consists of multiple students and a student can be in only one group. Here are the models:

class Group(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

class Student(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    section = models.PositiveSmallIntegerField(default=1)
    group = models.ForeignKey(
        Group, on_delete=models.SET_NULL, null=True, blank=True
    )

I am trying to build a form where I can select multiple students to create a group.

class CreateGroupForm(forms.ModelForm):
    students = forms.ModelMultipleChoiceField(
        required=True,
        queryset=Student.objects.all()
    )

    class Meta:
        model = Group
        fields = ('students', )

I am using the following view for the form:

class SelectGroup(CreateView):
    model = Group
    form_class = CreateGroupForm
    template_name = 'selection.html'
    success_url = reverse_lazy('test')

When I submit the form, it creates a group but the group's student_set is empty. I am guessing this is because I cannot add students to a group without saving the group first. Is there a way for me to modify this view to save the students or should I use something else?

Since students is not a field of the group model, the model form's save does not know what to do with it. You have to override the save method and handle the students field manually:

class CreateGroupForm(forms.ModelForm):
    # ...
    def save(self, commit=True):
        # you have to commit, else the reverse fk has nothing to point to
        group = super(CreateGroupForm, self).save(commit=True)
        group.student_set.add(*self.cleaned_data['students'])
        return group

If you prefer not to remove the non-commit save option on the form, you can also override the form_valid method of the view:

class SelectGroup(CreateView):
    # ...
    def form_valid(self, form):
        self.object.student_set.add(*self.form.cleaned_data['students'])
        return super(SelectGroup, self).form_valid(form)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM