2009-09-08 11 views
148

Ho una pagina modello che prevede due moduli. Se mi basta usare una forma, le cose vanno bene come in questo esempio tipico:Modo corretto per gestire più moduli su una pagina in Django

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

Se voglio lavorare con molteplici forme però, come faccio a lasciare che la vista so che sto presentando solo una delle forme e non l'altro (vale a dire è ancora request.POST ma voglio solo elaborare il modulo per il quale è avvenuto il submit)?


Questa è la soluzione in base alla risposta in cui expectedphrase e bannedphrase sono i nomi dei pulsanti per presentare le diverse forme e expectedphraseform e bannedphraseform sono le forme.

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

Non c'è un errore logico con la soluzione? Se pubblichi "bannedphrase", expectedphraseform non verrà popolato. – Ztyx

+1

Questo gestirà solo un modulo alla volta, la domanda riguarda la gestione dei moduli multipli allo stesso tempo – shining

risposta

98

avete alcune opzioni:

  1. Mettere URL differenti nell'azione per le due forme. Quindi avrai due diverse funzioni di visualizzazione per gestire le due diverse forme.

  2. Leggere i valori del pulsante di invio dai dati POST. Si può dire che pulsante di invio è stato cliccato: How can I build multiple submit buttons django form?

+3

3) Determina quale modulo viene inviato dai nomi dei campi nei dati POST. Includere alcuni input nascosti se i tuoi non hanno campi univoci con tutti i possibili valori non vuoti. –

+8

4) Aggiungi un campo nascosto identificando il modulo e controlla il valore di questo campo nella tua vista. – Soviut

+0

Sarei lontano dall'inquinamento dei dati POST, se possibile. Raccomando invece di aggiungere un parametro GET all'url dell'azione del modulo. – pygeek

34

Un metodo per riferimento futuro è qualcosa di simile. bannedphraseform è il primo modulo e expectedphraseform è il secondo. Se il primo è colpito, la seconda è saltata (che è un presupposto ragionevole in questo caso):

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

usando prefisso = è davvero il 'modo giusto' prefisso prefisso-kwarg – Rich

+0

, bello! –

+0

Ottima idea con quei prefissi, li abbiamo usati ora e funzionano come un incantesimo. Ma abbiamo ancora dovuto inserire un campo nascosto per rilevare quale modulo è stato inviato, perché entrambi i moduli sono in una lightbox (ciascuno in un altro). Poiché abbiamo bisogno di riaprire la lightbox corretta, dobbiamo sapere esattamente quale modulo è stato inviato, e se il primo modulo ha errori di validazione, il secondo vince automaticamente e il primo modulo viene resettato, anche se dobbiamo ancora visualizzare gli errori dal prima forma. Ho solo pensato che dovresti sapere – Enduriel

11

vista basato classe di Django forniscono un FormView generico, ma per tutti gli effetti è stato progettato per gestire solo una forma

Un modo per gestire più forme con lo stesso URL azione di destinazione utilizzando le viste generiche di Django è di estendere il "TemplateView" come mostrato di seguito; Uso questo approccio abbastanza spesso da averlo inserito in un modello IDE di Eclipse.

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 il codice HTML è il seguente effetto:

... 

<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

Sono alle prese con questo stesso problema e stavo cercando di trovare un modo per elaborare ogni post in una vista modulo separata e quindi reindirizzare a una vista modello comune. Il punto è rendere la visualizzazione del template responsabile per il contenuto get e le viste del modulo per il salvataggio. la validazione è un problema però. salvare i moduli per la sessione mi è venuto in mente ... Sto ancora cercando una soluzione pulita. –

2

Questo è un po 'tardi, ma questa è la soluzione migliore che ho trovato. Fai un dizionario di ricerca per il nome del modulo e la sua classe, devi anche aggiungere un attributo per identificare il modulo e nelle tue viste devi aggiungerlo come campo nascosto, con lo 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) 

Spero che questo possa aiutare in futuro.

0

Ecco un modo semplice per gestire quanto sopra.

Nel modello HTML mettiamo Messaggio

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

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



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

In URL Dare informazioni necessarie come

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

avevo bisogno di molteplici forme che vengono convalidati in modo indipendente sul stessa pagina. I concetti chiave che mi mancavano erano 1) utilizzando il prefisso del modulo per il nome del pulsante di invio e 2) un modulo illimitato non attiva la convalida. Se aiuta chiunque altro, ecco il mio esempio semplificato di due forme AForm e BForm utilizzando TemplateView sulla base delle risposte da @ adam-Nelson e @ Daniel-Sokolowski e commento di @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

Penso che questa sia in realtà una soluzione pulita. Grazie. – chhantyal

+0

Mi piace molto questa soluzione. Una domanda: c'è un motivo per cui _get_form() non è un metodo della classe MyView? –

+1

@ AndréTerra sicuramente potrebbe essere, anche se probabilmente vorrai averlo in una classe generica che eredita da TemplateView in modo da poterlo riutilizzare in altre viste. – ybendana

0

Se stai utilizzando l'approccio con viste basate su classi e diversi "azione" attrs intendo

Inserisci diversi URL nell'azione per le due forme. Quindi avrai due diverse funzioni di visualizzazione per gestire le due diverse forme.

Si può facilmente gestire gli errori di diverse forme utilizzando sovraccarico get_context_data metodo, es:

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 

modello:

<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

voluto condividere la mia soluzione dove le forme di Django non vengono utilizzate. Ho più elementi del modulo su una singola pagina e voglio usare una singola vista per gestire tutte le richieste POST da tutti i moduli.

Quello che ho fatto è che ho introdotto un tag di input invisibile in modo che possa passare un parametro alle viste per verificare quale modulo è stato inviato.

<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

vista:

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

modello:

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

Potresti spiegare la tua risposta? Airebbe gli altri con un problema simile e potrebbe aiutare a risolvere il tuo codice o il codice degli interrogatori ... – creyD

Problemi correlati