Reputation: 3817
I have a Django view which imports an Excel file, and if exceptions occur I would like to catch them and report them all, and roll back any saves. I'm getting a TransactionManagementError even though I have used the non_atomic_requests decorator.
As I'm also using the login_required decorator I thought they might be interfering with each other. First I reversed the order then I removed the login required. No change.
I've tried disabling automatic transactions globally. Maybe I didn't do it right but it's not the solution I want anyway.
I removed the offending line of code (see below) but the same error occurred when I tried to rollback
It is running on Python 3.7.3 with the latest Django and using SQLlite. I'm running it as a unit test right now, although perhaps I'm abusing the term. Suffice to say it's running as a Django TestCase.
@transaction.non_atomic_requests
@login_required(login_url='/accounts/login/?next=/finance/gl_upload/')
def gl_upload(request):
transaction.set_autocommit(False)
if upriv(request.user, ['admin', 'finance']) == 'admin':
if request.method == 'POST':
... file processing here ...
except Exception as e:
errs.append(format('Exception "{1}" at row {0}\n'.format(p['rownum'], e)))
if errs:
transaction.rollback()
rows_deleted = 0
rows_inserted = 0
print(''.join('Error: {0}\n'.format(e) for e in errs))
else:
transaction.commit()
rows_deleted = Gldata.objects.filter(item='Actual', period_gte=older, period_lte=newest).delete()
rows_inserted = Gldata.objects.filter(item=temp_item).update(item='Actual')
transaction.set_autocommit(True)
print('Deleted: {0}, inserted: {1}'.format(rows_delete, rows_inserted))
return render(request, 'gl_upload.html', {'inserted': rows_inserted, 'removed': rows_deleted, 'errors': errs})
else:
return render(request, 'gl_upload.html', {'form': form})
else:
form = uploadForm()
return render(request, 'gl_upload.html', {'form': form})
I receive a TransactionManagementError on the set_autocommit indicating that an atomic block is active even though I understood that the decorator would disable it. A few years back I used the old commit_manually decorator which worked just fine.
File "C:\Users\csullivan\responsive\finance\views.py", line 25, in gl_upload transaction.set_autocommit(False) File "C:\Users\csullivan\responsive\env\lib\site-packages\django\db\transaction.py", line 30, in set_autocommit return get_connection(using).set_autocommit(autocommit) File "C:\Users\csullivan\responsive\env\lib\site-packages\django\db\backends\base\base.py", line 394, in set_autocommit self.validate_no_atomic_block() File "C:\Users\csullivan\responsive\env\lib\site-packages\django\db\backends\base\base.py", line 433, in validate_no_atomic_block "This is forbidden when an 'atomic' block is active.") django.db.transaction.TransactionManagementError: This is forbidden when an 'atomic' block is active.
Upvotes: 2
Views: 688
Reputation: 16367
I think that the atomic()
decorator/context manager provides exactly what you need
--it commits the transaction if the underlying code succeeds and rolls back
when there's an exception.
So in your case I would just do:
with atomic():
process_xls_files()
You shouldn't need to call transaction.set_autocommit()
and similar low-level methods manually,
unless you have very specific needs and the "normal" transaction handling is not enough.
Upvotes: 1