Файл проекта: https://github.com/selfedu-rus/flasksite-18
Продолжаем предыдущее занятие и если мы сейчас
введем на странице неверные данные, например, некорректный пароль, то страница
просто обновится без указания каких-либо ошибок. Вместе с тем, ошибки
автоматически генерируются и доступны в шаблоне из коллекции:
form.<переменная поля>.errors
Добавим их
вывод:
{{ form.email.label() }}
{% if form.email.errors %}
{{ form.email(class="invalid") }}
<span class="invalid-feedback">
{% for e in form.email.errors %}
{{ e }}
{% endfor %}
</span>
{% else %}
{{ form.email() }}
{% endif %}
{{ form.psw.label() }}
{% if form.psw.errors %}
{{ form.psw(class="invalid") }}
<span class="invalid-feedback">
{% for e in form.psw.errors %}
{{ e }}
{% endfor %}
</span>
{% else %}
{{ form.psw() }}
{% endif %}
Мы здесь после
отображения имени поля проверяем: существуют ли ошибки и если да, то указываем
класс стилей для отображения ошибочных полей а, затем, выводим список ошибок
(их может быть несколько). Если же ошибок нет, то поле отображается в первоначальном
виде.
Чтобы все
выглядело относительно прилично, добавим стили для отображения ошибочных
состояний:
.form-contact .invalid {
display: inline-block;
background: #FF9898;
}
.form-contact .invalid-feedback {
color: #CC0000;
}
Перейдем в
браузер, наберем неверный email и увидим сообщение об ошибке:
Invalid email address.
По умолчанию в WTForms ошибки
показываются на английском языке. Чтобы прописать свои собственные, в классе LoginForm нужно их явно
прописать:
class LoginForm(FlaskForm):
email = StringField("Email: ", validators=[Email("Некорректный email")])
psw = PasswordField("Пароль: ", validators=[DataRequired(),
Length(min=4, max=100, message="Пароль должен быть от 4 до 100 символов")])
remember = BooleanField("Запомнить", default = False)
submit = SubmitField("Войти")
Теперь, при
неверном вводе информации мы будем видеть указанные нами сообщения.
Формирование полей в шаблоне через цикл
Если вернуться к
нашей форме авторизации, то увидим повторяющиеся части текста, что не очень
хорошо. Чтобы этого не было, мы можем создавать однотипные поля формы через
цикл, используя параметр form как итерируемый объект:
{% for field in form if field.name not in ['csrf_token', 'remember', 'submit'] -%}
{{ field.label() }}
{% if field.errors %}
{{ field(class="invalid") }}
<span class="invalid-feedback">
{% for e in field.errors %}
{{ e }}
{% endfor %}
</span>
{% else %}
{{ field() }}
{% endif %}
{% endfor %}
Смотрите, мы здесь выбираем все поля (field) из формы, кроме полей с именами 'csrf_token', 'remember'
и 'submit'.
Первое
поле – это специальный токен, служащий для защиты от CSRF-атак, а
последние два – это флажок «Запомнить» и кнопка «Войти». Мы их отображаем на
форме без указания списка ошибок.
Если теперь
перейти в браузер, то при обновлении страницы увидим ту же самую форму
авторизации со всеми полями. Причем, порядок полей тот же, что и порядок
переменных в классе LoginForm. Мало того, если будет добавлена еще
какая-либо типовая переменная поля, то она автоматически добавится в форму и
будет соответствующим образом обрабатываться, что очень удобно.
Форма регистрации
Давайте заменим
в нашем сайте еще одну форму для регистрации пользователей. Ее класс можно
прописать следующим образом:
class RegisterForm(FlaskForm):
name = StringField("Имя: ", validators=[Length(min=4, max=100, message="Имя должно быть от 4 до 100 символов")])
email = StringField("Email: ", validators=[Email("Некорректный email")])
psw = PasswordField("Пароль: ", validators=[DataRequired(),
Length(min=4, max=100, message="Пароль должен быть от 4 до 100 символов")])
psw2 = PasswordField("Повтор пароля: ", validators=[DataRequired(), EqualTo('psw', message="Пароли не совпадают")])
submit = SubmitField("Регистрация")
Мы здесь
используем еще один валидатор EqualTo для проверки совпадения паролей.
Все остальное очень похоже на форму авторизации.
Далее, шаблон register.html запишем в виде:
{% extends 'base.html' %}
{% block content %}
{{ super() }}
{% for cat, msg in get_flashed_messages(True) %}
<div class="flash {{cat}}">{{msg}}</div>
{% endfor %}
<form action="{{ url_for('register') }}" method="post" class="form-contact">
{{ form.hidden_tag() }}
{% for field in form if field.name not in ['csrf_token', 'submit'] -%}
{{ field.label() }}
{% if field.errors %}
{{ field(class="invalid") }}
<span class="invalid-feedback">
{% for e in field.errors %}
{{ e }}
{% endfor %}
</span>
{% else %}
{{ field() }}
{% endif %}
{% endfor %}
{{ form.submit() }}
</form>
{% endblock %}
И обработчик register:
@app.route("/register", methods=["POST", "GET"])
def register():
form = RegisterForm()
if form.validate_on_submit():
hash = generate_password_hash(request.form['psw'])
res = dbase.addUser(form.name.data, form.email.data, hash)
if res:
flash("Вы успешно зарегистрированы", "success")
return redirect(url_for('login'))
else:
flash("Ошибка при добавлении в БД", "error")
return render_template("register.html", menu=dbase.getMenu(), title="Регистрация", form=form)
Как видите, все
достаточно просто и при этом получаем доступ к богатому функционалу модуля WTForms.
Конечно, мы
здесь рассмотрели лишь общий принцип использования расширения WTForms. Этих знаний
вполне достаточно для простых реализаций. А для более глубокого погружения в
эту тему советую почитать официальную документацию по данному модулю.