2011-01-21 7 views
0

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

Любые идеи?

ответ

4

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

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

Это никогда не будет выдавать запрос SELECT, у всех, только что-то вдоль линий:

DELETE FROM Book WHERE id=1; 

это обрабатывает состояние гонки два одновременных запросов удаления ту же запись в то же самое время, но это не дает вам знать, является ли есть там ваше первое удаление. Для этого вам нужно будет получить необработанный курсор (который django lets you do), .execute() выше DELETE самостоятельно, а затем поднимите атрибут rowcount курсора, который будет равен 0, если вы не закончите удаление чего-либо.