# Create multiple objects in one form Django

**URL:** https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397
**Category:** Forms & APIs
**Created:** [December 2, 2022, 1:02pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397 "2022-12-02T13:02:06Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![danielkievit](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/danielkievit/32/11154_2.png) [@danielkievit](https://forum.djangoproject.com/u/danielkievit)
#### Post date: [December 2, 2022, 1:02pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/1 "2022-12-02T13:02:06Z")

</div>

I am trying to create a form in Django that can create one Student object with two Contact objects in the same form. The second Contact object must be optional to fill in (not required).

Schematic view of the objects created in the single form:

```
          Contact 1
Student <
          Contact 2 (not required)

```

I have the following models in models.py:

```
class User(AbstractUser):
    is_student = models.BooleanField(default=False)
    is_teacher = models.BooleanField(default=False)

class Student(models.Model):
    ACCOUNT_STATUS_CHOICES = (
            ('A', 'Active'),
            ('S', 'Suspended'),
            ('D', 'Deactivated'),
        )

    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    year = models.ForeignKey(Year, on_delete=models.SET_NULL, null=True)
    school = models.ForeignKey(School, on_delete=models.SET_NULL, null=True)
    student_email = models.EmailField() # named student_email because email conflicts with user email
    account_status = models.CharField(max_length=1, choices=ACCOUNT_STATUS_CHOICES)
    phone_number = models.CharField(max_length=50)
    homework_coach = models.ForeignKey(Teacher, on_delete=models.SET_NULL, null=True, blank=True, default='')
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
    plannings = models.ForeignKey(Planning, on_delete=models.SET_NULL, null=True)
  
    def __str__ (self):
        return f"{self.first_name} {self.last_name}"

class Contact(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    contact_first_name = models.CharField(max_length=50)
    contact_last_name = models.CharField(max_length=50)
    contact_phone_number = models.CharField(max_length=50)
    contact_email = models.EmailField()
    contact_street = models.CharField(max_length=100)
    contact_street_number = models.CharField(max_length=10)
    contact_zipcode = models.CharField(max_length=30)
    contact_city = models.CharField(max_length=100)

    def __str__ (self):
        return f"{self.contact_first_name} {self.contact_last_name}"

```

In forms.py, I have created two forms to register students and contacts. A student is also connected to a User object for login and authentication, but this is not relevant. Hence, when a user is created, the user is defined as the user.

```
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django.db import transaction
from .models import Student, Teacher, User, Year, School, Location, Contact

class StudentSignUpForm(UserCreationForm):
    ACCOUNT_STATUS_CHOICES = (
        ('A', 'Active'),
        ('S', 'Suspended'),
        ('D', 'Deactivated'),
    )

    #student
    first_name = forms.CharField(max_length=50, required=True)
    last_name = forms.CharField(max_length=50, required=True)
    year = forms.ModelChoiceField(queryset=Year.objects.all(), required=False)
    school = forms.ModelChoiceField(queryset=School.objects.all(), required=False) # not required for new schools / years that are not yet in the database
    student_email = forms.EmailField(required=True)
    account_status = forms.ChoiceField(choices=ACCOUNT_STATUS_CHOICES)
    phone_number = forms.CharField(max_length=50, required=True)
    homework_coach = forms.ModelChoiceField(queryset=Teacher.objects.all(), required=False)

    class Meta(UserCreationForm.Meta):
        model = User
        fields = (
            'username',
            'first_name',
            'last_name',
            'year',
            'school',
            'student_email',
            'account_status',
            'phone_number',
            'homework_coach',
            'password1',
            'password2',
            )

    @transaction.atomic
    def save(
        self, 
        first_name, 
        last_name, 
        year, 
        school, 
        student_email, 
        account_status, 
        phone_number, 
        homework_coach, 
        ):

        user = super().save(commit=False)
        user.is_student = True
        user.save()
        Student.objects.create( # create student object
            user=user,
            first_name=first_name,
            last_name=last_name,
            year=year,
            school=school,
            student_email=student_email,
            account_status=account_status,
            phone_number=phone_number,
            homework_coach=homework_coach
        )
        
        return user

class ContactForm(forms.ModelForm):
    contact_first_name = forms.CharField(max_length=50, required=True)
    contact_last_name = forms.CharField(max_length=50, required=True)
    contact_phone_number = forms.CharField(max_length=50, required=False)
    contact_email = forms.EmailField(required=False) # not required because some students might not know contact information
    contact_street = forms.CharField(max_length=100, required=False)
    contact_street_number = forms.CharField(max_length=10, required=False)
    contact_zipcode = forms.CharField(max_length=10, required=False)
    contact_city = forms.CharField(max_length=100, required=False)

    class Meta:
        model = Contact
        fields = ' __all__'

```

In views.py, I have created a view that saves the data (so far only student data, not contact data).

```
class StudentSignUpView(CreateView):
    model = User
    form_class = StudentSignUpForm
    template_name = 'registration/signup_form.html'

    def get_context_data(self, **kwargs):
        kwargs['user_type'] = 'student'
        return super().get_context_data(**kwargs)

    def form_valid(self, form):
        # student
        first_name = form.cleaned_data.get('first_name')
        last_name = form.cleaned_data.get('last_name')
        year = form.cleaned_data.get('year')
        school = form.cleaned_data.get('school')
        student_email = form.cleaned_data.get('student_email')
        account_status = form.cleaned_data.get('account_status')
        phone_number = form.cleaned_data.get('phone_number')
        homework_coach = form.cleaned_data.get('email')

        user = form.save(
            # student
            first_name=first_name, 
            last_name=last_name, 
            year=year,
            school=school,
            student_email=student_email,
            account_status=account_status,
            phone_number=phone_number,
            homework_coach=homework_coach,
            )
        
        login(self.request, user)
        return redirect('home')

```

And in registration/signup\_form.html, the template is as follows:

```
{% block content %} {% load crispy_forms_tags %}

<form method="POST" enctype="multipart/form-data">
    {{ formset.management_data }}
    {% csrf_token %}
    {{formset|crispy}}
    <input type="submit" value="Submit">
</form>
{% endblock %}

```

Urls.py:

```
from .views import StudentSignUpView

urlpatterns = [
    path('', views.home, name='home'),
    path('signup/student/', StudentSignupView.as_view(), name='student_signup'),
]

```

**How can I create one view that has one form that creates 1 Student object and 2 Contact objects (of which the 2nd Contact is not required)?**

Things I have tried:

**Using formsets to create multiple contacts at once** , but I only managed to create multiple Contacts and could not manage to add Students to that formset.

I added this to views.py:

```
def formset_view(request):
    context={}

    # creating the formset
    ContactFormSet = formset_factory(ContactForm, extra = 2)
    formset = ContactFormSet()

    # print formset data if it is valid
    if formset.is_valid():
        for form in formset:
            print(form.cleaned_data)

    context['formset']=formset
    return render(request, 'registration/signup_form.html', context)

```

Urls.py:

```
urlpatterns = [
    path('', views.home, name='home'),
    path('signup/student/', views.formset_view, name='student_signup'),
]

```

But I only managed to create multiple Contacts and was not able to add a Student object through that form. I tried creating a ModelFormSet to add fields for the Student object, but that did not work either.

---

<div class="post-metadata">

### Author: ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)
#### Post date: [December 2, 2022, 1:42pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/2 "2022-12-02T13:42:28Z")

</div>

You’re doing way too much manual work here. It’s a whole lot easier than what you’re trying to do.

First:

> [@danielkievit](#):
>
> but I only managed to create multiple Contacts and could not manage to add Students to that formset.

Right idea, but don’t add the Student to the formset. Limit the formset to Contact only.

What I would recommend:  
Create ModelForm for each of User and Student, and a model formset for Contact.

Make sure you use the [`prefix`](https://docs.djangoproject.com/en/4.1/ref/forms/api/#prefixes-for-forms) attribute for each of these to ensure they are handled cleanly within Django.

Yes, you will need to render, check `is_valid`, and save all three forms individually - but it ends up being a whole lot easier overall.

---

<div class="post-metadata">

### Author: ![danielkievit](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/danielkievit/32/11154_2.png) [@danielkievit](https://forum.djangoproject.com/u/danielkievit)
#### Post date: [December 5, 2022, 3:07pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/3 "2022-12-05T15:07:38Z")

</div>

Thanks a lot for your quick reply! I have tried to implement your tips, also inspired by the following stackoverflow answer: [Multiple forms with one single create view in Django - Stack Overflow](https://stackoverflow.com/questions/70030137/multiple-forms-with-one-single-create-view-in-django)

I created the following view in views.py:

```auto
def signup_view(request):
    if request.method == 'POST':
        student_signup_form = StudentSignUpForm(request.POST, request.FILES, prefix='student')
        ContactFormSet = formset_factory(ContactForm, extra = 2) # creates two ContactForms
        contact_formset = ContactFormSet(request.POST, request.FILES, prefix='contact')
        if all([student_signup_form.is_valid(), contact_formset.is_valid()]):
            student = student_signup_form.save()
            contact = contact_formset.save(commit=False)
            contact.form = student
            contact.save()
            return redirect('home')
    else:
        student_signup_form = StudentSignUpForm(prefix='student')
        ContactFormSet = formset_factory(ContactForm, extra = 2)
        ContactFormSet(request.POST, request.FILES, prefix='contact')
    return render(request, 'registration/signup_form.html', {'student_signup_form': student_signup_form, 'contact_form': ContactFormSet})

```

This creates 2 ContactForms as a formset using the prefix attribute like you mentioned.

In Forms.py, I have created two forms, one for User and Student and one for Contact:

```auto
class StudentSignUpForm(UserCreationForm):
    ACCOUNT_STATUS_CHOICES = (
        ('A', 'Active'),
        ('S', 'Suspended'),
        ('D', 'Deactivated'),
    )

    first_name = forms.CharField(max_length=50, required=True)
    last_name = forms.CharField(max_length=50, required=True)
    year = forms.ModelChoiceField(queryset=Year.objects.all(), required=False)
    school = forms.ModelChoiceField(queryset=School.objects.all(), required=False) # not required for new schools / years that are not yet in the database
    student_email = forms.EmailField(required=True)
    account_status = forms.ChoiceField(choices=ACCOUNT_STATUS_CHOICES)
    phone_number = forms.CharField(max_length=50, required=True)
    homework_coach = forms.ModelChoiceField(queryset=Teacher.objects.all(), required=False)

    class Meta(UserCreationForm.Meta):
        model = User
        fields = (
            'username',
            'first_name',
            'last_name',
            'year',
            'school',
            'student_email',
            'account_status',
            'phone_number',
            'homework_coach',
            'password1',
            'password2',
            )

    @transaction.atomic
    def save(
        self, 
        first_name, 
        last_name, 
        year, 
        school, 
        student_email, 
        account_status, 
        phone_number, 
        homework_coach, 
        ):

        user = super().save(commit=False)
        user.is_student = True
        user.save()
        
        Student.objects.create( # create student object
            user=user,
            first_name=first_name,
            last_name=last_name,
            year=year,
            school=school,
            student_email=student_email,
            account_status=account_status,
            phone_number=phone_number,
            homework_coach=homework_coach
        )
        
        return user

class ContactForm(ModelForm):
    contact_first_name = forms.CharField(max_length=50, required=True)
    contact_last_name = forms.CharField(max_length=50, required=True)
    contact_phone_number = forms.CharField(max_length=50, required=False)
    contact_email = forms.EmailField(required=False) # not required because some students might not know contact information
    contact_street = forms.CharField(max_length=100, required=False)
    contact_street_number = forms.CharField(max_length=10, required=False)
    contact_zipcode = forms.CharField(max_length=10, required=False)
    contact_city = forms.CharField(max_length=100, required=False)

    class Meta:
        model = Contact
        fields = ' __all__'
        exclude = ('student',)

```

It now renders all the fields for Student, User (the passwords and username) and Contact 1 and Contact 2, however, when submitted, nothing is saved in the database now. Do you know the cause of this? Thanks a lot!

---

<div class="post-metadata">

### Author: ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)
#### Post date: [December 5, 2022, 3:43pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/4 "2022-12-05T15:43:54Z")

</div>

If you’re not getting any error messages in the runserver console, then the most likely issue is that one (or more) of your forms are not valid.

There are a couple of items to mention here:

- If you’re not handling files being uploaded through the forms, there’s no need to include `request.FILES` in the form bindings. (This isn’t a problem, just helps reduce “code clutter”.)

- `ContactForm` is a `ModelForm`, but you don’t have `ContactFormSet` defined as a ModelFormset (it should be)

- In your `if` block, you’re needing to set the value of `student` on _each_ form within the formset. In this type of case, it is appropriate to iterate over the individual forms to set the appropriate fields for each form. See [Creating forms from models | Django documentation | Django](https://docs.djangoproject.com/en/4.1/topics/forms/modelforms/#saving-objects-in-the-formset).

- In your render, you should be rendering the instance of the formset (e.g. `contact_formset`) and not the formset class.

---

<div class="post-metadata">

### Author: ![danielkievit](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/danielkievit/32/11154_2.png) [@danielkievit](https://forum.djangoproject.com/u/danielkievit)
#### Post date: [December 5, 2022, 4:06pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/5 "2022-12-05T16:06:34Z")

</div>

Thanks so much again for your quick response! I have adapted 3 of your 4 points (I think), but I did not quite understand the 2nd point. Where can i define ContactFormSet as a ModelFormSet?

Here is the code I adapted according to your points:

```auto
def signup_view(request):
    if request.method == 'POST':
        student_signup_form = StudentSignUpForm(request.POST, prefix='student')
        ContactFormSet = formset_factory(ContactForm, extra = 2) # creates two ContactForms
        contact_formset = ContactFormSet(request.POST, prefix='contact')
        if all([student_signup_form.is_valid(), contact_formset.is_valid()]):
            student = student_signup_form.save()
            
            contacts = contact_formset.save(commit=False)
            for contact in contacts:
                contact.form = student
                contact.save()

            return redirect('home')
    else:
        student_signup_form = StudentSignUpForm(prefix='student')
        ContactFormSet = formset_factory(ContactForm, extra = 2)
        ContactFormSet(request.POST, prefix='contact')
    return render(request, 'registration/signup_form.html', {'student_signup_form': student_signup_form, 'contact_form': contact_formset})

```

One reason why the contact form could not be valid is that the contact model also requires a student field. I included the Contact Model for reference.

```auto
class Contact(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    contact_first_name = models.CharField(max_length=50)
    contact_last_name = models.CharField(max_length=50)
    contact_phone_number = models.CharField(max_length=50)
    contact_email = models.EmailField()
    contact_street = models.CharField(max_length=100)
    contact_street_number = models.CharField(max_length=10)
    contact_zipcode = models.CharField(max_length=30)
    contact_city = models.CharField(max_length=100)

    def __str__ (self):
        return f"{self.contact_first_name} {self.contact_last_name}"

```

And the ContactForm does not include a student field, which may cause the form to be incomplete compared to the model. I tried to test this hypothesis by deleting the student field in the model, but still, nothing was saved to the database.

---

<div class="post-metadata">

### Author: ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)
#### Post date: [December 5, 2022, 6:39pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/6 "2022-12-05T18:39:43Z")

</div>

See the docs for [model formsets](https://docs.djangoproject.com/en/4.1/topics/forms/modelforms/#model-formsets)

> [@danielkievit](#):
>
> ```auto
> contacts = contact_formset.save(commit=False)
> for contact in contacts:
> contact.form = student
> contact.save()
> 
> ```

The ModelForm (or formset) `save` method returns a list of the model instances being saved. This means that `contacts` are an iterable of `Contact`, and the individual `contact` in `contacts` are instances of the `Contact` model.

You do not have a field named `form` in the `Contact` model.

---

<div class="post-metadata">

### Author: ![danielkievit](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/danielkievit/32/11154_2.png) [@danielkievit](https://forum.djangoproject.com/u/danielkievit)
#### Post date: [December 5, 2022, 6:57pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/7 "2022-12-05T18:57:23Z")

</div>

I changed contact.form into contact.student. Still, the objects are not saved in the database. Why would the form still not be valid?

---

<div class="post-metadata">

### Author: ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)
#### Post date: [December 5, 2022, 7:15pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/8 "2022-12-05T19:15:18Z")

</div>

> [@danielkievit](#):
>
> I changed contact.form into contact.student.

Have you also changed this to use a modelformset instead of a standard formset?

> [@danielkievit](#):
>
> Shouldn’t I use Student.objects.create to create a new object?

Answered in the docs [Creating forms from models | Django documentation | Django](https://docs.djangoproject.com/en/4.1/topics/forms/modelforms/).

> [@danielkievit](#):
>
> Why would the form still not be valid?

No way to tell from here yet. From what I’m still seeing from what you have posted, you don’t have a working model formset yet.  
Also, I don’t know what your current templates look like, so I can’t tell whether you’re rendering any errors that may be caused by the forms.

You could add some print statements in your code to print any important data or error messages on the console. Or you could use a debugger and put a breakpoint in your code at the `if` statement to see what’s happening there.

For more information on this, see:

- [How errors are displayed](https://docs.djangoproject.com/en/4.1/ref/forms/api/#how-errors-are-displayed)
- [Rendering fields manually](https://docs.djangoproject.com/en/4.1/topics/forms/#rendering-fields-manually)  
and
- [Form.errors](https://docs.djangoproject.com/en/4.1/ref/forms/api/#django.forms.Form.errors)

---

<div class="post-metadata">

### Author: ![danielkievit](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/danielkievit/32/11154_2.png) [@danielkievit](https://forum.djangoproject.com/u/danielkievit)
#### Post date: [December 5, 2022, 7:25pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/9 "2022-12-05T19:25:03Z")

</div>

> [@KenWhitesell](#):
>
> Have you also changed this to use a modelformset instead of a standard formset?

I still don’t quite understand where I can change this. Could you give me a code example of where to change this into a modelformset?

---

<div class="post-metadata">

### Author: ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)
#### Post date: [December 5, 2022, 7:32pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/10 "2022-12-05T19:32:05Z")

</div>

Where do you create the formset?

Change _that_ code such that you’re creating a model formset instead.

---

<div class="post-metadata">

### Author: ![danielkievit](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/danielkievit/32/11154_2.png) [@danielkievit](https://forum.djangoproject.com/u/danielkievit)
#### Post date: [December 5, 2022, 8:03pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/11 "2022-12-05T20:03:58Z")

</div>

This is where I am creating the formset:

```auto
ContactFormSet = formset_factory(ContactForm, extra = 2) # creates two ContactForms
contact_formset = ContactFormSet(request.POST, prefix='contact')

```

I changed it into the following:

```auto
ContactFormSet = modelformset_factory(ContactForm, fields=('contact_first_name', 'contact_last_name', 'contact_phone_number', 'contact_email', 'contact_street', 'contact_street_number', 'contact_zipcode', 'contact_city'), extra = 2) # creates two ContactForms
contact_formset = ContactFormSet(request.POST, prefix='contact')

```

I had to add the fields because it was ‘explicitly required’, but now I get the following error:

```auto
AttributeError: 'ModelFormOptions' object has no attribute 'private_fields'

```

---

<div class="post-metadata">

### Author: ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)
#### Post date: [December 5, 2022, 8:47pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/12 "2022-12-05T20:47:47Z")

</div>

What is the first parameter that you need to pass in the `modelformset_factory` function?

---

<div class="post-metadata">

### Author: ![danielkievit](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/danielkievit/32/11154_2.png) [@danielkievit](https://forum.djangoproject.com/u/danielkievit)
#### Post date: [December 5, 2022, 9:29pm UTC](https://forum.djangoproject.com/t/create-multiple-objects-in-one-form-django/17397/13 "2022-12-05T21:29:46Z")

</div>

It works now! Thanks so much for your help!
