Cara yang tepat untuk menangani beberapa formulir pada satu halaman di Django


203

Saya memiliki halaman templat yang mengharapkan dua formulir. Jika saya hanya menggunakan satu formulir, semuanya baik-baik saja seperti dalam contoh khas ini:

if request.method == 'POST':
    form = AuthorForm(request.POST,)
    if form.is_valid():
        form.save()
        # do something.
else:
    form = AuthorForm()

Namun, jika saya ingin bekerja dengan beberapa formulir, bagaimana saya membiarkan pandangan tahu bahwa saya mengirimkan hanya satu formulir dan bukan yang lain (yaitu masih meminta. HOST tetapi saya hanya ingin memproses formulir yang dikirimkannya terjadi)?


Ini adalah solusi berdasarkan jawaban mana expectedphrase dan bannedphrase adalah nama-nama tombol submit untuk berbagai bentuk dan expectedphraseform dan bannedphraseform adalah bentuk.

if request.method == 'POST':
    if 'bannedphrase' in request.POST:
        bannedphraseform = BannedPhraseForm(request.POST, prefix='banned')
        if bannedphraseform.is_valid():
            bannedphraseform.save()
        expectedphraseform = ExpectedPhraseForm(prefix='expected')
    elif 'expectedphrase' in request.POST:
        expectedphraseform = ExpectedPhraseForm(request.POST, prefix='expected')
        if expectedphraseform.is_valid():
            expectedphraseform.save() 
        bannedphraseform = BannedPhraseForm(prefix='banned')
else:
    bannedphraseform = BannedPhraseForm(prefix='banned')
    expectedphraseform = ExpectedPhraseForm(prefix='expected')

2
Apakah tidak ada kesalahan logis dengan solusi Anda? Jika Anda mengeposkan 'bannedphrase', bentuk phas yang diharapkan tidak akan terisi.
Ztyx

2
Ini hanya akan menangani satu formulir pada satu waktu, pertanyaannya adalah tentang menangani berbagai bentuk pada saat yang sama
bersinar

Jawaban:


141

Anda punya beberapa pilihan:

  1. Masukkan URL yang berbeda dalam aksi untuk dua formulir. Maka Anda akan memiliki dua fungsi tampilan yang berbeda untuk menangani dua bentuk yang berbeda.

  2. Baca nilai tombol kirim dari data POST. Anda bisa tahu tombol kirim mana yang diklik: Bagaimana saya bisa membangun beberapa tombol kirim bentuk django?


5
3) Tentukan formulir mana yang dikirimkan dari nama bidang dalam data POST. Sertakan beberapa input tersembunyi jika dari Anda tidak memiliki bidang unik dengan semua nilai yang mungkin tidak kosong.
Denis Otkidach

13
4) Tambahkan bidang tersembunyi yang mengidentifikasi formulir dan periksa nilai bidang ini dalam tampilan Anda.
Soviut

Saya akan menjauh dari mencemari data POST jika memungkinkan. Saya sarankan menambahkan parameter GET ke url tindakan bentuk sebagai gantinya.
pygeek

6
# 1 benar-benar pilihan terbaik Anda di sini. Anda tidak ingin mencemari POST Anda dengan bidang tersembunyi dan Anda juga tidak ingin menambatkan tampilan Anda ke templat dan / atau formulir Anda.
meteorainer

5
@meteorainer jika Anda menggunakan nomor satu, apakah ada cara untuk meneruskan kesalahan kembali ke formulir di tampilan induk yang instantiate ini, tanpa menggunakan kerangka pesan atau string permintaan? Jawaban ini tampaknya yang paling dekat, tetapi di sini masih satu pandangan yang menangani kedua bentuk: stackoverflow.com/a/21271659/2532070
YPCrumble

45

Metode untuk referensi di masa depan adalah sesuatu seperti ini. bentuk bannedphrase adalah bentuk pertama dan bentuk bentuk yang diharapkan adalah yang kedua. Jika yang pertama terkena, yang kedua dilewati (yang merupakan asumsi yang masuk akal dalam kasus ini):

if request.method == 'POST':
    bannedphraseform = BannedPhraseForm(request.POST, prefix='banned')
    if bannedphraseform.is_valid():
        bannedphraseform.save()
else:
    bannedphraseform = BannedPhraseForm(prefix='banned')

if request.method == 'POST' and not bannedphraseform.is_valid():
    expectedphraseform = ExpectedPhraseForm(request.POST, prefix='expected')
    bannedphraseform = BannedPhraseForm(prefix='banned')
    if expectedphraseform.is_valid():
        expectedphraseform.save()

else:
    expectedphraseform = ExpectedPhraseForm(prefix='expected')

