Время прочтения: 11 мин.
Привет! В третьей части поста я научу создавать микроweb-сервер с помощью Python, покажу на примерах, как можно передавать и получать данные от клиента к серверу, не перегружая страницы, используя JS и jQuery.
В предыдущих публикациях я показывала на примерах: как использовать наследование, инкапсуляцию и полиморфизм в JS, и как применять библиотеку jQuery. Если вы ещё не знакомы с первой и второй частями поста, то предлагаю сначала прочитать их.
В материале использовался jQuery 3.4.1 и браузеры ЯндексБраузер 22.7.5.940 (64-bit), Microsoft Edge 105.0.1343.27 (Официальная сборка) (64-разрядная версия), Anaconda 4.4.0 (64 bit), Python 3.6.1. Также необходимо иметь доступ к БД PostgreSQL и установленную программу SQL редактора: pgAdmin или DBeaver.
Подготовка к работе
Перед демонстрацией нужно подготовиться:
- скачать и установить Anaconda с официального сайта;
- проверить установлены ли у вас Python библиотеки (после установки Anaconda):
- Flask – библиотека для реализации web-приложений, микрофреймворк, построенный для работы с WSGI;
- requests – библиотека для передачи данных HTTP методами GET, POST.
Для этого, после установки Anaconda, в строке поиска найти и запустить Anaconda Promt и ввести команду:
pip list
Результат:
alabaster (0.7.10)
anaconda-client (1.6.3)
anaconda-navigator (1.6.2)
anaconda-project (0.6.0)
asn1crypto (0.22.0)
astroid (1.4.9)
astropy (1.3.2)
Babel (2.4.0)
…
В итоге вы увидите список всех установленных библиотек Python. Если в списке не обнаружатся необходимые библиотеки, тогда нужно их установить, иначе при запуске скрипта будет возникать ошибка типа:
Traceback (most recent call last):
File "C:\workspace\form_es6\registry.py", line 2, in <module>
from flask import Flask, request, jsonify, make_response, Response
ModuleNotFoundError: No module named 'flask'
Наберите поочередно следующие команды в Anaconda Promt, чтобы установить необходимые библиотеки для используемого примера:
pip install flask
pip install requests
Версии библиотек могут не совпадать, т.к. они должны быть совместимы с версией Python, используемой в дистрибутиве Anaconda.
Версию Python можно посмотреть командой в Anaconda Promt:
python -V
Результат:
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
3. добавьте в корень проекта файловой структуры, созданной во второй части поста, ещё одну директорию — «upload». Пока эта директория будет пустой, но она понадобится позднее для сохранения данных.

Реализация веб-приложения
— Сохранение данных, введённых в html-форму, в файл на сервере
— Сохранение данных, введённых в html-форму, в БД
Для того чтобы наладить коммуникацию клиент-сервера, необходимо поднять веб-сервер, который умеет по http протоколу принимать и отправлять данные. Можно серьёзно подойти к вопросу: установить и настроить http apache, IIS и другие веб-серверы. Но для тестирования может быть вполне достаточно микро веб-фреймворка, который будет обрабатывать запросы не хуже, чем настоящие веб-серверы. Поэтому сымитирую с помощью скрипта Python маленький веб-сервер.
Создайте файл registry.py в корне проекта и вставьте в него скрипт:
C:/workspace/registry.py
# импортируем библиотеки (или пакеты) codecs и flask. Из библиотеки flask в скрипт
# импортируются классы: Flask, request, jsonify, make_response, Response.
import codecs
from flask import Flask, request, jsonify, make_response, Response
# в переменную app создается экземпляр класса Flask с параметрами (
# 1. __name__ {- имя пакета приложения, в данном случае используется по default},
# 2. static_url_path="/static" {- наименование пути в URL, которое из запроса будет
# обращаться к физической папке что указана в параметре «static_folder»},
# 3. static_folder="static" {- относительный путь до папки со статическими файлами,
# в нашем примере - это файлы, которые лежат в C:\workspace\form_es6\static},
# 4. template_folder="template" {- относительный путь до папки, содержащей шаблоны}
# )
app = Flask(__name__,static_url_path="/static",static_folder="static",template_folder="template")
# @app.route('/') и @app.route('/index') – метод route объекта app создаст правило:
# что при обращении по URL в браузере http://127.0.0.1:8080/ и
# http://127.0.0.1:8080/index будет запускаться функция def index().
# В функции index() переменная path_file передает путь до шаблона index.html и
# читает его с помощью модуля codecs и метода open в правильной кодировке
# return Response(file.read(), mimetype = 'text/html') – возвращает содержание
# ответа сервера, представленное в виде «text/html».
@app.route('/')
@app.route('/index')
def index():
path_file="C:\\workspace\\form_es6\\template\\index.html"
with codecs.open (path_file, "r", "utf-8") as file:
return Response(file.read(), mimetype = 'text/html')
# проверяем был ли запущен файл
# и запускаем приложение
if __name__== "__main__":
app.run(host='127.0.0.1', port=8080, debug=True)
Теперь необходимо запустить веб-приложение во фреймворке.
Для этого нужно запустить созданный скрипт в консоли Anaconda Promt с помощью интерпретатора python:
$ python C:/workspace/form_es6/registry.py
Чтобы проверить работу созданного веб-приложения, необходимо в браузере ввести http://127.0.0.1:8080/ или http://127.0.0.1:8080/index. Если приложение запустилось без ошибок, то браузер отобразит html форму, которая была создана в 1 и 2 части:

