Pull to refresh

Comments 46

Маленькая, клевая, очень удобная.
Отмечу пару моментов:
— весь орм в одном файле, почти 3к строк (на дворе 2013 г.)
— расширяемость под вопросом, т.к. QueryCompiler вызывает методы поля db_value и python_value для каждого объекта — если подсунуть ему итератор, то разберет и вызовет эти методы для каждого элемента. Помню, что пытался использовать постгресовские массивы да нифига не вышло: отдаешь ему [0, 1, 2], а он тебе [conv.db_value(i) for i in params] — список просто не доходит целиком до обработки в филде, следовательно надо переписывать компилятор, а это уже сама ормка. Хотя может я не разобрался как следует.
Исходники, да ещё и в одном модуле (ну кто так делает) меня смутили. Хотя не видел ещё ORM с нормальным кодом под капотом, везде макароны и магия.
В закладки, с нетерпением жду миграций.
P.S: Боюсь представить, каким количеством магии реализовано это
  .where((Person.birthday > d1940) & (Person.birthday < d1960))

Респект.
Так же как и в SQLAlchemy — перегрузкой операторов
С миграциями пока туго, так что — либо ручками, либо пишем свой модуль и выкладываем его в народ)
Как вариант — ссылка ниже.
UFO just landed and posted this here
Currently peewee does not have support for automatic schema migrations

Руками, как я понимаю.
Есть простой мигратор. Модуль, прямо скажем, так себе, но для простых случаев подойдёт.

Если хочется что-то вроде south, придётся написать самому.
Хорошая альтернатива Алхимии, как считаете?

Сейчас использую в pyhon в качестве orm ponyorm. А то я как человек избалованный Spring Data становлюсь грустной пандой от DSL в других python orm
Чем же вам не понравилась алхимия? Самая мощная Python ORM, да DSL у нее очень даже ничего
джанга и алхимия — тормозные, увы
DSL у него отстой. Я так в java два года назад писал. Сейчас пишу совсем по другому. И в этом плане ponyorm существенно лучше чем Алхимия. Я сначала то использовал алхимию, но на уровне SQL, а вот когда я захотел ORM… и я туда посмотрел, то мне захотелось помыть глаза с хлоркой. Такой подход еще в статическом языке как-то оправдан в динамическом нет.
Интересно, я уже давно слежу за PonyOrm, но мне почему-то наоборот казалось что оно настолько похоже на алхимию что мигрировать с одного на другое смысла нет. Единственное в чем я вижу разницу так это в способах создания запросов — генераторы в пони против обычных выражений типа: .filter((Person.birthday > d1940) & (Person.birthday < d1960)). Во всем всем остальном DSL почти 1 в 1 как алхимии, судя по докам.
Интересно, я уже давно слежу за PonyOrm, но мне почему-то наоборот казалось что оно настолько похоже на алхимию что мигрировать с одного на другое смысла нет.

Вы там DSL точно смотрели? Давайте обратимся к вот этому посту habrahabr.ru/post/188842/

SQLAlchemy
 u = session.query(AUser).filter(AUser.id==i+1)[0]


Pony ORM
 u = select(u for u in PUser if u.id==i+1)[:1][0]


Кстати можно написать даже лучше
 u = select(u for u in PUser if u.id==i+1).get()


И да как только потребуется написать более менее сложный запрос, портянка из функций будет все длиннее и длиннее и выглядит это не опрятно и читается еще хуже. В ORM java такое тоже было не так давно. Сейчас есть такая прекрасная штука как Spring Data

Которая позволяет делать так:
public interface UserRepository extends Repository<User, Long> {

  List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}


И так
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.emailAddress = ?1")
  User findByEmailAddress(String emailAddress);
}


Плюс PonyORM быстрее работает.
Ну, спорить не буду, как говорится на вкус и на цвет фломастеры разные. Но в SQLAlchemy цепочки методов сделаны не просто так, а для того что бы можно было программно конструировать разные запросы в разных частях приложения, в отличии от Pony где вы один раз написали генератор и все. Ну и то что все делается напрямую через сессию, которая не скрыта за какими-то декораторами для функций, тоже плюс, так как это питон и явное лучше неявного. Я тоже плевался когда переходил с Джанги, но как оказалось я просто не умел готовить алхимию. Сейчас работаю с Node.js и с грустью скучаю о алхимии.
Но в SQLAlchemy цепочки методов сделаны не просто так, а для того что бы можно было программно конструировать разные запросы в разных частях приложения, в отличии от Pony где вы один раз написали генератор и все.

