2013/9/19 Richard Ward <[email protected]>

> So what is the problem here? I assume it is one of:
>

>    1. 'innocent_looking_function' is badly written: it should not be
>    catching IntegrityErrors under any circumstances (though that seems like a
>    valid thing to do), it should instead use something like get_or_create.
>
>
It's possible to catch IntegrityErrors. The correct pattern — regardless of
function calls — is:

try:
    with transaction.atomic():
        do_stuff()
except IntegrityError:
    # transaction.atomic has already performed the rollback at this point
    handle_error()

It's documented:
https://docs.djangoproject.com/en/dev/topics/db/transactions/#django.db.transaction.atomic

>
>    1. 'innocent_looking_function' should have 'with
>    transaction.atomic():' just inside the 'try' block, and presumably so
>    should every other bit of code where an IntegrityError is caught. But what
>    if 'innocent_looking_function' also gets used elsewhere that may not be
>    inside an atomic block?
>
> Indeed that would implement the pattern shown above and resolve the
problem.

>
>    1. transaction.atomic is in some way buggy / oddly designed.
>
> As the designer of this API, I say: suggestions welcome :) Read below
before jumping to the drawing board, though.

> IMO this behaviour makes code that looks valid (to me) not work in the
> expected way.
>
Catching IntegrityError inside transaction.atomic is wrong because it hides
the exception from transaction.atomic.

As a consquence, transaction.atomic believe that everything went well and
merrily commits. But the database has a broken transaction in progress; it
can't commit.. transaction.atomic fails to commit, rolls back, and raises
an exception — which isn't the original IntegrityError, adding to confusion.

> In case its relevant I'm using InnoDB tables on MySQL.
>
You might have received more meaningful errors with another database
engine, but overall you've described the intended, cross-database behavior
of transaction.atomic.

> So Is there a bug? Or am I using this incorrectly, or misunderstanding
> something?
>
The code works as designed. It relies on exceptions to distinguish failure
from success. This is the cleanest way to implement transactions as a
context manager. If you swallow the exception, you make a failure look like
a success, and things go wrong.

The docs talk a lot about exceptions but they don't warn specifically about
this pitfall. It would be a worthy addition. Can you file a ticket?

Thank you,

-- 
Aymeric.

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" 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.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to