Как получить данные с формы flask
Перейти к содержимому

Как получить данные с формы flask

Мега-Учебник Flask, Часть 3: Формы

Это третья статья в серии, где я описываю свой опыт написания веб-приложения на 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 запросы, которые будут отдавать форму с веденными пользователем данными.

На этой стадии вы можете запустить приложение и посмотреть на вашу форму в браузере. После запуска откройте адрес, который мы связали с функцией представления login: http://localhost:5000/login

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

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

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

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

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

Когда validate_on_submit возвращает True , наша функция представления вызывает две новых функции, импортированных из Flask. Функция Flash — это быстрый способ отображения сообщения на следующей странице, представленной пользователю. В данном случае мы будем использовать это для отладки до тех пор, пока у нас нет инфраструктуры, необходимой для логирования, вместо этого мы просто будем выводить сообщение, которое будет показывать отправленные данные. Также flash чрезвычайно полезен на продакшн сервере для обеспечения обратной связи с пользователем.

Flash сообщения не будут автоматически появляться на нашей странице, наши шаблоны должны отображать сообщени в том виде, который подходит для макета нашего сайта. Мы добавим сообщения в базовый шаблон, так что все наши шаблоны наследуют эту функциональность. Это обновленный шаблон base (файл app/templates/base.html):

Надеюсь способ отображения сообщений не требует пояснений.

Другая новая функция, которую мы использовали в нашем представлении login — redirect . Эта функция перенаправляет клиентский веб-браузер на другую страницу, вместо запрашиваемой. В нашей функции представления мы использовали редирект на главную страницу, разработанную в предыдущих частях. Имейте в виду, что flash сообщения будут отображены даже если функция заканчивается перенаправлением.

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

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

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

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

Когда поле не проходит валидацию Flask-WTF добавляет наглядное сообщение об ошибке в объект формы. Эти сообщения доступны в шаблоне, так что нам просто нужно добавить немного логики для их отображения.

Это наш шаблон login с сообщениями валидации полей (файл app/templates/login.html):

Единственное изменение, которое мы сделали — добавили цикл, отрисовывающий любые добавленные валидатором сообщения об ошибках справа от поля openid. Как правило, любые поля имеющие прикрепленные валидаторы будут иметь ошибки, добавленные как form.errors.имя_поля . В нашем случае мы используем form.errors.openid . Мы отображаем эти сообщения в красном цвете, чтобы обратить на них внимание пользователя.

Взаимодействие с 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. Создайте его сами, следуя инструкциям в первой части, перед тем, как вы сможете запустить приложение.

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

Получение данных из запроса в приложении на Flask.

В материале рассматривается работа с контекстом запроса в приложении на Flask и доступ к различным данным запроса, а именно доступ к полям формы, дополнительным параметрам URL-адреса, получение данных JSON, извлечения информации о User-Agent и IP-адреса клиента, получение referrer-URL и так далее.

