web-dev-qa-db-fra.com

Plusieurs formulaires sur une seule page en utilisant flask et WTForms

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? 

51
iamgopal

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
53
AlexLordThorsen

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)!

Mettre à jour

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>
37
Grey Li

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"

    ...
17
Hieu

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"))
2
turdus-merula

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>
0
Viraj.Hadoop