Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Александр Козловский и Алексей Малашкевич (Pony) - Pony ORM: миграции + мега анонс

Александр Козловский и Алексей Малашкевич (Pony) - Pony ORM: миграции + мега анонс

Доклад с Moscow Python Conf 2016 (http://conf.python.ru)
Видео: https://conf.python.ru/pony-orm-migracii-mega-anons/

Мы занимаемся разработкой маппера Pony ORM в течение нескольких лет. За это время нам удалось разработать библиотеку, которую используют разработчики во всем мире. Помимо работы над Pony ORM, наша компания разрабатывает веб проекты, в которых мы используем наш маппер. Один из примеров - https://fineartbiblio.com
За последние несколько лет Pony ORM зарекомендовала себя как удобная библиотека для построения современных веб приложений. Единственное, чего не хватало Пони для полноценного использования в продакшене, это инструмента миграций, позволяющего изменять таблицы базы данных при изменении описания сущностей.
В нашем выступлении мы расскажем о том как мы делали инструмент миграций, с какими сложностями столкнулись, и чем наш инструмент отличается от того, что предоставляют другие мапперы.
Но это не единственная новость, которой мы хотим поделиться с нашими пользователями. Мы подготовили для вас мегаанонс, о котором вы узнаете во время выступления.

Moscow Python Meetup
PRO

October 12, 2016
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. Pony ORM:
    Миграции + мега-новость
    Александр Козловский, Алексей Малашкевич
    12 октября 2016

    View Slide

  2. Что такое Pony ORM?
    Pony ORM - это объектно-реляционный маппер на Питоне
    • Простой и понятный синтаксис запросов
    select(c for c in Customer if sum(c.orders.price) > 1000)
    • Высокая производительность, автоматическая
    оптимизация запросов
    select(c for c in Customer if sum(o.price for o in c.orders) > 1000)

    View Slide

  3. Pony ORM - это сочетание
    простоты и мощности

    View Slide

  4. Текущее состояние Pony ORM
    • Проект активно развивается в течение 10 лет
    • Много очень положительных отзывов 

    от пользователей
    • Основные пользователи:
    • Америка
    • Россия
    • Индия
    • Европа
    • Китай
    • Бразилия

    View Slide

  5. Отзывы пользователей Pony
    “Активно используем Pony ORM в наших проектах уже
    несколько лет. Очень легко писать бизнес логику приложения -
    запросы интуитивно понятны.
    Суб-коллекции - это просто подарок! Особенно с сортировкой и
    лифтингом атрибутов!”
    Марк Коренберг
    СТО, ideco.ru

    View Slide

  6. Отзывы пользователей Pony
    “В PonyORM я ценю высокую скорость работы, наличие DSL,
    который позволяет писать более понятный код.
    Это была кстати одна из причин того что я начал искать
    альтернативу SQLAlchemy.“
    Анатолий Шипицин
    Разработчик
    “It's certainly the most intui~ve & Pythonis~c approach to querying
    databases and is very similar to LINQ in that aspect.”
    Omer Katz
    Very passionate soˆware developer
    hŠp://omerkatz.com

    View Slide

  7. Отзывы пользователей Pony
    “At this point in ~me, I wouldn't even consider using a different
    ORM for projects.”
    Nicholas Brochu
    Maker of digital and physical things.
    @nbrochu
    “I use pony in produc~on on AMZShark.com. It allows me to create
    webapps much faster than any other ORM I've tried, and produces
    sensible, performant SQL both in produc~on with Postgres and
    when tes~ng with SQLite.”
    MaŠhew Bell
    AMZshark.com

    View Slide

  8. session.query(Product).filter(
    (Product.name.startswith('A') & (Product.image == None))
    | (extract('year', Product.added) < 2014))
    Сравнение синтаксиса запросов
    Product.objects.filter(
    Q(name__startswith='A', image__isnull=True)
    | Q(added__year__lt=2014))
    select(p for p in Product
    if p.name.startswith('A') and p.image is None
    or p.added.year < 2014)
    Pony
    Django
    SQLAlchemy

    View Slide

  9. Iden~ty Map vs Ac~ve Record

    View Slide

  10. Django ORM, Active Record
    s1 = Student.objects.get(pk=123)
    print(s1.name, s1.group.id)
    s2 = Student.objects.get(pk=456)
    print(s2.name, s2.group.id)
    Student 123
    Student 456
    Group 1
    Group 1

    View Slide

  11. Pony ORM, IdentityMap
    s1 = Student[123]
    print(s1.name, s1.group.id)
    s2 = Student[456]
    print(s2.name, s2.group.id)
    Student 123
    Student 456
    Group 1

    View Slide

  12. Другие преимущества PonyORM
    • Лифтинг атрибутов
    • Автоматическая оптимизация запросов
    • Решение проблемы “N+1 query”
    • Оптимистические транзакции
    • Множественное наследование сущностей
    • Отсутствие проблемы “срез объекта до базового
    класса”
    • Умное кеширование
    • Четкие сообщения об ошибках

    View Slide

  13. Последние добавленные фичи
    • Поддержка na~ve database JSON
    • Работа с Microsoˆ SQL Server
    • Миграции

    View Slide

  14. Поддержка database JSON
    • Лучшее из двух миров - SQL + noSQL
    • Поддерживается в PostgreSQL, SQLite, MySQL, Oracle
    • Использует эффективный binary JSON, где он есть
    • Поддержка JSON в запросах!

    View Slide

  15. Использование database JSON
    from pony.orm import *
    db = Database()
    class Product(db.En~ty):
    id = PrimaryKey(int, auto=True)
    name = Required(str)
    info = Required(Json)

    View Slide

  16. Использование database JSON
    p = Product(name='Samsung Galaxy Note7',
    info={
    'display': {
    'size': 5.7,
    },
    'baŠery': 3500,
    'exploding': True,
    'SD card slot': True
    'colors': ['Black', 'Grey', 'Gold'],
    })

    View Slide

  17. Изменение JSON атрибута
    p = Product[1]
    p.info['display']['resolu~on'] = [1440, 2560]
    p.info['colors'].append('Silver')

    View Slide

  18. JSON атрибут в запросах
    x = 2048
    select(p for p in Product if p.info['display']['resolu~on'][0] >= x)
    select(p for p in Product if 'Black' in p.info['colors'])

    View Slide

  19. PostgreSQL
    select(p for p in Product if p.info['display']['resolu~on'][0] >= x)


    SELECT "p"."id", "p"."name", "p"."info"
    FROM "product" "p"
    WHERE ("p"."info" #>> '{display,resolu~on,0}')::integer >= %(p1)s

    View Slide

  20. MySQL
    select(p for p in Product if p.info['display']['resolu~on'][0] >= x)


    SELECT `p`.`id`, `p`.`name`, `p`.`info`, `p`.`tags`
    FROM `product` `p`
    WHERE CAST(

    json_extract(`p`.`info`, '$.display.resolu~on[0]') AS SIGNED

    ) >= %s

    View Slide

  21. SQLite
    select(p for p in Product if p.info['display']['resolu~on'][0] >= x)

    SELECT "p"."id", "p"."name", "p"."info", "p"."tags"
    FROM "Product" "p"
    WHERE json_extract("p"."info", '$.display.resolu~on[0]') >= ?

    View Slide

  22. Oracle
    select(p for p in Product if p.info['display']['resolu~on'][0] >= x)

    SELECT "p"."ID", "p"."NAME", "p"."INFO", "p"."TAGS"
    FROM "PRODUCT" "p"
    WHERE JSON_VALUE(
    "p"."INFO", '$.display.resolu~on[0]' RETURNING NUMBER

    ) >= :p1

    View Slide

  23. Миграции
    • Отличия от других ORM
    • Django
    • SQLAlchemy Alembic
    • Внутренняя реализация
    • Пример использования

    View Slide

  24. Миграции в Django
    from django.db import migrations, models


    class Migration(migrations.Migration):


    dependencies = [

    ("migrations", "0001_initial")

    ]


    operations = [

    migrations.AddField("Person", "age",

    models.IntegerField(default=0)),

    ]

    View Slide

  25. Models
    Миграции в Django
    Table
    Column1
    Column2
    Model
    Field1
    Field2

    View Slide

  26. Pony En~~es
    Table
    Column1
    Column2
    Python
    Schema
    Table
    Column
    Column
    Diagram
    Entity
    Attribute1
    Attribute2

    View Slide

  27. Версии диаграммы/схемы
    Version1
    Schema1
    Table
    Column1
    Column2
    Diagram1
    Entity
    Attribute1
    Attribute2
    Version2
    S2
    Table
    Column1
    Column2
    D2
    Entity
    Attribute1
    Attribute2

    View Slide

  28. Интерфейс командной строки
    python -m pony.migrate make
    Создает новый файл миграции
    python -m pony.migrate apply
    Применяет миграции к базе
    python -m pony.migrate sql
    Выводит SQL который будет применен

    View Slide

  29. Примеры файлов миграций
    # 0001_initial.py


    from pony import orm


    dependencies = []


    def define_entities(db):

    class Person(db.Entity):

    name = orm.Required(str)


    View Slide

  30. Примеры файлов миграций
    # 0002_20161013_0922.py


    from pony import orm


    dependencies = ["0001_initial"]


    def define_entities(db):

    class Person(db.Entity):

    name = orm.Required(str)

    age = orm.Optional(int)

    View Slide

  31. Примеры файлов миграций
    # 0002_20161013_1015.py


    from pony import orm


    dependencies = ["0001_initial"]


    def define_entities(db):

    class Person(db.Entity):

    name = orm.Required(str)

    tel = orm.Optional(str)

    View Slide

  32. Граф миграций
    B
    A
    C
    0001_initial.py
    0002_20161013
    _0922.py
    0002_20161013
    _1015.py

    View Slide

  33. Примеры файлов миграций
    # 0003_20161013_1140.py


    from pony import orm


    dependencies = ["0002_20161013_0922",

    "0002_20161013_1015"]


    def define_entities(db):

    class Person(db.Entity):

    name = orm.Required(str)

    age = orm.Optional(int)

    tel = orm.Optional(str)

    View Slide

  34. Граф миграций
    D
    B
    A
    C
    0001_initial.py
    0002_20161013
    _0922.py
    0002_20161013
    _1015.py
    0003_20161013_1140.py

    View Slide

  35. Примеры файлов миграций
    # 0004_20161013_1140.py


    from pony import orm


    dependencies = ["0003_20161013_1140"]


    renames = {"Person.tel": "Person.phone"}


    def define_entities(db):

    class Person(db.Entity):

    name = orm.Required(str)

    age = orm.Optional(int)

    phone = orm.Optional(str)

    View Slide

  36. Граф миграций
    D
    B
    A
    E
    C
    0001_initial.py
    0002_20161013
    _0922.py
    0002_20161013
    _1015.py
    0003_20161013_1015.py
    0004_20161013_1015.py

    View Slide

  37. Сравнение с Django, SQLAlchemy
    • В Django и SQLAlchemy файл миграции содержит
    перечень операций изменения таблиц
    • В Пони файл миграций содержит не список операций
    изменения, а полный набор сущностей

    View Slide

  38. Сравнение с Django, SQLAlchemy
    • Когда Alembic создает новую автоматически
    сгенерированную миграцию, сравнивается
    содержимое БД с текущими моделями проекта
    • Когда Pony создает новую миграцию, сравниваются
    сущности последней миграции и текущие сущности
    проекта, содержимое БД не анализируется
    • Django как и Пони не анализирует БД, но прежнее
    состояние моделей восстанавливается из всей
    цепочки миграций

    View Slide

  39. Приятная новость!

    View Slide

  40. Теперь Pony ORM распространяется
    под лицензией Apache 2.0!
    То есть Pony полностью бесплатна для любого проекта!

    View Slide

  41. Ближайшие перспективы Pony
    • PEP8
    • Интеграция с фронтендом
    • to_json() для результата запроса
    • PonyJS
    • GraphQL
    • Online редактор как инструмент полного цикла
    разработки

    View Slide

  42. Вступайте в комьюнити Pony ORM
    • hŠps://ponyorm.com
    • hŠps://telegram.me/ponyorm
    • hŠps://github.com/ponyorm/pony
    • #ponyorm
    • @ponyorm

    View Slide

  43. Спасибо!
    Алексей Малашкевич, Александр Козловский
    12 октября 2016
    Q&
    A

    View Slide