2009-09-08 7 views
148

Ich habe eine Vorlage Seite erwartet zwei Formen. Wenn ich ein Formular nur verwenden, sind die Dinge in Ordnung, in diesem typischen Beispiel:Die richtige Art, mehrere Formulare auf einer Seite in Django zu behandeln

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

Wenn ich will, aber mit mehreren Formularen arbeitet, wie lasse ich die Ansicht, dass ich nur eine der Formen bin einreichen und nicht der andere (dh es ist immer noch request.POST, aber ich möchte nur das Formular bearbeiten, für das das Senden passiert ist)?


Dies ist die Lösung auf der Antwort basiert wo expectedphrase und bannedphrase sind die Namen der Senden-Schaltflächen für die verschiedenen Formen und expectedphraseform und bannedphraseform die Formulare.

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') 
+1

Gibt es nicht einen logischen Fehler mit Ihrer Lösung? Wenn Sie "bannedphrase" posten, wird das erwartete Passwort nicht ausgefüllt. – Ztyx

+1

Dies wird nur ein Formular zu einer Zeit behandeln, die Frage ist über die gleichzeitige Verarbeitung der Formulare – shining

Antwort

98

Sie haben ein paar Optionen:

  1. Setzen Sie verschiedene URLs in der Aktion für die beiden Formen. Dann haben Sie zwei verschiedene Ansichtsfunktionen, um mit den zwei verschiedenen Formen umzugehen.

  2. Lesen Sie die Sendeschaltflächenwerte aus den POST-Daten. Man kann sagen, die Senden-Schaltfläche geklickt wurde: How can I build multiple submit buttons django form?

+3

3) Bestimmen Sie, welche Form von Feldnamen in POST-Daten übermittelt wird. Schließen Sie einige versteckte Eingaben ein, wenn Ihre From-Felder keine eindeutigen Felder enthalten und alle möglichen Werte nicht leer sind. –

+8

4) Fügen Sie ein verstecktes Feld hinzu, das das Formular identifiziert, und überprüfen Sie den Wert dieses Feldes in Ihrer Ansicht. – Soviut

+0

Ich würde die Post-Daten nach Möglichkeit nicht verschmutzen. Ich empfehle, stattdessen einen GET-Parameter zur Formularaktions-URL hinzuzufügen. – pygeek

34

Verfahren für die Zukunft so etwas wie dieses. bannedphraseform ist die erste Form und erwartete phraseform ist die zweite. Wenn der erste getroffen wird, wird die zweite übersprungen (die in diesem Fall eine vernünftige Annahme ist):

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') 
+4

mit Präfix = ist in der Tat der "richtige Weg" – Rich

+0

Präfix-Kwarg hat den Job, schön! –

+0

Tolle Idee mit diesen Präfixen, wir haben diese jetzt verwendet und sie funktionieren wie ein Zauber. Aber wir mussten noch ein verstecktes Feld einfügen, um zu erkennen, welches Formular gesendet wurde, da sich beide Formulare in einem Leuchtkasten befinden (jeweils in einem separaten). Da wir den richtigen Leuchtkasten erneut öffnen müssen, müssen wir genau wissen, welches Formular gesendet wurde, und wenn das erste Formular Validierungsfehler aufweist, gewinnt der zweite automatisch und das erste Formular wird zurückgesetzt, obwohl wir weiterhin die Fehler anzeigen müssen erste Form. Nur gedacht, dass Sie wissen sollten – Enduriel

11

Djangos Klasse basierte Ansichten bieten eine allgemeine Formview, sondern für alle Absichten und Zwecke ist es entworfen, um nur handhaben eine Form.

Eine Möglichkeit, mehrere Formulare mit derselben Zielaktions-URL unter Verwendung von Djangos generischen Ansichten zu behandeln, besteht darin, die 'TemplateView' wie unten gezeigt zu erweitern; Ich benutze diesen Ansatz oft genug, um es in ein Eclipse-IDE-Template zu verwandeln.

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) 

