J'ai plusieurs formulaires sur la même page qui envoient une demande de publication au même gestionnaireIn flask.
Je génère des formulaires en utilisant wtform.
quel est le meilleur moyen d'identifier quel formulaire est soumis?
J'utilise actuellement action="?form=oneform"
. Je pense qu'il devrait y avoir une meilleure méthode Pour atteindre le même?
J'ai utilisé une combinaison de deux extraits de flacon. Le premier ajoute un préfixe à un formulaire , puis vous recherchez le préfixe avec validate_on_submit (). J'utilise aussi le modèle de Louis Roché pour déterminer quels boutons sont enfoncés dans un formulaire .
Pour citer Dan Jacob:
Exemple:
form1 = FormA(prefix="form1")
form2 = FormB(prefix="form2")
form3 = FormC(prefix="form3")
Ensuite, ajoutez un champ caché (ou vérifiez simplement un champ de soumission):
if form1.validate_on_submit() and form1.submit.data:
Pour citer Louis Roché:
J'ai dans mon modèle:
<input type="submit" name="btn" value="Save">
<input type="submit" name="btn" value="Cancel">
Et pour savoir quel bouton a été passé côté serveur, je l’ai dans mon fichier views.py:
if request.form['btn'] == 'Save':
something0
else:
something1
La solution ci-dessus a un bogue validation , lorsqu'un formulaire provoque une erreur de validation, les deux formulaires affichent un message d'erreur. Je change l'ordre de if
pour résoudre ce problème.
Tout d’abord, définissez votre multiple SubmitField
avec des noms différents, comme ceci:
class Form1(Form):
name = StringField('name')
submit1 = SubmitField('submit')
class Form2(Form):
name = StringField('name')
submit2 = SubmitField('submit')
....
Ajoutez ensuite un filtre dans view.py
:
....
form1 = Form1()
form2 = Form2()
....
if form1.submit1.data and form1.validate(): # notice the order
....
if form2.submit2.data and form2.validate(): # notice the order
....
Maintenant, le problème a été résolu.
Si vous souhaitez vous y plonger, continuez à lire.
Voici validate_on_submit()
:
def validate_on_submit(self):
"""
Checks if form has been submitted and if so runs validate. This is
a shortcut, equivalent to ``form.is_submitted() and form.validate()``
"""
return self.is_submitted() and self.validate()
Et voici is_submitted()
:
def is_submitted():
"""Consider the form submitted if there is an active request and
the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
"""
return _is_submitted() # bool(request) and request.method in SUBMIT_METHODS
Lorsque vous appelez form.validate_on_submit()
, il vérifie si le formulaire est soumis par la méthode HTTP, quel que soit le bouton d'envoi sur lequel vous avez cliqué. Donc, le petit truc ci-dessus est d’ajouter un filtre (pour vérifier si submit contient des données, c’est-à-dire form1.submit1.data
).
En outre, nous changeons l'ordre de if
, ainsi, lorsque nous cliquons sur une soumission, il n'appelle que validate()
vers ce formulaire , empêchant ainsi l'erreur de validation des deux formulaires.
L'histoire n'est pas encore terminée. Voici .data
:
@property
def data(self):
return dict((name, f.data) for name, f in iteritems(self._fields))
Il retourne un dict avec le nom du champ (clé) et les données du champ (valeur), cependant, notre bouton d'envoi de formulaire a le même nom submit
(clé)!
Lorsque nous cliquons sur le premier bouton d'envoi (dans Form1), l'appel de form1.submit1.data
renvoie un dict comme ceci:
temp = {'submit': True}
Il ne fait aucun doute que lorsque nous appelons if form1.submit.data:
, il retourne True
.
Lorsque nous cliquons sur le deuxième bouton de soumission (dans form2), l’appel de .data
dans if form1.submit.data:
ajoute une valeur-clé dans dict first , puis l’appel de if form2.submit.data:
ajoute une autre valeur-clé. ce:
temp = {'submit': False, 'submit': True}
Maintenant, nous appelons if form1.submit.data:
, il retourne True
, même si le bouton d'envoi sur lequel nous avons cliqué était dans form2.
C'est pourquoi nous devons définir ces deux SubmitField
avec des noms différents. Au fait, merci d'avoir lu (ici)!
Il existe un autre moyen de gérer plusieurs formulaires sur une même page. Vous pouvez utiliser plusieurs vues pour gérer les formulaires. Par exemple:
...
@app.route('/')
def index():
register_form = RegisterForm()
login_form = LoginForm()
return render_template('index.html', register_form=register_form, login_form=login_form)
@app.route('/register', methods=['POST'])
def register():
register_form = RegisterForm()
login_form = LoginForm()
if register_form.validate_on_submit():
... # handle the register form
# render the same template to pass the error message
# or pass `form.errors` with `flash()` or `session` then redirect to /
return render_template('index.html', register_form=register_form, login_form=login_form)
@app.route('/login', methods=['POST'])
def login():
register_form = RegisterForm()
login_form = LoginForm()
if login_form.validate_on_submit():
... # handle the login form
# render the same template to pass the error message
# or pass `form.errors` with `flash()` or `session` then redirect to /
return render_template('index.html', register_form=register_form, login_form=login_form)
Dans le modèle (index.html), vous devez restituer les deux formulaires et définir l'attribut action
sur la vue cible:
<h1>Register</h1>
<form action="{{ url_for('register') }}" method="post">
{{ register_form.username }}
{{ register_form.password }}
{{ register_form.email }}
</form>
<h1>Login</h1>
<form action="{{ url_for('login') }}" method="post">
{{ login_form.username }}
{{ login_form.password }}
</form>
Un moyen simple consiste à avoir des noms différents pour différents champs de soumission. À titre d'exemple:
forms.py :
class Login(Form):
...
login = SubmitField('Login')
class Register(Form):
...
register = SubmitField('Register')
views.py:
@main.route('/')
def index():
login_form = Login()
register_form = Register()
if login_form.validate_on_submit() and login_form.login.data:
print "Login form is submitted"
Elif register_form.validate_on_submit() and register_form.register.data:
print "Register form is submitted"
...
Comme l’autre répond, j’attribue également un nom unique à chaque bouton de soumission, à chaque formulaire de la page.
Ensuite, l’action Web du flacon se présente comme suit: notez les paramètres formdata
et obj
, qui aident à init/preserve les champs de formulaire en conséquence:
@bp.route('/do-stuff', methods=['GET', 'POST'])
def do_stuff():
result = None
form_1 = None
form_2 = None
form_3 = None
if "submit_1" in request.form:
form_1 = Form1()
result = do_1(form_1)
Elif "submit_2" in request.form:
form_2 = Form2()
result = do_2(form_2)
Elif "submit_3" in request.form:
form_3 = Form3()
result = do_3(form_3)
if result is not None:
return result
# Pre-populate not submitted forms with default data.
# For the submitted form, leave the fields as they were.
if form_1 is None:
form_1 = Form1(formdata=None, obj=...)
if form_2 is None:
form_2 = Form2(formdata=None, obj=...)
if form_3 is None:
form_3 = Form3(formdata=None, obj=...)
return render_template("page.html", f1=form_1, f2=form_2, f3=form_3)
def do_1(form):
if form.validate_on_submit():
flash("Success 1")
return redirect(url_for(".do-stuff"))
def do_1(form):
if form.validate_on_submit():
flash("Success 2")
return redirect(url_for(".do-stuff"))
def do_3(form):
if form.validate_on_submit():
flash("Success 3")
return redirect(url_for(".do-stuff"))
Exemple: plusieurs WTForm dans une seule page HTML
app.py
"""
Purpose Create multiple form on single html page.
Here we are having tow forms first is Employee_Info and CompanyDetails
"""
from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, FloatField, validators
from wtforms.validators import InputRequired
app = Flask(__name__)
app.config['SECRET_KEY'] = 'Thisisasecret'
class EmployeeInfo(FlaskForm):
"""
EmployeeInfo class will have Name,Dept
"""
fullName = StringField('Full Name',[validators.InputRequired()])
dept = StringField('Department',[validators.InputRequired()])
class CompanyDetails(FlaskForm):
"""
CompanyDetails will have yearOfExp.
"""
yearsOfExp = IntegerField('Year of Experiece',[validators.InputRequired()])
@app.route('/', methods = ['GET','POST'] )
def index():
"""
View will render index.html page.
If form is validated then showData.html will load the employee or company data.
"""
companydetails = CompanyDetails()
employeeInfo = EmployeeInfo()
if companydetails.validate_on_submit():
return render_template('showData.html', form = companydetails)
if employeeInfo.validate_on_submit():
return render_template('showData.html', form1 = employeeInfo)
return render_template('index.html',form1 = employeeInfo, form = companydetails)
if __== '__main__':
app.run(debug= True, port =8092)
templates/index.html
<html>
<head>
</head>
<body>
<h4> Company Details </h4>
<form method="POST" action="{{url_for('index')}}">
{{ form.csrf_token }}
{{ form.yearsOfExp.label }} {{ form.yearsOfExp }}
<input type="submit" value="Submit">
</form>
<hr>
<h4> Employee Form </h4>
<form method="POST" action="{{url_for('index')}}" >
{{ form1.csrf_token }}
{{ form1.fullName.label }} {{ form1.fullName }}
{{ form1.dept.label }} {{ form1.dept }}
<input type="submit" value="Submit">
</form>
</body>
</html>
showData.html
<html>
<head>
</head>
<body>
{% if form1 %}
<h2> Employee Details </h2>
{{ form1.fullName.data }}
{{ form1.dept.data }}
{% endif %}
{% if form %}
<h2> Company Details </h2>
{{ form.yearsOfExp.data }}
{% endif %}
</body>
</html>