Search code examples
pythondjangodjango-forms

how to create a form using multiple models in Django?


So I am trying to make a student result management project. Where I can add students subject grade and exam result. In the project i need to make a form where i can add marks of a subject to all the students of a grade(Class) at once. I want the look like the photo. Example Example .

I tried to create individual form for every sector and try to combine them. But it didn't work. Now I need to know how can I make a form using many models in Django.


Solution

  • As far as I understand you want to create a form in Django that handles multiple models at once, well you can use ModelForm combined with inline formsets, by using inline formsets in Django, you can manage forms for multiple related models (like Student, Subject, and ExamResult) in one form. This is a clean and efficient way to manage forms for related data, and it avoids the complexity of manually creating individual forms for each record. If you want to learn more about inline formsets click here.

    Let's do this step-by-step:

    1. Create four models Grade, Subject, Student, ExamResult.

      from django.db import models
      
      class Grade(models.Model):
          name = models.CharField(max_length=50)
      
      class Subject(models.Model):
          name = models.CharField(max_length=100)
      
      class Student(models.Model):
          first_name = models.CharField(max_length=100)
          last_name = models.CharField(max_length=100)
          grade = models.ForeignKey(Grade, on_delete=models.CASCADE)
      
      class ExamResult(models.Model):
          student = models.ForeignKey(Student, on_delete=models.CASCADE)
          subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
          marks = models.DecimalField(max_digits=5, decimal_places=2)
      
    2. For each model, create a ModelForm which helps in generating the form fields dynamically.

      from django import forms
      from .models import ExamResult
      
      class ExamResultForm(forms.ModelForm):
          class Meta:
              model = ExamResult
              fields = ['subject', 'marks']  # Specify the fields you want in the form
      
    3. Create an inline formset for the ExamResult model.

      from django.forms import modelformset_factory
      from django.forms.models import inlineformset_factory
      from .models import Student, ExamResult, Subject
      
      def exam_result_formset(request, grade_id):
          students = Student.objects.filter(grade_id=grade_id)
          subjects = Subject.objects.all()
          formsets = []
      
          for student in students:
              for subject in subjects:
                  formset = inlineformset_factory(Student, ExamResult, fields=('subject', 'marks'), extra=1)
                  formset_instance = formset(queryset=ExamResult.objects.filter(student=student, subject=subject))
                  formsets.append((student, subject, formset_instance))
      
          return render(request, 'exam_results_form.html', {'formsets': formsets})
      
    4. Create a template that will loop through all formsets and render them in the HTML form.

      <form method="post">
          {% csrf_token %}
          {% for student, subject, formset in formsets %}
              <h3>{{ student.first_name }} - {{ subject.name }}</h3>
              <div>
                  {% for form in formset %}
                      <div>{{ form.as_p }}</div>
                  {% endfor %}
              </div>
          {% endfor %}
          <button type="submit">Save Results</button>
      </form>
      
    5. When the form is submitted, you can process the data and save the results in views.py file.

      def save_exam_results(request, grade_id):
          if request.method == 'POST':
              formsets = []
              students = Student.objects.filter(grade_id=grade_id)
              subjects = Subject.objects.all()
      
              for student in students:
                  for subject in subjects:
                      formset = inlineformset_factory(Student, ExamResult, fields=('subject', 'marks'), extra=1)
                      formset_instance = formset(request.POST)
                      if formset_instance.is_valid():
                          formset_instance.save()
                      else:
                          formsets.append((student, subject, formset_instance))
      
              return redirect('exam_results_list')  # Redirect to a page with results or confirmation
      

    Thank you and have a good rest of your day buddy:)