In the end, I decided to process several forms in one view, a visitor model form for visitor details, and then a list of custom forms for each destination.
Processing several forms in one representation turned out to be quite simple (at least in this case, when there were no problems with checking the transverse field).
I am still surprised that there is no built-in support for many, many relationships with the intermediate model, and looking around the Internet, I did not find a direct link to it. I will send the code in case it helps anyone.
Custom forms first:
class VisitorForm(ModelForm): class Meta: model = Visitor exclude = ['destinations'] class VisitorDestinationForm(Form): visited = forms.BooleanField(required=False) activities = forms.MultipleChoiceField(choices = [(obj.pk, obj.name) for obj in Activity.objects.all()], required=False, widget = CheckboxSelectMultipleInline(attrs={'style' : 'display:inline'})) def __init__(self, visitor, destination, visited, *args, **kwargs): super(VisitorDestinationForm, self).__init__(*args, **kwargs) self.destination = destination self.fields['visited'].initial = visited self.fields['visited'].label= destination.destination
I customize each form by passing in Visitor and Destination objects (and a visited flag, which is calculated externally for convenience)
I use a boolean field to allow the user to select each destination. The field is called “visited,” however I set a shortcut to the destination so that it displays beautifully.
Actions are handled by the usual MultipleChoiceField (I used a custom widget to display checkboxes in a table, quite simple, but I can post it if someone needs it)
Then a code of the form:
def edit_visitor(request, pk): visitor_obj = Visitor.objects.get(pk=pk) visitorDestinations = visitor_obj.destinations.all() if request.method == 'POST': visitorForm = VisitorForm(request.POST, instance=visitor_obj) # set up the visitor destination forms destinationForms = [] for destination in Destination.objects.all(): visited = destination in visitorDestinations destinationForms.append(VisitorDestinationForm(visitor_obj, destination, visited, request.POST, prefix=destination.destination)) if visitorForm.is_valid() and all([form.is_valid() for form in destinationForms]): visitor_obj = visitorForm.save() # clear any existing entries, visitor_obj.destinations.clear() for form in destinationForms: if form.cleaned_data['visited']: visitorDestination_entry = VisitorDestination(visitor = visitor_obj, destination=form.destination) visitorDestination_entry.save() for activity_pk in form.cleaned_data['activities']: activity = Activity.objects.get(pk=activity_pk) visitorDestination_entry.activities.add(activity) print 'activities: %s' % visitorDestination_entry.activities.all() visitorDestination_entry.save() success_url = reverse('visitor_detail', kwargs={'pk' : visitor_obj.pk}) return HttpResponseRedirect(success_url) else: visitorForm = VisitorForm(instance=visitor_obj) # set up the visitor destination forms destinationForms = [] for destination in Destination.objects.all(): visited = destination in visitorDestinations destinationForms.append(VisitorDestinationForm(visitor_obj, destination, visited, prefix=destination.destination)) return render_to_response('testapp/edit_visitor.html', {'form': visitorForm, 'destinationForms' : destinationForms, 'visitor' : visitor_obj}, context_instance= RequestContext(request))
I just collect my destination forms in a list and pass that list to my template so that it can iterate over them and display. It works well until you forget to pass a different prefix for each of the constructors.
I will leave the question open for a few days if anyone has a cleaner method.
Thanks!