HTML-Template ist mit folgendem Inhalt:

... 

<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

Ich habe Probleme mit demselben Problem und habe versucht, einen Weg zu finden, jeden Beitrag in einer separaten Formularansicht zu bearbeiten und dann in eine gemeinsame Vorlagenansicht umzuleiten. Der Zweck besteht darin, die Vorlagenansicht für den Get-Inhalt und die Formularansichten für das Speichern verantwortlich zu machen. Validierung ist jedoch ein Problem. Das Speichern der Formulare in der Sitzung kam mir in den Sinn ... Immer noch auf der Suche nach einer sauberen Lösung. –

2

Dies ein bisschen spät, aber das ist die beste Lösung, die ich gefunden. Sie erstellen ein Nachschlagewörterbuch für den Formularnamen und seine Klasse, Sie müssen auch ein Attribut hinzufügen, um das Formular zu identifizieren, und in Ihren Ansichten müssen Sie es als ein verstecktes Feld mit dem form.formlabel hinzufügen.

# 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) 

Ich hoffe, dass dies in der Zukunft helfen wird.

0

Hier ist ein einfacher Weg, um das oben genannte zu behandeln.

in Html Template setzen wir Beitrag

<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> 

In View

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



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

In URL Give benötigt Informationen wie

urlpatterns = patterns('', 
url(r'^addnewroute/$', views.addnewroute, name='addnewroute'), 
url(r'^addarea/', include('usermodules.urls')), 
7

ich mehrere Formen benötigt, die auf die unabhängig validiert werden selbe Seite. Die Schlüsselkonzepte, die ich vermisste, waren 1) das Formularpräfix für den Namen der Übergabeschaltfläche zu verwenden und 2) ein unbeschränktes Formular löst keine Überprüfung aus. Wenn es jemand anderes hilft, hier ist mein vereinfachtes Beispiel von zwei Formen AForm und BForm mit TemplateView basiert auf den Antworten von @ adam-nelson und @ daniel-sokolowski und Kommentar von @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> 
+0

Ich denke, das ist eigentlich eine saubere Lösung. Vielen Dank. – chhantyal

+0

Ich mag diese Lösung wirklich. Eine Frage: Gibt es einen Grund, warum _get_form() keine Methode der MyView-Klasse ist? –

+1

@ AndréTerra könnte es definitiv sein, obwohl Sie es wahrscheinlich in einer generischen Klasse haben möchten, die von TemplateView erbt, damit Sie es in anderen Ansichten wiederverwenden können. – ybendana

0

Wenn Sie verwenden Ansatz mit klassenbasierten Ansichten und verschiedene 'Aktion' attrs ich meine

Setzen Sie verschiedene URLs in die Aktion für die beiden Formen. Dann haben Sie zwei verschiedene Ansichtsfunktionen, um mit den zwei verschiedenen Formen umzugehen.

Sie leicht Fehler von verschiedenen Formen mit überladenen get_context_data Methode, ex umgehen kann:

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 

Vorlage:

<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> 
0

wollte meine Lösung teilen wo Django-Formulare nicht verwendet werden. Ich habe mehrere Formularelemente auf einer einzelnen Seite und möchte eine einzige Ansicht verwenden, um alle POST-Anfragen von allen Formularen zu verwalten.

Was ich getan habe ist, dass ich ein unsichtbares Eingabe-Tag eingeführt habe, so dass ich einen Parameter an die Ansichten übergeben kann, um zu überprüfen, welches Formular übermittelt wurde.

<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 
0

Ansicht:

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") 
    ... 

Vorlage:

{% 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 %} 
+2

Könnten Sie bitte Ihre Antwort erklären? Es würde anderen mit einem ähnlichen Problem helfen und könnte helfen, Ihren oder den Fragesteller-Code zu debuggen ... – creyD

Verwandte Themen