Hello, I’ve been searching for an answer to this for a while, but haven’t come across anything applicable to my situation.
I have a form with 3 levels. Level 1 being the main form, level 2 being a formset and level 3 being a formset belonging to a form of a level 2 formset. Level 2 and 3 forms have dynamic prefixes.
Yes, this is a case where you want to restructure your view a little bit. How you do that is going to depend upon what exactly is happening within #DO STUFF and #Do stuff, and whether your formsets should be processed if and only if all formsets are valid.
If you want all forms to be validated before doing any of the work within them, then perform your nested validations first before doing “stuff” on any of them. You could even set a variable for tracking across levels whether any error exists.
Then, after you’ve validated all formsets, check that variable and either process the formsets as appropriate or rerender the page with all the bound formsets and error messages.
I would recommend writing everything from scratch in this situation. You’re going to spend more time trying to get formsets to submit to your will than you will gain from using it.
Again, to some degree this depends upon what “do stuff” really is. Without knowing the details of what you’re doing with the data in these forms, it’s tough to make a firm recommendation.
The simplest answer is to do what you’re doing now, except remove the #do stuff from those loops. Bind and validate all the forms, then iterate through the forms again to “do stuff”.
Create a list in the “outer” formsets to hold the list of the “inner” formsets.
I kind of understand what you mean, but I’m a little confused about the list component. Will the lists be used in the context if the formsets are invalid?
With your above advice I’ve modified the code to validate and bind the form data first
monthly_formset = MonthlyActivityDaysFormset(request.POST, prefix='m')
monthly_list = []
if monthly_formset.is_valid():
for monthly_form in monthly_formset:
diff_times_formset = DifferentTimesFormset(request.POST, prefix=monthly_form.prefix+'-dt')
if diff_times_formset.is_valid():
diff_times_list = []
diff_times_list.append(diff_times_formset)
monthly_list.append(diff_times_list)
else:
diff_times_list = []
diff_times_list.append(diff_times_formset)
monthly_list.append(diff_times_list)
else:
for monthly_form in monthly_formset:
diff_times_formset = DifferentTimesFormset(request.POST, prefix=monthly_form.prefix+'-dt')
if diff_times_formset.is_valid():
diff_times_list = []
diff_times_list.append(diff_times_formset)
monthly_list.append(diff_times_list)
else:
diff_times_list = []
diff_times_list.append(diff_times_formset)
monthly_list.append(diff_times_list)
I appreciate your assistance. I’m just trying to wrap my head around this.
On a side note, I realised I didn’t need to check if the individual forms of the diff_times_formset were valid, so I have removed that.
I don’t think you need what you’re showing as diff_times_list here, as all you’re doing is creating a list with one element (diff_times_formset).
If I’m understanding what you have, you should be able to append diff_times_formset to monthly_list, and remove all the code related to diff_times_list.
Additionally, if you’re going to do the same things on both sides of an if xxx.is_valid(), then you don’t need to use an if. Call the function to perform validation and move on.
If I’m following this correctly, this should leave you with something like:
monthly_formset = MonthlyActivityDaysFormset(request.POST, prefix='m')
monthly_list = []
monthly_formset.is_valid()
for monthly_form in monthly_formset:
diff_times_formset = DifferentTimesFormset(request.POST, prefix=monthly_form.prefix+'-dt')
diff_times_formset.is_valid()
monthly_list.append(diff_times_formset)
Now, since you want to track to see if any form has any errors, you might want to add something like this:
any_errors = False
monthly_formset = MonthlyActivityDaysFormset(request.POST, prefix='m')
if not monthly_formset.is_valid():
any_errors = True
monthly_list = []
for monthly_form in monthly_formset:
diff_times_formset = DifferentTimesFormset(request.POST, prefix=monthly_form.prefix+'-dt')
if not diff_times_formset.is_valid():
any_errors = True
monthly_list.append(diff_times_formset)
I do have a follow up question regarding the structure of the template if you’re able to assist. Assuming the formset is invalid at some level and I return the bound forms to the template, is there a correct way to structure it so that each diff_times_formset is placed beneath it’s respective monthly_form?
Would it be best to use javascript to place them in the correct position or loop through the list inside the monthly_formset loop and place them if their prefix matches?
Neither. If you have multiple MonthlyActivityDaysFormset on a page, then you need to organize these nested formsets accordingly.
Instead of having monthly_list be a variable at the function level, you could make it an attribute of the formset. e.g., monthly_formset.monthly_list = []
You would then be able to iterate through the different lists for each one.
strong agree here, op should consider revisiting why there is a need to nest their forms in this way and how the desired function of the form and associated models and views are meant to “work”