Рубрики
Без рубрики

Использование SQLAlchemy с Flask и PostgreSQL

В настоящее время объектно-реляционные картографы, такие как SQLAlchemy, используются в качестве моста между приложениями и базами данных SQL и облегчают работу с ними программно.

Автор оригинала: Robley Gori.

Вступление

Базы данных являются важной частью современных приложений, поскольку они хранят данные, используемые для их питания. Как правило, мы используем 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 по выбору:

sqlalchemy_create_db

Имея базу данных на месте, давайте подключимся к ней. Мы начнем с начальной загрузки нашего 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 выполняет миграцию и создает нашу таблицу:

sqlalchemy_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 () , который закрывает транзакцию БД и сохраняет наш автомобиль.

Давайте попробуем добавить автомобиль с помощью такого инструмента, как Почтальон:

sqlalchemy_postman

Ответное сообщение уведомляет нас о том, что наш автомобиль был создан и сохранен в базе данных:

sqlalchemy_db

Вы можете видеть, что теперь в нашей базе данных есть запись об этом автомобиле.

С автомобилями, сохраненными в нашей базе данных, запрос GET поможет нам получить все записи. Мы запрашиваем все автомобили, хранящиеся в нашей базе данных, используя модель Cars.query.call() функция, предоставляемая Flask-SQLAlchemy.

Это возвращает список объектов CarsModel , который мы затем форматируем и добавляем в список с помощью понимания списка и передаем его в ответ вместе с количеством автомобилей в нашей базе данных. Когда мы запрашиваем список автомобилей через API в Postman:

sqlalchemy_postman_2

Метод 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 , то данные автомобиля будут просто возвращены:

sqlalchemy_postman_3

Чтобы обновить детали нашего автомобиля, мы используем метод PUT , а не PATCH . Оба метода могут быть использованы для обновления деталей, однако метод PUT принимает обновленную версию нашего ресурса и заменяет ту, которую мы сохранили в базе данных.

Метод PATCH просто изменяет тот, который у нас есть в нашей базе данных, не заменяя его. Поэтому, чтобы обновить запись CarsModel в нашей базе данных, мы должны предоставить все атрибуты вашего автомобиля, включая те, которые должны быть обновлены.

Мы используем детали для изменения нашего объекта car и фиксируем эти изменения с помощью db.session.commit() , а затем возвращаем ответ пользователю:

sqlalchemy_postman_4

Наш автомобиль был успешно обновлен.

Наконец, чтобы удалить автомобиль, мы отправляем запрос DELETE в ту же конечную точку. Поскольку объект CarsModel уже запрошен, все, что нам нужно будет сделать, это использовать текущий сеанс, чтобы удалить его, выполнив db.session.delete(car) и фиксация нашей транзакции для отражения наших изменений в базе данных:

sqlalchemy_postman_5

Вывод

Реальные приложения не так просты, как наши, и обычно обрабатывают данные, которые связаны и распределены по нескольким таблицам.

SQLAlchemy позволяет нам определять отношения и манипулировать связанными данными. Более подробную информацию об обработке отношений можно найти в официальной документации Flask-SQLAlchemy .

Наше приложение может быть легко расширено для размещения отношений и даже большего количества таблиц. Мы также можем подключаться к нескольким базам данных с помощью привязок. Более подробную информацию о привязках можно найти на странице Binds documentation .

В этом посте мы представили ORMS и, в частности, SQLAlchemy ORM. Используя Flask и Flask-SQLAlchemy, мы создали простой API, который предоставляет и обрабатывает данные об автомобилях, хранящиеся в локальной базе данных PostgreSQL.

Исходный код проекта в этом посте можно найти на GitHub .