Эм. Поясните мне про что речь. Обычно если вы уж написали последовательный вызов функций в тексте, то так он и будет. Как вы его там менять собираетесь мне право интересно.

Ну и то что все делается напрямую через сессию, которая не скрыта за какими-то декораторами для функций, тоже плюс, так как это питон и явное лучше неявного.

В ponyorm скрыта сессия? Где?
Эм. Поясните мне про что речь. Обычно если вы уж написали последовательный вызов функций в тексте, то так он и будет. Как вы его там менять собираетесь мне право интересно.

Так я именно про это и говорю — алхимия позволяет не писать последовательный вызов функций в одном месте. В одном месте мы можем сделать JOIN, во втором — второй JOIN, в третьем мы можем опять таки программно сконструировать фильтр, а в четвертом применить сортировку. Это же обычные методы, как мы захотим так их и скомбинируем. Это позволяет разбивать и выделать общие запросы и на этом и основывается вся работа в алхимии — вы создаете классы которые уже имеют все нужные вам реляции (с джоинами или без) которые затем просто фильтруются или на их основе делаются более сложные запросы.

В ponyorm скрыта сессия? Где?

Вот и я не знаю где, все что я вижу это глобальные функции типа commit и rollback которые на самом деле где-то управляют сессией но саму сессию я не вижу, и это меня смущает.
Так я именно про это и говорю — алхимия позволяет не писать последовательный вызов функций в одном месте. В одном месте мы можем сделать JOIN, во втором — второй JOIN, в третьем мы можем опять таки программно сконструировать фильтр, а в четвертом применить сортировку. Это же обычные методы, как мы захотим так их и скомбинируем.

Эм. У меня дурацкий вопрос. Что мешает это делать в случае ponyorm? Вызов там ровно точно такой же. Вызвали select написали что надо оно сгенерило.

Это позволяет разбивать и выделать общие запросы и на этом и основывается вся работа в алхимии — вы создаете классы которые уже имеют все нужные вам реляции (с джоинами или без) которые затем просто фильтруются или на их основе делаются более сложные запросы.

Эм… а кто вам сказал что реляций в ponyorm то нет?

Вот и я не знаю где, все что я вижу это глобальные функции типа commit и rollback которые на самом деле где-то управляют сессией но саму сессию я не вижу, и это меня смущает.

Официальная дока:

Another option for working with the database is using db_session as a context manager instead of a decorator:
with db_session:
    p = Person(name='Kate', age=33)
    Car(make='Audi', model='R8', person=p)
    # commit() will be done automatically
    # transaction cache will be cleared automatically
    # the database connection will be returned to the pool



Я уж не знаю что там вам еще надо.
Эм. У меня дурацкий вопрос. Что мешает это делать в случае ponyorm? Вызов там ровно точно такой же. Вызвали select написали что надо оно сгенерило.

Ну как мне сделать примерно так?
class User(db.Model):
  id =  db.Column(db.Integer)
  verified_transactions = db.relationship(
     Transaction,  primaryjoin=lambda: (Transaction.user_id == User.id) &
                                       (Transaction.verified == True)
  )
  unverified_transactions = db.relationship(
     Transaction, primaryjoin=lambda: (Transaction.user_id == User.id) &
                                      (Transaction.verified == False)
 )

class Transaction(db.Model):
  id =  db.Column(db.Integer)
  type = db.Column(db.String(32))
  amount = db.BigInteger() 
  user =  db.Column(db.Integer, db.ForeignKey(User.id))

def filter_txs_by_type(query, type)
   return query.filter(Transaction.type == type)

def filter_txs_by_amount_gt(query, amount):
   return query.filter(Transaction.amount > amount)

all_verified_txs = User.verified_transactions
verified_received_txs  = filter_txs_by_type(all_verified_txs, 'RECEIVE')
verified_received_txs_gt_1000 = filter_txs_by_amount_gt(verified_received_txs, 1000)   

Это конечно немного наигранный пример, но в целом показывает именно то что я хочу описать — программное конструирование запросов
Эм… а кто вам сказал что реляций в ponyorm то нет?

Они там есть, но не такие мощные как в алхимии

Официальная дока:

Ну вот вы и привели пример глобальных функций которые сами управляют сессией где-то у себя про что я собственно и говорю
Это конечно немного наигранный пример, но в целом показывает именно то что я хочу описать — программное конструирование запросов

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

Они там есть, но не такие мощные как в алхимии

