Create a formset with nested formsets

Hi all, in my project I currently manage to collect data with a formset and create a JSON to add to my model. To give a context, each form correspond with a shock (sector + amount fields) and now I generate the JSON putting that data into a dictionary:
{'sector_name1': amount1, 'sector_name2': amount2, 'sector_name3': amount3} to be stored on the model like this:
shocks = models.JSONField(null=True, blank=True)

Now, I need to create several of those formsets into a higher level key for a year, so each year will have a dict composed by several shocks generated with my current formset. This is what I want to get from the user input:

{
    year1: {'sector_name1': amount1, 'sector_name2': amount2, 'sector_name3': amount3}, 
    year2: {'sector_name1': amount1, 'sector_name2': amount2, 'sector_name3': amount3}, 
    year3: {'sector_name1': amount1, 'sector_name2': amount2, 'sector_name3': amount3}
}

My questions is how is the best way to approach this data collection in a single page form stage. I think of creating a form for year to store the year_number and a formset of my shocks, but I am struggling to implement that. As UX, I want to display a first year and allow the user by clicking to add more years with its correspondings shocks forms, and save all in a go. Not using any JS framework. Any ideas of how to get this done?

The relevant part of the code is here (forms, view, template and JavaScript) if anyone wants to take a deeper look.

Many thanks in advance!

To implement a multi-year input structure with formsets for each year, here’s an approach that should work without needing a JavaScript framework:
Define a Form and Formset:
Create a form for the Shock model to capture sector and amount.
Use a formset for ShockForm, allowing multiple shocks per year.
Define a separate form for the Year, which will capture the year number.

python

from django import forms
from django.forms import formset_factory

class ShockForm(forms.Form):
sector = forms.CharField()
amount = forms.DecimalField()

ShockFormSet = formset_factory(ShockForm, extra=1)

class YearForm(forms.Form):
year = forms.IntegerField()

Create the View:
Use a formset for YearForm, where each form in the formset represents a different year.
Within each YearForm, nest a ShockFormSet for the shocks of that year.

python
from django.shortcuts import render
from django.forms import formset_factory

YearFormSet = formset_factory(YearForm, extra=1)

def collect_data_view(request):
if request.method == ‘POST’:
year_formset = YearFormSet(request.POST, prefix=‘year’)
shock_formsets = [
ShockFormSet(request.POST, prefix=f’shock_{i}')
for i in range(len(year_formset))
]

    if year_formset.is_valid() and all(sf.is_valid() for sf in shock_formsets):
        data = {
            year_form.cleaned_data['year']: {
                shock_form.cleaned_data['sector']: shock_form.cleaned_data['amount']
                for shock_form in shock_formset
            }
            for year_form, shock_formset in zip(year_formset, shock_formsets)
        }

        # Save data to the model
        # MyModel.objects.create(shocks=data)

else:
    year_formset = YearFormSet(prefix='year')
    shock_formsets = [ShockFormSet(prefix='shock_0')]

return render(request, 'my_template.html', {
    'year_formset': year_formset,
    'shock_formsets': shock_formsets
})

Template Structure:
Display each YearForm with its corresponding ShockFormSet.
Allow users to add another YearForm and its shock formset by submitting the form with an “Add Year” button.

html

{% csrf_token %} {{ year_formset.management_form }} {% for year_form in year_formset %} {{ year_form.year }}
{{ shock_formsets.forloop.counter0.management_form }} {% for shock_form in shock_formsets.forloop.counter0 %} {{ shock_form.sector }} {{ shock_form.amount }} {% endfor %}
{% endfor %} Add Year Save ```

This setup should give you the structure to capture data for multiple years and corresponding shocks in a single form.