В этой статье мы рассмотрим новый фреймворк Arrested, который используется для создания REST API при помощи Python. Мы используем Docker, SQLAlchemy и прочие инструменты для создания API на тему Звездных Войн всего за пять минут!
Это первый пост в серии в будущей серии статей, нацеленных на помощь людям в построении REST API Python. Мы собрали коллекцию инструментов, которые помогут вам быстро начать и не слишком напрягаться на протяжении работы. В данном материале мы представим вам фреймворк Arrested, который используется для создания API при помощи Flask. Данный фреймворк нацелен сделать создание REST API безболезненным процессом. Подходит для быстрого использования в проектах, при этом легко расширяется для особых требований.
В данной статье мы рассмотрим
- Использование Cookie Cutter шаблона для установки приложения Flask вместе с базой данных SQLAlchemy ORM для взаимодействия с базой данных, Kim Mappers для сериализации и сортировки, хранилище Docker для среды разработки и пример пользовательского API;
- Создание ресурсов на тему Звездных Войн, для получения списков персонажей, создания новых персонажей, поиск персонажей по ID и наконец, обновление и удаление персонажа.
Список ресурсов инструментов, которые мы будем использовать
- Docker – используется во всех наших примерах;
- Git – для клонирования некоторых хранилищ;
- Cookie Cutter – инструмент для создания проектных шаблонов;
- Flask – наш фреймворк Arrested работает на Flask, микро-фреймворке для Python, который в свою очередь базируется на Werkzeug;
- Kim – фреймворк Python для сортировки и сериализации;
- Arrested – фреймворк для быстрого создания API при помощи Flask
Создаем приложение ?
Мы используем Cookie Cutter для быстрого создания базовой структуры приложения и избегания всех скучных этапов перед созданием ресурса, который будет выдавать персонажей из нашей базы данных «Звездных Войн«. Если вы не хотите использовать Cookie Cutter, вы можете скачать готовую структуру здесь.
1 2 3 4 |
$ cookiecutter gh:mikeywaites/arrested-cookiecutter project_name [Arrested Users API]: star wars project_slug [star-wars]: package_name [star_wars]: |
Теперь у нас есть базовый скелет приложения, давайте создадим контейнер Docker и создадим базу данных:
1 2 3 |
$ cd star_wars $ docker-compose build $ docker-compose run --rm api flask db upgrade |
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Теперь запускаем контейнер API и создаем запрос к конечной точке, чтобы убедиться в том, что все работает корректно.
1 2 3 4 5 |
$ docker-compose up api $ curl -u admin:secret localhost:8080/v1/users | python -m json.tool { "payload": [] } |
Ву а ля. Мы создали рабочий REST API за 5 минут.
Конечно, мы получили рабочий REST API, но вся тяжелая работа уже была сделана за вас, но вы все еще понятия не имеет, как использовать Arrested для создания API в Python. В следующем разделе мы рассмотрим, как создавать базовый API для нашей базы данных персонажей Звездных Войн.
Создаем ресурс персонажей
Теперь, когда у нас есть установленное приложение, мы можем начать создание Python API наших персонажей. Мы добавим конечные точки, которые позволяют клиенту получать список персонажей, создавать новых, выполнять поиск персонажей, обновлять и удалять персонажей. Перед созданием нового API нам нужно создать модель Character и CharacterMapper.
1 2 3 |
$ touch star_wars/models/character.py $ touch star_wars/apis/v1/characters.py $ touch star_wars/apis/v1/mappers/character.py |
Начнем с очень простого объекта Character, который нужно назвать.
1 2 3 4 5 6 7 8 9 10 |
from .base import db, BaseMixin __all__ = ['Character'] class Character(BaseMixin, db.Model): __tablename__ = 'character' name = db.Column(db.Unicode(255), nullable=False) |
Далее нам нужно импортировать модель в models/__init__.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import datetime from flask_sqlalchemy import SQLAlchemy from sqlalchemy.ext.declarative import declared_attr db = SQLAlchemy() class BaseMixin(object): @declared_attr def id(cls): return db.Column(db.Integer, primary_key=True) @declared_attr def created_at(cls): return db.Column(db.DateTime, default=datetime.datetime.utcnow) @declared_attr def updated_at(cls): return db.Column(db.DateTime, default=datetime.datetime.utcnow) from .user import * from .character import * |
Создаем миграцию для новой модели
1 2 3 4 5 6 7 8 |
$ docker-compose run --rm api flask db revision "character model" INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.autogenerate.compare] Detected added table 'character' Generating /opt/code/star_wars/migrations/d7d80c02d806_character_model.py ... done # Выполняем файлы миграции для создание таблиц в базе данных $ docker-compose run --rm api flask db upgrade |
Редактируем файл «star_wars/apis/v1/mappers/character.py» который мы ранее создали для объекта CharacterMapper. Он отвечает за сериализацию и сортировку наших данных в API.
1 2 3 4 5 6 7 8 9 10 11 12 |
from kim import field from .base import BaseMapper from star_wars.models import Character class CharacterMapper(BaseMapper): __type__ = Character name = field.String() |
Теперь импортируем мэппер в модуль mapper/__init__.py
1 2 |
from .user import UserMapper from .character import CharacterMapper |
Великолепно! Теперь у нас есть модель базы данных и способ её сериализации, давайте создадим конечную точку, которая позволяет нашим клиентам API получить список персонажей. Добавьте следующий код в созданный нами файл «api/v1/characters.py«.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from arrested import Resource from arrested.contrib.kim_arrested import KimEndpoint from arrested.contrib.sql_alchemy import DBListMixin from star_wars.models import db, Character from .mappers import CharacterMapper characters_resource = Resource('characters', __name__, url_prefix='/characters') class CharactersIndexEndpoint(KimEndpoint, DBListMixin): name = 'list' many = True mapper_class = CharacterMapper model = Character def get_query(self): stmt = db.session.query(Character) return stmt characters_resource.add_endpoint(CharactersIndexEndpoint) |
Теперь нам нужно зарегистрировать наш Resource при помощи объекта Arrested API. Откройте модуль «api/v1/__init__.py» и импортируйте characters_resource. Укажите «defer=True«, чтобы Arrested отложил регистрацию маршрутов с Flask, пока объект API не инициализируется.
1 2 3 4 5 6 7 8 |
from arrested import ArrestedAPI from .users import users_resource from .characters import characters_resource from .middleware import basic_auth api_v1 = ArrestedAPI(url_prefix='/v1', before_all_hooks=[basic_auth]) api_v1.register_resource(users_resource, defer=True) api_v1.register_resource(characters_resource, defer=True) |
Давайте проведем тестовый запуск нашего API. Убедитесь в том, что контейнер Docker запущен и затем используйте команду curl, которая выполнит GET запрос к ресурсу персонажей.
1 2 3 4 5 6 |
$ docker-compose up api $ curl -u admin:secret localhost:8080/v1/characters | python -m json.tool { "payload": [] } |
Чудесно! Мы получили ответ, но не получили ни одного объекта персонажа из нашей базы данных, но это пока что. Теперь нам нужно получить способность создавать новых персонажей при помощи DBCreateMixin
Наш модуль characters.py теперь выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
from arrested import Resource from arrested.contrib.kim_arrested import KimEndpoint from arrested.contrib.sql_alchemy import DBListMixin, DBCreateMixin from star_wars.models import db, Character from .mappers import CharacterMapper characters_resource = Resource('characters', __name__, url_prefix='/characters') class CharactersIndexEndpoint(KimEndpoint, DBListMixin, DBCreateMixin): name = 'list' many = True mapper_class = CharacterMapper model = Character def get_query(self): stmt = db.session.query(Character) return stmt characters_resource.add_endpoint(CharactersIndexEndpoint) |
Все, что нам нужно для поддержки создания нашего объекта Character – это добавить DBCreateMixin в CharactersIndexEndpoint. Интеграция Arrested с SQLAlchemy и Kim позволяет обработать входящие данные, конвертируя вводные данные JSON Python в объект Character, с последующим сохранением в базе данных. Давайте пойдем дальше и создадим Персонажа.
1 2 3 4 5 6 7 8 9 10 |
curl -u admin:secret -H "Content-Type: application/json" -d '{"name":"Darth Vader"}' -X POST localhost:8080/v1/characters | python -m json.tool { "payload": { "created_at": "2017-11-22T08:18:26.044931", "id": 1, "name": "Darth Vader", "updated_at": "2017-11-22T08:18:26.044958" } } |
Дарт Вейдер удачно создан! Теперь, если мы выполним запрос GET, как мы делали это ранее в нашем API, то наш Дарт Вейдер должен вернуться.
1 2 3 4 5 6 7 8 9 10 11 12 |
curl -u admin:secret localhost:8080/v1/characters | python -m json.tool { "payload": [ { "created_at": "2017-11-22T08:18:26.044931", "id": 1, "name": "Darth Vader", "updated_at": "2017-11-22T08:18:26.044958" } ] } |
Теперь нам нужно установить конечную точку, которая позволяет клиентам получить персонажа, используя ID ресурса.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
from arrested import Resource from arrested.contrib.kim_arrested import KimEndpoint from arrested.contrib.sql_alchemy import DBListMixin, DBCreateMixin, DBObjectMixin from star_wars.models import db, Character from .mappers import CharacterMapper characters_resource = Resource('characters', __name__, url_prefix='/characters') class CharactersIndexEndpoint(KimEndpoint, DBListMixin, DBCreateMixin): name = 'list' many = True mapper_class = CharacterMapper model = Character def get_query(self): stmt = db.session.query(Character) return stmt class CharacterObjectEndpoint(KimEndpoint, DBObjectMixin): name = 'object' url = '/<string:obj_id>' mapper_class = CharacterMapper model = Character def get_query(self): stmt = db.session.query(Character) return stmt characters_resource.add_endpoint(CharactersIndexEndpoint) characters_resource.add_endpoint(CharacterObjectEndpoint) |
Мы добавили новую конечную точку – CharacterObjectEndpoint и зарегистрировали её в ресурсе персонажей. DBObjectMixin позволяет нам получать ресурс по ID, обновлять тот или иной ресурс, а также удалить ресурс. Давайте попробуем!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# Получаем персонажа с ID 1 curl -u admin:secret localhost:8080/v1/characters/1 | python -m json.tool { "payload": { "created_at": "2017-11-22T08:18:26.044931", "id": 1, "name": "Darth Vader", "updated_at": "2017-11-22T08:18:26.044958" } } # После этого мы можем обновить имя персонажа curl -u admin:secret -H "Content-Type: application/json" -d '{"id": 1, "name":"Anakin Skywalker"}' -X PUT localhost:8080/v1/characters/1 | python -m json.tool { "payload": { "created_at": "2017-11-22T08:18:26.044931", "id": 1, "name": "Anakin Skywalker", "updated_at": "2017-11-22T08:18:26.044958" } } # И наконец, мы можем удалить персонажа curl -u admin:secret -X DELETE localhost:8080/v1/characters/1 |
Что-ж, вот и все. Это было введение в создание REST API в Python при помощи Arrested. Следите за дальнейшими обновлениями, у нас готовится много чего полезного!
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»