Вступление
Проверка формы - один из наиболее важных компонентов ввода данных в веб-приложениях. Пользователи могут совершать ошибки, некоторые - злонамеренные. С помощью проверки ввода мы защищаем наше приложение от неверных данных, влияющих на бизнес-логику, и от вредоносного ввода, предназначенного для нанесения вреда нашим системам.
Попытка обработать непроверенные вводимые пользователем данные может вызвать неожиданные / необработанные ошибки, если не сбой сервера. В этом контексте проверка данных означает проверку входных данных и проверку их соответствия определенным ожиданиям или критериям. Проверка данных может выполняться как на передней, так и на задней стороне.
В этом руководстве мы узнаем, как проверять ввод данных пользователем в формах Flask с помощью расширения Flask-WTForms.
К концу этого руководства у нас будет следующая форма регистрации пользователя с критериями проверки:
Мы будем использовать Flask версии 1.1.2 и Flask-WTF версии 0.14.3 .
Настраивать
Хотя в этом нет необходимости, мы рекомендуем вам создать виртуальную среду, чтобы следовать:
$ mkdir flask-form-validation
$ cd flask-form-validation
$ python3 -m venv .
$ . bin/activate
В вашей активированной виртуальной среде мы установим наши пакеты, набрав:
$ pip install Flask Flask-WTF
Обратите внимание, что если вы хотите использовать проверку
электронной почты, вам также необходимо установить email_validator
(текущая версия - 1.1.1 ):
$ pip3 install email_validator
Теперь создадим необходимые нам файлы. Мы начнем с создания базового
app.py
, который для простоты будет содержать наше приложение Flask,
маршруты и формы:
from flask import Flask, render_template
app = Flask(__name__, template_folder='.')
app.config['SECRET_KEY']='LongAndRandomSecretKey'
Мы создали объект Flask и установили template_folder
в текущую папку.
Затем мы присвоили объект Flask
переменной app
Мы добавили
SECRET_KEY
в конфигурацию объекта app
SECRET_KEY
обычно используется для шифрования соединений с базой
данных и сеансов браузера. WTForms будет использовать SECRET_KEY
как
соль для создания токена CSRF. Вы можете узнать больше о CSRF на этой
вики-странице.
Если ваше приложение уже использует SECRET_KEY
для других целей, вы
можете создать новую конфигурацию для WTForms. В этом случае вы можете
установить конфигурацию WTF_CSRF_SECRET_KEY
Давайте создадим и добавим форму в наш текущий app.py
:
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
class GreetUserForm(FlaskForm):
username = StringField(label=('Enter Your Name:'))
submit = SubmitField(label=('Submit'))
# ...
Наш простой класс GreetUserForm
StringField
. Как следует из
названия, это поле ожидает и вернет строковое значение (вы всегда можете
преобразовать этот ввод в другие типы данных по мере необходимости). Имя
поля - username
, и мы будем использовать это имя для доступа к данным
элемента формы.
Параметры label
- это то, что будет отображаться на нашей странице,
чтобы пользователи понимали, какие данные захватывает элемент формы. У
нас также есть submit
, которая попытается отправить форму, если все
поля соответствуют нашим критериям проверки.
Теперь, когда мы настроены, давайте использовать WTForms для проверки наших данных!
Проверка формы Flask с помощью Flask-WTForms
Начнем с создания маршрута для отображения и обработки нашей формы:
# ...
@app.route('/', methods=('GET', 'POST'))
def index():
form = GreetUserForm()
if form.validate_on_submit():
return f'''<h1> Welcome {form.username.data} </h1>'''
return render_template('index.html', form=form)
В нашем маршруте есть методы GET
и POST
Метод GET
отображает
форму, тогда как POST
обрабатывает данные формы при отправке. Мы
устанавливаем URL-путь на /
или корневой URL-адрес, чтобы он
отображался как домашняя страница нашего веб-приложения. Мы
визуализируем index.html
и передаем form
в качестве параметра.
Сделаем паузу и внимательно рассмотрим эту строку:
if form.validate_on_submit():
Это правило гласит: «Если метод запроса
- POST и если поля формы действительны, продолжайте». Если введенная
нами форма соответствует нашим критериям проверки, на следующей странице
будет отображено простое приветственное сообщение с именем пользователя.
Обратите внимание, что здесь мы использовали имя поля (
username
) для доступа к входным данным.
Чтобы увидеть форму, нам нужно создать шаблон index.html
Создайте файл
и добавьте в него следующий код:
<form method="POST" action="">
<div class="form-row">
<div class="form-group col-md-6">
{{ form.csrf_token() }}
<label for=""> {{ form.username.label }}</label>
{{ form.username }}
</div>
<div class="form-group">
{{ form.submit(class="btn btn-primary")}}
</div>
</div>
</form>
Мы используем наш form
для передачи элементов WTform в Jinja2 -
синтаксический анализатор шаблонов для Flask.
Примечание : csrf_token
автоматически генерируется WTForms и
изменяется каждый раз при отрисовке страницы. Это помогает нам защитить
наш сайт от атак CSRF. По умолчанию это скрытое поле. Вы также можете
использовать {{ form.hidden_field() }}
для отображения всех скрытых
полей, включая токен CSRF, но это не рекомендуется.
Теперь давайте перейдем к нашему терминалу, чтобы запустить приложение Flask, набрав:
$ FLASK_ENV=development flask run
Для удобства мы установили для FLASK_ENV
значение «разработка» во
время разработки. Это позволяет приложению перезагружаться каждый раз,
когда мы нажимаем «Сохранить». Для Windows вам, возможно, придется
использовать set FLASK_ENV=development
в вашем терминале / консоли
перед запуском вашего приложения flask.
Вот что мы увидим, если перейдем на localhost:
{.ezlazyload}
Введите имя в поле ввода и отправьте форму. Вы увидите приветственное сообщение, которое мы определили в нашем маршруте:
{.ezlazyload}
Работает как положено. Но что, если мы ничего не вводим в поле ввода? Он все равно подтвердит форму:
{.ezlazyload}
Давайте предотвратим это и разрешим просматривать следующую страницу
только тем пользователям, которые ввели свое имя. Для этого нам нужно
убедиться, что в нашем username
есть входные данные.
Мы импортируем один из встроенных методов проверки WTForms:
DataRequired()
из wtforms.validators
и передадим его в наше поле
username
# ...
from wtforms.validators import ValidationError, DataRequired
class GreetUserForm(FlaskForm):
username = StringField(label=('Enter Your Name:'),
validators=[DataRequired()])
submit = SubmitField(label=('Submit'))
# ...
Обратите внимание, что мы передаем validators
в виде списка. Это
говорит нам о том, что у нас может быть несколько валидаторов для
каждого поля.
Теперь, когда мы используем DataRequired()
, username
не будет
проверяться, если нет входных данных:
{.ezlazyload}
Фактически, если мы щелкнем правой кнопкой мыши и проверим элемент
формы, мы увидим, что WTForms автоматически добавил required
атрибут в
поле ввода:
{.ezlazyload}
Таким образом, WTForms добавляет базовую проверку внешнего вида в наше
поле формы. Вы не сможете отправить эту форму без username
даже если
попытаетесь опубликовать форму с помощью таких инструментов, как cURL
или Postman.
Теперь предположим, что мы хотим установить новое правило проверки,
которое будет разрешать только имена длиной не менее 5 символов. Мы
можем использовать валидатор Length()
с параметром min
# ...
from wtforms.validators import ValidationError, DataRequired, Length
class GreetUserForm(FlaskForm):
username = StringField(label=('Enter Your Name:'),
validators=[DataRequired(), Length(min=5)])
submit = SubmitField(label=('Submit'))
# ...
Если мы попытаемся отправить форму с входными данными длиной менее 5 символов, критерии проверки не будут выполнены, и отправка завершится ошибкой:
{.ezlazyload}
Нажатие на кнопку отправки ничего не делает для недопустимых данных, а также не отображает никаких ошибок для пользователя. Нам нужно предоставлять сообщения об ошибках, чтобы пользователь понимал, что происходит и как это исправить.
В нашем index.html
прямо под {{ form.username }}
добавьте следующий
цикл for Jinja2 для отображения ошибок:
{% for field, errors in form.errors.items() %}
<small class="form-text text-muted ">
{{ ', '.join(errors) }}
</small>
{% endfor %}
Теперь наша форма может отображать чистые ошибки проверки:
{.ezlazyload}
По любой причине, если нам нужно ограничить максимальную длину данных
нашего поля, мы можем сделать это, передав параметр max
валидатору
Length()
Также можно настроить сообщение об ошибке, передав
необязательный message
с настраиваемой строкой ошибки.
Давайте соответствующим образом обновим поле username
# ...
class GreetUserForm(FlaskForm):
username = StringField(label=('Enter Your Name:'),
validators=[DataRequired(),
Length(min=5, max=64, message='Name length must be between %(min)d and %(max)dcharacters') ])
submit = SubmitField(label=('Submit'))
# ...
{.ezlazyload}
Дополнительные поля и валидаторы WTForms в форме регистрации пользователя
В нашей текущей форме есть одно поле, которое довольно скучно. WTForms предоставляет обширные критерии проверки формы и множество полей формы, поэтому давайте воспользуемся этим и создадим что-то с практическим применением.
Мы создадим форму регистрации пользователя и будем использовать встроенные валидаторы WTForms.
Мы будем использовать DataRequired()
для полей, которые мы хотим
убедиться, что пользователь заполняет их. Мы проверим минимальную и
максимальную длину полей с помощью Length()
, подтвердим электронные
письма с помощью Email()
и проверим если два поля содержат одинаковые
данные с валидатором EqualTo()
Удалите GreetUserForm
и замените начало кода нашей новой формой:
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, \
SubmitField
from wtforms.validators import ValidationError, DataRequired, \
Email, EqualTo, Length
class CreateUserForm(FlaskForm):
username = StringField(label=('Username'),
validators=[DataRequired(),
Length(max=64)])
email = StringField(label=('Email'),
validators=[DataRequired(),
Email(),
Length(max=120)])
password = PasswordField(label=('Password'),
validators=[DataRequired(),
Length(min=8, message='Password should be at least %(min)d characters long')])
confirm_password = PasswordField(
label=('Confirm Password'),
validators=[DataRequired(message='*Required'),
EqualTo('password', message='Both password fields must be equal!')])
receive_emails = BooleanField(label=('Receive merketting emails.'))
submit = SubmitField(label=('Submit'))
# ...
В наших формах есть четыре разных поля. Последняя - обычная кнопка
отправки. Мы использовали StringField
для получения строковых данных
от пользователей, таких как username
и email
. С другой стороны,
PasswordField
скрывает текст пароля во внешнем интерфейсе.
BooleanField
отображается как флажок во внешнем интерфейсе, поскольку
он содержит только значения True (отмечен) или False (не отмечен).
Нам нужно изменить index.html
чтобы отобразить наши новые поля формы:
<div class="container">
<h2>Registration Form</h2>
{% for field, errors in form.errors.items() %}
{{ ', '.join(errors) }}
{% endfor %}
<form class="form-horizontal" method="POST" action="">
{{ form.csrf_token() }}
<div class="form-group">
{{ form.username.label }}
{{ form.username(class="form-control") }}
</div>
<div class="form-group">
{{ form.email.label }}
{{ form.email(class="form-control") }}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control") }}
</div>
<div class="form-group">
{{ form.confirm_password.label }}
{{ form.confirm_password(class="form-control") }}
</div>
<div class="form-group">
{{ form.receive_emails.label }}
</div>
<div class="form-group">
{{ form.submit(class="btn btn-primary")}}
</div>
</form>
</div>
Наши поля формы отображаются правильно, как вы можете видеть:
{.ezlazyload}
Примечание . Если на вашем веб-сайте будет несколько разных форм, вы можете использовать макросы Jinja2 вместо того, чтобы вводить каждое поле формы по одному. Использование макросов выходит за рамки этой статьи, но это значительно ускоряет процессы создания форм.
Создание собственных пользовательских валидаторов
На большинстве веб-сайтов использование определенных символов в именах пользователей запрещено. Может быть в целях безопасности, может быть в косметике. WTForms не имеет такой логики по умолчанию, но мы можем определить ее сами.
WTForms позволяет нам добавлять собственные валидаторы, добавляя
метод валидации в наш класс UserRegistrationForm
Давайте внедрим
эту настраиваемую проверку в нашу форму, добавив метод
validate_username()
прямо под кнопкой submit
# ...
class UserRegistrationForm(FlaskForm):
# ...
submit = SubmitField(label=('Submit'))
def validate_username(self, username):
excluded_chars = " *?!'^+%&/()=}][{$#"
for char in self.username.data:
if char in excluded_chars:
raise ValidationError(
f"Character {char} is not allowed in username.")
# ...
Мы можем добавить столько методов проверки, сколько захотим. WTForms будет запускать методы проверки автоматически после определения.
Класс ValidationError
дает нам удобный способ определить наше
настраиваемое сообщение проверки. Обратите внимание, что вам нужно будет
импортировать его из wtforms.validators
прежде чем использовать.
Давайте протестируем этот новый метод, введя правильные данные во все
поля, кроме поля имени username
, которое будет содержать исключенный
символ - «%».
{.ezlazyload}
Как видите, наш настраиваемый метод проверки работает отлично и дает нам чистую ошибку проверки, которая помогает нам понять, что не так с нашими входными данными. Это значительно улучшает пользовательский опыт.
Вы можете использовать внешние библиотеки, вашу базу данных или API для
объединения с WTForms и для проверки входящих входных данных. Если вы
хотите захватить {{ form.some_field.data }}
и записать в базу данных
или запросить из нее, используйте валидаторы WTForms, чтобы
гарантировать безопасность сохранения.
Примечание . Мы исключили большинство кодов HTML, поскольку они не имеют прямого отношения к нашему руководству. Полный код будет доступен в этом репозитории GitHub , если вы захотите оформить заказ.
Заключение
Проверка данных - одна из самых важных частей веб-приложений Flask. Flask-WTforms предоставляет очень мощные и простые в освоении способы обработки данных форм.
Теперь, когда вы знаете основы проверки данных с помощью Flask-WTF, вы можете продолжить и применить свою собственную логику проверки и / или реализовать свои собственные методы как для безопасности, так и для улучшения взаимодействия с пользователем.