Содержание:

    . . . . .
    Другие полезные атрибуты объекта запроса:

      . . . .

    Доступ к параметрам, передаваемых в URL GET запросом.

    Параметры GET запроса, передаются в качестве параметров URL-адреса. Это та часть URL- адреса, которая расположена после знака вопроса, например http://example.ru/search?query=str.lower&opt=9 , параметрами запроса будут считаться query=str.lower&opt=9 . Такие адреса в основном используются в поисковых запросах, REST API сайта или в AJAX-запросах.

    Для получения этих параметров необходимо воспользоваться свойством args входящего запроса flask.Request . По умолчанию, свойство request.args , представляет собой объект словаря, доступ к значениям параметров осуществляется по ключу request.args['opt'] Для безопасного извлечения ключей из словаря, что бы избежать исключений (при отсутствии ключа), можно воспользоваться методом dict.get() . Во Flask, синтаксис этого метода выглядит следующим образом:

    Где аргумент key — это ключ (имя параметра в URL); default — возвращаемое значение, если ключа key не существует. Пример извлечения параметров GET-запроса, если URL-адрес имеет вид http://example.ru/search?query=str.lower&opt=9 :

    Если важно сохранение порядка передаваемых параметров, то можно изменить тип данных, присвоив request.parameter_storage_class другой тип, например werkzeug.datastructures.ImmutableList .

    Доступ к данным формы, передаваемой POST запросом.

    Для доступа к данным формы (передаваемым в запросах POST или PUT ) нужно использовать атрибут входящего запроса request.form , который так же представляет собой словароподобный объект. Ключи request.form имеют значения атрибута name HTML-тэга <input> в форме (например <input name="username"> ).

    Так как request.form представляет собой словароподобный объект, то к нему рекомендуется обращаться с помощью метода словаря dict.get() или путем перехвата KeyError и выдачи собственной страницы с ошибкой HTTP 404 Not Found .

    В примере, для извлечения полей формы, воспользуемся методом словаря dict.get() . Предположим, что была передана форма авторизации пользователя на сайте:

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

    Примечание: объект запроса flask.Request еще содержит атрибут request.values , который объединяет в себе значения, передаваемые GET и POST запросами. Другими словами, используя request.values.get() можно получить доступ как к элементам передаваемой формы в HTML-полях <input> , так и параметрам, передаваемым в URL-адресе.

    Обратите внимание, что атрибут объекта запроса request.method содержит в себе строковое значение HTTP-метода.

    Получение текущего URL-адреса страницы.

    Так как URL-адрес может иметь переменные части, которые указываются в угловых скобках, иногда (например, в целях сбора статистики) необходимо получить его оригинал, что бы потом, в функции-представлении, не воссоздавать его вручную.

    Объект запроса flask.Request позволяет это сделать. За URL-адрес в объекте запроса отвечают несколько атрибутов:

    • request.url — полный URL-адрес запроса со схемой request.scheme , хостом request.host_url , корневым путем request.root_path , путем request.path и строкой запроса request.query_string .
    • request.base_url — как request.url , но без строки запроса.
    • request.url_root — URL-адрес со схемой request.scheme , хостом request.host_url , корневым путем request.root_path .
    • request.scheme — схема URL-адресов ( http или https ).
    • request.host — только хост, включая порт, если он доступен.
    • request.root_path — префикс, под которым монтируется приложение, без завершающей косой черты (еще известен как environ["SCRIPT_ROOT"] ).
    • request.path — часть пути URL-адреса после root_path . Это путь, используемый для маршрутизации внутри приложения.
    • request.full_path — запрошенный путь URL, включая строку запроса (параметры URL, после знака вопроса).
    • request.query_string — часть URL-адреса после знака вопроса '?' .
    • request.url_charset — кодировка, которая предполагается для URL-адресов.

    Разберем значение приведенных выше атрибутов объекта запроса на примере. Допустим, что приложение имеет следующий корень: http://www.example.com/myapp и пользователь запрашивает следующий URI-адрес: http://www.example.com/myapp/%CF%80/page.html?x=y

    В этом случае значения вышеупомянутых атрибутов будут следующими:

    • request.url : 'http://www.example.com/myapp/π/page.html?x=y' ;
    • request.base_url : 'http://www.example.com/myapp/π/page.html' ;
    • request.url_root : 'http://www.example.com/myapp/'
    • request.scheme : 'http' ;
    • request.host : 'www.example.com' ;
    • request.root_path : '/myapp' ;
    • request.path : '/π/page.html' ;
    • request.full_path : '/π/page.html?x=y' ;
    • request.query_string : x=y.

    Информация об окружении WSGI сервера.

    Если запуск и работоспособность приложение Flask обеспечивается WSGI сервером (например gunicorn ), то информацию об окружении этого сервера можно получить из атрибута объекта запроса request.environ . Этот атрибут представляет собой словарный объект и содержит информацию об окружении WSGI, а так же HTTP-заголовки, полученные вместе с запросом.

    Реальный IP-адрес клиента.

    Переменные WSGI сервера необходимы, например, что бы узнать реальный IP-адрес посетителя сайта, если приложение Flask работает за прокси сервером, таким как Nginx. В ситуации с прокси, атрибут запроса request.remote_addr будет возвращать IP-адрес самого прокси сервера (т. е. локальный IP-адрес, на котором он работает, например 127.0.0.1), а не клиента. Реальный IP-адрес клиента можно узнать из окружения WSGI, обратившись к ключу 'HTTP_X_FORWARDED_FOR' или 'X-Real-IP' , это зависит, какая переменная использовалась при настройке Nginx:

    Примечание. Заголовок X-Forwarded-For (XFF) является де-факто стандартным заголовком для идентификации происхождения IP-адреса клиента, подключающегося к веб-серверу через HTTP-прокси или балансировщик нагрузки. Когда трафик перехватывается между клиентом и сервером, то журналы доступа к серверу содержат только IP-адрес прокси-сервера или балансировки нагрузки.

    И вообще, при связке: прокси сервер -> WSGI сервер -> приложение Flask, максимум информации лучше брать из окружения request.environ , т.к. здесь она будет более правдива.

    Переменные окружения WSGI сервера.

    Из словарного атрибута запроса request.environ можно узнать очень много интересного, например реферальный URL-адрес (это адрес, с которого клиент перешел на текущий URL), его ключ в окружении 'HTTP_REFERER' . Возвращаемые ключи словарного объекта request.environ во многом зависят от того где работает и как настроен WSGI сервер, т.е. здесь все индивидуально.

    Вот некоторые из них:

    ‘wsgi.version’, ‘PATH_INFO’ ‘HTTP_SEC_CH_UA’
    ‘wsgi.url_scheme’ ‘QUERY_STRING’ ‘HTTP_SEC_CH_UA_MOBILE’
    ‘wsgi.input’ ‘REQUEST_URI’ ‘HTTP_SEC_CH_UA_PLATFORM’
    ‘wsgi.errors’ ‘RAW_URI’ ‘HTTP_UPGRADE_INSECURE_REQUESTS’
    ‘wsgi.multithread’ ‘REMOTE_ADDR’ ‘HTTP_DNT’
    ‘wsgi.multiprocess’ ‘REMOTE_PORT’ ‘HTTP_USER_AGENT’
    ‘wsgi.run_once’ ‘SERVER_NAME’ ‘HTTP_ACCEPT’
    ‘werkzeug.socket’ ‘SERVER_PORT’ ‘HTTP_REFERER’
    ‘SERVER_SOFTWARE’ ‘SERVER_PROTOCOL’ ‘HTTP_ACCEPT_ENCODING’
    ‘REQUEST_METHOD’ ‘HTTP_HOST’ ‘HTTP_ACCEPT_LANGUAGE’
    ‘SCRIPT_NAME’ ‘HTTP_CONNECTION’ ‘HTTP_COOKIE’

    Только не забывайте при обращении к словарному атрибуту request.environ , применять метод dict.get() , что бы избежать появления исключения, если какой-то ключ отсутствует.

    Получение данных в формате JSON.

    Что бы извлечь JSON данные, передаваемые в запросе необходимо воспользоваться методом объекта запроса .get_json() , который имеет следующий синтаксис:

    • force : ( bool ) — игнорирует тип mimetype и всегда пытается анализировать JSON.
    • silent : ( bool ) — отключает появление возможных ошибок при анализе JSON и если они происходят то возвращает None .
    • cache : ( bool ) — сохраняет проанализированный JSON для последующих вызовов.

    Если заголовок mimetype не указывает на JSON данные ( application/json ), то этот метод, по умолчанию ( force=False ), возвратит None .

    Если синтаксический анализ JSON завершается неудачно, то вызывается функция request.on_json_loading_failed() , и в качестве возвращаемого значения используется ее значение.

    Другие полезные атрибуты объекта запроса.

    request.headers :

    Атрибут объекта запроса request.headers содержит все заголовки, полученные вместе с запросом. Представляет собой словарный объект, следовательно, для безопасного получения заголовка можно воспользоваться методом словаря dict.get() :

    Если заголовок с именем 'HEADER-NAME' не существует то код выше вернет None (исключения не будет), поэтому его можно использовать в конструкции:

    Но прямое обращение к ключу приведет к появлению исключения KeyError: 'HEADER-NAME'

    #11 Работа с формами во Flask

    Формы — важный элемент любого веб-приложения, но, к сожалению, работать с ними достаточно сложно. Сначала нужно подтвердить данные на стороне клиента, затем — на сервере. И даже этого недостаточно, если разработчик приложения озабочен такими проблемами безопасности как CSRF, XSS, SQL Injection и так далее. Все вместе — это масса работы. К счастью, есть отличная библиотека WTForms, выполняет большую часть задач за разработчика. Перед тем как узнать больше о WTForms, следует все-таки разобраться, как работать с формами без библиотек и пакетов.

    Работа с формами — сложный вариант

    Для начала создадим шаблон login.html со следующим кодом:

    Этот код нужно добавить после функции представления books() в файле main2.py :

    Стоит обратить внимание, что аргумент methods передан декоратору route() . По умолчанию обработчик запросов вызывается только в тех случаях, когда метод request.method — GET или HEAD. Это можно изменить, передав список разрешенных HTTP-методов аргументу-ключевому слову methods . С этого момента функция представления login будет вызываться только тогда, когда запрос к /login/ будет сделан с помощью методов GET, POST или HEAD. Если попробовать получить доступ к URL /login/ другим методом, появится ошибка HTTP 405 Method Not Allowed.

    В прошлых уроках обсуждалось то, что объект request предоставляет информацию о текущем веб-запросе. Информация, полученная с помощью формы, хранится в атрибуте form объекта request . request.form — это неизменяемый объект типа словарь, известный как ImmutableMultiDict .

    Дальше нужно запустить сервер и зайти на https://localhost:5000/login/ . Откроется такая форма.

    форма во Flask

    Запрос к странице был сделан с помощью метода GET, поэтому код внутри блока if функции login() пропущен.

    Если попробовать отправить форму без ввода данных, страница будет выглядеть следующим образом:

    ошибка в форме во Flask

    В этот раз страница была отправлена методом POST, поэтому код внутри if оказался исполнен. Внутри этого блока приложение принимает имя пользователя и пароль и устанавливает сообщение для message . Поскольку форма оказалась пустой, отобразилось сообщение об ошибке.

    Если заполнить форму с корректными именем пользователям и паролем и нажать Enter, появится приветственное сообщение “Correct username and password” :

    Заполненная форма во Flask

    Таким образом можно работать с формами во Flask. Теперь же стоит обратить внимание на пакет WTForms.

    WTForms

    WTForms – это мощная библиотека, написанная на Python и независимая от фреймворков. Она умеет генерировать формы, проверять их и предварительно заполнять информацией (удобно для редактирования) и многое другое. Также она предлагает защиту от CSRF. Для установки WTForms используется Flask-WTF.

    Flask- WTF – это расширение для Flask, которое интегрирует WTForms во Flask. Оно также предлагает дополнительные функции, такие как загрузка файлов, reCAPTCHA, интернационализация (i18n) и другие. Для установки Flask-WTF нужно ввести следующую команду.

    Создание класса Form

    Начать стоит с определения форм в виде классов Python. Каждая форма должна расширять класс FlaskForm из пакета flask_wtf . FlaskForm — это обертка, содержащая полезные методы для оригинального класса wtform.Form , который является основной для создания форм. Внутри класса формы, поля формы определяются в виде переменных класса. Поля формы определяются путем создания объекта, ассоциируемого с типом поля. Пакет wtform предлагает несколько классов, представляющих собой следующие поля: StringField , PasswordField , SelectField , TextAreaField , SubmitField и другие.

    Для начала нужно создать файл forms.py внутри словаря flask_app и добавить в него следующий код.

    Здесь определен класс формы ContactForm с четырьмя полями: name , email , message и sumbit . Эти переменные будут использоваться, чтобы отрендерить поля формы, а также назначать и получать информацию из них. Эта форма создана с помощью двух StringField , TextAreaField и SumbitField . Каждый раз когда создается объект поля, определенные аргументы передаются его функции-конструктору. Первый аргумент — строка, содержащая метку, которая будет отображаться внутри тега <label> в тот момент, когда поле отрендерится. Второй опциональный аргумент — список валидаторов (элементов системы проверки), которые передаются конструктору в виде аргументов-ключевых слов. Валидаторы — это функции или классы, которые определяют, корректна ли введенная в поле информация. Для каждого поля можно использовать несколько валидаторов, разделив их запятыми ( , ). Модуль wtforms.validators предлагает базовые валидаторы, но их можно создавать самостоятельно. В этой форме используются два встроенных валидатора: DataRequired и Email .

    DataRequired: он проверяет, ввел ли пользователь хоть какую-информацию в поле.

    Email: проверяет, является ли введенный электронный адрес действующим.

    Введенные данные не будут приняты до тех пор, пока валидатор не подтвердит соответствие данных.

    Примечание: это лишь основа полей форм и валидаторов. Полный список доступен по ссылке https://wtforms.readthedocs.io.

    Установка SECRET_KEY

    По умолчанию Flask-WTF предотвращает любые варианты CSFR-атак. Это делается с помощью встраивания специального токена в скрытый элемент <input> внутри формы. Затем этот токен используется для проверки подлинности запроса. До того как Flask-WTF сможет сгенерировать csrf-токен, необходимо добавить секретный ключ. Установить его в файле main2.py необходимо следующим образом:

    Здесь используется атрибут config объекта Flask . Атрибут config работает как словарь и используется для размещения параметров настройки Flask и расширений Flask, но их можно добавлять и самостоятельно.

    Секретный ключ должен быть строкой — такой, которую сложно разгадать и, желательно, длинной. SECRET_KEY используется не только для создания CSFR-токенов. Он применяется и в других расширениях Flask. Секретный ключ должен быть безопасно сохранен. Вместо того чтобы хранить его в приложении, лучше разместить в переменной окружения. О том как это сделать — будет рассказано в следующих разделах.

    Формы в консоли

    Откроем оболочку Python с помощью следующей команды:

    Это запустит оболочку Python внутри контекста приложения.

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

    Стоит обратить внимание, что данные передаются в виде объекта MultiDict , потому что функция-конструктор класса wtforms.Form принимает аргумент типа MutiDict . Если данные формы не определены при создании экземпляра объекта формы, а форма отправлена с помощью запроса POST, wtforms.Form использует данные из атрибута request.form . Стоит вспомнить, что request.form возвращает объект типа ImmutableMultiDict . Это то же самое, что и MultiDict , но он неизменяемый.

    Метод validate() проверяет форму. Если проверка прошла успешно, он возвращает True , если нет — False .

    Форма не прошла проверку, потому что обязательному полю message при создании объекта формы не было передано никаких данных. Получить доступ к ошибкам форм можно с помощью атрибута errors объекта формы:

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

    Отключить CSFR-защиту можно, передав csfr_enabled=False при создании экземпляра класса формы. Пример:

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

    Проверка формы в этот раз прошла успешно.

    Следующий шаг — рендеринг формы.

    Рендеринг формы

    Существует два варианта рендеринга:

    1. Один за одним.
    2. С помощью цикла

    Рендеринг полей один за одним

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

    Стоит протестировать этот способ в консоли:

    Здесь экземпляр объекта формы был создан без данных запроса. Так и происходит, когда форма отображается первый раз с помощью запроса GET.

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

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

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

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

    Стоит обратить внимание, что у атрибута value в полях name и email есть данные. Но элемент <textarea> для поля message пуст, потому что ему данные переданы не были. Получить доступ к ошибке валидации для поля message можно следующим образом:

    Как вариант, form.errors можно использовать, чтобы перебрать все ошибки валидации за раз.

    Стоит обратить внимание, что ошибки csfr-токена нет, потому что запрос был отправлен без токена. Отрендерить поле csfr можно как и любое другое поле:

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

    Рендеринг полей с помощью цикла

    Следующий код демонстрирует, как можно отрендерить поля с помощью цикла for.

    Важно заметить, что вне зависимости от используемого метода нужно вручную добавлять тег <form> , чтобы обернуть поля формы.

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

    Вначале нужно создать шаблон contact.html со следующим кодом:

    Единственный недостающий кусочек пазла — функция представления, которая будет создана далее.

    Работа с подтверждением формы

    Откроем main2.py , чтобы добавить следующий код после функции представления login() .

    В 7 строке создается объект формы. На 8 строке проверяется значение, которое вернул метод validate_on_submit() для исполнения кода внутри инструкции if.

    Почему используется validate_on_sumbit() , а не validate() , как это было в консоли?

    validate() всего лишь проверяет, корректны ли данные формы. Он не проверяет, был ли запрос отправлен с помощью метода POST. Это значит, что если использовать метод validate() , тогда запрос GET к /contact/ запустит форму проверки, а пользователь увидит ошибки валидации. Вообще процедура проверки запускается только в том случае, если данные были отправлены с помощью метода POST. В противном случае вернется False . Метод validate_on_submit() вызывает метод validate() внутри себя. Также нужно обратить внимание, что при создании экземпляра объекта формы данные не передаются, потому что когда форма отправляется с помощью запроса POST, WTForm считывает данные формы из атрибута request.form .

    Поля формы, определенные в классе формы становятся атрибутами объекта формы. Чтобы получить доступ к данным поля используется атрибут data поля формы:

    Чтобы получить доступ ко всем данные формы сразу нужно использовать атрибут data к объекту формы:

    Если использовать запрос GET при посещении /contact/ , метод validate_on_sumbit() вернет False . Код внутри if будет пропущен, а пользователь получит пустую HTML-форму.

    Когда форма отправляется с помощью запроса POST, validate_on_sumbit() возвращает True , предполагая, что данные верны. Вызовы print() внутри блока if выведут данные, введенные пользователем, а функция redirect() перенаправит пользователя на страницу /contact/ . С другой стороны, если validate_on_sumbit() вернет False , исполнение инструкций внутри тела if будет пропущено, и появится сообщение об ошибке валидации.

    Если сервер не запущен, его нужно запустить и открыть https://localhost:5000/contact/ . Появится следующая контактная форма:

    контактная форма во Flask

    Если попробовать нажать Submit, не вводя данных, появятся следующие сообщения об ошибках валидации:

    ошибка валидации формы во Flask

    ошибка валидации email во Flask

    Теперь можно ввести определенные данные в поля Name и Message и некорректные данные в поле Email, и попробовать отправить форму снова.

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

    Теперь можно ввести корректный email в поле Email и нажать Submit. Теперь проверка пройдет успешно, а в оболочке появится следующий вывод:

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

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

    Всплывающие сообщения

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

    Для отображения сообщения используется функция flash() из пакета flask . Функция flash() принимает два аргумента: сообщение и категория (опционально). Категория указывает на тип сообщения: _success_ , _error_ , _warning_ и так далее. Категория может быть использована в шаблоне, чтобы определить тип сообщения.

    Снова откроем main2.py , чтобы добавить flash(“Message Received”, “success”) прямо перед вызовом redirect() в функции представления contact() :

    Сообщение, заданное с помощью функции flash() , будет доступно только последующему запросу, а потом удалится.

    Это только настройка сообщения. Для его отображения нужно поменять также шаблон.

    Для этого нужно открыть файл contact.html и изменить его следующим образом:

    Jinja предлагает функцию get_flashed_messages() , которая возвращает список активных сообщений без категории. Чтобы получить их вместе с категорией нужно передать with_category=True при вызове get_flashed_messages() . Когда значение with_categories – True , get_flashed_messages() вернет список кортежей формы (category, message) .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *