Создание веб-приложения с использованием Python Flask
В предыдущей статье в блоге Timeweb Cloud мы познакомились с веб-разработкой на python с использованием Flask и рассмотрели способы работы с входящими данными. В этом туториале мы шагнем чуть дальше и напишем простое веб-приложение на python с базой данных для авторизации пользователей.
Мы будем работать в PyCharm+pipenv, а сайт сделаем на HTML+CSS. Операционная система — Win 10. Установка Flask, PyCharm и pipenv подробно описана в этой статье.
В рамках статьи мы будем использовать тестовые, учебные примеры, которые не подойдут для реализации на продакшене. Например, для проверки пароля в БД необходимо хранить хэш пароля и сравнивать хэши, а не пароли. Также для работы с СУБД нужно использовать ORM, а не писать «сырой» SQL (подробнее про ORM см. здесь).
Кстати, в официальном канале Timeweb Cloud собрали комьюнити из специалистов, которые говорят про IT-тренды, делятся полезными инструкциями и даже приглашают к себе работать.
БД для логинов и паролей
После того, как вы установите Flask и остальные инструменты, можно перейти к работе. Для хранения данных пользователей будем использовать СУБД SQlite. Она отлично подходит для небольших проектов. Её основное преимущество заключается в автономности: для работы с ней не потребуется сервер. К тому же в python встроен модуль sqlite3 для работы с ней. Но если вы решите работать с СУБД на сервере, то обратите внимание на облачные серверы Timeweb Cloud.
Итак, работа с модулем начинается с импорта:
Теперь мы можем создать БД и таблицу с логинами и паролями:
db_lp = sqlite3.connect('login_password.db')
cursor_db = db_lp.cursor()
sql_create = '''CREATE TABLE passwords(
login TEXT PRIMARY KEY,
password TEXT NOT NULL);'''
cursor_db.execute(sql_create)
db_lp.commit()
cursor_db.close()
db_lp.close()
Распишем подробно, что этот код делает:
- Подключаемся к БД с помощью метода connect(). Метод будет искать файл login_password.db в каталоге проекта. Если не найдет, то создаст самостоятельно.
- Создаем объект cursor_db для взаимодействия с БД;
- sql_create — это SQL-запрос для создания таблицы с логинами и паролями;
- С помощью метода execute() выполняем sql_create;
- Сохраняем изменения в БД методом commit();
- Закрываем объекты cursor_db и db_lp во избежание проблем с БД;
Авторизация
Основная форма
Сначала создадим форму, с помощью которой будет осуществляться авторизация в абстрактный сервис.
- — указываем, что будем использовать POST-запросы;
- label for=»Login» и label for=»Password» — отмечаем Login, который в дальнейшем будем обрабатывать с помощью метода get() из модуля request;
- >»> — сообщаем HTML, где хранятся css файл. Чуть дальше подробнее разберем, где нужно его размещать.
form font: 14px Stem-Regular,arial, sans-serif;/*Выбираем шрифт */
border: 1px solid black;/*Цвет, размер и тип границы */
-webkit-border-radius: 20px;/*Скругляем углы */
color: #777991;/*Цвет label */
width: 25%; /*Ширина формы */
margin-right: auto; /*Положение формы */
margin-left: auto; /*Положение формы */
text-align: center; /*Центровка текста*/
>
input[type=text], input[type=password] text-align: center; /*Центровка текста*/
-webkit-border-radius: 4px;/*Скругляем углы */
width: auto; /*Ширина */
padding: 15px 20px; /*Размер внутренних отступов*/
margin: 10px 0; /*Размер внешних отступов*/
margin-right: auto; /*Размер внешних отступов справа*/
margin-left: auto; /*Размер внешних отступов слева*/
display: block; /*тип отображения*/
border: 1px solid #050c26;/*Цвет, размер и тип границы */
box-sizing: border-box; /*Размер объекта по отношению к родительскому */
font: 14px Stem-Regular,arial, sans-serif;/*Выбираем шрифт */
color: #777991;/*Цвет текста в input */
>
button font: 16px Stem-medium, arial, sans-serif; /*Выбираем шрифт кнопки */
background-color: #454cee; /*Выбираем цвет фона */
-webkit-border-radius: 8px; /*Закругление */
color: white; /*Выбираем цвет текста*/
padding: 16px 20px; /*Размер внутренних отступов*/
margin: 8px 0;/*Размер внешних отступов*/
border: none; /*Без границы*/
cursor: pointer; /*Изменение курсора при наведении на кнопку*/
width: auto; /*Ширина*/
>
button:hover <
opacity: 0.9; /*Изменение яркости кнопки при наведении*/
>
.container padding: 20px; /*Размер внутренних отступов контейнера*/
>
Вот как выглядит эта форма:
Сообщение для успешной авторизации
Если пользователь ввёл верную пару логин-пароль, то выведем соответствующее сообщение.
>">
form font: 14px Stem-Regular,arial, sans-serif;/*Выбираем шрифт */
border: 1px solid black;/*Цвет, размер и тип границы */
-webkit-border-radius: 20px;/*Скругляем углы */
color: #777991;/*Цвет label */
width: 25%; /*Ширина формы */
margin-right: auto; /*Положение формы */
margin-left: auto; /*Положение формы */
text-align: center; /*Центровка текста*/
>
.container padding: 30px; /*Размер внутренних отступов контейнера*/
>
Сообщение при неудачной авторизации
>">
form font: 14px Stem-Regular,arial, sans-serif;/*Выбираем шрифт */
border: 1px solid black;/*Цвет, размер и тип границы */
-webkit-border-radius: 20px;/*Скругляем углы */
color: #777991;/*Цвет label */
width: 25%; /*Ширина формы */
margin-right: auto; /*Положение формы */
margin-left: auto; /*Положение формы */
text-align: center; /*Центровка текста*/
>
.container padding: 30px;/*Размер внутренних отступов контейнера*/
>
Теперь создадим форму для регистрации.
Регистрация
С помощью формы регистрации пользователь сможет создать свой аккаунт. Вот HTML и CSS основной формы:
>">
form font: 14px Stem-Regular,arial, sans-serif;/*Выбираем шрифт */
border: 1px solid black;/*Цвет, размер и тип границы */
-webkit-border-radius: 20px;/*Скругляем углы */
color: #777991;/*Цвет label */
width: 25%; /*Ширина формы */
margin-right: auto; /*Положение формы */
margin-left: auto; /*Положение формы */
text-align: center; /*Центровка текста*/
>
input[type=text], input[type=password] text-align: center; /*Центровка текста*/
-webkit-border-radius: 4px;/*Скругляем углы */
width: auto; /*Ширина */
padding: 15px 20px; /*Размер внутренних отступов*/
margin: 10px 0; /*Размер внешних отступов*/
margin-right: auto; /*Размер внешних отступов справа*/
margin-left: auto; /*Размер внешних отступов слева*/
display: block; /*тип отображения*/
border: 1px solid #050c26;/*Цвет, размер и тип границы */
box-sizing: border-box; /*Размер объекта по отношению к родительскому */
font: 14px Stem-Regular,arial, sans-serif;/*Выбираем шрифт */
color: #777991;/*Цвет текста в input */
>
button font: 16px Stem-medium, arial, sans-serif; /*Выбираем шрифт кнопки */
background-color: #454cee; /*Выбираем цвет фона */
-webkit-border-radius: 8px; /*Закругление */
color: white; /*Выбираем цвет текста*/
padding: 16px 20px; /*Размер внутренних отступов*/
margin: 8px 0;/*Размер внешних отступов*/
border: none; /*Без границы*/
cursor: pointer; /*Изменение курсора при наведении на кнопку*/
width: auto; /*Ширина*/
>
button:hover <
opacity: 0.9; /*Изменение яркости кнопки при наведении*/
>
.container padding: 20px; /*Размер внутренних отступов контейнера*/
>
Форма регистрации выглядит вот так:
При завершении регистрации пользователь увидит такое сообщение:
form font: 14px Stem-Regular,arial, sans-serif;/*Выбираем шрифт */
border: 1px solid black;/*Цвет, размер и тип границы */
-webkit-border-radius: 20px;/*Скругляем углы */
color: #777991;/*Цвет label */
width: 25%; /*Ширина формы */
margin-right: auto; /*Положение формы */
margin-left: auto; /*Положение формы */
text-align: center; /*Центровка текста*/
>
.container padding: 20px;/*Размер внутренних отступов контейнера*/
>
Декоратор авторизации
После создания всех форм и БД мы можем начать разработку веб-приложения Flask. Для отправления html-документа в ответ на запрос клиента в Flask нужно использовать метод render_template()
Этот метод Flask использует папку templates для хранения html-файлов в каталоге проекта. Нам необходимо создать её. Также в html-документах нужно указать относительную ссылку на css. В нашем случае мы размещаем их в соседней папке static/css в каталоге проекта, чтобы не допускать беспорядка в файлах. Сейчас каталог проекта с html и css имеет такую структуру:
Timeweb
|— template
| `— authorization.html
| `— auth_bad.html
| `— successauth.html
| `— successregis.html
| `— registration.html
|— static
| — css
| `— auth_bad.css
| `— auth.css
| `— successauth.css
| `— regis.css
| `— successfulregis.css
Импортируйте пакет Flask и другие модули:
from flask import Flask, request, render_template
Код для авторизации пользователей представлен ниже. Подробнее об аутентификации рекомендуем почитать здесь.
@app.route('/authorization', methods=['GET', 'POST'])
def form_authorization():
if request.method == 'POST':
Login = request.form.get('Login')
Password = request.form.get('Password')
db_lp = sqlite3.connect('login_password.db')
cursor_db = db_lp.cursor()
cursor_db.execute(('''SELECT password FROM passwords
WHERE login = '<>';
''').format(Login))
pas = cursor_db.fetchall()
cursor_db.close()
try:
if pas[0][0] != Password:
return render_template('auth_bad.html')
except:
return render_template('auth_bad.html')
db_lp.close()
return render_template('successfulauth.html')
return render_template('authorization.html')
- Пользователь переходит на /authorization — это GET-запрос и декоратор возвращает authorization.html;
- Когда пользователь введет логин, пароль и нажмет кнопку “Войти”, сервер получит POST-запрос, который декоратор будет обрабатывать. Декоратор получит логин и пароль, которые ввел пользователь;
- Затем подключаемся к БД и выполняем SQL-запрос к ней. С помощью cursor_db.execute() и cursor_db.fetchall() получаем строку password(возможно, пустую), соответствующую введенному логину;
- Из строки “вытаскиваем” пароль и:
- Если строка пустая, то это вызовет ошибку (выход за пределы массива), которую мы обрабатываем конструкцией try-except и сообщаем пользователю о неверных введенных данных. Декоратор завершает работу;
- Если пароль в БД не совпадает с полученным паролем, то просто возвращает сообщение о некорректности данных и завершаем работу;
- Если пароль верный, то выдаем сообщение о успешной авторизации и завершаем работу Flask- декоратора.
Декоратор регистрации
На страницу /registration пользователь попадает из формы авторизации. Вот код декоратора:
@app.route('/registration', methods=['GET', 'POST'])
def form_registration():
if request.method == 'POST':
Login = request.form.get('Login')
Password = request.form.get('Password')
db_lp = sqlite3.connect('login_password.db')
cursor_db = db_lp.cursor()
sql_insert = '''INSERT INTO passwords VALUES('<>','<>');'''.format(Login, Password)
cursor_db.execute(sql_insert)
db_lp.commit()
cursor_db.close()
db_lp.close()
return render_template('successfulregis.html')
return render_template('registration.html')- Сначала обрабатывается GET-запрос /registration. Возвращаем registration.html;
- Когда пользователь введет данные и нажмет кнопку “Зарегистрироваться”, сервер получит POST-запрос. Из него получаем Login и Password;
- Подключаемся к БД;
- sql_insert — запрос на добавление новой строки с данными пользователя;
- Выполняем sql_insert и сохраняем изменения;
- Закрываем cursor_db, db_lp и возвращаем сообщение об успешной регистрации.
Полный код программы
from flask import Flask, request, render_template
import sqlite3
app = Flask(__name__)
@app.route('/authorization', methods=['GET', 'POST'])
def form_authorization():
if request.method == 'POST':
Login = request.form.get('Login')
Password = request.form.get('Password')
db_lp = sqlite3.connect(login_password.db')
cursor_db = db_lp.cursor()
cursor_db.execute(('''SELECT password FROM passwords
WHERE login = '<>';
''').format(Login))
pas = cursor_db.fetchall()
cursor_db.close()
try:
if pas[0][0] != Password:
return render_template('auth_bad.html')
except:
return render_template('auth_bad.html')
db_lp.close()
return render_template('successfulauth.html')
return render_template('authorization.html')
@app.route('/registration', methods=['GET', 'POST'])
def form_registration():
if request.method == 'POST':
Login = request.form.get('Login')
Password = request.form.get('Password')
db_lp = sqlite3.connect('login_password.db')
cursor_db = db_lp.cursor()
sql_insert = '''INSERT INTO passwords VALUES('<>','<>');'''.format(Login, Password)
cursor_db.execute(sql_insert)
cursor_db.close()
db_lp.commit()
db_lp.close()
return render_template('successfulregis.html')
return render_template('registration.html')
if __name__ == "__main__":
app.run()