Безопасное удаление модели Django из базы данных с помощью транзакции - PullRequest
0 голосов
/ 21 января 2011

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

Проблема в том, что при удалении экземпляра с delete() Django не возвращает никакой информации о том, была ли команда успешной или нет. Этот код иллюстрирует проблему:

b0 = Book.objects.get(id=1)
b1 = Book.objects.get(id=1)
b0.delete()
b1.delete()

Только одна из этих двух delete() команд фактически удалила объект, но я не знаю, какая именно. Не выдается никаких исключений, и ничего не возвращается, чтобы указать на успешное выполнение команды. В чистом SQL команда вернула бы количество удаленных строк, и если бы значение было 0, я бы знал, что мое удаление не удалось.

Я использую PostgreSQL со стандартным уровнем изоляции Read Commited. Насколько я понимаю, этот уровень заключается в том, что каждая команда (SELECT, DELETE и т. Д.) Видит снимок базы данных, но следующая команда может видеть другой снимок базы данных. Я считаю, что это означает, что я не могу сделать что-то вроде этого:

# I believe this wont work
@commit_on_success
def view(request):
  try:
    book = Book.objects.get(id=1)
    # Possibility that the instance is deleted by the other request
    # before we get to the next delete()
    book.delete()
  except ObjectDoesntExist:
    # Already been deleted

Есть идеи?

1 Ответ

4 голосов
/ 22 января 2011

Вы можете поместить ограничение прямо в оператор SQL DELETE, используя QuerySet.delete вместо Model.delete:

Book.objects.filter(pk=1).delete()

Это вообще никогда не вызовет запрос SELECT, просто что-то вроде:

DELETE FROM Book WHERE id=1;

Это обрабатывает состояние состязания двух одновременных запросов на удаление одной и той же записи в одно и то же время, но не позволяет узнать, было ли ваше удаление первым. Для этого вам нужно получить необработанный курсор (который django позволяет вам сделать ), .execute () вышеупомянутый УДАЛИТЬ самостоятельно, а затем выбрать атрибут курсора rowcount , быть 0, если вы ничего не удалили.

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