adam nelson
Tengo una página de plantilla que espera dos formularios. Si solo uso un formulario, las cosas están bien como en este ejemplo típico:
if request.method == 'POST':
form = AuthorForm(request.POST,)
if form.is_valid():
form.save()
# do something.
else:
form = AuthorForm()
Sin embargo, si quiero trabajar con múltiples formularios, ¿cómo le hago saber a la vista que estoy enviando solo uno de los formularios y no el otro (es decir, sigue siendo request.POST pero solo quiero procesar el formulario para el cual se envió sucedió)?
esta es la solucion basado en la respuesta donde frase esperada y frase prohibida son los nombres de los botones de envío para los diferentes formularios y forma de frase esperada y forma de frase prohibida son las formas.
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')
Ned Batchelder
Tienes pocas opciones:
-
Ponga diferentes URL en la acción para los dos formularios. Entonces tendrá dos funciones de vista diferentes para manejar las dos formas diferentes.
-
Lea los valores del botón Enviar de los datos POST. Puede saber en qué botón de envío se hizo clic: ¿Cómo puedo crear varios botones de envío en el formulario django?
-
3) Determine qué formulario se envía a partir de los nombres de campo en los datos POST. Incluya algunas entradas ocultas si sus formularios no tienen campos únicos y todos los valores posibles no están vacíos.
– Denis Otkidach
24 de octubre de 2009 a las 16:39
-
4) Agregue un campo oculto que identifique el formulario y verifique el valor de este campo en su vista.
– Soviético
14 de junio de 2011 a las 3:40
-
Me mantendría alejado de contaminar los datos POST si es posible. Recomiendo agregar un parámetro GET a la URL de acción del formulario.
– pygeek
29 de julio de 2013 a las 17:28
-
# 1 es realmente su mejor apuesta aquí. No desea contaminar su POST con campos ocultos y tampoco desea vincular su vista a su plantilla y/o formulario.
– meteorito
17 de diciembre de 2013 a las 0:15
-
@meteorainer si usa el número uno, ¿hay alguna forma de devolver los errores a los formularios en la vista principal que los instancia, sin usar el marco de mensajes o las cadenas de consulta? Esta respuesta parece la más cercana, pero aquí sigue siendo solo una vista que maneja ambos formularios: stackoverflow.com/a/21271659/2532070
– YPCrumble
15/10/2014 a las 10:10
Un método para futuras referencias es algo como esto. la forma de frase prohibida es la primera forma y la forma de frase esperada es la segunda. Si se golpea el primero, se omite el segundo (lo cual es una suposición razonable en este 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')
-
usar prefix= es de hecho la ‘manera correcta’
– Rico
18 de julio de 2012 a las 5:30
-
prefix-kwarg hizo el trabajo, ¡bien!
– Stephan Hoyer
7 de enero de 2013 a las 14:08
-
Gran idea con esos prefijos, los usamos ahora y funcionan de maravilla. Pero aún teníamos que insertar un campo oculto para detectar qué formulario se envió, porque ambos formularios están en una caja de luz (cada uno en uno separado). Debido a que necesitamos reabrir el lightbox correcto, necesitamos saber exactamente qué formulario se envió, y luego, si el primer formulario tiene algún error de validación, el segundo gana automáticamente y el primer formulario se reinicia, aunque todavía necesitamos mostrar los errores del primera forma. Solo pensé que deberías saber
– Enduriel
3 mayo 2013 a las 16:12
-
¿No sería torpe extender este patrón al caso de tres formas? Por ejemplo, al verificar is_valid() desde el primer formulario, luego los dos primeros, etc. Tal vez solo tenga un
handled = False
que se actualiza aTrue
cuando se encuentra una forma compatible?– binki
13 de enero de 2016 a las 22:31
ybendana
Necesitaba varios formularios validados de forma independiente en la misma página. Los conceptos clave que me faltaban eran 1) usar el prefijo del formulario para el nombre del botón de envío y 2) un formulario ilimitado no activa la validación. Si ayuda a alguien más, aquí está mi ejemplo simplificado de dos formularios AForm y BForm usando TemplateView basado en las respuestas de @adam-nelson y @daniel-sokolowski y el comentario de @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>
-
Creo que esto es en realidad una solución limpia. Gracias.
– Chantyal
28 de enero de 2016 a las 13:12
-
Me gusta mucho esta solución. Una pregunta: ¿hay alguna razón por la que _get_form() no sea un método de la clase MyView?
– ataque aéreo
17 de agosto de 2016 a las 4:13
-
@AndréTerra definitivamente podría serlo, aunque probablemente desee tenerlo en una clase genérica que herede de TemplateView para poder reutilizarlo en otras vistas.
– ybendana
17 de agosto de 2016 a las 17:48
-
Esta es una gran solución. Necesitaba cambiar una línea del __get_form para que funcionara:
data = request.POST if prefix in next(iter(request.POST.keys())) else None
De lo contrarioin
no funcionó– larapsodia
27 de agosto de 2016 a las 16:27
-
El uso de una sola etiqueta
Quería compartir mi solución donde Django Forms no se usa. Tengo varios elementos de formulario en una sola página y quiero usar una sola vista para administrar todas las solicitudes POST de todos los formularios.
Lo que hice fue introducir una etiqueta de entrada invisible para poder pasar un parámetro a las vistas para comprobar qué formulario se ha enviado.
<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>
vistas.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
daniel sokolowski
Las vistas basadas en clases de Django proporcionan un FormView genérico, pero para todos los efectos está diseñado para manejar solo un formulario.
Una forma de manejar múltiples formularios con la misma URL de acción de destino usando las vistas genéricas de Django es extender ‘TemplateView’ como se muestra a continuación; Utilizo este enfoque con tanta frecuencia que lo convertí en una plantilla IDE de 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)
La plantilla html tiene el siguiente efecto:
...
<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>
...
-
Estoy luchando con este mismo problema y estaba tratando de encontrar una manera de procesar cada publicación en una vista de formulario separada y luego redirigir a una vista de plantilla común. El punto es hacer que la vista de plantilla sea responsable de obtener el contenido y las vistas de formulario del guardado. Sin embargo, la validación es un problema. Se me pasó por la cabeza guardar los formularios en la sesión… Sigo buscando una solución limpia.
–Daniel Bernardini
18 de julio de 2014 a las 16:58
e-nutrir
Esto es un poco tarde, pero esta es la mejor solución que encontré. Usted hace un diccionario de búsqueda para el nombre del formulario y su clase, también debe agregar un atributo para identificar el formulario, y en sus vistas debe agregarlo como un campo oculto, con el 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)
Espero que esto ayude en el futuro.
-
Estoy luchando con este mismo problema y estaba tratando de encontrar una manera de procesar cada publicación en una vista de formulario separada y luego redirigir a una vista de plantilla común. El punto es hacer que la vista de plantilla sea responsable de obtener el contenido y las vistas de formulario del guardado. Sin embargo, la validación es un problema. Se me pasó por la cabeza guardar los formularios en la sesión… Sigo buscando una solución limpia.
–Daniel Bernardini
18 de julio de 2014 a las 16:58
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")
...
plantilla:
{% 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 %}
-
¿Podría por favor explicar su respuesta? Ayudaría a otros con un problema similar y podría ayudar a depurar su código o el de los interrogadores …
– CreyD
9 de enero de 2018 a las 9:33
¿No hay un error lógico con su solución? Si publica ‘frase prohibida’, la forma de frase esperada no se completará.
– Ztyx
26 de septiembre de 2011 a las 18:39
Esto manejará solo un formulario a la vez, la pregunta es sobre el manejo de múltiples formularios al mismo tiempo
– brillando
6 de abril de 2016 a las 6:34
Todas estas respuestas son útiles, pero no tienen una solución para un formulario no válido. ¿Alguien tiene alguna idea de cómo devolver un formulario no válido cuando dos formularios pueden no ser válidos?
– judías verdes
12 de enero de 2021 a las 17:59
Estoy realmente sorprendido de que ninguna de estas respuestas haga referencia a conjuntos de formularios: “Un conjunto de formularios es una capa de abstracción para trabajar con múltiples formularios en la misma página”. Ver documentos aquí docs.djangoproject.com/en/3.2/topics/forms/modelforms/… y aquí docs.djangoproject.com/en/3.2/topics/forms/formsets. Sé que esta es una vieja pregunta, pero los conjuntos de formularios no son nuevos.
– Malik A. Rumi
26 de enero de 2022 a las 11:34