В итоге я выбрал обработку нескольких форм в одном представлении, форму модели посетителя для сведений о посетителе, а затем список пользовательских форм для каждого из мест назначения.
Обработка нескольких форм в одном и том же виде оказалась достаточно простой (по крайней мере, в этом случае, когда не было проблем с проверкой между полями).
Я все еще удивлен, что нет никакой встроенной поддержки для многих-многих отношений с промежуточной моделью, и, просматривая в Интернете, я не нашел прямой ссылки на нее. Я выложу код на случай, если он кому-нибудь поможет.
Первые пользовательские формы:
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
# load initial choices for activities
activities_initial = []
try:
visitorDestination_entry = VisitorDestination.objects.get(visitor=visitor, destination=destination)
activities = visitorDestination_entry.activities.all()
for activity in Activity.objects.all():
if activity in activities:
activities_initial.append(activity.pk)
except VisitorDestination.DoesNotExist:
pass
self.fields['activities'].initial = activities_initial
Я настраиваю каждую форму, передавая объекты Visitor
и Destination
(и флаг "посещен", который для удобства вычисляется снаружи)
Я использую логическое поле, чтобы позволить пользователю выбрать каждый пункт назначения. Поле называется «посещено», однако я установил метку на место назначения, чтобы оно хорошо отображалось.
Действия обрабатываются обычным MultipleChoiceField (я использовал настроенный виджет, чтобы флажки отображались на столе, довольно просто, но могу опубликовать его, если это кому-то нужно)
Тогда код просмотра:
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))
Я просто собираю свои формы назначения в списке и передаю этот список моему шаблону, чтобы он мог перебирать их и отображать. Он работает хорошо, если вы не забыли передать разные префиксы для каждого в конструкторе
Я оставлю вопрос открытым на несколько дней, если у кого-нибудь есть более чистый метод.
Спасибо!