Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Kursovaya_v1.docx
Скачиваний:
49
Добавлен:
01.04.2022
Размер:
7.86 Mб
Скачать

МИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ, СВЯЗИ И МАССОВЫХ КОММУНИКАЦИЙ РФ Федеральное учреждение высшего профессионального образования Московский технический университет связи и информатики

Кафедра математическая кибернетика и информационные технологии

Курсовая работа по теме

«Разработка REST API сервиса»

по дисциплине:

Web- программирование

Выполнил студент

Проверила:

Полянцева Ксения Андреевна

Москва 2021

Оглавление

Цель 3

Разработка 6

Setup.py 7

База данных. Проектируем схему 10

Описываем схему в SQLAlchemy 13

Настраиваем Alembic 14

Генерируем миграции 18

Приложение 20

Сериализация данных 23

Обработчики 25

POST /imports 25

GET /imports/$import_id/citizens 30

PATCH /imports/$import_id/citizens/$citizen_id 32

GET /imports/$import_id/citizens/birthdays 36

GET /imports/$import_id/towns/stat/percentile/age 38

Тестирование 39

Обработчики 41

GET /imports/$import_id/citizens 45

POST /imports 48

PATCH /imports/$import_id/citizens/$citizen_id 52

GET /imports/$import_id/citizens/birthdays 55

GET /imports/$import_id/towns/stat/percentile/age 57

Миграции 59

Сборка 61

CI 63

Результаты работы 64

Вывод: 70

Список использованных источников 70

Цель

Разработать REST API-сервис посредством языка Python, протестировать, упаковать его в Docker-контейнер, а также развернуть с помощью Ansible.

Рис. 1 – структура проекта

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

Давайте разработаем REST API-сервис на Python, который будет анализировать предоставленные данные и выявлять спрос на подарки у жителей разных возрастных групп в разных городах по месяцам.

В сервисе реализуем следующие обработчики:

  • POST /imports

Добавляет новую выгрузку с данными;

  • GET /imports/$import_id/citizens

Возвращает жителей указанной выгрузки;

  • PATCH /imports/$import_id/citizens/$citizen_id

Изменяет информацию о жителе (и его родственниках) в указанной выгрузке;

  • GET /imports/$import_id/citizens/birthdays

Вычисляет число подарков, которое приобретет каждый житель выгрузки своим родственникам (первого порядка), сгруппированное по месяцам;

  • GET /imports/$import_id/towns/stat/percentile/age

Вычисляет 50-й, 75-й и 99-й перцентили возрастов (полных лет) жителей по городам в указанной выборке.

Для реализации предлагается выбрать СУБД PostgreSQL (или другую, по вашему усмотрению), зарекомендовавшую себя как надежное решение c отличной документацией на русском языке, сильным русским сообществом (всегда можно найти ответ на вопрос на русском языке). Реляционная модель достаточно универсальна и хорошо понятна многим разработчикам.

Основная задача сервиса — передача данных по сети между БД и клиентами — не предполагает большой нагрузки на процессор, но требует возможности обрабатывать несколько запросов в один момент времени. Асинхронный подход позволяет эффективно обслуживать нескольких клиентов в рамках одного процесса ОС (в отличие, например, от используемой во Flask/Django pre-fork-модели, которая создает несколько процессов для обработки запросов от пользователей, каждый из них потребляет память, но простаивает большую часть времени). Поэтому в качестве библиотеки для написания сервиса предлагается выбрать асинхронный aiohttp.

Рис 2. – Схема обработки запросов

SQLAlchemy позволяет декомпозировать сложные запросы на части, переиспользовать их, генерировать запросы с динамическим набором полей (например, PATCH-обработчик позволяет частичное обновление жителя с произвольными полями) и сосредоточиться непосредственно на бизнес-логике. С выполнением этих запросов и передачей данных быстрее всех справится драйвер asyncpg, а подружить их поможет asyncpgsa. Один из инструментов для управления состоянием БД и работы с миграциями — Alembic.

Логику валидации можно лаконично описать схемами Marshmallow (включая проверки на родственные связи). С помощью модуля aiohttp-spec связать aiohttp - обработчики и схемы для валидации данных, а бонусом сгенерировать документацию в формате Swagger и отобразить ее в графическом интерфейсе.

Для написания тестов используйте pytest. Для отладки и профилирования можно использовать отладчик PyCharm.

На любом компьютере с Docker (и даже на разных ОС) можно запускать упакованное приложение без необходимости настраивать окружение для запуска и легко устанавливать/обновлять/удалять приложение на сервере.

