Вступление
Базы данных являются важной частью современных приложений, поскольку они хранят данные, используемые для их питания. Как правило, мы используем Structured Query Language (SQL) для выполнения запросов к базе данных и манипулирования данными внутри нее. Хотя первоначально это делалось с помощью специальных инструментов SQL, мы быстро перешли к использованию SQL из приложений для выполнения запросов.
Естественно, со временем появились Объектно – реляционные картографы (ORM), которые позволяют нам безопасно, легко и удобно подключаться к нашей базе данных программно, не прибегая к фактическому выполнению запросов для манипулирования данными.
Одной из таких ФОРМ является SQLAlchemy . В этом посте мы углубимся в Форумы и, в частности, в SQLAlchemy, а затем используем его для создания веб-приложения, управляемого базой данных, с использованием фреймворка Flask .
Что такое норма и зачем ее использовать?
Объектно-реляционное отображение , как следует из названия, сопоставляет объекты с реляционными сущностями. В объектно-ориентированных языках программирования объекты не сильно отличаются от реляционных сущностей – у них есть определенные поля/атрибуты, которые могут быть сопоставлены взаимозаменяемо.
Тем не менее, поскольку сопоставить объект с базой данных довольно легко, обратное также очень просто. Это облегчает процесс разработки программного обеспечения и снижает вероятность совершения ошибок вручную при написании простого SQL-кода.
Еще одно преимущество использования ORMS заключается в том, что они помогают нам писать код, который придерживается принципов DRY ( Don’t Repeat Yourself ), позволяя нам использовать наши модели для манипулирования данными вместо написания SQL-кода каждый раз, когда нам нужно получить доступ к базе данных.
Формирует абстрактные базы данных из нашего приложения, позволяя нам легко использовать несколько или переключать базы данных. Скажем, если бы мы использовали SQL в нашем приложении для подключения к базе данных MySQL, нам пришлось бы изменить наш код, если бы мы переключились на базу данных MSSQL, поскольку они отличаются синтаксисом.
Если наш SQL был интегрирован в нескольких точках нашего приложения, это будет довольно хлопотно. С помощью ORM изменения, которые нам нужно будет внести, будут ограничены простым изменением нескольких параметров конфигурации.
Несмотря на то, что ORMS облегчают нашу жизнь, абстрагируя операции с базой данных, мы должны быть осторожны, чтобы не забыть, что происходит под капотом, поскольку это также будет определять, как мы используем ORMS. Мы также должны быть знакомы с ORMS и изучать их, чтобы использовать их более эффективно, и это вводит некоторую кривую обучения.
SQLAlchemy ORM
SQLAlchemy – это ORM, написанный на Python, чтобы дать разработчикам мощь и гибкость SQL без хлопот его реального использования.
SQLAlchemy оборачивается вокруг Python Database API (Python DB API) , который поставляется вместе с Python и был создан для облегчения взаимодействия между модулями Python и базами данных.
API DB был создан для обеспечения согласованности и переносимости, когда речь заходит об управлении базами данных, хотя нам не нужно будет взаимодействовать с ним напрямую, так как SQLAlchemy будет нашей точкой контакта.
Также важно отметить, что SQLAlchemy ORM построен поверх SQLAlchemy Core – который обрабатывает интеграцию DB API и реализует SQL. Другими словами, ядро SQLAlchemy предоставляет средства для генерации SQL-запросов.
Хотя SQLAlchemy ORM делает наши приложения агностичными по отношению к базам данных, важно отметить, что для подключения к конкретным базам данных потребуются определенные драйверы. Одним из хороших примеров является Pyscopg , который представляет собой реализацию PostgreSQL API DB, которая при использовании в сочетании с SQLAlchemy позволяет нам взаимодействовать с базами данных Postgres.
Для баз данных MySQL библиотека PyMySQL предлагает реализацию DBAPI, необходимую для взаимодействия с ними.
SQLAlchemy также можно использовать с Oracle и Microsoft SQL Server. Некоторые крупные имена в отрасли, которые полагаются на SQLAlchemy, включают Reddit, Yelp, DropBox и SurveyMonkey.
Представив ФОРМУ, давайте построим простой API Flask, который взаимодействует с базой данных Postgres.
Колба с SQLAlchemy
Flask-это легкий микро-фреймворк, который используется для создания минимальных веб-приложений, и с помощью сторонних библиотек мы можем использовать его гибкость для создания надежных и многофункциональных веб-приложений.
В нашем случае мы построим простой RESTful API и используем расширение Flask-SQLAlchemy для подключения нашего API к базе данных Postgres.
Предпосылки
Мы будем использовать PostgreSQL (также известный как Postgres) для хранения наших данных, которые будут обрабатываться и манипулироваться нашим API.
Для взаимодействия с нашей базой данных Postgres мы можем использовать командную строку или клиенты, оснащенные графическими пользовательскими интерфейсами, что упрощает их использование и значительно ускоряет навигацию.
Для Mac OS я рекомендую использовать Portico , который довольно прост и интуитивно понятен и обеспечивает чистый пользовательский интерфейс.
pgAdmin – еще один отличный клиент, который поддерживает все основные операционные системы и даже предоставляет докеризованную версию.
Мы будем использовать эти клиенты для создания базы данных, а также для просмотра данных во время разработки и выполнения нашего приложения.
С установками покончено, давайте создадим нашу среду и установим зависимости, которые нам понадобятся для нашего приложения:
$ virtualenv --python=python3 env --no-site-packages $ source env/bin/activate $ pip install psycopg2-binary $ pip install flask-sqlalchemy $ pip install Flask-Migrate
Приведенные выше команды создадут и активируют virtualenv, установят драйвер Psycopg2, установят flask-sqlalchemy и установят Flask-Migrate для обработки миграции баз данных.
Flask-Migrate
использует Alembic , который является легким инструментом миграции баз данных, который помогает нам взаимодействовать с нашей базой данных гораздо более понятным способом, помогая нам создавать и воссоздавать базы данных, перемещать данные в базы данных и между ними, а также идентифицировать состояние нашей базы данных.
В нашем случае нам не придется воссоздавать базу данных или таблицы каждый раз, когда наше приложение запускается, и мы сделаем это автоматически для нас, если ни того, ни другого не существует.
Реализация
Мы создадим простой API для обработки и манипулирования информацией об автомобилях. Данные будут храниться в базе данных PostgreSQL, и через API мы будем выполнять CRUD-операции.
Во-первых, мы должны создать базу данных cars_api
, используя наш клиент PostgreSQL по выбору:
Имея базу данных на месте, давайте подключимся к ней. Мы начнем с начальной загрузки нашего API Flask в apps.py
файл:
from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return {"hello": "world"} if __name__ == '__main__': app.run(debug=True)
Мы начинаем с создания приложения Flask и одной конечной точки, которая возвращает объект JSON.
Для нашей демонстрации мы будем использовать Flask-SQLAlchemy , которое является расширением, специально предназначенным для добавления функциональности SQLAlchemy в приложения Flask.
Давайте теперь интегрируем Flask-SQLAlchemy и Flask-Migrate в наш app.py
и создать модель , которая будет определять данные о наших автомобилях, которые мы будем хранить:
# Previous imports remain... from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://postgres:[email protected]:5432/cars_api" db = SQLAlchemy(app) migrate = Migrate(app, db) class CarsModel(db.Model): __tablename__ = 'cars' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String()) model = db.Column(db.String()) doors = db.Column(db.Integer()) def __init__(self, name, model, doors): self.name = name self.model = model self.doors = doors def __repr__(self): return f""
После импорта flask_sqlalchemy
мы начинаем с добавления URI базы данных в конфигурацию вашего приложения. Этот URL-адрес содержит наши учетные данные, адрес сервера и базу данных, которую мы будем использовать для нашего приложения.
Затем мы создаем экземпляр Flask-SQLAlchemy с именем db
и используем его для всех наших взаимодействий с базой данных. Экземпляр Flask-Migrate, называемый migrate
, создается после этого и будет использоваться для обработки миграций для нашего проекта.
Модель Cars
– это класс модели, который будет использоваться для определения и обработки наших данных. Атрибуты класса представляют поля, которые мы хотим сохранить в базе данных.
Мы определяем имя таблицы, используя __tablename__
рядом со столбцами, содержащими ваши данные.
Flask поставляется с интерфейсом командной строки и выделенными командами. Например, для запуска нашего приложения мы используем команду flask run
. Чтобы подключиться к этому сценарию, нам просто нужно определить переменную среды, которая определяет сценарий, в котором размещается наше приложение Flask:
$ export FLASK_APP=app.py $ flask run * Serving Flask app "app.py" (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 172-503-577
С нашей моделью на месте и Flask-Migrate
интегрированной, давайте использовать ее для создания таблицы cars
в нашей базе данных:
$ flask db init $ flask db migrate $ flask db upgrade
Мы начинаем с инициализации базы данных и включения миграции. Сгенерированные миграции-это просто сценарии, которые определяют операции, которые будут выполняться в нашей базе данных. Поскольку это первый раз, скрипт просто сгенерирует таблицу cars
со столбцами, указанными в нашей модели.
Команда flask db upgrade
выполняет миграцию и создает нашу таблицу:
В случае добавления, удаления или изменения каких-либо столбцов мы всегда можем выполнить команды migrate
и upgrade
, чтобы отразить эти изменения и в нашей базе данных.
Создание и чтение сущностей
С базой данных на месте и подключенной к нашему приложению, все, что осталось, – это реализовать операции CRUD. Давайте начнем с создания car
, а также извлечения всех существующих в данный момент:
# Imports and CarsModel truncated @app.route('/cars', methods=['POST', 'GET']) def handle_cars(): if request.method == 'POST': if request.is_json: data = request.get_json() new_car = CarsModel(name=data['name'], model=data['model'], doors=data['doors']) db.session.add(new_car) db.session.commit() return {"message": f"car {new_car.name} has been created successfully."} else: return {"error": "The request payload is not in JSON format"} elif request.method == 'GET': cars = CarsModel.query.all() results = [ { "name": car.name, "model": car.model, "doors": car.doors } for car in cars] return {"count": len(results), "cars": results}
Мы начинаем с определения маршрута /cars
, который принимает как GET
, так и POST
запросы. Запрос GET
вернет список всех автомобилей, хранящихся в нашей базе данных, в то время как метод POST
получит данные автомобиля в формате JSON и заполнит нашу базу данных предоставленной информацией.
Чтобы создать новый автомобиль, мы используем класс Cars Model
и предоставляем информацию, необходимую для заполнения столбцов нашей таблицы cars
. После создания объекта Cars Model
мы создаем сеанс базы данных и добавляем в него ваш car
.
Чтобы сохранить наш автомобиль в базе данных, мы фиксируем сеанс через db.session.commit ()
, который закрывает транзакцию БД и сохраняет наш автомобиль.
Давайте попробуем добавить автомобиль с помощью такого инструмента, как Почтальон:
Ответное сообщение уведомляет нас о том, что наш автомобиль был создан и сохранен в базе данных:
Вы можете видеть, что теперь в нашей базе данных есть запись об этом автомобиле.
С автомобилями, сохраненными в нашей базе данных, запрос GET
поможет нам получить все записи. Мы запрашиваем все автомобили, хранящиеся в нашей базе данных, используя модель Cars.query.call()
функция, предоставляемая Flask-SQLAlchemy.
Это возвращает список объектов CarsModel
, который мы затем форматируем и добавляем в список с помощью понимания списка и передаем его в ответ вместе с количеством автомобилей в нашей базе данных. Когда мы запрашиваем список автомобилей через API в Postman:
Метод GET
в конечной точке /cars
возвращает список автомобилей в том виде, в каком они появляются в нашей базе данных, а также общее количество.
Примечание: Обратите внимание, что в коде нет ни одного SQL-запроса. SQLAlchemy заботится об этом за нас.
Обновление и удаление сущностей
Пока что мы можем создать один автомобиль и получить список всех автомобилей, хранящихся в базе данных. Чтобы завершить набор операций CRUD над автомобилями в нашем API, нам нужно добавить функциональность для возврата деталей, изменения и удаления одного автомобиля.
HTTP-методы/глаголы, которые мы будем использовать для достижения этой цели , будут GET
, PUT
и DELETE
, которые будут объединены в один метод под названием handle_car()
:
# Imports, Car Model, handle_cars() method all truncated @app.route('/cars/', methods=['GET', 'PUT', 'DELETE']) def handle_car(car_id): car = CarsModel.query.get_or_404(car_id) if request.method == 'GET': response = { "name": car.name, "model": car.model, "doors": car.doors } return {"message": "success", "car": response} elif request.method == 'PUT': data = request.get_json() car.name = data['name'] car.model = data['model'] car.doors = data['doors'] db.session.add(car) db.session.commit() return {"message": f"car {car.name} successfully updated"} elif request.method == 'DELETE': db.session.delete(car) db.session.commit() return {"message": f"Car {car.name} successfully deleted."}
Наш метод handle_car()
получает car_id
из URL-адреса и получает объект car в том виде, в каком он хранится в нашей базе данных. Если метод запроса GET
, то данные автомобиля будут просто возвращены:
Чтобы обновить детали нашего автомобиля, мы используем метод PUT
, а не PATCH
. Оба метода могут быть использованы для обновления деталей, однако метод PUT
принимает обновленную версию нашего ресурса и заменяет ту, которую мы сохранили в базе данных.
Метод PATCH
просто изменяет тот, который у нас есть в нашей базе данных, не заменяя его. Поэтому, чтобы обновить запись CarsModel
в нашей базе данных, мы должны предоставить все атрибуты вашего автомобиля, включая те, которые должны быть обновлены.
Мы используем детали для изменения нашего объекта car и фиксируем эти изменения с помощью db.session.commit()
, а затем возвращаем ответ пользователю:
Наш автомобиль был успешно обновлен.
Наконец, чтобы удалить автомобиль, мы отправляем запрос DELETE
в ту же конечную точку. Поскольку объект CarsModel
уже запрошен, все, что нам нужно будет сделать, это использовать текущий сеанс, чтобы удалить его, выполнив db.session.delete(car)
и фиксация нашей транзакции для отражения наших изменений в базе данных:
Вывод
Реальные приложения не так просты, как наши, и обычно обрабатывают данные, которые связаны и распределены по нескольким таблицам.
SQLAlchemy позволяет нам определять отношения и манипулировать связанными данными. Более подробную информацию об обработке отношений можно найти в официальной документации Flask-SQLAlchemy .
Наше приложение может быть легко расширено для размещения отношений и даже большего количества таблиц. Мы также можем подключаться к нескольким базам данных с помощью привязок. Более подробную информацию о привязках можно найти на странице Binds documentation .
В этом посте мы представили ORMS и, в частности, SQLAlchemy ORM. Используя Flask и Flask-SQLAlchemy, мы создали простой API, который предоставляет и обрабатывает данные об автомобилях, хранящиеся в локальной базе данных PostgreSQL.
Исходный код проекта в этом посте можно найти на GitHub .