Бесплатная миграция IT-инфраструктуры в облако

Создание веб-приложения с использованием Python Flask

Команда Timeweb Cloud
Команда Timeweb Cloud
Наши инженеры, технические писатели, редакторы и маркетологи
24 мая 2022 г.
6151
15 минут чтения
Средний рейтинг статьи: 2.4

В предыдущей статье в блоге Timeweb Cloud мы познакомились с веб-разработкой на python с использованием Flask и рассмотрели способы работы с входящими данными. В этом туториале мы шагнем чуть дальше и напишем простое веб-приложение на python с базой данных для авторизации пользователей.

Мы будем работать в PyCharm+pipenv, а сайт сделаем на HTML+CSS. Операционная система — Win 10. Установка Flask, PyCharm и pipenv подробно описана в этой статье.

Создание Веб Приложения С Использованием Python Flask (2)

В рамках статьи мы будем использовать тестовые, учебные примеры, которые не подойдут для реализации на продакшене. Например,  для проверки пароля в БД необходимо хранить хэш пароля и сравнивать хэши, а не пароли. Также для работы с СУБД нужно использовать 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; /*Размер внутренних отступов контейнера*/
}

В итоге наша форма будет выглядеть так:

Image5

Сообщение для успешной авторизации

Если пользователь ввёл верную пару логин-пароль, то выведем соответствующее сообщение.

В директории 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; /*Размер внутренних отступов контейнера*/
}

Image2

Сообщение при неудачной авторизации

В директории 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;/*Размер внутренних отступов контейнера*/
}

Image3

Теперь создадим форму для регистрации.

Регистрация

С помощью формы регистрации пользователь сможет создать свой аккаунт. 

В 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; /*Размер внутренних отступов контейнера*/
}

Форма регистрации будет выглядть так:

Image1

При завершении регистрации пользователь увидит такое сообщение:

Image4

Для этого в 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;
  • Когда пользователь введет логин, пароль и нажмет кнопку «Войти», сервер получит POST-запрос, который декоратор будет обрабатывать. Декоратор получит логин и пароль, которые ввел пользователь;
  • Затем подключаемся к БД и выполняем SQL-запрос к ней. С помощью cursor_db.execute() и cursor_db.fetchall() получаем строку password (возможно, пустую), соответствующую введенному логину;
  • Из строки «вытаскиваем» пароль, и:
    • Если строка пустая, то это вызовет ошибку (выход за пределы массива), которую мы обрабатываем конструкцией try-except и сообщаем пользователю о неверных введенных данных. Декоратор завершает работу;
    • Если пароль в БД не совпадает с полученным паролем, то просто возвращает сообщение о некорректности данных и завершаем работу;
    • Если пароль верный, то выдаем сообщение о успешной авторизации и завершаем работу Flask-декоратора.

Декоратор регистрации

На страницу /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')
  • Сначала обрабатывается 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()
Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
24 мая 2022 г.
6151
15 минут чтения
Средний рейтинг статьи: 2.4
Комментарии 4
Dmitry Bryantsev
Dmitry Bryantsev
10.06.2023, 15:12

В коде указано:

return render_template('successfulregis.html')

А сам шаблон имеет имя successregis.html

аналогично с авторизацией.

И все равно, спасибо за хорошую статью! )

Команда Timeweb Cloud
Команда Timeweb Cloud
14.08.2023, 11:33

И вам спасибо за фидбэк и замечания к статье!
Доработали некоторые пункты и постарались везде указать, что и где размещаем 😉

Dmitry Bryantsev
Dmitry Bryantsev
10.06.2023, 15:01

Еще у вас ошибка, которая заставила меня искать решения на других сайтах - папка каталога с шаблонами должна называться не template, а templates (на конце буква s)

Dmitry Bryantsev
Dmitry Bryantsev
10.06.2023, 12:04

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