Да, я думаю, что это тот случай, когда transaction.atomic()
работает не так, как вы ожидаете.
Чтобы понять, что он делает, вы должны понимать уровни изоляции транзакций SQL и именно то, что поведение они гарантируют. Вы не упоминаете, какую базу данных вы используете, но PostgreSQL имеет хорошую документацию по теме.
Вы ожидаете, что она будет работать так, как если бы уровень изоляции был SERIALIZABLE
. Фактически уровень изоляции по умолчанию в Django равен READ COMMITTED
. И на этом уровне изоляции, если у вас есть две из этих транзакций, работающих одновременно, они обе будут перезаписывать likes_num
одним и тем же номером.
Одним из решений является использование F-объекта вместо установки likes_num
до заданного значения c. В этом случае новое значение будет основываться на любом значении в поле на момент записи, а не на том, какое значение было в поле в более ранней точке при чтении строки.
entry.likes_num = F('likes_num') + 1
Другим решением является использование select_for_update()
, которое заблокирует строку entry
. Лучше избегать блокировок, если вы можете, поэтому я бы выбрал версию F-объекта.