Файл проекта: https://github.com/selfedu-rus/flasksite-18
Во всех наших
предыдущих занятиях мы создавали формы вручную, прописывая их целиком в
шаблонах. И если так делать, то за веб-программистом остаются задачи:
-
проверить
принимаемые данные на стороне сервера;
-
сформировать
ошибки для формы (если что-то пошло не так);
-
продумать
безопасное сохранение и представление данных на сервере
и так далее. Это
необходимая и не всегда тривиальная работа. Но, к счастью, есть расширения для Flask, которые
значительно облегчают реализацию этих и подобных им типовых задач. В частности,
библиотека
WTForms
позволяет
достаточно просто оперировать формами и выполняет большую часть задач за
разработчика. На этом занятии мы, как раз, и познакомимся с основами ее
функционала на примере нашего тестового сайта.
Вообще, WTForms
– это библиотека, написанная на Python и независимая от фреймворков. Она способна
генерировать формы, проверять их, наполнять начальной информацией, работать с reCaptcha и многое другое.
Кроме того, в нее встроена защита от CSRF:
CSRF (Cross-Site Request Forgery) – межсайтовая подделка
запросов.
Это атака, при
которой происходит имитация запроса пользователя к стороннему сайту со страницы
другого сайта. То есть, злоумышленник создает некую страницу на своем сайте,
жертва заходит на нее и из формы запроса отправляется запрос на сайт, в котором
посетитель авторизован. Если на сайте нет защиты от CSRF-атаки, злоумышленник,
от имени пользователя получает доступ к стороннему ресурсу и творит свои
«черные дела».
Так вот, такие
атаки будут нипочем, при использовании WTForms. И первым делом
нужно установить это расширение. Для Flask оно называется
Flask-WTF
и устанавливается с помощью команды:
pip install flask_wtf
Концепция
создания форм здесь состоит в расширении базового класса
FlaskForm
А все поля формы
описываются переменными этого класса и ссылаются на соответствующие объекты,
которые могут быть образованы из следующих встроенных классов:
-
StringField – для работы с
полем ввода;
-
PasswordField – для работы с
полем ввода пароля;
-
BooleanField – для checkbox
полей;
-
TextAreaField
– для работы с вводом текста;
-
SelectField
– для работы со списком;
-
SubmitField – для кнопки submit.
Это лишь часть
классов. Полную документацию можно посмотреть на официальном сайте.
Создание класса формы
Давайте для
примера создадим в нашем проекте вспомогательный файл forms.py, в котором
будем определять все классы форм и начнем с класса LoginForm:
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, BooleanField, PasswordField
from wtforms.validators import DataRequired, Email, Length
class LoginForm(FlaskForm):
email = StringField("Email: ", validators=[Email()])
psw = PasswordField("Пароль: ", validators=[DataRequired(), Length(min=4, max=100)])
remember = BooleanField("Запомнить", default = False)
submit = SubmitField("Войти")
Смотрите, мы
прописали переменные: email, psw, remember и submit, которые
ссылаются на соответствующие объекты. У каждого объекта вначале указана строка,
которую, затем, можно будет отобразить рядом с полем ввода и параметр validators. Этот параметр
содержит список валидаторов, с помощью которых выполняется проверка корректности
введенных данных. Например:
-
DataRequired – валидатор,
требующий ввода каких-либо данных;
-
Email – проверяет
корректность введенного email-адреса;
-
Length – проверяет
количество введенных символов.
Конечно, это не
все валидаторы, которые есть в WTForms. Полный их список и набор
параметров можно посмотреть на странице официальной документации.
И, обратите
внимание, валидатор Email может требовать отдельной
дополнительной установки:
pip install email-validator
Поэтому, если
при запуске программы будет отображаться ошибка с указанием класса Email, то просто
выполните его установку.
Создание шаблона формы
Итак, класс
определен. Как им теперь пользоваться? Для начала в основном модуле программы выполним
импорт:
from forms import LoginForm
И, далее, в
функции представления login создадим его экземпляр и передадим
шаблону login.html:
@app.route("/login", methods=["POST", "GET"])
def login():
form = LoginForm()
return render_template("login.html", menu=dbase.getMenu(), title="Авторизация", form=form)
То есть, в
шаблоне будет доступ к переменным этого класса через параметр form:
{% extends 'base.html' %}
{% block content %}
{{ super() }}
{% for cat, msg in get_flashed_messages(True) %}
<div class="flash {{cat}}">{{msg}}</div>
{% endfor %}
<form action="" method="post" class="form-contact">
{{ form.hidden_tag() }}
{{ form.email.label() }} {{ form.email() }}
{{ form.psw.label() }} {{ form.psw() }}
{{ form.remember.label() }} {{ form.remember() }}
{{ form.submit() }}
<hr align=left width="300px">
<a href="{{url_for('register')}}">Регистрация</a>
</form>
{% endblock %}
Смотрите, здесь
в самом начале идет вызов метода:
form.hidden_tag()
который создает
скрытое поле, содержащее токен, используемый для защиты формы от CSRF-атак. Это все,
что от нас требуется, остальное Flask-WTF сделает
автоматически. Как видите, все просто и удобно.
Далее, мы
вызываем методы label, которые вставляют в форму тег:
<label>Название</label>
А методы email, psw, remember, submit – создают
соответствующие теги полей ввода, кнопок, чекбоксов и так далее.
Давайте запустим
программу и посмотрим как будет выглядеть эта форма и что представлять на
уровне HTML-документа.
Отлично, это
сделано, теперь в обработчике login выполним обработку элементов этой формы
и свяжем ее с ранее созданным функционалом:
@app.route("/login", methods=["POST", "GET"])
def login():
if current_user.is_authenticated:
return redirect(url_for('profile'))
form = LoginForm()
if form.validate_on_submit():
user = dbase.getUserByEmail(form.email.data)
if user and check_password_hash(user['psw'], form.psw.data):
userlogin = UserLogin().create(user)
rm = form.remember.data
login_user(userlogin, remember=rm)
return redirect(request.args.get("next") or url_for("profile"))
flash("Неверная пара логин/пароль", "error")
return render_template("login.html", menu=dbase.getMenu(), title="Авторизация", form=form)
Смотрите, здесь
вначале идет проверка validate_on_submit() корректности
переданных данных по POST-запросу и правильность заполненных
полей формы. И это очень удобно, т.к. программа пойдет дальше только в случае
верных данных. В этом случае мы дополнительно проверяем корректность ввода
пароля и, затем, авторизовываем пользователя и перенаправляем его на
соответствующий URL. Иначе, снова отображается форма авторизации.