Ну да бывает что хочется использовать связи с дополнительными фильтрами, но это в ORM вообще довольно редкая фича. И я не могу сказать, что это вызывает серьезные проблемы.

Ну вот вы и привели пример глобальных функций которые сами управляют сессией где-то у себя про что я собственно и говорю

В смысле управляют сессией? Сессия да глобальная, но вызывать где надо commit это совершенно не мешает. Единственное но когда это может мешать это два подключения к РСУБД, но это надо автора пытать :) Пока к сожалению документация освещает далеко не все.
Первое что я могу сразу сказать, что вот такой код я сразу прошу переписать, без обид. Вы понимаете что делаете? Для того чтобы понять что в итоге выбирается надо залезть в два разных места. Теряется прозрачность за счет этого весьма просто что-то сломать да так что еще и причина будет не очевидна. По этой причине лучше запрос так не дробить.

Очень-очень странное заявление, если например у нас есть список транзакций и у транзакции есть тип, статус, размер и дата, и нам нужно сделать фильтр по всем этим параметрам не будете же вы писать кучу запросов для каждого варианта. Более того, я могу с уверенностью сказать что любой нормальный проект на SQLAlchemy использует эти возможности и это абсолютно никого не смущает, а наоборот делает код проще и понятнее:
github.com/mitsuhiko/bf3-aggregator/blob/master/bf3.py#L586 bitbucket.org/danjac/newsmeme/src/43c4760f94a1308abce92661084b302ca229d7fe/newsmeme/models/posts.py?at=default#cl-45
То же самое относится и к реляциям, фильтрованным или нет — это отнюдь не редкая, а наоборот — самая и наиболее часто используемая фича в алхимии, это же в конце концов ORM, а не что-то там еще. Я просто не представляю как можно писать ручками каждый запрос если можно разбить все — вынести реляции, вынести часто используемые фильтры и группировки и зачем просто комбинировать их по мере надобности.
Очень-очень странное заявление, если например у нас есть список транзакций и у транзакции есть тип, статус, размер и дата, и нам нужно сделать фильтр по всем этим параметрам не будете же вы писать кучу запросов для каждого варианта.

Ничего странного. Просто на выходе все равно будет генерация sql запроса. Я понимаю что DRY и все дела, но вот в этом я бы не советовал.

То же самое относится и к реляциям, фильтрованным или нет — это отнюдь не редкая, а наоборот — самая и наиболее часто используемая фича в алхимии

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

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

Запросто при нормальном DSL. В случае DSL как в Alchemy это выглядит как крокодил, по этой причине и начинают делать сокрытие кода и думать как писать лучше, в итоге получаем более сложный код. С дополнительными уровнями сокрытия где и что вызывается.

Молодец. Порекламировался? Статья была вообще-то про Peewee, один из самых легковесных ORM.

Немного низковато использовать чужой бренд, чужие имена для саморекламы, вы не находите? Неужели так плохо с поклонниками PonyORM, что приходится прибегать к таким методам рекламы? Может, просто, людям и нафиг не нужен этот непривычный синтаксис построения запросов? Может у них другие требования к коду, — не задумывались?

Когда мне Гугл выдает по запросу «Python ORM» первой строкой эту мальчишескую подделку ponyorm, и только потом показывает серьезный и глубоко продуманный труд SQLAlchemy, который завоевал действительно широкую аудиторию, я начинаю понимать смысл пословицы «Хороший товар продает себя сам», к которой так и хочется добавить «и не нуждается в услугах SEO-оптимизаторов».
Молодец. Порекламировался? Статья была вообще-то про Peewee, один из самых легковесных ORM.

Учитывая что к ponyorm я имею отношение только как пользователь, то весьма странное заявление. Я описываю свой опыт работы. И то что я бы хотел видеть. Если вам что-то не нравится это явно ваши проблемы.

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

По моему немного низковато, начитать наезды. Причем мы тут опытом делимся что как и где. И да многое мне в PonyORM не нравится, о чем я так же писал автору. Если хотите могу и тут рассказать что с моей точки зрения там плохо.

Может, просто, людям и нафиг не нужен этот непривычный синтаксис построения запросов? Может у них другие требования к коду, — не задумывались?

Тогда такие люди возьмут другие средства. Почему мне не удобно я указывал.

Когда мне Гугл выдает по запросу «Python ORM» первой строкой эту мальчишескую подделку ponyorm, и только потом показывает серьезный и глубоко продуманный труд SQLAlchemy

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

— Процитируете?

> и рекламируете SQLAlchemy

