#26323 closed Bug (duplicate)
TransactionManagementError is raised when autocommit is false
Reported by: | Tore Lundqvist | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
TransactionManagementError: "An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block." is raised when you try to used a database connection after a database exception even those autocommit was set to false from the start. It should be up to the user how to handle the database exception and the transaction as autocommit was set to false.
Change History (11)
comment:1 by , 9 years ago
follow-up: 4 comment:2 by , 9 years ago
When you say "autocommit was set to false from the start", are you referring to settings.DATABASES['default']['AUTOCOMMIT'] = False
? Can you provide a test case or sample code demonstrating the problem?
comment:4 by , 9 years ago
Replying to aaugustin:
No, I run a management command and start it with set_autocommit(False)
but that might give the same end result in this case. I looked for tests for similar things in Django to have something to start from but could not find any. I need to fake a database exception to be able to make a good test case for this I think.
follow-up: 6 comment:5 by , 9 years ago
Can you check the note titled Avoid catching exceptions inside atomic?
Can you share a code sample?
comment:6 by , 9 years ago
Replying to aaugustin:
Can you check the note titled Avoid catching exceptions inside atomic?
I'm not in a atomic block when handling the exception, no atomic blocks are used in the code. But deep down in Django atomic blocks are used anyway and thats where need_rollback
is set. e.g. in save_base()
/Users/torel/work/djangoproject/django/django/db/models/base.py:725
Can you share a code sample?
I can't share the actual code and it would not be very useful anyway I think. I have to write a test somehow, could you point me to a test I could use to get started?
comment:7 by , 9 years ago
Component: | Uncategorized → Database layer (models, ORM) |
---|
tests/transactions
seems like a natural place.
comment:8 by , 9 years ago
Thanks,
I added a test that shows the problem to the PR. If you comment out the fix and run the test with mysql you get the exception.
comment:9 by , 9 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
Thanks for providing this example. Now I can see what you're talking about.
Django's transaction management is specifically designed to prevent the following pattern, which appears in your test:
- start a transaction
- run a statement that fails, typically because it breaks an integrity constraint
- run another statement without rolling back
That pattern isn't portable across databases. It doesn't work on PostgreSQL.
Also, in my opinion, it's an illegal behavior for a transactional database. A transaction is supposed to guarantee that either all statements have executed or none of them.
In my example, the first statement fails, the second succeeds, and MySQL happily commits the transaction, even though only one of the two statements executed.
To sum up, while MySQL doesn't care about transactional integrity, Django does. It's a design decision I made when I wrote the current transaction management.
If you want to discuss this decision, please write to the DevelopersMailingList.
comment:10 by , 9 years ago
In case that isn't clear — you're hitting this issue because you aren't following the documentation I linked to in my earlier comment. Just add a with transaction.atomic inside your try/except and things should work.
comment:11 by , 9 years ago
I may have managed to reproduce this exception: https://code.djangoproject.com/ticket/26340
comment:12 by , 9 years ago
Resolution: | wontfix → duplicate |
---|
The OP confirmed that #26340 is the same issue. Let's continue the discussion over there.
My suggestion is that "needs_rollback" is set to false in the end of exit in Atomic when connection.commit_on_exit is false. commit_on_exit == false seems to be there to indicate that autocommit was false before the atomic block. in_atomic_block is also set to false here for the same reason. See pull requset: https://github.com/django/django/pull/6240