DevGang
Авторизоваться

Flask: Проверка формы с помощью Flask-WTF

Проверка формы - один из наиболее важных компонентов ввода данных в веб-приложениях. Пользователи могут совершать ошибки, некоторые - злонамеренные. С помощью проверки ввода мы защищаем наше приложение от неверных данных, влияющих на бизнес-логику, и от злонамеренного ввода, предназначенного для нанесения вреда нашим системам.

Попытка обработать вводимые пользователем данные, не прошедшие проверку, может вызвать неожиданные / необработанные ошибки, если не сбой сервера. В этом контексте проверка данных означает проверку входных данных и проверку их соответствия определенным ожиданиям или критериям. Проверка данных может выполняться как на стороне клиента, так и на стороне сервера.

В этом руководстве мы узнаем, как проверить ввод данных пользователем в 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 значение development во время разработки. Это позволяет приложению перезагружаться каждый раз, когда мы нажимаем «Сохранить». Для Windows вам, возможно, придется использовать set FLASK_ENV=development в своем терминале / консоли перед запуском приложения Flask.

Вот что мы увидим, если перейдем на localhost:

Введите имя в поле ввода и отправьте форму. Вы увидите приветственное сообщение, которое мы определили в нашем маршруте:

Работает как положено. Но что, если мы ничего не вводим в поле ввода? Он все равно подтвердит форму:

Давайте предотвратим это и позволим видеть следующую страницу только тем пользователям, которые ввели свое имя. Для этого нам нужно убедиться, что в нашем поле 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 не будет проверяться, если нет входных данных:

Фактически, если мы щелкнем правой кнопкой мыши и проверим элемент формы, мы увидим, что WTForms автоматически добавили атрибут required в поле ввода:

Таким образом, 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 символов, критерии проверки не будут выполнены, и отправка завершится ошибкой:

Нажатие на кнопку отправки ничего не делает для недопустимых данных, а также не отображает никаких ошибок для пользователя. Нам нужно предоставлять сообщения об ошибках, чтобы пользователь понимал, что происходит и как это исправить.

В нашем шаблоне index.html прямо под шаблоном {{ form.username }} добавьте следующий цикл для отображения ошибок:

 {% for field, errors in form.errors.items() %}
    <small class="form-text text-muted ">
        {{ ', '.join(errors) }}
    </small>
{% endfor %}

Теперь наша форма может отображать чистые ошибки проверки:

По какой-либо причине, если нам нужно ограничить максимальную длину данных нашего поля, мы можем сделать это, передав параметр 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'))

# ...

Дополнительные поля и валидаторы 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 и emailPasswordField скрывает текст пароля во внешнем интерфейсе. 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>

Поля нашей формы отображаются правильно, как вы можете видеть:

Примечание. Если на вашем веб-сайте будет несколько разных форм, вы можете использовать макросы 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, которое будет содержать исключенный символ - «%».

Как видите, наш собственный метод проверки работает отлично и выдает чистую ошибку проверки, которая помогает нам понять, что не так с нашими входными данными. Это значительно улучшает пользовательский опыт.

Вы можете использовать внешние библиотеки, вашу базу данных или API для объединения с WTForms и для проверки входящих данных. Если вы хотите захватить {{ form.some_field.data }} и записать в базу данных или запросить из нее, используйте валидаторы WTForms, чтобы гарантировать безопасность для сохранения.

Вывод

Проверка данных - одна из самых важных частей веб-приложений Flask. Flask-WTforms предоставляет очень мощные и простые в освоении способы обработки данных форм.

Теперь, когда вы знаете основы проверки данных с помощью Flask-WTF, вы можете продолжить и применить свою собственную логику проверки и / или реализовать свои собственные методы как для безопасности, так и для улучшения взаимодействия с пользователем.

Источник:

#Flask #Python
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

Поделитесь своим опытом, расскажите о новом инструменте, библиотеке или фреймворке. Для этого не обязательно становится постоянным автором.

Попробовать

Оплатив хостинг 25$ в подарок вы получите 100$ на счет

Получить