Теперь возникает вопрос: а для чего создавать веб-сервер, ведь можно просто открыть файл с html формой, и браузер также её отобразит? Да, можно до тех пор, пока не понадобится передавать данные от клиента к серверу и наоборот. Например, есть задача: зарегистрировать пользователей и собрать данные о них в файле или БД. Т.е. html форма регистрации, что отобразит браузер – это клиентская часть, а файл или БД – серверная часть. Теперь попробую введённые в html форму данные сохранить в файл. Нужно остановить веб-приложение, нажав в консоли Anaconda Promt Ctrl+C.
Добавлю строки в файле C:/workspace/form_es6/registry.py:
# импортируем библиотеки (или пакеты) codecs и flask. Из библиотеки flask в скрипт
# импортируются классы: Flask, request, jsonify, make_response, Response.
import codecs
from flask import Flask, request, jsonify, make_response, Response
# в переменную app создается экземпляр класса Flask с параметрами (
# 1. __name__ {- имя пакета приложения, в данном случае используется по default},
# 2. static_url_path="/static" {- наименование пути в URL, которое из запроса будет
# обращаться к физической папке что указана в параметре «static_folder»},
# 3. static_folder="static" {- относительный путь до папки со статическими файлами,
# в нашем примере - это файлы, которые лежат в C:\workspace\form_es6\static},
# 4. template_folder="template" {- относительный путь до папки, содержащей шаблоны}
# )
app = Flask(__name__,static_url_path="/static",static_folder="static",template_folder="template")
# @app.route('/') и @app.route('/index') – метод route объекта app создаст правило:
# что при обращении по URL в браузере http://127.0.0.1:8080/ и
# http://127.0.0.1:8080/index будет запускаться функция def index().
# В функции index() переменная path_file передает путь до шаблона index.html и
# читает его с помощью модуля codecs и метода open в правильной кодировке
# return Response(file.read(), mimetype = 'text/html') – возвращает содержание
# ответа сервера, представленное в виде «text/html».
@app.route('/')
@app.route('/index')
def index():
path_file="C:\\workspace\\form_es6\\template\\index.html"
with codecs.open (path_file, "r", "utf-8") as file:
return Response(file.read(), mimetype = 'text/html')
# создаем новое правило, которое при обращении по URL http://127.0.0.1:8080/login
# будет срабатывать функция def login()
# а также укажем метод обмена данными POST
@app.route('/login', methods=["POST"])
def login():
if request.method == 'POST':
fio = request.form.get('fio')
region = request.form.get('region')
np = request.form.get('np')
email = request.form.get('email')
birthdate = request.form.get('birthdate')
sex = request.form.get('sex[]')
about = request.form.get('about')
# объединяем значения полей формы в одну строку
str = fio+" "+region+" "+np+" "+email+" "+birthdate+" "+sex+" "+about+"\n"
# Сохраняем строку в файл logins2.txt
# Можно заменить сохранение данных в файл на сохранение данных
# в базу данных
save_data(str)
# возвращаем ответ в виде json для отображения ответа
answer_json = '{"fio":"'+fio+'", "status":"OK"}'
return answer_json
# сохраняет данные в файл logins2.txt
def save_data(content):
with open("C:\\workspace\\form_es6\\upload\\logins2.txt", "a") as file:
file.write(content)
return
# проверяем был ли запущен файл
# и запускаем приложение
if __name__== "__main__":
app.run(host='127.0.0.1', port=8080, debug=True)
Чтобы показать пользователю, что данные были сохранены, можно на стороне серверной части вернуть данные, которые потом передать клиентской части и, в свою очередь, использовать этот ответ в удобном отображении. Для этого в функции def login() возвращу строку в формате JSON:
# возвращаем ответ в виде json для отображения ответа
answer_json = '{"fio":"'+fio+'", "status":"OK"}'
return answer_json
А на стороне клиентской части обработаю серверный ответ и покажу пользователю в читабельном виде. В файле C:\workspace\form_es6\static\js\form1ES6.js в методе form1.on(‘submit’,(e)) добавлю строки в части ответа от сервера:
this.form1.on('submit',(e)=>{
var self = this;
$.ajax({
type: "POST",
dataType: 'html',
url: "/login",
data: $("#registrity").serialize(),
success: function(data) { //ответ от сервера
//console.log(data);
//ответ от серверной части в виде строки JSON
//превращаем в JSON объект
var data1 = JSON.parse(data);
//если у объекта JSON элемент status равен «OK»,
//тогда выводим значение элемента fio в отдельном теге.
if(data1.status=='OK'){
$("#p_answer_hello").html("Уважаемый "+data1.fio+"! Ваши данные записаны.");
}
},
error: function() {
// ignored
}
});
return false;
});
Если вывести ответ от сервера в лог консоли: в console.log(data), то можно увидеть строку:

