Multiple forms that are different in a view Django
I was wondering if there was a way to have multiple different forms in a single view and to press a single submit button for the information to be stored. I have the following forms, the first one is my general form that should be created first:
class GeneralForm(forms.ModelForm):
name = forms.CharField(max_length=50)
TYPE_CHOICES = (
("C","C"),
("E","E")
)
STATUS_CHOICES = (
("A", "A"),
("F", "F"),
)
type=forms.ChoiceField(choices=STATUS_CHOICES)
number=forms.CharField(max_length=50)
TURN_CHOICES = (
("1", "1"),
("2", "2"),
("3", "3")
)
turn = forms.ChoiceField(choices=TURN_CHOICES)
class Meta:
model = models.General
fields=["name","type","number","turn"]
The second one needs an instance of the first one and so does the third:
class TypeOneForm(forms.ModelForm):
num_chairs=forms.IntegerField()
num_instalations = forms.IntegerField()
total = forms.IntegerField()
num_programs = forms.IntegerField()
class Meta:
model = models.TypeOne
fields=["num_chairs","num_instalations","total","billetes_cien"]
class TypeTwoForm(forms.ModelForm):
num_available_seats=forms.IntegerField()
num_available_instalations = forms.IntegerField()
total = forms.IntegerField()
num_new_programs = forms.IntegerField()
class Meta:
model = models.TypeTwo
fields=["num_available_seats", "num_available_instalations","total","num_programs" ]
These are my models:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
# Create your models here.
class General(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
name = models.CharField(max_length=100)
TYPE_CHOICES = {
"C": "C",
"E": "E",
}
type = models.CharField(max_length=10, choices=TYPE_CHOICES)
STATUS_CHOICES = {
"A": "A",
"F": "F",
}
status = models.CharField(max_length=10, choices=STATUS_CHOICES)
number = models.CharField(max_length=100)
TURN_CHOICES = {
"1": "1",
"2": "2",
"3": "3",
}
turn = models.CharField(max_length=10, choices=TURN_CHOICES)
def __str__(self):
return self.name
class One(models.Model):
one = models.OneToOneField(General, on_delete=models.CASCADE, primary_key=True)
num_chairs = models.IntegerField(default=0)
num_installations = models.IntegerField(default=0)
total = models.IntegerField(default=0)
num_programs = models.IntegerField(default=0)
class Two(models.Model):
two = models.OneToOneField(General, on_delete=models.CASCADE, primary_key=True)
num_available_seats = models.IntegerField(default=0)
num_available_installations = models.IntegerField(default=0)
total = models.IntegerField(default=0)
num_new_programs = models.IntegerField(default=0)
These are my views, but im also not sure how to write the create view and update view for this specific case.
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
)
from .models import General, One,Two
# Create your views here.
class GeneralListView(ListView):
model = General
template_name = "testing/user_testing.html"
context_object_name = 'generals'
class GeneralDetailView(DetailView):
model = General
class GeneralCreateView(LoginRequiredMixin, CreateView):
model = General
fields = '__all__'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class GeneralUpdateView(LoginRequiredMixin, UpdateView):
pass
I was reading I could use FormSets but im not sure if i can use different forms instead of multiple instances of the same form.
The tool you're looking for here is the prefix argument to the form classes. You'll need to override some view methods like get_form and potentially get_form_class because class based views aren't designed to take more than one form type by default. You may also need to override the post method on the views to load the data from the post request into the forms.
The prefix argument means that the form data sent to your django app will be "namespaced" so the form class will only load in the post data it needs from the request.
Depending on how you're rendering your forms you may want to ensure the prefix is also rendered in the HTML input's name attribute.
It's going to be more work to override all the model based view code (in my experience) to get this all wired up: probably use the more generic FormView class but you will need to replicate much of the same logic of the CreateView and UpdateView to do the processing and saving (e.g. calling form.save() in form_valid method of the FormView)
I've always liked CCBV for disentangling class based view method call order.
One other note: you can always render more than one form on a page but have the submissions hit different views. If the forms are dependent on each other it might make life easier to write one form that handles pushing cleaned_data into model instances and saving them. That is to say, depending on the use case and the experience you want your users to have, you'll want to make a decision on how you process the form.