В предыдущей статье в блоге Timeweb Cloud мы познакомились с веб-разработкой на python с использованием Flask и рассмотрели способы работы с входящими данными. В этом туториале мы шагнем чуть дальше и напишем простое веб-приложение на python с базой данных для авторизации пользователей.
Мы будем работать в PyCharm+pipenv, а сайт сделаем на HTML+CSS. Операционная система — Win 10. Установка Flask, PyCharm и pipenv подробно описана в этой статье.
В рамках статьи мы будем использовать тестовые, учебные примеры, которые не подойдут для реализации на продакшене. Например, для проверки пароля в БД необходимо хранить хэш пароля и сравнивать хэши, а не пароли. Также для работы с СУБД нужно использовать ORM, а не писать «сырой» SQL (подробнее про ORM см. здесь).
Кстати, в официальном канале Timeweb Cloud собрали комьюнити из специалистов, которые говорят про IT-тренды, делятся полезными инструкциями и даже приглашают к себе работать.
Для начала работы создадим директорию проекта — Timeweb
. В ней разместим директории templates
и static
, а в директории static
создадим директорию css
. Теперь структура проекта выглядит так:
Timeweb
|— templates
|— static
| — css
После того, как вы установите Flask и остальные инструменты, можно перейти к работе. Для хранения данных пользователей будем использовать СУБД SQlite. Она отлично подходит для небольших проектов. Её основное преимущество заключается в автономности: для работы с ней не потребуется сервер. К тому же в python встроен модуль sqlite3 для работы с ней. Но если вы решите работать с СУБД на сервере, то обратите внимание на облачные серверы Timeweb Cloud.
В директории проекта создаем файл db.py
. В файле импортируем модуль sqlite3
и создаем базу данных и таблицу с логинами и паролями:
import sqlite3
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
во избежание проблем с БД.Для создания базы данных выполняем команду:
python db.py
Сначала создадим форму, с помощью которой будет осуществляться авторизация в абстрактный сервис.
В директории templates
создаем файл authorization.html
с содержанием:
<form method="POST">
<div class="container">
<label for="Login"><b>Логин</b></label>
<input type="text" placeholder="" name="Login" required>
<label for="Password"><b>Пароль</b></label>
<input type="password" placeholder="" name="Password" required>
<button type="submit">Войти</button>
<div class="container">
<span class="reg"> <a href="/registration">Регистрация</a></span>
</div>
</form>
<link rel="stylesheet" href="{{ url_for('static', filename= 'css/auth.css') }}">
Здесь важно 3 момента:
<form method="POST">
— указываем, что будем использовать POST-запросы;label for="Login"
и label for="Password"
— отмечаем Login, который в дальнейшем будем обрабатывать с помощью метода get()
из модуля request
;<link rel="stylesheet" href="{{ url_for('static', filename= 'css/auth.css') }}">
— сообщаем HTML, где хранится css-файл.В директории /static/css
создаем файл auth.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; /*Размер внутренних отступов контейнера*/
}
В итоге наша форма будет выглядеть так:
Если пользователь ввёл верную пару логин-пароль, то выведем соответствующее сообщение.
В директории templates
создаем файл successfulauth.html
с содержанием:
<form method="POST">
<div class="container">
<label><b>Вы успешно авторизовались</b></label>
</form>
<link rel="stylesheet" href="{{ url_for('static', filename= 'css/successfulauth.css') }}">
В директории /static/css
создаем файл successfulauth.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; /*Центровка текста*/
}
.container {
padding: 30px; /*Размер внутренних отступов контейнера*/
}
В директории templates
создаем файл auth_bad.html
с содержанием:
<form method="POST">
<form method="POST">
<div class="container">
<label><b>Введен неправильный логин или пароль</b></label>
</form>
<link rel="stylesheet" href="{{ url_for('static', filename= 'css/auth_bad.css') }}">
В директории /static/css
создаем файл auth_bad.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; /*Центровка текста*/
}
.container {
padding: 30px;/*Размер внутренних отступов контейнера*/
}
Теперь создадим форму для регистрации.
С помощью формы регистрации пользователь сможет создать свой аккаунт.
В templates
создаем файл registration.html
:
<form method="POST">
<div class="container">
<label for="Login"><b>Логин</b></label>
<input type="text" placeholder="" name="Login" required>
<label for="Password"><b>Пароль</b></label>
<input type="password" placeholder="" name="Password" required>
<button type="submit">Зарегистрироваться</button>
</div>
</form>
<link rel="stylesheet" href="{{ url_for('static', filename= 'css/regis.css') }}">
В /static/css
создаем regis.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; /*Размер внутренних отступов контейнера*/
}
Форма регистрации будет выглядть так:
При завершении регистрации пользователь увидит такое сообщение:
Для этого в templates
создаем файл successfulregis.html
:
<form method="POST">
<div class="container">
<label><b>Вы успешно зарегистрировались</b></label>
<div class="container">
<span class="reg"> <a href="/authorization">Вернуться к авторизации</a></span>
</div>
</form>
<link rel="stylesheet" href="{{ url_for('static', filename= 'css/successfulregis.css') }}">
А в /static/css
создаем successfulregis.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; /*Центровка текста*/
}
.container {
padding: 20px;/*Размер внутренних отступов контейнера*/
}
После создания всех форм и БД мы можем начать разработку веб-приложения Flask. Для отправления html-документа в ответ на запрос клиента в Flask нужно использовать метод render_template()
. Эта функция модуля Flask используется для отображения шаблонов из папки templates
.
Сейчас каталог проекта имеет такую структуру:
Timeweb
|— db.py
|— login_password.db
|— templates
| `— authorization.html
| `— auth_bad.html
| `— successfulauth.html
| `— successfulregis.html
| `— registration.html
|— static
| — css
| `— auth_bad.css
| `— auth.css
| `— successfulauth.css
| `— regis.css
| `— successfulregis.css
В директории проекта /Timeweb
создаем файл main.py
, в котором импортируем необходимые модули из Flask и добавляем код для авторизации пользователей. Подробнее об аутентификации рекомендуем почитать здесь.
from flask import Flask, request, render_template
import sqlite3
@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
;cursor_db.execute()
и cursor_db.fetchall()
получаем строку password
(возможно, пустую), соответствующую введенному логину;try-except
и сообщаем пользователю о неверных введенных данных. Декоратор завершает работу;На страницу /registration
пользователь попадает из формы авторизации. Вот код декоратора, добавляем его в конец main.py
:
@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')
/registration
. Возвращаем registration.html
;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()
В коде указано:
А сам шаблон имеет имя
successregis.html
аналогично с авторизацией.
И все равно, спасибо за хорошую статью! )
И вам спасибо за фидбэк и замечания к статье!
Доработали некоторые пункты и постарались везде указать, что и где размещаем 😉
Еще у вас ошибка, которая заставила меня искать решения на других сайтах - папка каталога с шаблонами должна называться не template, а templates (на конце буква s)
Здравствуйте. Спасибо за хорошую и понятную статью. Пожелания от новичка - в описании нигде не сказано, что для css надо создать отдельный файл и положить его "по такому-то адресу". Ваш код ведь рассчитан на новичков, а для них понимание этого не приходит само.