Flask-WTF ========= ![category](développement) ![tag](python) ![tag](flask) Cette extension permet de gérer simplement des formulaires. ```python pip install flask pip install Flask-WTF ``` Nous allons contruire une application possédant un formulaire de contact avec 4 champs: - nom - mail - sujet - message Tout les champs sont obligatoires et nous validerons le format du mail Notre application aura l'arborescence suivante ``` dir |__ myapp.py |__ myapp.cfg |__ templates | |__ contact.html | |__ home.html | |__ layout.html |__ statics |__ css |__ main.css ``` contact.html ```html {% extends "layout.html" %} {% block content %}

Contact

{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
{{ form.hidden_tag() }} {{ form.name.label }} {{ form.name }} {% for message in form.name.errors %}
{{ message }}
{% endfor %} {{ form.email.label }} {{ form.email }} {% for message in form.email.errors %}
{{ message }}
{% endfor %} {{ form.subject.label }} {{ form.subject }} {% for message in form.subject.errors %}
{{ message }}
{% endfor %} {{ form.message.label }} {{ form.message }} {% for message in form.message.errors %}
{{ message }}
{% endfor %} {{ form.submit }}
{% endblock %} ``` layout.html ```html {% extends "layout.html" %} {% block content %} home {% endblock %} ``` home.html ```html``` Flask

Flask App

{% block content %} {% endblock %}
```note``` on utilise içi les templates avec des notions d'extends myapp.cfg ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import os import logging DEBUG = True PATH_STATICS = './statics' SECRET_KEY = 'secret_key' PORT = 5001 HOST = '0.0.0.0' MAIL_SERVER = 'srvpzex1' ``` myapp.py ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import os import logging from flask import Flask import flask from flask_wtf import Form from wtforms import TextField, TextAreaField, SubmitField, validators app = Flask(__name__) app.config.from_pyfile('myapp.cfg') ``` class RegistrationForm(Form): username = TextField('Username', [validators.Length(min=4, max=25)]) email = TextField('Email Address', [validators.Length(min=6, max=35)]) name = TextField("Name", [validators.Required("Please enter your name.")]) subject = TextField("Subject", [validators.Required("Please enter a subject.")]) message = TextAreaField("Message", [validators.Required("Please enter a message.")]) datefield = DateField('datefield') datetimefield = DateTimeField('datetimefield') decimalfield = DecimalField('decimalfield') filefield = FileField('filefield') floatfield = FloatField('floatfield') integerfield = IntegerField('integerfield') radiofield = RadioField('radiofield', choices=[('cpp', 'C++'), ('py', 'Python'), ('text', 'Plain Text')]) selectfield = SelectField('selectfield', choices=[('cpp', 'C++'), ('py', 'Python'), ('text', 'Plain Text')]) password = PasswordField('New Password', [ validators.Required(), validators.EqualTo('confirm', message='Passwords must match') ]) confirm = PasswordField('Repeat Password') accept_tos = BooleanField('I accept the TOS', [validators.Required()]) submit = SubmitField("Send") @app.route('/home') def home(): app.logger.debug(flask.render_template('home.html')) return flask.render_template('home.html') @app.route('/contact', methods=['GET', 'POST']) def contact(): form = ContactForm() if flask.request.method == 'POST': if form.validate() == False: app.logger.error('All fields are required.') flask.flash('All fields are required.') return flask.render_template('contact.html', form=form) else: app.logger.debug("name : %s" % form.name.data) app.logger.debug("email : %s" % form.email.data) app.logger.debug("subject : %s" % form.subject.data) app.logger.debug("message : %s" % form.message.data) return 'Form posted.' elif flask.request.method == 'GET': return flask.render_template('contact.html', form=form) @app.route("/") def static_web(path): app.logger.debug('load static %s' % path) return flask.send_from_directory(app.config['PATH_STATICS'],path) if __name__ == "__main__": handler = logging.StreamHandler() handler.setLevel(app.config['LEVEL']) app.logger.addHandler(handler) app.run(host = app.config['HOST'], port = app.config['PORT']) On peut tester sur http://127.0.0.1:5001/contact Il est important de noter que nous gérons les erreurs champs par champs au niveau du contact.html mais aussi au niveau gloabl grâce à la fonction flash. On peut aussi facilement rajouter un form de gestion du login ```python``` from wtforms import TextField, TextAreaField, SubmitField, validators, PasswordField, BooleanField class RegistrationForm(Form): username = TextField('Username', [validators.Length(min=4, max=25)]) email = TextField('Email Address', [validators.Length(min=6, max=35)]) password = PasswordField('New Password', [ validators.Required(), validators.EqualTo('confirm', message='Passwords must match') ]) confirm = PasswordField('Repeat Password') accept_tos = BooleanField('I accept the TOS', [validators.Required()]) @app.route('/register', methods=['GET', 'POST']) def register(): form = RegistrationForm(flask.request.form) if flask.request.method == 'POST' and form.validate(): flash('Thanks for registering') return redirect(url_for('login')) return flask.render_template('register.html', form=form) Et pour register.html afin de simplifier son écriture on peut créer une fonction de template register.html ```html``` {% from "_formhelpers.html" import render_field %} {% extends "layout.html" %} {% block content %}
{% for field in form %} {% if field.widget.__class__.__name__ in ('TextInput','PasswordInput','CheckboxInput','TextArea', 'ListWidget', 'Select') %} {{ render_field(field, class='wtf') }} {% endif %} {% endfor %} {{ form.submit }}
{% endblock %} _formhelpers.html ```html``` {% macro render_field(field) %}
{{ field.label }}
{{ field(**kwargs)|safe }} {% if field.errors %} {% endif %}
{% endmacro %} Concernant les éléments qu'on peut uiliser via le module wtform il y a une excellente doc https://wtforms.readthedocs.org/en/latest Il existe plusieurs validateurs: - validators.DataRequired(message=None) - validators.Email(message=None) - validators.EqualTo(fieldname, message=None) - validators.InputRequired(message=None) - validators.IPAddress(ipv4=True, ipv6=False, message=None) - validators.Length(min=-1, max=-1, message=None) - validators.MacAddress(message=None) - validators.NumberRange(min=None, max=None, message=None) - validators.Optional(strip_whitespace=True) - validators.Regexp(regex, flags=0, message=None) - validators.URL(require_tld=True, message=None) - validators.UUID(message=None) - validators.AnyOf(values, message=None, values_formatter=None) - validators.NoneOf(values, message=None, values_formatter=None) Il est possible de créer facilement des nouveaux validateurs ```python``` def my_length_check(form, field): if len(field.data) > 50: raise ValidationError('Field must be less than 50 characters') class MyForm(Form): name = StringField('Name', [InputRequired(), my_length_check])