06.02.2020       Выпуск 320 (03.02.2020 - 09.02.2020)       Статьи

Опыт создания web-приложения с Pony ORM

Недавно передо мной встала задача написать на Python web-приложение для разделения счёта в ресторане между участниками трапезы. Так как нужна DB для хранения данных о заказах и пользователях, встал вопрос выбора ORM для работы с базой. Разработка велась на Flask, так что сразу отметается Django ORM и выбор изначально пал в сторону SQLAlchemy. С одной стороны эта ORM почти всемогущая, но за счет этого она довольно тяжела в освоении. Помучившись с алхимией какое-то время, я решил найти более простой вариант, чтоб разработка пошла быстрее. В итоге для проекта была выбрана Pony ORM.

Читать>>




Экспериментальная функция:

Ниже вы видите текст статьи по ссылке. По нему можно быстро понять ссылка достойна прочтения или нет

Просим обратить внимание, что текст по ссылке и здесь может не совпадать.

Привет, Хабр!

Недавно передо мной встала задача написать на Python web-приложение для разделения счёта в ресторане между участниками трапезы. Так как нужна DB для хранения данных о заказах и пользователях, встал вопрос выбора ORM для работы с базой. Разработка велась на Flask, так что сразу отметается Django ORM и выбор изначально пал в сторону SQLAlchemy. С одной стороны эта ORM почти всемогущая, но за счет этого она довольно тяжела в освоении. Помучившись с алхимией какое-то время, я решил найти более простой вариант, чтоб разработка пошла быстрее. В итоге для проекта была выбрана Pony ORM.
image

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

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

Преимущества PonyORM:

  • При написании запросов используется исключительно питоновский синтаксис (генераторные выражения или lambda функции)
  • Автоматическое кэширование запросов и объектов
  • Полная поддержка композитных ключей

В качестве основного недостатка я бы выделил отсутствие механизма миграций (разработчики сейчас работают над этим)

Взяв в расчет всё вышеперечисленное, можно сказать, что Pony довольно хорошее решение для работы с базами данных, кроме тех случаев, когда часто возникает необходимость изменять схему базы на продакшене.

Далее я продемонстрирую основы работы с Pony на примере фрагментов кода из проекта, о котором я говорил выше.

Настройка окружения

Для начала нужно через pip установить Pony:

pip install pony

Также нужно установить DB API для нужной СУБД:

  • PostgreSQL: psycopg или psycopg2cffi
  • Oracle: cx_Oracle
  • MySQL: PyMySQL

После этого импортируем в проект Pony:

from pony.orm import *

Создание объекта БД и привязка его к существующей базе

Объект базы данных — это представление базы в питоновском коде. С базой работа происходит через этот объект.

db = Database()

Далее этот объект нужно привязать к базе. Это можно сделать передав нужные параметры в конструктор класса Database либо вызвав метод db.bind() с нужными параметрами. Для разных СУБД они различаются. В данном проекте я работаю с базами SQLite (на этапе разработки) и PostgreSQL (продакшен). Для быстрого переключения между СУБД я прописал параметры в словаре, который храню в отдельном файле и передаю в конструктор объекта Database. Файл с параметрами подключения у меня выглядит так:

settings = dict(
    sqlite=dict(provider='sqlite', filename='pony.db', create_db=True),
    postgres=dict(provider='postgres', user='pony', password='pony', host='localhost', 
        database='pony')
)

А сама привязка происходит при создании объекта базы :

db = Database(**settings['postgres'])

Создание сущностей
Сначала я сделал схему базы в редакторе для создания ER диаграмм на сайте пони. Удобен он тем, что он может автоматически сгенерировать Python код для описания сущностей по сделанной в редакторе схеме. Сущности можно описать и вручную, но я воспользовался редактором для экономии времени.

image

На её основе был сгенерирован следующий код:

from datetime import datetime
from pony.orm import *

db = Database()

class User(db.Entity):
    id = PrimaryKey(int, auto=True)
    fullname = Optional(str)
    password = Optional(str)
    nickname = Optional(str)
    lended = Set('Credit', reverse='lender')
    borrowed = Set('Credit', reverse='borrower')
    credit_editions = Set('CreditEdition', reverse='user')
    sessions = Set('UserInSession')
    affected_editions = Set('CreditEdition', reverse='affected_user')

class Session(db.Entity):
    id = PrimaryKey(int, auto=True)
    title = Optional(str)
    start = Optional(datetime)
    end = Optional(datetime)
    ordereders = Set('OrderedItem')
    users = Set('UserInSession')

class OrderedItem(db.Entity):
    id = PrimaryKey(int, auto=True)
    session = Required(Session)
    title = Optional(str)
    price = Optional(int)
    user_in_sessions = Set('UserInSession')

class Credit(db.Entity):
    id = PrimaryKey(int, auto=True)
    lender = Required(User, reverse='lended')
    borrower = Required(User, reverse='borrowed')
    value = Optional(int)
    credit_editions = Set('CreditEdition')

class UserInSession(db.Entity):
    id = PrimaryKey(int, auto=True)
    user = Required(User)
    session = Required(Session)
    orders = Set(OrderedItem)
    value = Optional(int)

class CreditEdition(db.Entity):
    id = PrimaryKey(int, auto=True)
    user = Required(User, reverse='credit_editions')
    affected_user = Required(User, reverse='affected_editions')
    credit = Required(Credit)
    old_value = Optional(int)
    new_value = Optional(int)

db.generate_mapping()

Обобщив, можно сказать, что этот редактор может ускорить разработку для новичка в вопросах работы с базами.

Запросы
Обращение к объекту по первичному ключу в пони реализовано оператором доступа к элементу (квадратные скобки). Например:

session = Session[sid]

В данном примере из таблицы Session берется строка, в которой id == sid.

Также, как я уже говорил выше, запросы в Pony можно писать генераторными выражениями, что делает их написание гораздо удобнее, чем в алхимии.
Снизу приведен пример, в котором базе посылается запрос на выборку пользователей, которые являются участниками данной сессии и не являются виртуальными пользователями.

users = select(u for u in User if u not in session.users.user and not u.virtual)

Заключение

Надеюсь, моя статья оказалась кому-нибудь полезной. Если вас тоже заинтересовал Pony, внизу я оставлю ссылки на документацию и на телеграм-канал комьюнити.

Спасибо за внимание!

Документация Pony
Телеграм коммьюнити






Разместим вашу рекламу

Пиши: mail@pythondigest.ru

Нашли опечатку?

Выделите фрагмент и отправьте нажатием Ctrl+Enter.

Система Orphus