I've been trying to process the data captured from a simple Django Model Form, and for some reason, it isn't possible to access the group of items selected under ModelMultipleChoiceField. I'm only able to access only one item from the selected items in the form.
The Model:
class Student(models.Model):
name = models.CharField(max_length=80)
subjects = models.ManyToManyField(Subject)
house = models.ForeignKey(House, on_delete=models.CASCADE, related_name="students_of_house")
The Model form:
class AddStudentForm(forms.Form):
name = forms.CharField(label="Full Name")
house = ModelChoiceField(queryset=House.objects.all())
subjects = ModelMultipleChoiceField(queryset=Subject.objects.all())
The view function to process POST and create a Student Object.:
def add_student(request):
if request.method == "POST":
name = request.POST["name"]
house = House.objects.get(id=int(request.POST["house"]))
subject_ids = request.POST["subjects"]
new_student = Student(name=name, house=house)
new_student.save()
for sub_id in subject_ids:
new_student.subjects.add(Subject.objects.get(id=int(sub_id))
new_student.save()
return HttpResponseRedirect(reverse("administration:students"))
context = {
"add_student_form": AddStudentForm(),
}
return render(request, "administration/add_student.html", context)
Now, I later tried using form = AddStudentForm(request.POST)
and if form.is_valid():
route, which made it work successfully. But I'm having a hard time figuring out the reason why the above method isn't working.
The code which worked:
def add_student(request):
if request.method == "POST":
form = AddStudentForm(request.POST)
if form.is_valid():
name = form.cleaned_data["name"]
house = form.cleaned_data["house"]
subject_ids = form.cleaned_data["subjects"]
new_student = Student(name=name, house=house)
new_student.save()
for sub_id in subject_ids:
new_student.subjects.add(Subject.objects.get(id=sub_id.id))
new_student.save()
return HttpResponseRedirect(reverse("administration:students"))
context = {
"add_student_form": AddStudentForm(),
}
return render(request, "administration/add_student.html", context)
Why does this work and the view before does not? Any help is much appreciated.
Also tried directly adding subjects instead of using objects.get(), which also didn't work.
In an HttpRequest
object, the GET
and POST
attributes are instances of QueryDict
[django-docs] which is a dictionary-like class customized to deal with multiple values for the same key. This is necessary because some HTML form
elements, notably <select multiple>
, pass multiple values for the same key much like what you are currently facing.
Django Form
handles getting all the values for you by giving you an Iterable
containing all the values in this field. But if you will get it yourself, you have to do this:
subject_ids = request.POST.getlist("subjects", default=[]) #[1]
[1] You are guaranteed to get a list
unless the default
value provided isn’t a list
.