2016-01-11 14 views
7

Próbuję utworzyć widok, w którym zapisuję obiekt, ale chciałbym, aby cofnąć, które można zapisać, jeśli zgłoszony zostanie wyjątek. Oto, co próbowałem:Django - Cofnij zapis z transakcją atomową

class MyView(View): 

    @transation.atomic 
    def post(self, request, *args, **kwargs): 
     try: 
      some_object = SomeModel(...) 
      some_object.save() 

      if something: 
       raise exception.NotAcceptable() 
       # When the workflow comes into this condition, I think the previous save should be undome 
       # Whant am I missing? 

     except exception.NotAcceptable, e: 
      # do something 

Co robię źle? nawet gdy wyjątek zostanie podniesiony, some_object nadal znajduje się w bazie danych.

Odpowiedz

11

Atomicity Documentation

Podsumowując, @transaction.atomic spowoduje wykonanie transakcji w bazie danych, jeśli widok wygeneruje odpowiedź bez błędów. Ponieważ sam chwytasz wyjątek, Django wygląda na to, że twój widok został poprawnie wykonany.

Jeśli złapać wyjątek, trzeba obsłużyć siebie: Controlling Transactions

Jeśli trzeba wytworzyć właściwą odpowiedź json w przypadku awarii:

from django.db import SomeError, transaction 

def viewfunc(request): 
    do_something() 

    try: 
     with transaction.atomic(): 
      thing_that_might_fail() 
    except SomeError: 
     handle_exception() 

    render_response() 
+0

Ten widok dotyczy interfejsu API, więc myślę, że muszę obsłużyć każdy możliwy błąd, aby dać odpowiedź json proppera. Czy nie można tego zrobić z dekoratorem atomowym? – Gocht

+0

Nie z dekoratorem imho, ponieważ obsługuje transakcję poza twoją funkcją. Dobry przykład z menedżerem kontekstu! – jpic

+0

Musisz mieć blok atomowy wewnątrz try .. z wyjątkiem bloku, jak w odpowiedzi. Możesz także użyć dekoratora atomowego na widoku, jeśli chcesz. – Alasdair

5

Jeśli jednak wyjątek dzieje w funkcji ozdobione transaction.atomic, to nie ma nic wspólnego, to będzie rollback automatically to the savepoint created by the decorator before running the your function, jak documented: umożliwia

atomowy nam stworzyć blok kodu, w ramach którego gwarantowana jest atomowość bazy danych. Jeśli blok kodu zostanie pomyślnie zakończony, zmiany zostaną zatwierdzone do bazy danych. W przypadku wyjątku zmiany zostaną wycofane.

Jeśli wyjątek jest przechwycony w sposób wyjątkiem bloku, to należy ponownie podniesiony atomowy go złapać i zrobić rollback, tj .:

try: 
     some_object = SomeModel(...) 
     some_object.save() 

     if something: 
      raise exception.NotAcceptable() 
      # When the workflow comes into this condition, I think the previous save should be undome 
      # Whant am I missing? 

    except exception.NotAcceptable, e: 
     # do something 
     raise # re-raise the exception to make transaction.atomic rollback 

Ponadto, jeśli chcesz mieć większą kontrolę można cofnąć ręcznie previously set savepoint, tj .:

class MyView(View): 
    def post(self, request, *args, **kwargs): 
     sid = transaction.savepoint() 
     some_object = SomeModel(...) 
     some_object.save() 

     if something: 
      transaction.savepoint_rollback(sid) 
     else: 
      try: 
       # In worst case scenario, this might fail too 
       transaction.savepoint_commit(sid) 
      except IntegrityError: 
       transaction.savepoint_rollback(sid) 
+0

to co ja, choć i to dlaczego zrobiłem tę funkcję w ten sposób, ale jak powiedziałem w pytaniu, obiekt jest nadal w zdarzeniu bazy danych, gdy wyjątek jest podniesiony, czy jest jakiś dodatkowy krok do korzystania z punktów zapisu? – Gocht

+0

Może dlatego, że wyjątek został przechwycony przez blok wyjątkowy, a nie przebudzony, więc atomowy pomyślał, że to funkcja wykonana z sukcesem. – jpic

+0

Jeśli nie złapię błędu, nie mogę dać dobrej odpowiedzi. Jak mogę zbudować swoją funkcję? – Gocht