7
menggunakan awalan = memang merupakan 'cara yang tepat'
Kaya

awalan-kwarg berhasil, bagus!
Stephan Hoyer

1
Ide bagus dengan awalan-awalan itu, kami gunakan sekarang dan itu berfungsi seperti pesona. Tetapi kami masih harus menyisipkan bidang tersembunyi untuk mendeteksi formulir mana yang dikirimkan, karena kedua formulir berada di lightbox (masing-masing dalam yang terpisah). Karena kita perlu membuka kembali lightbox yang benar, kita perlu tahu persis form mana yang dikirimkan, dan kemudian jika form pertama memiliki kesalahan validasi, yang kedua otomatis menang dan form pertama di-reset, meskipun kita masih perlu menampilkan kesalahan dari bentuk pertama. Hanya pikir Anda harus tahu
Enduriel

Bukankah kikuk untuk memperluas pola ini ke kasus tiga bentuk? Seperti, dengan memeriksa is_valid () dari formulir pertama, lalu dua yang pertama, dll ... Mungkin hanya ada handled = Falseyang diperbarui Trueketika ditemukan formulir yang kompatibel?
binki

14

Tampilan berbasis kelas Django menyediakan FormView umum tetapi untuk semua maksud dan tujuan itu dirancang untuk hanya menangani satu formulir.

Salah satu cara untuk menangani beberapa formulir dengan url tindakan target yang sama menggunakan tampilan umum Django adalah dengan memperpanjang 'TemplateView' seperti yang ditunjukkan di bawah ini; Saya menggunakan pendekatan ini cukup sering sehingga saya membuatnya menjadi template Eclipse IDE.

class NegotiationGroupMultifacetedView(TemplateView):
    ### TemplateResponseMixin
    template_name = 'offers/offer_detail.html'

    ### ContextMixin 
    def get_context_data(self, **kwargs):
        """ Adds extra content to our template """
        context = super(NegotiationGroupDetailView, self).get_context_data(**kwargs)

        ...

        context['negotiation_bid_form'] = NegotiationBidForm(
            prefix='NegotiationBidForm', 
            ...
            # Multiple 'submit' button paths should be handled in form's .save()/clean()
            data = self.request.POST if bool(set(['NegotiationBidForm-submit-counter-bid',
                                              'NegotiationBidForm-submit-approve-bid',
                                              'NegotiationBidForm-submit-decline-further-bids']).intersection(
                                                    self.request.POST)) else None,
            )
        context['offer_attachment_form'] = NegotiationAttachmentForm(
            prefix='NegotiationAttachment', 
            ...
            data = self.request.POST if 'NegotiationAttachment-submit' in self.request.POST else None,
            files = self.request.FILES if 'NegotiationAttachment-submit' in self.request.POST else None
            )
        context['offer_contact_form'] = NegotiationContactForm()
        return context

    ### NegotiationGroupDetailView 
    def post(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)

        if context['negotiation_bid_form'].is_valid():
            instance = context['negotiation_bid_form'].save()
            messages.success(request, 'Your offer bid #{0} has been submitted.'.format(instance.pk))
        elif context['offer_attachment_form'].is_valid():
            instance = context['offer_attachment_form'].save()
            messages.success(request, 'Your offer attachment #{0} has been submitted.'.format(instance.pk))
                # advise of any errors

        else 
            messages.error('Error(s) encountered during form processing, please review below and re-submit')

        return self.render_to_response(context)

Template html adalah untuk efek berikut:

...

<form id='offer_negotiation_form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8">
    {% csrf_token %}
    {{ negotiation_bid_form.as_p }}
    ...
    <input type="submit" name="{{ negotiation_bid_form.prefix }}-submit-counter-bid" 
    title="Submit a counter bid"
    value="Counter Bid" />
</form>

...

<form id='offer-attachment-form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8">
    {% csrf_token %}
    {{ offer_attachment_form.as_p }}

    <input name="{{ offer_attachment_form.prefix }}-submit" type="submit" value="Submit" />
</form>

...

1
Saya bergumul dengan masalah yang sama ini dan berusaha menemukan cara untuk memproses setiap posting dalam tampilan formulir yang terpisah dan kemudian mengalihkan ke tampilan template umum. Intinya adalah untuk membuat tampilan template bertanggung jawab atas konten get dan tampilan form untuk penyimpanan. validasi adalah masalah. menyimpan formulir ke sesi terlintas dalam pikiran saya ... Masih mencari solusi bersih.
Daniele Bernardini

14