— Не рекламирую, а отмываю от грязи. Поскольку реклама Pony ORM построена на обгаживании SQLAlchemy так же как и Peewee.

Peewee не может быть популярным ввиду своего узкого предназначения. Разработчики ставили цель создать легковесный клон Django ORM, и с этой задачей они справились успешно. Уже потом у проекта появилась своя философия, и они уже далеко ушли от клона Django ORM, но все равно они позиционируются как легковесное решение.

Но в отличии от Peewee, SQLAlchemy очень популярен, и имеет довольно хорошие отзывы весьма авторитетных Питонщиков, вот только один из них (python core developer) asvetlov.blogspot.com/2008/09/sqlalchemy-vs-sqlobject.html

В жизни не поверю, что Pony ORM настолько стал популярным, что оттеснил SQLAlchemy на вторую позицию в выдаче Гугла. Из чего просто напрашивается вывод, что в ход пошли манипуляции SEO.
— Процитируете?

Цитирую

Когда мне Гугл выдает по запросу «Python ORM» первой строкой эту мальчишескую подделку ponyorm

Но в отличии от Peewee, SQLAlchemy очень популярен, и имеет довольно хорошие отзывы весьма авторитетных Питонщиков, вот только один из них (python core developer) asvetlov.blogspot.com/2008/09/sqlalchemy-vs-sqlobject.html

Я отрицаю что он популярен?

В жизни не поверю, что Pony ORM настолько стал популярным, что оттеснил SQLAlchemy на вторую позицию в выдаче Гугла. Из чего просто напрашивается вывод, что в ход пошли манипуляции SEO.

Есть такое понятие как релевантность. Достаточно посмотреть что написано в описании и понять почему SQLAlchemy на втором месте. Потому что вы вводите Python ORM причем в случае PonyORM сразу в тексте написано:
Pony is a cool and new Python ORM that lets you query a database using Python generators.

А у SQLAlchemy:
SQLAlchemy is the Python SQL toolkit and Object Relational Mapper that gives
SQLAlchemy is most famous for its object-relational mapper (ORM)


Если ведем в яндексе то на первом месте получим вообще на wiki питона про ORM. Ну и да объясните зачем это авторам PonyORM такое SEO?
> Причем мы тут опытом делимся что как и где.

— Мы тут обсуждаем Peewee. У Вас есть опыт использования Peewee? Тогда расскажите о нем. А иначе Вы не опытом делитесь, а просто присасываетесь к чужому труду (автора статьи, разработчиков Peewee), для продвижения собственных интересов и элементарной рекламы.

Разрабатывали Вы Pony ORM, проплачены Вы ими, или просто добровольный рекламатор, — это сути дела не меняет.
— Мы тут обсуждаем Peewee. У Вас есть опыт использования Peewee? Тогда расскажите о нем. А иначе Вы не опытом делитесь, а просто присасываетесь к чужому труду (автора статьи, разработчиков Peewee), для продвижения собственных интересов и элементарной рекламы.

ЕЩЕ РАЗ ДЛЯ ТЕХ КТО В ТАНКЕ! Я не имею никакого отношения к разработке PonyORM или его продвижению. Я ПРОСТО ИСПОЛЬЗУЮ ЕГО В СВОИХ СКРИПТАХ! И ОПИСЫВАЮ СВОЙ ОПЫТ! А указывать что и как мне писать будете на своем ресурсе. Этот публичный ресурс и не вам указывать мне что и где мне писать.

Разрабатывали Вы Pony ORM, проплачены Вы ими, или просто добровольный рекламатор, — это сути дела не меняет.

Еще как меняет. Во первых я ничего не рекламирую, а делюсь опытом. И я прежде чем взять PonyORM в использование посмотрел все популярные ORM под Python. И я привожу четкие примеры почему я считаю его лучше. И вот вам как «защитнику» Peeweee абсолютно не делают чести методы его защиты. Фактически вместо того чтобы сказать вот это там лучше, а это там хуже и поэтому тут лучше использовать Peewee вы пришли и заочно без каких либо разбирательств обвинили меня в очернении других ORM и рекламе PonyORM.
РЕЛЕВАНТНОСТЬЮ! В случае PonyORM указано Python ORM на центральной странице. И да никто не ищет PonyORM по этому запросу. По одной простой причине, он не является настолько популярным и настолько на слуху как sqlalchemy вполне очевидно, что sqlalchemy ищут по его ключевому слову. И да по вашей логике Peewee накручивает в яндексе. Так-как он там на третьей строчке а sqlalchemy на первой странице вообще нет.

Прежде чем делать такие громкие заявления, разберитесь как работает поиск. Вы еще меня давайте обвините, что мой сайт openembedded.ru находится на первой странице поиска по фразе openembedded потому что я накручиваю SEO.
> РЕЛЕВАНТНОСТЬЮ!

— когда-то это называлось просто поисковой оптимизацией текста и семантическим ядром. А может и накруткой PR, — не знаю, мне это не интересно. Я не разбирался какие именно методы оптимизации имели место. Но я знаю, что хороший товар продает себя сам.

> что мой сайт openembedded.ru находится на первой странице поиска по фразе openembedded потому что я накручиваю SEO.

— а что, слово openembedded так часто ищут в поисковиках? Не льстите себе…
не знаю, мне это не интересно. Я не разбирался какие именно методы оптимизации имели место. Но я знаю, что хороший товар продает себя сам.

Тогда будьте добры расскажите что плохо в PonyORM. Кроме обвинений вида это мальчишеская поделка и они накручивают Google я никакого конструктива не увидел.

— а что, слово openembedded так часто ищут в поисковиках? Не льстите себе…

Я тоже могу задать вопрос «А что Python ORM так часто ищут в поисковиках?». И воспользуюсь тем же инструментом что пользовались вы www.google.com/trends/explore#q=Python+ORM,sqlalchemy
Как видим фразу Python ORM в google ищут существенно реже чем SQLAlchemy. В итоге имеем тот же результат что и про openembedded. Ну а себе я льстить не собираюсь.
А есть аналог джангового unique_together? и как насчет составных ключей для индексов?
Нет. Видимо я не разбираюсь в терминологии. Просто индекс по нескольким полям. Наверное это то, что нужно:
create_index(model_class, fields[, unique=False])

Правда это делается не в определении модели.
Не знаю, насколько правильно понял вопрос.
Можно проставлять обычные индексы и с unique constraint на одно или несколько полей.
Составные ключи по индексам прописываются в class Meta, как в ссылке выше.
Насчет unique_together, попробуйте:
class Meta:
        indexes = (
            (('field1', 'field2'), True),
        )

Второй элемент кортежа это значение для атрибута unique.
Не уверен именно ли это together или для каждого поля по-отдельности. Вместе было бы логичнее, но нужно пробовать.
Чего только не придумают, что бы не пользовать SQL. Я понимаю, хочется, что бы было проще. Но в результате, код, по моему, ужасен (имеется ввиду не примеры типа hello world а реальные). Так и хожу вокруг да около: вроде как все используют (ORM), а как гляну на очередной «легкий и гибкий» — понимаю, это не мое, не созрел видимо
ORM – это уровень абстракции, со всеми вытекающими, в виде очень ускоренной разработки, отсутствия необходимости переключаться между языками в проекте, отсутствия необходимости помнить об особенностях каждого sql-движка и т.п. Не без компромиссов, как и у любой абстракции.

На типовых веб-проектах вся мощь sql нужна крайне редко. На большинстве сайтов оверхед орм никогда не станет узким местом, зато времени и денег на разработку сэкономит изрядно. Говорить о том, что скуль лучше, только потому что не осилили орм (и наоборот) некорректно, всему свой инструмент.
Простите за глупый вопрос: возможно ли сделать так:

Добавим например боба и добавим ему в качестве домашнего животного другого боба(со своими животными).

Если я сделаю так:
class Person(Model):
name = CharField()

class Person2(Model):
owner = ForeignKeyField(Person, related_name='person2')
name = CharField()

то мне придёться создать over 9000 таких Person[] (если их действительно сможет оказаться столько)
Хотелось бы иметь некую иерархию, но такую в которой мне не известны заранее кто у кого являеться домашним животным и при этом некоторые из них могут иметь теже свойства что и самый первый Person.

тоесть получаеться что-то вроде:

class Person(Model):
owner = ForeignKeyField(Person, related_name='person')
name = CharField()

но так не работает :(

А у меня ещё один глупый вопрос:
возможно ли при изменении модели изменить её и в «базе» не удаляя саму базу?
Просто сейчас я ещё разрабатываю будущею структуру базы, но если мне вдруг захочется изменить что-то когда у меня будет ОЧЕНЬ много записей — как мне поступить?
То что вам хочется, называется «миграции». Конкретно в Peewee они есть, но не сильно мощные.
Сначала было повелся на вашу статью и решил попробовать Peewee,
но потом решил погулить как с ее помощью сделать query с sum
и не нашел. Это пугает.
Sign up to leave a comment.

Articles