Джанго: Избегание сценария ABA в базе данных и ORM - PullRequest
1 голос
/ 02 декабря 2011

У меня есть ситуация, когда мне нужно обновить голоса за кандидата.

Граждане могут голосовать за этого кандидата, имея более одного голоса на кандидата. то есть один человек может проголосовать 5 голосами, а другой - 2. В этом случае этот кандидат должен получить 7 голосов.

Теперь я использую Django . И вот как выглядит псевдокод

votes = candidate.votes 
vote += citizen.vote 

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

Как можно избежать этого с помощью ORM, например, Django ?

Ответы [ 2 ]

2 голосов
/ 02 декабря 2011

Если это чисто арифметическое выражение, то у Django есть хороший API, который называется F выражений


Обновление атрибутов на основе существующих полей

Иногда вам нужно выполнить простую арифметическую задачу в поле, например, увеличить или уменьшить текущее значение.Очевидный способ добиться этого - сделать что-то вроде:

>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold += 1
>>> product.save()

Если старое значение number_sold, полученное из базы данных, было 10, то значение 11 будет записано обратно в базу данных.

Это можно немного оптимизировать, выражая обновление относительно исходного значения поля, а не как явное присвоение нового значения.Django предоставляет выражения F () для выполнения такого рода относительного обновления.Используя выражения F (), предыдущий пример будет выражен как:

>>> from django.db.models import F
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold = F('number_sold') + 1
>>> product.save()

Этот подход не использует начальное значение из базы данных.Вместо этого он заставляет базу данных выполнять обновление, основываясь на текущем значении на момент выполнения save ().

После сохранения объекта необходимо перезагрузить объект, чтобы получить доступ к фактическомузначение, которое было применено к обновленному полю:

>>> product = Products.objects.get(pk=product.pk)
>>> print product.number_sold
42
0 голосов
/ 02 декабря 2011

Возможно, select_for_update Метод QuerySet будет полезен для вас.

Выдержка из документов:

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

Обычно, если другая транзакция уже получила блокировку для одной из выбранных строк, запрос будет блокироваться до тех пор, пока блокировка не будет снята.Если это не то поведение, которое вам нужно, вызовите select_for_update (nowait = True).Это сделает вызов неблокирующим.Если конфликтующая блокировка уже получена другой транзакцией, DatabaseError будет вызываться при оценке набора запросов.

Помните, что это доступно только в версии разработки Django (т. Е.> 1.3).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...