python - tipos - Manera correcta de manejar múltiples formularios en una página en Django
post django (9)
Tengo una página de plantilla esperando 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 deseo trabajar con varios formularios, ¿cómo puedo dejar que la vista sepa que estoy enviando solo uno de los formularios y no el otro (es decir, sigue siendo request.POST pero solo quiero procesar el formulario sucedió)?
Esta es la solución basada en la respuesta donde waitingphrase y bannedphrase son los nombres de los botones de envío para las diferentes formas, y expectedphraseform y bannedphraseform son los formularios.
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'')
Aquí hay una manera simple de manejar lo anterior.
En la plantilla Html ponemos mensaje
<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>
En vista
def addnewroute(request):
if request.method == "POST":
# do something
def addarea(request):
if request.method == "POST":
# do something
En URL, brinde la información necesaria, como
urlpatterns = patterns('''',
url(r''^addnewroute/$'', views.addnewroute, name=''addnewroute''),
url(r''^addarea/'', include(''usermodules.urls'')),
Esto es un poco tarde, pero esta es la mejor solución que encontré. Usted crea 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 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.
Las vistas basadas en clases de Django proporcionan un FormView genérico, pero a 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 objetivo utilizando las vistas genéricas de Django es ampliar el ''TemplateView'' como se muestra a continuación; Utilizo este enfoque con la frecuencia suficiente para convertirlo 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>
...
Necesitaba formularios múltiples que se validen independientemente 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) una forma ilimitada no desencadena 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 comentario de @zeraien ( https://.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>
Quería compartir mi solución donde Django Forms no está siendo utilizado. Tengo múltiples 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 y verificar qué formulario se envió.
<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
Si está utilizando el enfoque con vistas basadas en clases y diferentes atributos de "acción" me refiero
Ponga diferentes URL en la acción para los dos formularios. Luego tendrá dos funciones de visualización diferentes para tratar las dos formas diferentes.
Puede manejar fácilmente los errores de diferentes formas utilizando el método get_context_data
sobrecargado, por ejemplo:
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
modelo:
<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>
Tienes pocas opciones:
Ponga diferentes URL en la acción para los dos formularios. Luego tendrá dos funciones de visualización diferentes para tratar las dos formas diferentes.
Lea los valores del botón de envío de los datos POST. Puede decir en qué botón de envío se hizo clic: ¿cómo puedo crear múltiples formularios de envío de formulario django?
Un método para futuras referencias es algo como esto. bannedphraseform es la primera forma y expectedphraseform es la segunda. Si se golpea al primero, se salta 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'')
ver:
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")
...
modelo:
{% 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 %}