Можно ли отправить несколько форм одновременно, используя Django просмотров? - PullRequest
1 голос
/ 30 января 2020

То, чего я хочу достичь sh - это возможность отправлять несколько форм, содержащихся на одной странице. То, что я сделал, предоставлено представление, которое возвращает 8 формы. Возможно ли в шаблоне иметь, скажем, кнопку, которая будет отправлять все формы, содержащиеся на странице в одном запросе POST?

Вот некоторый код, на мой взгляд:

def get_all_forms(request):
    context = {}
    if request.method == 'POST':
        return
    else:
        for x in range(8):
            context[x] = Form()
    return render(request, 'app/all_forms.html', {'form': context})

Ответы [ 3 ]

3 голосов
/ 26 февраля 2020

Некоторые проблемы с вашим кодом:

  1. Отсутствует проверка ввода.
  2. Вероятно, вы не сможете различить данные, представленные 8 экземплярами Form(), потому что все экземпляры будут иметь одинаковый атрибут HTML name для элементов <input .../>.

    Один из способов выделить экземпляры - использовать аргумент ключевого слова prefix . Например:

    def get_all_forms(request):
        NUM_FORMS = 8
        if request.method == 'POST':
            forms = [Form(request.POST, prefix=i) for i in range(NUM_FORMS)]
            if all((form.is_valid() for form in forms)):
                # Do something with the submitted data ...
                pass
        else:
            forms = [Form(prefix=i) for i in range(NUM_FORMS)]
        return render(request, 'app/all_forms.html', {'forms': forms})
    

    Но гораздо лучшим решением является использование наборов форм (см. Ниже).

Решение

Formsets позволяют вам иметь несколько копий одной и той же формы на одной странице и обрабатывать их все в одном POST-запросе. Используя набор форм, Django способен обрабатывать проверку для всех форм. Пример:

Если ваша форма:

from django import forms

class MyForm(forms.Form):
    # ...

Создайте набор форм с 8 пустыми формами:

from django.forms import formset_factory

MyFormSet = formset_factory(MyForm, extra=8)  # Create 8 forms.

Затем вы можете использовать набор форм в своем представлении:

def get_all_forms(request):
    if request.method == 'POST':
        formset = MyFormSet(request.POST)
        if formset.is_valid():  # Validates all 8 forms.
            # ... (submitted data is in formset.cleaned_data)
            return redirect('somewhere ......')
    else:
        formset = MyFormSet()
    return render(request, 'app/all_forms.html', {'formset': formset})

И вы можете отобразить все формы в вашем шаблоне, используя только это:

<form action="" method="post">
    {% csrf_token %}
    <table>
        {{ formset }}
    </table>
    <input type="submit"/>
</form>

Дополнительная информация из документации:

1 голос
/ 30 января 2020

Да, это возможно. В вашем шаблоне HTML вы должны поместить все данные формы в один тег <form>.

В представлении django вы должны использовать префиксы для форм https://docs.djangoproject.com/en/3.0/ref/forms/api/#prefixes -for-forms

0 голосов
/ 27 февраля 2020

Да. Ответ Лин Тролля включает в себя два важных момента.

Вероятно, вы найдете это проще всего, используя функциональные представления (Generi c FormView и производные классы действительно не работают с> 1 формой). О. Я также не нахожу каноническую форму FBV полезной, особенно с двумя формами.

Вот представление в двух формах, которое я написал ранее. Код был преобразован в то, что я считаю лучшим шаблоном, для проверки обеих форм и повторного отображения, если что-то не так в верхней части, а затем вы просто выполняете фактическую работу по созданию и сохранению объектов внизу.

def receive_uncoated( request): #Function based view

    # let's put form instantiation in one place not two, and reverse the usual test. This
    # makes for a much nicer layout with actions not sandwiched by "boilerplate" 
    # note any([ ]) forces invocation of both .is_valid() methods 
    # so errors in second form get shown even in presence of errors in first

    args = [request.POST, ] if request.method == "POST" else []
    batchform = CreateUncWaferBatchForm( *args )
    po_form =  CreateUncWaferPOForm(     *args, prefix='po')
    if request.method != "POST" or any(  
        [ not batchform.is_valid(), not po_form.is_valid() ]):

        return render(request, 'wafers/receive_uncoated.html',   # can get this out of the way at the top
            {'batchform': batchform,  
            'po_form': po_form, 
        })

    #POST, everything is valid, do the work here
    # create and save some objects based on the validated forms ... 

    return redirect( 'wafers:ok' )   
...