Это та самая строка, которую возвращает сервер в функции def login():
…
# возвращаем ответ в виде json для отображения ответа
answer_json = '{"fio":"'+fio+'", "status":"OK"}'
return answer_json
…
Все готово. В консоли Anaconda Promt нужно перезапустить веб-приложение с добавленными строками:
$ python C:/workspace/form_es6/registry.py
Далее необходимо обновить страницу в браузере и заполнить html форму данными и нажать кнопку «Отправить»:

И теперь, если посмотреть в файл C:\workspace\form_es6\upload\logins2.txt, то можно увидеть сохраненные данные, которые были введены в html форму:

Аналогичным способом можно сохранить данные в БД. В качестве примера создам скрипт, в котором данные будут сохраняться в БД PostgreSQL.
Для этого нужно установить библиотеку psycopg2, которая позволит общаться python с PostgreSQL:
pip install psycopg2
Далее импортирую библиотеку в код C:/workspace/form_es6/registry.py:
# импортируем библиотеки (или пакеты) codecs и flask. Из библиотеки flask в скрипт
# импортируются классы: Flask, request, jsonify, make_response, Response.
import codecs
from flask import Flask, request, jsonify, make_response, Response
# импортируем библиотеку – адаптер python и postgreSQL
import psycopg2
…
Теперь нужна таблица, куда будут вставляться данные из html формы. Поэтому создам её в БД PostgreSQL. Необходимо подключиться к БД PostgreSQL в pgAdmin или DBeaver (свой SQL редактор) и выполнить скрипт:
CREATE TABLE sch.users (
id serial NOT NULL,
fio varchar(150) NOT NULL,
region varchar(100) NOT NULL,
np varchar(100) NULL,
email varchar(50) NOT NULL,
birthdate date NULL,
sex varchar(2) NULL,
about varchar(1000) null,
constraint users_pk primary key (id)
);
Если ошибок при выполнении не возникло, то при обновлении дерева таблиц в DBeaver должна появится таблица users в схеме sch.
Теперь необходимо создать соединение к БД PostgreSQL, вставить данные в таблицу, выполнив SQL запрос в скрипте python. Для этого создам функцию save_data_in_db в файле C:/workspace/form_es6/registry.py:
# сохраняет данные в БД
def save_data_in_db(fio, region, np, email, birthdate, sex, about):
try:
# создадим соединение к БД
# Буква r перед строкой означает, что строка будет передается в байтах,
# что позволит избежать передачи неэкранированных спецсимволов
p_user_name = r"Login0005"
p_user_password = "Pass0005"
p_database_name = "pgdb"
p_host = "host-db-pg0005.ru"
conn = psycopg2.connect(host=p_host, dbname=p_database_name, user=p_user_name, password=p_user_password, port=5433)
# сохраняем данные о пользователе в таблицу users
row_data = conn.cursor()
row_data.execute("INSERT INTO sch.users (fio, region, np, email, birthdate, sex, about) VALUES (%s,%s,%s,%s,%s,%s,%s)", (fio, region, np, email, birthdate, sex, about))
conn.commit()
# Не забывайте закрывать коннекцию к БД - это правило хорошего тона
# Это поможет избежать генерации пустых сессий в БД
if conn:
conn.close()
return True
Finally:
if conn:
conn.close()
return False
В функцию def login() добавляю вызов функции save_data_in_db(fio, region, np, email, birthdate, sex, about) после функции save_data:
def login():
if request.method == 'POST':
fio = request.form.get('fio')
region = request.form.get('region')
np = request.form.get('np')
email = request.form.get('email')
birthdate = request.form.get('birthdate')
sex = request.form.get('sex[]')
about = request.form.get('about')
# объединяем значения полей формы в одну строку
str = fio+" "+region+" "+np+" "+email+" "+birthdate+" "+sex+" "+about+"\n"
# Сохраняем строку в файл logins2.txt
save_data(str)
# Можно заменить сохранение данных в файле на сохранение данных
# в базу данных
save_data_in_db(fio, region, np, email, birthdate, sex, about)
# возвращаем ответ в виде json для отображения на ответной странице
answer_json = '{"fio":"'+fio+'", "status":"OK"}'
return answer_json
Запущу приложение в консоли:
$ python C:/workspace/form_es6/registry.py
Заполню html форму в браузере и нажму кнопку «Отправить»:

Если все отработает без ошибок, то введённые данные должны сохраниться и в файл C:\workspace\form_es6\upload\logins2.txt, и в таблицу users БД PostgreSQL:

В публикации я постаралась показать на простых примерах, как можно использовать сразу несколько технологий и «подружить» их между собой. Теперь вы понимаете основы технологии обмена данными клиент-сервера и можете получать серверные данные в веб-приложении. Можно создать диалоговое окно и использовать введенные данные на клиентской части и отправлять их серверной части в качестве фильтра данных, потом выводить полученные данные на экран.
Надеюсь, этот краткий курс в трёх частях поможет вам реализовать свои интересные проекты.