Для деплоя воспользуйтесь Ansible. Он позволяет декларативно описывать желаемое состояние сервера и его сервисов, работает по ssh и не требует специального софта.

Разработка

Задайте Python-пакету название analyzer и используйте следующую структуру:

Рис. 3 – структура пакета

В файле analyzer/ init .py разместите общую информацию о пакете: описание (docstring), версию, лицензию, контакты разработчиков.

Затем ее можно посмотреть встроенной командой help

$ python

>>> import analyzer

>>> help(analyzer)

Пример:

Рисунок 4 -Результат работы

Пакет имеет две входных точки — REST API-сервис (analyzer/api/__main__.py) и утилита управления состоянием БД (analyzer/db/__main__.py). Благодаря этому подходу к входным точкам можно обращаться с помощью команды python -m:

Рисунок 5 – входные точки

Setup.py

Главная цель файла setup.py — описать пакет с приложением для distutils/setuptools. В файле необходимо указать общую информацию о пакете (название, версию, автора и т. д.), но также в нем можно указать требуемые для работы модули, «экстра»-зависимости (например для тестирования), точки входа (например, исполняемые команды) и требования к интерпретатору. Плагины setuptools позволяют собирать из описанного пакета артефакт. Есть встроенные плагины: zip, egg, rpm, macOS pkg. Остальные плагины распространяются через PyPI: wheel, xar, pex. В сухом остатке, описав один файл, мы получаем огромные возможности. Именно поэтому разработку нового проекта нужно начинать с setup.py. В функции setup() зависимые модули указываются списком:

Но необходимо описать зависимости в отдельных файлах requirements.txt и requirements.dev.txt, содержимое которых используется в setup.py.

Это кажется более гибким, плюс тут есть секрет: впоследствии это позволит собирать Docker-образ быстрее. Зависимости будут ставиться отдельным шагом до установки самого приложения, а при пересборке Docker-контейнера попадать в кеш. Чтобы setup.py смог прочитать зависимости из файлов requirements.txt и requirements.dev.txt, написана функция:

Рисунок 6- функция для прочтения зависимостей

Стоит отметить, что setuptools при сборке source distribution по умолчанию включает в сборку только файлы .py, .c, .cpp и .h. Чтобы файлы с зависимостями requirements.txt и requirements.dev.txt попали в пакет, их необходимо явно указать в файле MANIFEST.in. setup.py целиком

Рисунок 7 – Программный код

Рисунок 8 – Продолжение программного кода

Установить проект в режиме разработки можно следующей командой (в editable режиме Python не установит пакет целиком в папку site-packages, а только создаст ссылки, поэтому любые изменения, вносимые в файлы пакета, будут видны сразу):

Рисунок 9 – Устанавливаю в режим разработки

База данных. Проектируем схему

В описании обработчика POST /imports приведен пример выгрузки с информацией о жителях:

Рисунок 10 – пример выгрузки с данных жителей

Реализация структуры данных:

Рисунок 11 -Реализация структуры данных

  1. Таблица imports состоит из автоматически инкрементируемого столбца import_id. Он нужен для создания проверки по внешнему ключу в таблице citizens.

  2. В таблице citizens хранятся скалярные данные о жителе (все поля за исключением информации о родственных связях). В качестве первичного ключа используется пара (import_id, citizen_id), гарантирующая уникальность жителей citizen_id в рамках import_id. Внешний ключ citizens.import_id -> imports.import_id гарантирует, что поле citizens.import_id будет содержать только существующие выгрузки.

  3. Таблица relations содержит информацию о родственных связях

Одна родственная связь представлена двумя записями (от жителя к родственнику и обратно): эта избыточность позволяет использовать более простое условие при слиянии таблиц citizens и relations и получать информацию более эффективно. Первичный ключ состоит из столбцов (import_id, citizen_id, relative_id) и гарантирует, что в рамках одной выгрузки import_id у жителя citizen_id будут родственники c уникальными relative_id. Также в таблице используются два составных внешних ключа: (relations.import_id, relations.citizen_id) -> (citizens.import_id, citizens.citizen_id) и (relations.import_id, relations.relative_id) -> (citizens.import_id, citizens.citizen_id), гарантирующие, что в таблице будут указаны существующие житель citizen_id и родственник relative_id из одной выгрузки. Такая структура обеспечивает целостность данных средствами PostgreSQL, позволяет эффективно получать жителей с родственниками из базы данных, но подвержена состоянию гонки во время обновления информации о жителях конкурентными запросами (подробнее рассмотрим при реализации обработчика PATCH).