Любое веб-приложение содержит такой важный элемент, как формы. К сожалению, не всегда с ними просто работать. Логика взаимодействия с формами для новичка довольно непростая. Ведь сперва необходимо подтвердить данные на стороне клиента, а потом – на сервере. И даже этого недостаточно, если разработчику нужно сделать этот обмен данными безопасным. В совокупности даже опытному программисту нужно немало работать. К счастью, можно воспользоваться библиотекой WTForms, которая позволяет без библиотек и пакетов работать с формами.
Работа с формами – непростой метод
Сперва давайте создадим шаблон login.html, в котором будет содержаться следующий код.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> {% if message %} <p>{{ message }}</p> {% endif %} <form action="" method="post"> <p> <label for="username">Username</label> <input type="text" name="username"> </p> <p> <label for="password">Password</label> <input type="password" name="password"> </p> <p> <input type="submit"> </p> </form> </body> </html>
А после функции представления books необходимо добавить этот код.
from flask import Flask, render_template, request #... @app.route('/login/', methods=['post', 'get']) def login(): message = '' if request.method == 'POST': username = request.form.get('username') # запрос к данным формы password = request.form.get('password') if username == 'root' and password == 'pass': message = "Correct username and password" else: message = "Wrong username or password" return render_template('login.html', message=message) #...
Учтите, что аргумент methods был передан декоратору route().
Стандартные параметры следующие – вызов обработчика запросов осуществляется лишь в той ситуации, когда метод request.method – GET или HEAD. Если передать перечень разрешенных HTTP-методов аргументу methods, то это можно изменить. После этого, вызов представленной login функции будет осуществляться исключительно в тех случаях, когда запрос к /login/ будет осуществляться с использованием методов GET, POST, HEAD. Если же использовать другие методы, то появится ошибка HTTP 405 Method Not Allowed.
Объект класса request позволяет получить данные о текущем веб-запросе. Данные, полученные с использованием формы, находятся в атрибуте form объекта request. Это словарь, который нельзя редактировать и имеющий название ImmutableMutiDict.
После этого осуществляется переход по адресу https://localhost:5000/login/. Далее открывается форма.
Далее, для получения доступа к странице будет использоваться метод GET. Следовательно, код, находящийся внутри if-блока функции login() не будет учитываться в ходе исполнения приложения.
Если осуществить попытку отправки формы без ввода данных, то страница в итоге будет выглядеть так.
В этом случае для отправки страницы используется метод POST. Поэтому инструкции, находящиеся в if-блоке, учитываются при выполнении приложения. Внутри него программой принимается имя пользователя и пароль, а также устанавливается сообщение для message. Поскольку в форме не содержится никаких данных, показывается сообщение об ошибке.
Если использовать правильное имя пользователя и пароль при заполнении формы и нажать Enter, то тогда появится соответствующее уведомление над формой.
Вот, как осуществляется работа с формами во Flask. Но также необходимо обратить внимание на пакет WTForms.
Что такое WTForms?
Это специальная библиотека, которая может использоваться как в совокупности с фреймворками, так и без связи с ними. Она умеет создавать формы, осуществлять их проверку и заполнять их определенными данными. Также в ее функционал входит ряд других возможностей. Помимо этого, библиотека позволяет реализовать защиту от CSRF.
Чтобы установить ее, используется Flask-WTF – специальное расширение, которое делает совместимым Flask и WTForms. В функционал также входят такие функции, как загрузка файлов, ввод каптчи, интернационализация, и так далее.
Чтобы осуществить инсталляцию библиотеки, используется такая инструкция.
(env) gvido@vm:~/flask_app$ pip install flask-wtf
Как создать класс Form?
Для начала необходимо определить формы в виде классов Python. Каждая из форм должна расширять класс FlaskForm, который находится в пакете flask_wtf. FlaskForm – это обертка, которая позволяет использовать набор полезных методов для класса wtform.Form.
Внутри класса, определение полей осуществляется в формате переменных класса и происходит путем создания объекта, который ассоциируется с типом поля.
Пакет wtform предлагает набор классов, которые представляют собой такие поля: StringField, PasswordField, SelectField, TextAreaField, SubmitField и ряд других.
Сперва необходимо создать файл с названием forms.py внутри flask_app, и в него интегрировать такой код.
from flask_wtf import FlaskForm from wtforms import StringField, SubmitField, TextAreaField from wtforms.validators import DataRequired, Email class ContactForm(FlaskForm): name = StringField("Name: ", validators=[DataRequired()]) email = StringField("Email: ", validators=[Email()]) message = TextAreaField("Message", validators=[DataRequired()]) submit = SubmitField("Submit")
Здесь определяется класс формы ContactForm, в котором есть четыре поля: name, email, message, submit. Эти переменные нужны, чтобы осуществить рендеринг полей формы, а также получения данных, которые в них находятся. Помимо этого, через них можно изменять содержимое этих форм.
При создании этой формы использовались два StringField, TextAreaField, SubmitField.
Каждый раз, при создании поля, функции-конструктору, используемой для этого, передаются определенные аргументы:
- Строка, содержащая метку, отображаемую внутри тега <label> в момент рендеринга поля.
- Список валидаторов (элементов для проверки полей формы), передача которых осуществляется в виде аргументов-ключевых слов. Под валидаторами подразумеваются функции или классы, определяющие, правильно ли введены данные в поле.
Для каждого поля возможно применение нескольких валидаторов, которые разделяются запятыми.
Что касается валидаторов, то их можно создать самостоятельно или воспользоваться уже предустановленными в wtforms.validators. Так, в этой форме мы использовали два из них:
- DataRequired. Проверяет, была ли хоть какая-то информация введена в это поле.
- Email. Выполняет проверку, является ли содержимое конкретного поля действующим электронным адресом.
Валидатор нужен для того, чтобы форма не отправлялась, пока не будут соблюдены все требования.
Это лишь базовая информация о том, какие бывают формы и валидаторы. Ознакомиться с полным перечнем можно в официальной документации WTForms.
Установка SECRET_KEY
Во Flask_WTF имеется встроенная поддержка возможности предотвращения CSFR-атак. Более того, это данный компонент делает по умолчанию. Как это делается? Путем встраивания в элемент <input> внутри формы специального токена.
Затем он проверяет, насколько подлинный запрос. Еще до генерации csrf-токена необходимо добавить секретный ключ в файл main2.py. Для этого используются следующие строки.
#... app.debug = True app.config['SECRET_KEY'] = 'a really really really really long secret key' manager = Manager(app) #...
Обратите внимание, что атрибут config, относящийся к объекту Flask, работает, как словарь, нужен для задания параметров настройки Flask и его расширений. Тем не менее, возможно их самостоятельное добавление.
Секретный ключ должен соответствовать следующим требованиям:
- Относиться к строковому типу данных.
- Желательно, чтобы он был длинным.
- Подбираться должен таким образом, чтобы его было трудно разгадать.
Впрочем, секретный ключ используется не только для генерации CSFR-токенов, но и в других расширениях Flask.
Важное требование к секретному ключу: он должен быть безопасно сохранен.
Формы в консоли
Теперь давайте используем следующую инструкцию, чтобы открыть оболочку Python.
(env) gvido@vm:~/flask_app$ python main2.py shell
Она будет запущена внутри контекста приложения.
Далее импортируется класс ContactForm и создается экземпляр объекта новой формы. Для этого передаются ее данные таким образом.
>>> >>> from forms import ContactForm >>> from werkzeug.datastructures import MultiDict >>> >>> >>> form1 = ContactForm(MultiDict([('name', 'jerry'),('email', 'jerry@mail.com')])) >>>
Необходимо учесть, что передача данных осуществляется в виде объекта MultiDict, так как функция-конструктор класса wtfroms.Form принимает аргумент соответствующего типа.
Если определения данных формы при генерации объекта формы не происходит, а при ее отправке использовался POST-запрос, wtforms.Form использует данные, находящиеся в атрибуте request.form. Учтите, что request.form возвращает объект, относящийся к типу ImmutableMultiDict.
Отличие этих типов в том, что ImmutableMultiDict хранит данные такого же типа, только их нельзя изменять.
А валидация формы осуществляется с использованием метода validate(), который возвращает True в случае успешности проверки.
>> >>> form1.validate() False >>>
В данном случае проверка формы оказалась неудачной, поскольку в ходе создания объекта не была передана никакая информация. Чтобы получить более подробную информацию об ошибке и обработать ее, необходимо использовать атрибут errors объекта формы.