Как создать элементы формы в фреймворки flask

Обновлено: 30.06.2024

20.04) . Setting up python3-venv (3.8.2-0ubuntu2) . Processing triggers for man-db (2.9.1-1) .

pip freeze < requirements.txt
touch app.py
vi app.py

from flask import Flask app = Flask(__name__) @app.route("/") def home(): return "home"

export FLASK_ENV=development
export FLASK_APP=app.py
flask run

* Serving Flask app "app.py" (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 377-210-320 127127.0.0.1 - - [04/Nov/2020 22:41:50] "GET / HTTP/1.1" 200 -

mkdir templates
cd templates
touch base.html
vi base.html

touch home.html
vi home.html

Обновите код: нужно подключить render_template и добавить возврат шаблона home.html в return

from flask import Flask, render_template app = Flask(__name__) @app.route("/") def home(): return render_template("home.html")

mkdir static
cd static
mkdir css
cd css
touch style.css
vi style.css

cd ../../templates/
vi base.html

Добавьте в head строку

А в body добавьте

Теперь base.html выглядит так

cd ../static
mkdir images

Загрузите в images какой-нибудь логотип. Файл назовите logo.jpg

Адаптивное меню

Сразу после лого вставьте код для меню

Теперь base.html должен выглядеть так

Выделение активной страницы

Нужно в шаблоне создать переменную active_page и присвоить ей имя шаблона

А в базовом шаблоне нужно добавить условие для стиля пункта меню

Теперь base.html должен выглядеть так

Добавление нового элемента

from flask import Flask , request app = Flask(__name__) @app.route("/") def home(): return "home" @app.route("/item/new", methods=["GET", "POST"]) def new_item(): return render_template("new_item.html")

Внесём кое-какие изменения в app.py чтобы облегчить debug

Debug

from flask import Flask, request import pdb app = Flask(__name__) @app.route("/") def home(): return "home" @app.route("/item/new", methods=["GET", "POST"]) def new_item(): pdb.set_trace() return render_template("new_item.html")

> /home/andrei/python/projects/heihei_ru/app.py(13)new_item() -> return render_template("new_item.html") (Pdb)

Чтобы продолжить без каких либо действий - введите c и нажмите Enter

Изучить отправленный из формы запрос можно выполнив команду request

ImmutableMultiDict - это класс предоставленный Werkzeug

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

*** werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.

127.0.0.1 - - [05/Nov/2020 22:23:54] "POST /item/new HTTP/1.1" 200 -

Если заменить в форме method с POST на GET поведение изменится

Введите в форму новые Title и Description (Title for get, get-get)

Теперь можно удалить строчку

И вернуть в форму метод POST

Обработка данных из формы

Подключение базы данных

Обычно нужно создать несколько скриптов для работы с базой данных.

Самый первый из них - это инициализация, то есть создание базы данных и таблиц в ней.

Проще всего работать с SQLite, эта СУБД поддерживается питоном из коробки и всё хранится в одном файле.

Database is created and initialized. You can see the tables with the show_tables.py script.

Также понадобятся скрипты для работы с БД, например:

Это третья статья в серии, где я описываю свой опыт написания веб-приложения на Python с использованием микрофреймворка Flask.

Цель данного руководства — разработать довольно функциональное приложение-микроблог, которое я за полным отсутствием оригинальности решил назвать microblog.

Краткое повторение

В предыдущей части мы определили простой шаблон для домашней страницы и использовали мнимые объекты в качестве прототипов вещей, которых у нас еще нет. К примеру пользователи или записи.

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

Формы являются одними из самых основных блоков в любом веб-приложении. Использование форм позволит пользователям оставлять записи в блоге, а также логиниться в приложение.

Чтобы следовать этой части, ваше приложение микроблога должно быть таким, каким мы оставили его в конце предыдущей. Пожалуйста, убедитесь, что прилолжение установлено и работает.

Конфигурация

Для обработки форм мы будем использовать расширение Flask-WTF , которое является оберткой WTForms и прекрасно интегрируется с Flask приложениями.

Многие расширения Flask требуют некоторой настройки, поэтому мы создадим файл конфигурации внутри нашей корневой папки microblog, так что он будет легко доступен для изменения, если понадобится. Вот с чего мы начнем (файл config.py):


Все просто, это две настройки, которые нужны нашему расширению Flask-WTF . CSRF_ENABLED активирует предотвращение поддельных межсайтовых запросов. В большинстве случаев вы захотите включить эту опцию, что сделает ваше приложение более защищенным.

SECRET_KEY нужен только тогда, когда включен CSRF . Он используется для создания криптографического токена, который используется при валидации формы. Когда вы пишете свое приложение, убедитесь, что ваш секретный ключ сложно подобрать.

Теперь у нас есть конфиг, и мы должны сказать Flask'у прочесть и использовать его. Мы сможем сделать это сразу после того, как объект приложения Flask создан. (файл app/__init__.py):

Форма входа

В Flask-WTF формы представлены в виде объектов подкласса от класса Form . Подкласс форм просто определяет поля форм как переменные в классе.

Мы создадим форму логина, которая будет использоваться вместе с системой идентификации. Механизм входа, который мы будем поддерживать в нашем приложении, не стандартного типа имя пользователя/пароль, — мы будем использовать OpenID в качестве логинов. Преимущество OpenID в том, что авторизация пройдена у провайдера OpenID, поэтому нам не нужно проверять пароли, что сделает наш сайт более защищенным для наших пользователей.

OpenID логин требует только одну строку под названием OpenID. Также мы закинем чекбокс 'Запомнить меня' в форму, чтобы пользователь мог установить cookie в свой браузер, который будет помнить их логин, когда они вернутся.

Напишем нашу первую форму (файл app/forms.py):


Уверен, что класс говорит сам за себя. Мы импортировали класс Form и два класса полей, который нам понадобятся, TextField и BooleanField .

Импортированный Required — это валидатор, функция, которая может быть прикреплена к полю, для выполнения валидации данных отправленных пользователем. Валидатор Required просто проверяет, что поле не было отправлено пустым. В Flask-WTF есть много валидаторов, мы будем использовать несколько новых в будущем.

Шаблоны форм

Еще нам нужен HTML шаблон, который содержит форму. Хорошей новостью будет то, что класс LoginForm , который мы только что создали, знает как отдавать поля формы в HTML, поэтому нам просто нужно сконцентрироваться на макете. Вот наш шаблон логина: (файл app/templates/login.html):


Обратите внимание, что мы снова используем шаблон base.html через оператор наследования, расширяя его. Мы будем делать так со всеми нашими шаблонами, обеспечивая согласование макета на всех страницах.

Есть несколько интересных отличий между обычной HTML формой и нашим шаблоном. Шаблон ожидает экземпляр класса формы, который мы только что назначили в аргументе шаблона form . Мы позаботимся об отправке этого аргумента шаблона в будущем, когда напишем функцию представления, которая отдает этот шаблон.

Параметр шаблона form.hidden_tag() будет заменен скрытым полем для предотвращения CSRF, включенное в нашем файле настроек. Это поле должно быть во всех ваших формах, если CSRF включен.

Поля нашей формы отданы объектом формы, вы просто должны обращаться к аргументу > в том месте шаблона, где должно быть вставлено поле. Некоторые поля могут принимать аргументы. В нашем случае, мы просим форму создать наше поле openid с шириной в 80 символов.

Так как мы не определили кнопку отправки в классе формы, мы должны определить её как обычное поле. Поле отправки не несет каких-либо данных, поэтому нет нужды определять его в классе формы.

Представления форм

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

На самом деле это весьма просто, так как мы должны всего лишь передать объект формы в шаблон. вот наша новая функция представления (файл app/views.py):


Мы импортировали наш класс LoginForm , создали его экземпляр и отправили в шаблон. Это все что нужно для того, чтобы отрисовать поля формы.

Не будем обращать внимания на импорт flash и redirect . Мы используем их чуть позже.

Еще одно нововведение — это аргументы метода в декораторе route . Здесь мы говорим Flask, что функция представления принимает GET и POST запрос. Без этого представление будет принимать только GET запросы. Мы хотим получать POST запросы, которые будут отдавать форму с веденными пользователем данными.

Мы еще не запрограммировали ту часть, которая принимает данные, поэтому нажатие на кнопку submit не принесет никакого эффекта.

Получение данных формы
Еще одна область, где Flask-WTF облегчает нашу работу — обработка отправленных данных. Это новая версия нашей функции представления login , которая валидирует и сохраняет данные формы (файл app/views.py):


Метод validate_on_submit делает весь процесс обработки. Если вы вызвали метод, когда форма будет представлена пользователю (т.е. перед тем, как у пользователя будет возможность ввести туда данные), то он вернет False , в таком случае вы знаете, что должны отрисовать шаблон.

Если validate_on_submit вызывается вместе как часть запроса отправки формы, то он соберет все данные, запустит любые валидаторы, прикрепленные к полям, и если все в порядке вернет True , что указывает на валидность данных. Это означает, что данные безопасны для включения в приложение.

Улучшение валидации полей

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

Что мы пропустили, так это уведомления пользователя о том, что именно не так с формой. К счастью, Flask-WTF также облегчает эту задачу.

Взаимодействие с OpenID

На деле, мы будем сталкиваться с тем, что много людей даже не знают, что у них уже есть парочка OpenID. Не слишком известно, что ряд крупных поставщиков услуг в интернете поддерживает OpenID аутентификацию для их пользователей. Например, если у вас есть аккаунт в Google, то с ним у вас есть и OpenID. Так же как и в Yahoo, AOL, Flickr и множестве других сервисов.

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

Начнем с определения списка OpenID провайдеров, которых мы хотим представить. Мы можем сделать это в нашем файле конфигурации (файл config.py):


Теперь посмотрим как мы используем этот список в нашей функции представления login:


Тут мы получаем настройки путем их поиска по ключу в app.config . Дальше список добавляется в вызов render_template как аргумент шаблона.

Как вы догадались, нам нужно сделать еще один шаг, чтобы покончить с этим. Сейчас нам нужно указат как мы хотели бы отображать ссылки на этих провайдеров в нашем шаблоне login (файл app/templates/login.html):


Шаблон получился несколько длинным в связи со всеми этими изменениями. Некоторые OpenID включают в себя имена пользователей, для них у нас должно быть немного javascript магии, которая запрашивает имя пользователя, а затем создает OpenID. Когда пользователь кликает на ссылку OpenID провайдера и (опционально) вводит имя пользователя, OpenID для этого провайдера вставляется в текстовое поле.



скриншот нашей страницы входа после нажатия на ссылку Google OpenID

Заключительные слова

Хотя мы добились большого прогресса с нашими формами логина, в действительности мы не сделали ничего для входа пользователей в нашу систему. Все что мы сделали имело отношение к GUI процесса входа. Это потому, что прежде чем мы сможем сделать реальные логины, нам нужно иметь базу данных, где мы можем записывать наших пользователей.

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

Приложение microblog в его текущем состоянии доступно для загрузки здесь:
microblog-0.3.zip

Учтите, что zip файл не содержит в себе виртуального окружения flask. Создайте его сами, следуя инструкциям в первой части, перед тем, как вы сможете запустить приложение.

Если у вас есть какие-то вопросы или комментарии, то свободно оставляйте их ниже.

Ни один веб-сайт не обходится без HTML-форм, будь это страница обратной связи, страница авторизации или даже форма для комментариев. Когда нужно работать с данными HTML-формы, то код быстро становится очень трудным для чтения. Существуют библиотеки, призванные упростить управление этим процессом. Одним из них является WTForms .

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

Примечание. Расширение Flask-WTF добавляет несколько небольших помощников, которые делают работу с формами и фреймворком Flask более быстрой и удобной.

Пример определения класса HTML-формы для приложения Flask.

Пример формы для типичной страницы регистрации:

Использование класса HTML-формы в функции-представлении Flask.

В функции-представлении, обработка формы, определенной выше, выглядит так:

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

То, что нужно помнить:

Использование класса HTML-формы в шаблонах Jinja2.

Модуль WTForms генерирует практически всю форму за нас. Чтобы это было еще приятнее, можно написать макрос, который отображает поле с меткой и списком ошибок, если таковые имеются.

Пример шаблона jinja2 _formhelpers.html с таким макросом:

Пример шаблона register.html , который использует преимущества макроса, импортируемого из шаблона _formhelpers.html , созданного ранее:

Краткое описание модуля WTForm .

Класс WTForm.Form

Класс Form содержит определения полей, делегирует проверку/валидацию, принимает ввод, объединяет ошибки и в целом служит связующим звеном, скрепляющим все вместе.

Чтобы определить форму, нужно создать подкласс Form и декларативно определить поля как атрибуты класса:

Имена полей могут быть любыми допустимыми идентификаторами python со следующими ограничениями:

Наследование форм.

Формы могут при необходимости подклассифицировать другие формы. Новая форма будет содержать все поля родительской формы, а также любые новые поля, определенные в подклассе. Повторное использование имени поля в подклассе приводит к тому, что новое определение переопределяет исходное.

Метод validate_<fieldname> в качестве валидатора поля формы.

Чтобы обеспечить настраиваемую проверку/валидацию, для каждого поля формы можно определить метод с именем validate_<fieldname> , где fieldname - это имя поля:

Атрибуты и методы экземпляра класса Form .

Атрибуты экземпляра класса Form :

  • Form.data : словарь, содержащий данные для каждого поля. Обратите внимание, что он генерируется каждый раз, когда к нему обращаются. При неоднократном обращении - это может быть дорогостоящей операцией. Обычно используется, для перебора всех значений полей формы. Если нужно получить доступ к данным для известных полей, то должны использовать form.<field>.data , а не прокси form.data[ field ] .
  • Form.errors : словарь, содержащий список ошибок (после проверки методом Form.validate() ) для каждого поля формы. Будет пустой, если форма не была проверена или ошибок не было.

Методы экземпляра класса Form :

    Form.validate() : проверяет форму, вызвав функцию validate() для каждого поля. Возвращает True , если проверка прошла успешно. Если форма определяет метод `validate_ , то он добавляется как дополнительный валидатор для проверки поля.

Form.populate_obj(obj) : заполняет атрибуты переданного объекта obj данными из полей формы.

Примечание: Любой атрибут переданного obj с тем же именем, что и поле, будет переопределен. Используйте с осторожностью.

Одним из распространенных способов использования:

Определение полей формы

Поля формы отвечают за визуализацию и преобразование данных. Они делегируют полномочия валидаторам для проверки данных.

Поля декларативно определяются как атрибуты класса формы Form :

Когда в форме определено поле, параметры построения сохраняются до тех пор, пока форма не будет создана. Во время создания экземпляра формы создается копия поля со всеми параметрами, указанными в определении. Каждый экземпляр поля хранит свои собственные данные поля и список ошибок.

Метка label и валидаторы могут быть переданы конструктору в качестве позиционных аргументов, в то время как все остальные аргументы должны передаваться в качестве ключевых аргументов. Некоторые поля (например, SelectField ) также могут принимать дополнительные аргументы ключевых слов для конкретных полей. Обратитесь к справочнику по встроенным полям для получения информации о них.

Аргументы конструктора класса типа поля.

  • label : метка поля.
  • validators : последовательность встроенных валидаторов или вызываемых объектов, которые вызываются методом Form.validate() и по очереди применяются к значению поля.
  • filters : последовательность фильтров, которые запускаются для входных данных процессом.
  • description : описание поля, обычно используется для текста справки.
  • Id : - идентификатор для использования в поле. Разумное значение по умолчанию задается формой, и вам не нужно устанавливать его вручную.
  • default : значение по умолчанию для присвоения полю, если не предусмотрена форма или ввод объекта. Может быть вызываемым.
  • widget : если предоставляется, переопределяет виджет, используемый для визуализации поля.
  • render_kw : словарь, если предоставлен, то должен содержать ключевые слова (по умолчанию), которые будут переданы виджету widget во время рендеринга.

Встроенные типы полей для формы.

Встроенные типы полей обычно представляют скалярные типы данных с отдельными значениями и относятся к одному полю формы.

Во всех встроенных типах полей, аргумент field_arguments - это аргументы конструктора базового класса поля.

BooleanField(field_arguments, false_values=None) : представляет . Устанавливает статус checked с помощью аргумента конструктора default . Любое значение default , например default="checked", делает отметку в html-элементе и устанавливает данные в значение True .

DecimalField(field_arguments, places=2, rounding=None, use_locale=False, number_format=None) : текстовое поле, в котором отображаются и приводятся данные типа decimal.Decimal .

  • places : На сколько знаков после запятой нужно округлить значение для отображения в форме. Если None , то значение не округляется.
  • rounding : Как округлить значение, например decimal.ROUND_UP . Если значение не установлено, то используется значение из контекста текущего потока.
  • use_locale : Если True , то форматирование чисел будет на основе локали. Для форматирования чисел на основе локали требуется пакет babel .
  • `number_format – Необязательный числовой формат для языкового стандарта. Если этот параметр опущен, то для языкового стандарта используется десятичный формат.

FileField(field_arguments) : отображает поле загрузки файла. По умолчанию, значением будет имя файла, отправленное в данных формы.

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

MultipleFileField(field_arguments) : то же самое, что и FileField , но позволяет выбирать несколько файлов.

FloatField(field_arguments) : текстовое поле, за исключением того, что все вводимые данные приводятся к типу float . Ошибочный ввод игнорируется и не принимается в качестве значения. Для большинства применений DecimalField предпочтительнее FloatField , за исключением случаев, когда IEEE float абсолютно желателен вместо десятичного значения.

IntegerField(field_arguments) : текстовое поле, за исключением всего ввода, приводится к целому числу. Ошибочный ввод игнорируется и не принимается в качестве значения.

RadioField(field_arguments, choices=[], coerce=unicode) : подобно SelectField() , за исключением того, что отображает список переключателей.

Итерация/цикл по полю приведет к созданию подполей (каждое из которых также содержит метку).

Простой вывод поля без создания цикла, приведет к получению <ul> списка.

SelectField(field_arguments, choices=[], coerce=unicode, option_widget=None, validate_choice=True) :

Поля <select> принимают параметр choices , который представляет собой список пар (value, label) . Это также может быть список только значений value , и в этом случае значение используется в качестве метки label . Значение может быть любого типа, но т.к. данные формы отправляются в браузер в виде строк, необходимо будет предоставить функцию coerce , которая преобразует строку обратно в ожидаемый тип.

Пример поля SelectField() со статическими значениями:

Обратите внимание, что ключевое слово choices оценивается только один раз, поэтому, если надо создать динамический раскрывающийся список, то нужно будет назначить список вариантов для поля после создания экземпляра. Любые введенные варианты, которых нет в данном списке, приведут к сбою проверки в поле. НО можно пропустить проверку, передав аргумент validate_choice=False .

Пример поля SelectField() со статическими значениями:

Обратите внимание, что мы не передали варианты выбора конструктору SelectField , а создали список в функции-представлении. Кроме того, ключевое слово coerce для SelectField() говорит о том, что для приведения данных формы используется int() .

SelectMultipleField(field_arguments, choices=[], coerce=unicode, option_widget=None) : ни чем не отличается от обычного поля SelectField() , за исключением того, что оно может принимать и проверять несколько вариантов. Для этого необходимо указать HTML-атрибут size для поля <select> при рендеринге.

SubmitField(field_arguments) : представляет <input type="submit"> . Это позволяет проверить, была ли нажата данная кнопка отправки.

StringField(field_arguments) : это поле является основой для большинства более сложных полей и представляет собой <input type="text"> .

HiddenField(field_arguments) : это строковое поле с виджетом HiddenInput . Оно будет отображаться как , но в противном случае принудительно преобразуется в строку.

Скрытые поля похожи на любое другое поле в том смысле, что они могут принимать валидаторы и значения и быть доступны в объекте формы. Рассмотрите возможность проверки/валидации скрытых полей так же, как обычных.

PasswordField(field_arguments) : поле ввода пароля <input type="password"> , всегда преобразуется в строку. Кроме того, любое значение, принятое этим полем, не отображается обратно в браузер, как обычные поля.

TextAreaField(field_arguments) : поле представляет собой текстовое поле <textarea> и может использоваться для многострочного ввода.

Встроенные валидаторы полей.

Поля формы, определенные в подклассе класса Form , могут проверяться встроенными валидаторами, определенными в wtform.validators . Встроенные валидаторы передаются списком, как аргумент типа поля формы.

Список встроенных валидаторов, определяемых в wtforms.validators :

Email(message=None, granular_message=False, check_deliverability=False, allow_smtputf8=True, allow_empty_local=False) :

Проверяет адрес электронной почты. Требуется установка модуля email_validator .

EqualTo(fieldname, message=None) : сравнивает значения двух полей.

Этот валидатор можно использовать для облегчения одного из наиболее распространенных сценариев формы смены пароля:

Здесь используется валидатор InputRequired() , чтобы предотвратить попытку валидатора EqualTo() проверить, не совпадают ли пароли, если пароли не были указаны вообще. Поскольку InputRequired() останавливает цепочку проверки, то EqualTo() не запускается в случае, если поле пароля остается пустым.

InputRequired(message=None) : проверяет, что для поля были предоставлены данные. Другими словами, значение поля - не пустая строка. Этот валидатор также устанавливает флаг обязательного поля формы для заполнения.

IPAddress(ipv4=True, ipv6=False, message=None) : проверяет IP-адрес. Аргумент Ipv4 - если True , принимать адреса IPv4 как действительные (по умолчанию True ). Аргумент Ipv6 - если True , принимать IPv6-адреса как действительные (по умолчанию False )

Length(min=- 1, max=- 1, message=None) : проверяет длину строки. Аргумент min - минимальная необходимая длина строки. Если не указан, минимальная длина проверяться не будет. Аргумент max - максимальная длина строки. Если не указан, максимальная длина проверяться не будет.

NumberRange(min=None, max=None, message=None) : проверяет, что число имеет минимальное и/или максимальное значение включительно. Это будет работать с любым сопоставимым типом чисел, таким как числа с плавающей запятой и десятичные дроби, а не только с целыми числами.

Optional(strip_whitespace=True) : разрешает пустой ввод (необязательное поле) и останавливает продолжение цепочки проверки. Если ввод пуст, также удаляются предыдущие ошибки из поля (например, ошибки обработки). Если аргумент strip_whitespace=True (по умолчанию), то также остановит цепочку проверки, если значение поля состоит только из пробелов.

Regexp(regex, flags=0, message=None) : проверяет поле на соответствие регулярному выражению, предоставленному пользователем. Аргумент regex - cтрока регулярного выражения для использования. Также может быть скомпилированным шаблоном регулярного выражения. Аргумент flags - используемые флаги регулярного выражения, например re.IGNORECASE . Игнорируется, если регулярное выражение не является строкой.

URL(require_tld=True, message=None) : простая проверка URL на основе регулярного выражения. Вероятно потребуется его проверка на доступность другими способами.

UUID(message=None) : проверяет UUID.

Создание собственного валидатора.

Выше было показано использование встроенного в класс Form валидатора (как метода с определенным именем) для проверки одного поля. Встроенные валидаторы хороши, но их сложно использовать повторно.

Так как тип поля формы принимает аргумент validators в качестве последовательности вызываемых объектов, то это может быть простая функция Python, которая возвращает значение bool . НО лучше использовать фабричную функцию, которая возвращает вызываемый объект:

и так далее. Это необходимая и не всегда тривиальная работа. Но, к счастью, есть расширения для Flask, которые значительно облегчают реализацию этих и подобных им типовых задач. В частности, библиотека

позволяет достаточно просто оперировать формами и выполняет большую часть задач за разработчика. На этом занятии мы, как раз, и познакомимся с основами ее функционала на примере нашего тестового сайта.

Вообще, WTForms – это библиотека, написанная на Python и независимая от фреймворков. Она способна генерировать формы, проверять их, наполнять начальной информацией, работать с reCaptcha и многое другое. Кроме того, в нее встроена защита от CSRF:

CSRF (Cross-Site Request Forgery) – межсайтовая подделка запросов.

Это атака, при которой происходит имитация запроса пользователя к стороннему сайту со страницы другого сайта. То есть, злоумышленник создает некую страницу на своем сайте, жертва заходит на нее и из формы запроса отправляется запрос на сайт, в котором посетитель авторизован. Если на сайте нет защиты от CSRF-атаки, злоумышленник, от имени пользователя получает доступ к стороннему ресурсу и творит свои «черные дела».

Так вот, такие атаки будут нипочем, при использовании WTForms. И первым делом нужно установить это расширение. Для Flask оно называется

и устанавливается с помощью команды:

pip install flask_wtf

Концепция создания форм здесь состоит в расширении базового класса

  • StringField – для работы с полем ввода;
  • PasswordField – для работы с полем ввода пароля;
  • BooleanField – для checkbox полей;
  • TextAreaField – для работы с вводом текста;
  • SelectField – для работы со списком;
  • SubmitField – для кнопки submit.

Это лишь часть классов. Полную документацию можно посмотреть на официальном сайте.

Создание класса формы

Давайте для примера создадим в нашем проекте вспомогательный файл forms.py, в котором будем определять все классы форм и начнем с класса LoginForm:

  • DataRequired – валидатор, требующий ввода каких-либо данных;
  • Email – проверяет корректность введенного email-адреса;
  • Length – проверяет количество введенных символов.

Конечно, это не все валидаторы, которые есть в WTForms. Полный их список и набор параметров можно посмотреть на странице официальной документации.

И, обратите внимание, валидатор Email может требовать отдельной дополнительной установки:

pip install email-validator

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

Создание шаблона формы

Итак, класс определен. Как им теперь пользоваться? Для начала в основном модуле программы выполним импорт:

И, далее, в функции представления login создадим его экземпляр и передадим шаблону login.html:

То есть, в шаблоне будет доступ к переменным этого класса через параметр form:

Смотрите, здесь в самом начале идет вызов метода:

который создает скрытое поле, содержащее токен, используемый для защиты формы от CSRF-атак. Это все, что от нас требуется, остальное Flask-WTF сделает автоматически. Как видите, все просто и удобно.

Далее, мы вызываем методы label, которые вставляют в форму тег:

А методы email, psw, remember, submit – создают соответствующие теги полей ввода, кнопок, чекбоксов и так далее.

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

Отлично, это сделано, теперь в обработчике login выполним обработку элементов этой формы и свяжем ее с ранее созданным функционалом:

Смотрите, здесь вначале идет проверка validate_on_submit() корректности переданных данных по POST-запросу и правильность заполненных полей формы. И это очень удобно, т.к. программа пойдет дальше только в случае верных данных. В этом случае мы дополнительно проверяем корректность ввода пароля и, затем, авторизовываем пользователя и перенаправляем его на соответствующий URL. Иначе, снова отображается форма авторизации.

Видео по теме
























© 2021 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта


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

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

В этом руководстве мы узнаем, как проверить ввод данных пользователем в Flask формах с помощью расширения Flask-WTForms.

К концу этого руководства у нас будет следующая форма регистрации пользователя с критериями проверки:


Мы будем использовать Flask версии 1.1.2 и Flask-WTF с версией 0.14.3.

Настройка

Хотя в этом нет необходимости, мы рекомендуем вам создать виртуальную среду:

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

Обратите внимание: если вы хотите использовать проверку адреса электронной почты, вам также необходимо установить пакет email_validator (текущая версия - 1.1.1):

Теперь создадим необходимые нам файлы. Мы начнем с создания базового app.py , который для простоты будет содержать наше приложение Flask, маршруты и формы:

Мы создали объект Flask и установили template_folder в текущую папку. Затем мы присвоили объект Flask переменной app . Мы добавили SECRET_KEY в конфигурацию нашего объекта app .

Обычно SECRET_KEY используется для шифрования соединений с базами данных и сессий браузера. WTForms будет использовать SECRET_KEY в качестве соли для создания токена CSRF. Вы можете узнать больше о CSRF в вики.

Если ваше приложение уже использует конфигурацию SECRET_KEY для других целей, вы можете создать новую конфигурацию для WTForms. В этом случае вы можете установить WTF_CSRF_SECRET_KEY .

Создадим и добавим форму в наш текущий app.py :

Наш простой класс GreetUserForm содержит StringField . Как следует из названия, это поле ожидает и вернет строковое значение (вы всегда можете преобразовать этот ввод в другие типы данных по мере необходимости). Поля username , мы будем использовать для доступа к данным элемента формы.

Теперь, когда у нас все настроено, давайте воспользуемся WTForms для проверки наших данных!

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

Начнем с создания маршрута для отображения и обработки нашей формы:

Наш маршрут имеет методы GET и POST . В методе GET отображает форму, в то время как метод POST по запросу обрабатывает данные формы. Мы устанавливаем путь URL-адреса / или корневой URL-адрес, чтобы он отображался как домашняя страница нашего веб-приложения. Мы визуализируем шаблон index.html и передаем объект form в качестве параметра.

Чтобы увидеть форму, нам нужно создать шаблон index.html . Создайте файл и добавьте в него следующий код:

Мы используем наш объект form для передачи элементов WTform в Jinja2 - синтаксический анализатор шаблонов для Flask.

Примечание: csrf_token создается автоматически WTForms и изменяется каждый раз при отображении страницы. Это помогает нам защитить наш сайт от атак CSRF. По умолчанию это скрытое поле. Вы также можете использовать > для отображения всех скрытых полей, включая токен CSRF, но это не рекомендуется.

Теперь давайте перейдем к нашему терминалу, чтобы запустить приложение Flask, набрав:

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

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



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


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

Мы импортируем один из встроенных методов проверки WTForms: DataRequired() из wtforms.validators и передадим его в наше поле username .

Обратите внимание, что мы передаем параметр validators в виде списка. Это говорит нам о том, что у нас может быть несколько валидаторов для каждого поля.

Теперь, когда мы используем DataRequired() , поле username не будет проверяться, если нет входных данных:


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


Теперь предположим, что мы хотим установить новое правило проверки, которое будет разрешать только имена длиной не менее 5 символов. Мы можем использовать валидатор Length() с параметром min :


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

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


Обновим поле username :


Дополнительные поля и валидаторы WTForms с формой регистрации пользователя

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

Мы создадим форму регистрации пользователя и будем использовать встроенные валидаторы WTForms.

Мы будем использовать валидатор DataRequired() для полей, которые мы хотим убедиться, что пользователь заполняет. Мы будем проверять минимальную и максимальную длину полей с помощью валидатора Length() , проверять электронные письма с помощью валидатора Email() и проверять, содержат ли два поля одинаковые данные с помощью валидатора EqualTo() .

Удалите класс GreetUserForm и замените начало кода нашей новой формой:

В наших формах есть четыре разных поля. Последняя - обычная кнопка отправки. С помощью StringField мы устанавливаем строковый инпут от пользователей, например username и email . PasswordField скрывает текст пароля во внешнем интерфейсе. BooleanField отображается как флажок во внешнем интерфейсе, поскольку он содержит только значения True (отмечен) или False (не отмечен).

Нам нужно изменить шаблон index.html , чтобы отобразить наши новые поля формы:

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


Примечание. Если на вашем веб-сайте будет несколько разных форм, вы можете использовать макросы Jinja2 вместо того, чтобы вводить каждое поле формы по одному. Использование макросов выходит за рамки этой статьи, но это значительно ускоряет процессы создания форм.

Создание собственных пользовательских валидаторов

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

WTForms позволяет нам добавлять настраиваемые валидаторы, добавляя метод проверки к нашему классу UserRegistrationForm . Давайте внедрим эту настраиваемую проверку в нашу форму, добавив метод validate_username() прямо под кнопкой submit .

Мы можем добавить столько методов проверки, сколько захотим. WTForms будет запускать методы проверки автоматически после определения.

Давайте протестируем этот новый метод, введя правильные данные во все поля, кроме поля username , которое будет содержать исключенный символ - «%».


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

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

Вывод

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

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

Читайте также: