Комментарии 33
Есть ли возможность обернуть в транзакцию набор последовательных операций на уровне ORM?
+2
Да, причем так лучше и делать, а не оборачивать весь view:
docs.djangoproject.com/en/1.7/topics/db/transactions/#controlling-transactions-explicitly
with transaction.atomic():
user1.update_balance(money1)
user2.update_balance(money2)
docs.djangoproject.com/en/1.7/topics/db/transactions/#controlling-transactions-explicitly
0
Не называйте словом ORM модели в Django. ORM – это паттерн програмирования, который _не_ реализован в Django models. Если вы посмотрите официальную документацию, вы нигде не найдете там этого слова. Пожалуйста, не вводите людей в заблуждение.
-8
Действительно. Спасибо за замечание :)
0
Да есть оно там. Вот пример.
Я не спора для, проясните вопрос: почему это не ORM? Дайте, пожалуйста, определение ORM, такое, чтобы Django под определение не попало. Если использовать наивное понимание Object-Relational Mapping, то код в Django именно что выполняет отображение объектов в реляционную БД и обратно. Сами они утверждают, что код в Django — ActiveRecord. Если ActiveRecord не является разновидностью ORM, объясните разницу, пожалуйста.
Я не спора для, проясните вопрос: почему это не ORM? Дайте, пожалуйста, определение ORM, такое, чтобы Django под определение не попало. Если использовать наивное понимание Object-Relational Mapping, то код в Django именно что выполняет отображение объектов в реляционную БД и обратно. Сами они утверждают, что код в Django — ActiveRecord. Если ActiveRecord не является разновидностью ORM, объясните разницу, пожалуйста.
+9
Честно говоря, я не понял что должна была показать мне ваша ссылка.
Поясняю.
ORM призванный решить проблемы «persistence» вашего приложения. Например, выступая прослойкой между БД и приложением, ORM гарантирует, что если приложение в рамках одной сессии несколько раз выбирает один и тот же «объект» из БД, то физически это будет один и тот же объект.
В Django это выглядело бы так:
Заметьте, я ипользовал оператор `is`, а не `==`. Этот принцип выполняется для разных запросов любой сложности, это не просто результат кеширования. Django ничего подобного вам не гарантирует, так что никакой это не object-relational mapping.
То что реализовано в Django – именно что реализация паттерна Active Record, который хоть и можеть быть частью реализацию ORM, самом по себе ORM совершенно не является. Но все привыкли называть это ORM в основном с подачи авторов документаций по PHP фреймворкам, у которых к сожалению не редко возникают проблемы с терминологией.
Из того что мне известно, ORM реализуют:
— SqlAlchemy (а точнее та ее часть, которая называется SqlAlchemy ORM)
— .NET Entity Framework
— Hibernate
Не имеют никакого отношения к ORM и реализуют паттерн Active Record:
— Django Models
— Yii и большинство других PHP фрейморков
Поясняю.
ORM призванный решить проблемы «persistence» вашего приложения. Например, выступая прослойкой между БД и приложением, ORM гарантирует, что если приложение в рамках одной сессии несколько раз выбирает один и тот же «объект» из БД, то физически это будет один и тот же объект.
В Django это выглядело бы так:
>>> entry1 = Entry.objects.get(pk=1)
>>> entry2 = Entry.objects.get(pk=1)
>>> entry1 is entry2
True
Заметьте, я ипользовал оператор `is`, а не `==`. Этот принцип выполняется для разных запросов любой сложности, это не просто результат кеширования. Django ничего подобного вам не гарантирует, так что никакой это не object-relational mapping.
То что реализовано в Django – именно что реализация паттерна Active Record, который хоть и можеть быть частью реализацию ORM, самом по себе ORM совершенно не является. Но все привыкли называть это ORM в основном с подачи авторов документаций по PHP фреймворкам, у которых к сожалению не редко возникают проблемы с терминологией.
Из того что мне известно, ORM реализуют:
— SqlAlchemy (а точнее та ее часть, которая называется SqlAlchemy ORM)
— .NET Entity Framework
— Hibernate
Не имеют никакого отношения к ORM и реализуют паттерн Active Record:
— Django Models
— Yii и большинство других PHP фрейморков
0
Пока что вы меня только убедили в обратном: код в Django — ORM, т.к. сопоставляет объекты и записи в реляционной БД. Другими словами, сопоставление объектов и записей есть? Значит, ORM.
Дайте всё-таки определение ORM, пожалуйста, так, чтобы было понятно, что сравнение объектов должно выполняться не при помощи операторов типа ==, а исключительно с помощью операторов типа is. (И не относящееся к конкретному языку.)
Дайте всё-таки определение ORM, пожалуйста, так, чтобы было понятно, что сравнение объектов должно выполняться не при помощи операторов типа ==, а исключительно с помощью операторов типа is. (И не относящееся к конкретному языку.)
0
А Django models не маппит записи бд в объекты. Если хотите, django models маппит записи в бд в состояние объектов на момент их выборки из бд. Состояния одного и того же объекта могут быть несколькими объектами, в то время как сам объект только один. В этом одно из принципиальных различий, и на самом деле оно вытекает даже из самого названия (и да, я согласен что это совершенно не очевидно на первый взгляд). ORM – это не просто штука, позволяющая работать с данными в таблицах «как с объектами», это именно что биекция данных бд в объектную модель, где каждой записи сопоставлен один и только один объект. В этом смылсе ORM, так же как и БД занимается вопросами консистентности данных и борется с коллизиями на свое уровне абстракции, и она обязана этим заниматся, будучи отображением базы данных в приложеии, в то время как Active Record не занимается этими вопросами вообще, создавая тем самым abstraction leak.
В любом случае, это вопрос определений, и честно говоря, я не хочу продолжать этот спор. В Computer Science eсть много понятий, которые за последнее время сильно исказились. Наверное для вас будет открытием если я скажу, что большинство веб-фреймворков не являются MVC, даже не смотря на то, что их разработчики бьют себя в грудь и утверждают об обратном. Дело в том, что MVC предполагает прямое взаимодействие между моделью и отображением посредством событий, а в большинстве веб-фрейворков ничего подобного нет.
В любом случае, это вопрос определений, и честно говоря, я не хочу продолжать этот спор. В Computer Science eсть много понятий, которые за последнее время сильно исказились. Наверное для вас будет открытием если я скажу, что большинство веб-фреймворков не являются MVC, даже не смотря на то, что их разработчики бьют себя в грудь и утверждают об обратном. Дело в том, что MVC предполагает прямое взаимодействие между моделью и отображением посредством событий, а в большинстве веб-фрейворков ничего подобного нет.
0
НЛО прилетело и опубликовало эту надпись здесь
Еще кое что по транзазакциям и MySQL:
docs.djangoproject.com/en/dev/topics/db/transactions/#using-a-high-isolation-level
Переведите MySQL в read committed
docs.djangoproject.com/en/dev/topics/db/transactions/#using-a-high-isolation-level
Переведите MySQL в read committed
+1
а почему
а не
Profile.objects.\
filter(pk=self.pk)\
.update(balance=F('balance') + balance)
а не
self.balance = F('balance') + balance
0
Потому что потом вы скорее всего напишите
self.save()
а save перезаписывает все данные в базе на текущие значения в модели
self.save()
а save перезаписывает все данные в базе на текущие значения в модели
0
нет, я напишу self.save(update_fields=['balance'])
+1
self.balance = F('balance') + balance
#… тут кто-то в другом процессе тоже обновил этот же баланс
self.save(update_fields=['balance']) тут данные этого кого-то перезапишутся вашими
#… тут кто-то в другом процессе тоже обновил этот же баланс
self.save(update_fields=['balance']) тут данные этого кого-то перезапишутся вашими
0
это не отличается от:
в обоих случаях на уровне SQL будет update set balance = balance + «balance_из_параметра»
#… тут кто-то в другом процессе тоже обновил этот же баланс
Profile.objects.\
filter(pk=self.pk)\
.update(balance=F('balance') + balance)
в обоих случаях на уровне SQL будет update set balance = balance + «balance_из_параметра»
+1
если что, я не докапываюсь, я просто пытаюсь выяснить, может я что-то упустил.
+1
НЛО прилетело и опубликовало эту надпись здесь
И еще:
В некоторых участках когда, возможно, стоит писать так:
Чтобы не допустить, что balance может стать отрицательным
В некоторых участках когда, возможно, стоит писать так:
def update_balance(self, balance):
Profile.objects.select_for_update().\
filter(pk=self.pk, balance=self.balance)\
.update(balance=F('balance') + balance)
Чтобы не допустить, что balance может стать отрицательным
+1
не понятна мысль, выборка идет по primary key, это однозначное определение одной записи, и как ваше дополнительное условие связно с отрицательным балансом?
+1
Фильтр не найдет запись с таким балансом — т.е. в паралелльном запросе у пользователя он изменился по каким-либо причинам, и, возможно, требуется отдельная обработка такой ситуации
+2
очень спорно, он может изменится по вполне легальной причине
+1
На самом деле это очень правильно решение. Не меняйте данные не глядя. Перед тем, как прибавить или вычесть баланс вы же должны были сделать какие-то проверки. Как минимум, что баланс будет не меньше нуля (как было сказано выше). Но могут быть и другие, что пользователь потратил не больше, чем установил лимит, что положил не больше, чем возможно по законодательству. Мало ли. И поэтому, важно не только проверить по текущему значению баланса, что все ок, но и то, что этот баланс не поменялся за время проверок.
Но надо подумать, как это будет сочетаться с транзакциями, возможно они будут тут только мешать.
Но надо подумать, как это будет сочетаться с транзакциями, возможно они будут тут только мешать.
0
Поясните, почему в вашем примере связка F + update не решает проблему?
def update_balance(self, balance):
Profile.objects.filter(pk=self.pk).update(balance=F('balance') + balance)
0
Ожидал большего от такого заголовка и вступления, чем пара пунктов из документации.
Зачем вообще было писать про самый обычный и всеми применимый способ?
Зачем вообще было писать про самый обычный и всеми применимый способ?
+4
Тема дедлоков не раскрыта. Хорошо конечно, когда select… for update на request только один.
Вообще, можно не бороться с конкурентростью, а делегировать все обновления баланса единственнуму инстансу celery worker'a.
Если вы не платежная система с сотней тысяч пользователей, производительности одного worker'a хватит за глаза.
Вообще, можно не бороться с конкурентростью, а делегировать все обновления баланса единственнуму инстансу celery worker'a.
Если вы не платежная система с сотней тысяч пользователей, производительности одного worker'a хватит за глаза.
+1
Ну кто ж так делает-то?!: balance = balance + x
За свой опыт работы с системами, где нужно отслеживать движение финансов я уяснил для себя такую вещь — везде, где финансы есть, нужно использовать отдельную таблицу, где будет отражено любое движение средств, и баланс — это должна быть не циферка в базе, а сумма всех транзакций, которую можно поместить в отдельную view с кешированием, если нужно быстро. Иначе куча проблем потом может быть.
За свой опыт работы с системами, где нужно отслеживать движение финансов я уяснил для себя такую вещь — везде, где финансы есть, нужно использовать отдельную таблицу, где будет отражено любое движение средств, и баланс — это должна быть не циферка в базе, а сумма всех транзакций, которую можно поместить в отдельную view с кешированием, если нужно быстро. Иначе куча проблем потом может быть.
+4
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Модели Django и решение проблем с конкурентным доступом к данным