Hello there,
I have recently updated to django 1.7, from 1.5 and one of the main changes
I had to do was to replace all deprecated transaction.commit_manually to
transaction.atomic, so far so good.
After this, I have found out that if an IntegrityError or DatabaseError
exception happen inside a code that is decorated with @transaction.atomic
(or inside a with transaction.atomic) and the exception is handled (not
throwing it out from the atomic block) the whole transaction gets invalid,
and any other database access will fail, just as described in the docs
(silly me I didn't read them with enough depth).
As mentioned in the docs, the solution is to also put the "exception
throwing code" inside another atomic block, and catch the exception outside
of it. I can not describe how anoying this is, compared to the old
behaviour where I could easly decide when to commit or rollback, now I have
to review my whole code to detect the places where a database save is
performed and the exception is handled, and add another atomic block to it.
I believe this was an issue heavily discussed with the develpers and they
came to this as the best option, but there needs to be another easier way
to handle this kind of issue.
What are the complications of leaving the transaction in a correct state
even if an operation raises a database error and the exception is handled
silently (not thrown outside of the atomic block)? This was totally
possible with the deprecated transaction functions, where you could do all
your logic and just at the end handle the transaction commit or rollback,
and didn't matter what happened inside of it. But now this is imposible,
you need to keep a sharp eye on every database save you perform and
surround it with another atomic block just in case it raises a database
error exception.
To make the issue clear, here's a sample code:
First one shows what would my current code look like, where add_children()
would raise an exception because generate_relationships() was not inside
another atomic block.
With the old transactions api, I could easily surround the whole code in
another try/except, and at the very end commit or rollback, and everything
would be fine even if generate_relationships() throws an exception, it
would be siltently ignored.
from django.db import IntegrityError, transaction
@transaction.atomicdef viewfunc(request):
create_parent()
try:
generate_relationships()
except IntegrityError:
handle_exception()
add_children()
Now this is how the code should actually be with current django 1.7 in
order to prevent an error and get the excepted behaviour.
from django.db import IntegrityError, transaction
@transaction.atomicdef viewfunc(request):
create_parent()
try:
with transaction.atomic():
generate_relationships()
except IntegrityError:
handle_exception()
add_children()
It would be great, if the transaction api could work as the first code,
with the results of the second one. Meaning, even if
generate_relationships() raises an exception, and it is handled correctly,
the transaction would still be valid to be used.
Thanks!
--
You received this message because you are subscribed to the Google Groups
"Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/e347fe6d-78c8-4c15-848d-3a82415c3550%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.