La solution ci-dessus a un bug de validation lorsqu'un formulaire provoque une erreur de validation, les deux formulaires affichent un message d'erreur. Je change l'ordre des if
pour résoudre ce problème.
Tout d'abord, définissez vos multiples 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')
....
Ensuite, ajoutez 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
....
Le problème était maintenant résolu.
Si vous voulez vous y plonger, alors 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 de soumission sur lequel on a cliqué. La petite astuce ci-dessus consiste donc à ajouter un filtre (pour vérifier si le formulaire a des données, par exemple, form1.submit1.data
).
De plus, nous changeons l'ordre des if
donc quand on clique sur "soumettre", il ne fait qu'appeler validate()
à ce formulaire ce qui évite l'erreur de validation pour les 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 renvoie cependant un dict avec le nom du champ (clé) et les données du champ (valeur), le bouton de soumission de nos deux formulaires a le même nom submit
(clé) !
Lorsque nous cliquons sur le premier bouton d'envoi (dans le formulaire 1), l'appel de la fonction form1.submit1.data
retourner un dict comme ceci :
temp = {'submit': True}
Il n'y a aucun doute quand nous appelons if form1.submit.data:
il retourne True
.
Lorsque nous cliquons sur le deuxième bouton d'envoi (dans le formulaire 2), l'appel à la fonction .data
en if form1.submit.data:
ajouter une clé-valeur dans le dict premièrement puis l'appel de if form2.submit.data:
ajouter une autre clé-valeur, à la fin, le dict sera comme ceci :
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é se trouvait dans le formulaire 2.
C'est pourquoi nous devons définir ces deux SubmitField
avec des noms différents. Au fait, merci d'avoir lu (jusqu'ici) !
Mise à jour
Il existe un autre moyen de gérer plusieurs formulaires sur une même page. Vous pouvez utiliser des vues multiples pour traiter 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 rendre les deux formulaires et définir l'attribut action
à 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>