Making all fields in the model formset required in HTML

I have a page that uses an inlineformset_factory. The goal is to have parents add their information and then their students information. I need ALL of the student fields to be required in HTML. So far, only the parent information has the required attribute in HTML. The number of students is passed in as part of the url - example.com/register/3 would be if a parent has 3 students to register.

So, if a parent chooses 3 students on the previous page, they would be sent to example.com/register/3 and it would show the parent form with the 3 student forms. THIS IS WORKING. What is not working is making ALL three of the student forms required in the HTML.

My view:

def homeschoolRegister(request, num):
    parent_form = ParentInformationForm()
    StudentFormSet = inlineformset_factory(ParentInformation, StudentInformation, fields=('student_first_name', 'student_last_name', 'grade'), can_delete=False, extra=int(num))
    if request.method == 'POST':
        parent_form_post = ParentInformationForm(request.POST)
        if parent_form_post.is_valid():
            parent = parent_form_post.save(commit=False)
            parent.save()
            student_form_post = StudentFormSet(request.POST, instance=parent)
            if student_form_post.is_valid():
                student_form_post.save()
    try:
        if int(num) <= 0:
            return redirect(reverse('home'))
        if int(num) > 15:
            return redirect(reverse('home'))
        context = {'parent_form':parent_form ,'formset':StudentFormSet}
        return render(request, 'home/home-register.html', context)
    except:
        return redirect(reverse('homeschool-home'))

My HTML:

{% extends 'base.html' %}
{% load tailwind_filters %}
{% load static %}
{% block title %} Register {% endblock title %}
{% block content %}

<div class="max-w-4xl mx-auto pb-20 px-5">
    <form method="POST" class="pb-10">
        {% csrf_token %}
        {{parent_form|crispy}}
        {{ formset.management_form}}
        {% for form in formset %}
        {{form|crispy}}
        {% endfor %}
        <button class="standard-button" type="submit">Submit</button>
    </form>
</div>

{% endblock content %}

The models:

class ParentInformation(models.Model):
    parent_name = models.CharField(max_length=250, null=False, blank=False)
    parent_phone = models.CharField(max_length=11, null=False, blank=False)
    parent_email = models.EmailField(max_length=500, null=False, blank=False)
    school_year = models.CharField(max_length=4, null=True, blank=True)

    def __str__(self): 
        return str(self.parent_name)

class StudentInformation(models.Model):
    GRADES = (
        ('Youngest Scholar', 'Youngest Scholar'),
        ('Kindergarten', 'Kindergarten'),
        ('1st', '1st Grade'),
        ('2nd', '2nd Grade'),
        ('3rd', '3rd Grade'),
        ('4th', '4th Grade'),
        ('5th', '5th Grade'),
        ('6th', '6th Grade'),
        ('7th', '7th Grade'),
        ('8th', '8th Grade'),
        ('9th', '9th Grade'),
        ('10th', '10th Grade'),
        ('11th', '11th Grade'),
        ('12th', '12th Grade'),
    )
    parent = models.ForeignKey(ParentInformation, on_delete=models.PROTECT, null=False, blank=False)
    student_first_name = models.CharField(max_length=150, null=False, blank=False)
    student_last_name = models.CharField(max_length=150, null=False, blank=False)
    grade = models.CharField(max_length=16, choices=GRADES, null=False, blank=False)

    def __str__(self): 
        return str(self.student_first_name) + " " + str(self.student_last_name)

I understand that it is unusual to have all the formsets required but that is what the business logic calls for here. Thank you in advance

Looking at the docs for required, the first idea that pops into my head would be to specify the required attribute for each field in the widgets dict being passed through the modelformset_factory to the modelform_factory.

Or, after you’ve created the formset, you could iterate over the forms and set the required attribute on all the fields programmatically.

(Since you’re not dynamically adding or removing forms in the formset, I don’t believe this poses any real risk.)

Ken,
Thanks for the thoughtful answer. Did not realize the widget attribute was usable. Worked great.

For anyone looking in the future, I did

    StudentFormSet = inlineformset_factory(ParentInformation, StudentInformation, fields=('student_first_name', 'student_last_name', 'grade'), can_delete=False, extra=int(num), widgets={'student_first_name': TextInput(attrs={'required': True}), 'student_last_name': TextInput(attrs={'required': True}), 'grade': Select(attrs={'required': True})})