Вы хотите избежать условий гонки. F()
объекты - это только первый шаг в этом.
Первое, что вы хотели бы сделать в своей транзакции, - это получить блокировку в строке, которую вы меняете. Это предотвращает чтение устаревшего значения, когда вы пишете в базу данных. Это может быть сделано с обновлением ряда, который заблокирует строку из дальнейших обновлений, пока транзакция не будет зафиксирована или откат:
with transaction.atomic():
obj.contacts = F('contacts') - quantity
Если у вас есть замок, и сделал обновление, проверьте целостность данных (т. е. количество контактов не ниже 0). Если, по-прежнему, в противном случае, откатить транзакцию, вызывая исключение:
obj.refresh_from_db()
if obj.contacts < 0:
raise ValueError("Capacity exceeded")
Если есть осталось достаточно контактов, вы будете выйти из atomic()
блок в этой точке, то сделка совершается, и другие запросы могут приобретите блокировку и попробуйте обновить значение. Если контактов недостаточно, транзакция откатывается назад, а другие запросы никогда не узнают, что значение изменилось, поскольку они ожидали получить блокировку.
Теперь, чтобы поставить все это вместе:
from django.db import transaction
from django.db.models import F
def update_contacts(obj, quantity):
with transaction.atomic():
obj.contacts = F('contacts') - quantity
obj.save()
obj.refresh_from_db()
if obj.contacts < 0:
raise ValueError("Not enough contacts.")
(Примечание:. obj.refresh_from_db()
требует 1,8, в противном случае просто используйте MyModel.objects.get(pk=obj.pk)
)
AFAIK нет никакого способа определения такого ограничения, но вы могли бы сделать проверка транзакции ('@ transaction.atomic'). Существует также [эта библиотека] (https://code.google.com/p/django-check-constraints/wiki/Features), но она действительно старая. – Ivan
@Ivan, Спасибо за это. Если я использовал декоратор '@ transaction.atomic', тогда мне не нужно было бы использовать выражение' F() ', правильно? –
Вы уже все извлекли, так что нет. – Ivan