views:

53

answers:

2

I have a problem with needing to provide multiple model backed forms on the same page. I understand how to do this with single forms, i.e. just create both the forms call them something different then use the appropriate names in the template.

Now how exactly do you expand that solution to work with modelformsets? The wrinkle, of course, is that each 'form' must be rendered together in the appropriate fieldset.

For example I want my template to produce something like this:

Home Base Description: Want ice cream? Home Base Description: Want ice cream?

I am using a loop like this to process the results (after form validation)

base_models = base_formset.save(commit=False)
like_models = like_formset.save(commit=False)
for base_model, likes_model in map(None, base_models, likes_models):

which works as I'd expect (I'm using map because the # of forms can be different). The problem is that I can't figure out a way to do the same thing with the templating engine. The system does work if I layout all the base models together then all the likes models after wards, but it doesn't meet the layout requirements.

EDIT: Updated the problem statement to be more clear about what exactly I'm processing (I'm processing models not forms in the for loop)

+2  A: 

In the view:

forms = itertools.izip(base_forms, likes_forms)

In the template:

{% for (base_form,like_form) in forms %}
Ignacio Vazquez-Abrams
Change the above to forms = itertools.izip_longest(base_forms.forms, likes_forms.forms) and you are close. Iterating over these in a shell seems to produce the correct result, but... I am actually dealing with 3 formsets lets call them base, likes, dislikes. In the render I get the likes displayed correctly, but I get no output for base or dislikes. I think this is the right track though. I can change the order of the izip_longest call and it will always only display the middle option.
JT
A: 

After doing a fair amount of poking around and hack experimenting I've come up with the following solution thanks in huge part to Ignacio Vazquez-Abrams :)

In the view:

forms = itertools.izip_longest((None,),base_formset.forms, likes_formset.forms)

In the template:

{% for (garbage1, base_form, like_form, garbage2) in forms %}

The astute reader might notice that the number of arguments in the unpacking list is one larger than what was given to the izip_longest() method. You might also note that there is an effectively blank list as the first argument.

I could never get the template to display the first argument in the list hence the dummy first argument. Also I found that N-1 list elements were being rendered in the template. I also stumbled upon the fact that the template doesn't much care about a size mismatch so by padding the front and the back I was able to get the forms I actually wanted to display!

NOTE: When processing the POST I simply construct the formsets that I'm working with since all the phantom data isn't being sent back through the POST.

Certainly not the cleanest solution and it is probably extremely vulnerable to upgrade breakage, but it's practical enough to work for me for now.

JT