Saya membutuhkan beberapa formulir yang divalidasi secara independen pada halaman yang sama. Konsep kunci yang saya lewatkan adalah 1) menggunakan awalan formulir untuk nama tombol kirim dan 2) formulir tidak terikat tidak memicu validasi. Jika ini membantu orang lain, berikut adalah contoh sederhana saya dari dua bentuk AForm dan BForm menggunakan TemplateView berdasarkan jawaban oleh @ adam-nelson dan @ daniel-sokolowski dan komentar oleh @zeraien ( https://stackoverflow.com/a/17303480 / 2680349 ):

# views.py
def _get_form(request, formcls, prefix):
    data = request.POST if prefix in request.POST else None
    return formcls(data, prefix=prefix)

class MyView(TemplateView):
    template_name = 'mytemplate.html'

    def get(self, request, *args, **kwargs):
        return self.render_to_response({'aform': AForm(prefix='aform_pre'), 'bform': BForm(prefix='bform_pre')})

    def post(self, request, *args, **kwargs):
        aform = _get_form(request, AForm, 'aform_pre')
        bform = _get_form(request, BForm, 'bform_pre')
        if aform.is_bound and aform.is_valid():
            # Process aform and render response
        elif bform.is_bound and bform.is_valid():
            # Process bform and render response
        return self.render_to_response({'aform': aform, 'bform': bform})

# mytemplate.html
<form action="" method="post">
    {% csrf_token %}
    {{ aform.as_p }}
    <input type="submit" name="{{aform.prefix}}" value="Submit" />
    {{ bform.as_p }}
    <input type="submit" name="{{bform.prefix}}" value="Submit" />
</form>

Saya pikir ini sebenarnya solusi bersih. Terima kasih.
chhantyal

Saya sangat suka solusi ini. Satu pertanyaan: apakah ada alasan mengapa _get_form () bukan metode kelas MyView?
serangan udara

1
@ AndréTerra pasti bisa, meskipun Anda mungkin ingin memilikinya di kelas generik yang diwarisi dari TemplateView sehingga Anda dapat menggunakannya kembali di tampilan lain.
ybendana

1
Ini solusi hebat. Saya perlu mengubah satu baris __get_form sehingga akan berfungsi: data = request.POST if prefix in next(iter(request.POST.keys())) else None Jika intidak, tidak akan berhasil.
larapsodia

Menggunakan tag <form> tunggal seperti ini berarti bidang wajib diisi secara global saat itu harus berbentuk sesuai dengan tombol kirim yang diklik. Membagi menjadi dua tag <form> (dengan aksi yang sama) berfungsi.
Flash

3

Ingin membagikan solusi saya di mana Formulir Django tidak digunakan. Saya memiliki beberapa elemen formulir pada satu halaman dan saya ingin menggunakan tampilan tunggal untuk mengelola semua permintaan POST dari semua formulir.

Apa yang saya lakukan adalah saya telah memperkenalkan tag input yang tidak terlihat sehingga saya bisa meneruskan parameter ke tampilan untuk memeriksa formulir yang telah dikirimkan.

<form method="post" id="formOne">
    {% csrf_token %}
   <input type="hidden" name="form_type" value="formOne">

    .....
</form>

.....

<form method="post" id="formTwo">
    {% csrf_token %}
    <input type="hidden" name="form_type" value="formTwo">
   ....
</form>

views.py

def handlemultipleforms(request, template="handle/multiple_forms.html"):
    """
    Handle Multiple <form></form> elements
    """
    if request.method == 'POST':
        if request.POST.get("form_type") == 'formOne':
            #Handle Elements from first Form
        elif request.POST.get("form_type") == 'formTwo':
            #Handle Elements from second Form

Saya pikir ini adalah jalan keluar yang baik dan lebih mudah
Shedrack

2

Ini agak terlambat, tetapi ini adalah solusi terbaik yang saya temukan. Anda membuat kamus pencarian untuk nama formulir dan kelasnya, Anda juga harus menambahkan atribut untuk mengidentifikasi formulir, dan dalam pandangan Anda, Anda harus menambahkannya sebagai bidang tersembunyi, dengan form.formlabel.

# form holder
form_holder = {
    'majeur': {
        'class': FormClass1,
    },
    'majsoft': {
        'class': FormClass2,
    },
    'tiers1': {
        'class': FormClass3,
    },
    'tiers2': {
        'class': FormClass4,
    },
    'tiers3': {
        'class': FormClass5,
    },
    'tiers4': {
        'class': FormClass6,
    },
}

for key in form_holder.keys():
    # If the key is the same as the formlabel, we should use the posted data
    if request.POST.get('formlabel', None) == key:
        # Get the form and initate it with the sent data
        form = form_holder.get(key).get('class')(
            data=request.POST
        )

        # Validate the form
        if form.is_valid():
            # Correct data entries
            messages.info(request, _(u"Configuration validée."))

            if form.save():
                # Save succeeded
                messages.success(
                    request,
                    _(u"Données enregistrées avec succès.")
                )
            else:
                # Save failed
                messages.warning(
                    request,
                    _(u"Un problème est survenu pendant l'enregistrement "
                      u"des données, merci de réessayer plus tard.")
                )
        else:
            # Form is not valid, show feedback to the user
            messages.error(
                request,
                _(u"Merci de corriger les erreurs suivantes.")
            )
    else:
        # Just initiate the form without data
        form = form_holder.get(key).get('class')(key)()

    # Add the attribute for the name
    setattr(form, 'formlabel', key)

    # Append it to the tempalte variable that will hold all the forms
    forms.append(form)

Saya harap ini akan membantu di masa depan.


2

Jika Anda menggunakan pendekatan dengan tampilan berbasis kelas dan attr 'action' yang berbeda yang saya maksud

Masukkan URL yang berbeda dalam aksi untuk dua formulir. Maka Anda akan memiliki dua fungsi tampilan yang berbeda untuk menangani dua bentuk yang berbeda.

Anda dapat dengan mudah menangani kesalahan dari berbagai bentuk menggunakan kelebihan beban get_context_data metode , mis:

views.py:

class LoginView(FormView):
    form_class = AuthFormEdited
    success_url = '/'
    template_name = 'main/index.html'

    def dispatch(self, request, *args, **kwargs):
        return super(LoginView, self).dispatch(request, *args, **kwargs)

    ....

    def get_context_data(self, **kwargs):
        context = super(LoginView, self).get_context_data(**kwargs)
        context['login_view_in_action'] = True
        return context

class SignInView(FormView):
    form_class = SignInForm
    success_url = '/'
    template_name = 'main/index.html'

    def dispatch(self, request, *args, **kwargs):
        return super(SignInView, self).dispatch(request, *args, **kwargs)

    .....

    def get_context_data(self, **kwargs):
        context = super(SignInView, self).get_context_data(**kwargs)
        context['login_view_in_action'] = False
        return context

templat:

<div class="login-form">
<form action="/login/" method="post" role="form">
    {% csrf_token %}
    {% if login_view_in_action %}
        {% for e in form.non_field_errors %}
            <div class="alert alert-danger alert-dismissable">
                {{ e }}
                <a class="panel-close close" data-dismiss="alert">×</a>
            </div>
        {% endfor %}
    {% endif %}
    .....
    </form>
</div>

<div class="signin-form">
<form action="/registration/" method="post" role="form">
    {% csrf_token %}
    {% if not login_view_in_action %}
        {% for e in form.non_field_errors %}
            <div class="alert alert-danger alert-dismissable">
                {{ e }}
                <a class="panel-close close" data-dismiss="alert">×</a>
            </div>
        {% endfor %}
    {% endif %}
   ....
  </form>
</div>

2

melihat:

class AddProductView(generic.TemplateView):
template_name = 'manager/add_product.html'

    def get(self, request, *args, **kwargs):
    form = ProductForm(self.request.GET or None, prefix="sch")
    sub_form = ImageForm(self.request.GET or None, prefix="loc")
    context = super(AddProductView, self).get_context_data(**kwargs)
    context['form'] = form
    context['sub_form'] = sub_form
    return self.render_to_response(context)

def post(self, request, *args, **kwargs):
    form = ProductForm(request.POST,  prefix="sch")
    sub_form = ImageForm(request.POST, prefix="loc")
    ...

templat:

{% block container %}
<div class="container">
    <br/>
    <form action="{% url 'manager:add_product' %}" method="post">
        {% csrf_token %}
        {{ form.as_p }}
        {{ sub_form.as_p }}
        <p>
            <button type="submit">Submit</button>
        </p>
    </form>
</div>
{% endblock %}

4
Bisakah Anda jelaskan jawaban Anda? Ini akan membantu orang lain dengan masalah yang serupa dan dapat membantu men-debug kode penanya atau Anda ...
creyD

0

Berikut ini cara sederhana untuk menangani hal di atas.

Dalam Templat Html kami menempatkan Posting

<form action="/useradd/addnewroute/" method="post" id="login-form">{% csrf_token %}

<!-- add details of form here-->
<form>
<form action="/useradd/addarea/" method="post" id="login-form">{% csrf_token %}

<!-- add details of form here-->

<form>

Dalam penglihatan

   def addnewroute(request):
      if request.method == "POST":
         # do something



  def addarea(request):
      if request.method == "POST":
         # do something

Di URL Berikan info yang dibutuhkan seperti

urlpatterns = patterns('',
url(r'^addnewroute/$', views.addnewroute, name='addnewroute'),
url(r'^addarea/', include('usermodules.urls')),
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.