support multi db atomic_requests (#7739)

This commit is contained in:
Jonathan Mortensen 2021-03-03 03:15:39 -08:00 committed by GitHub
parent 8f6d2d2f9c
commit de7468d0b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 4 deletions

View File

@ -3,7 +3,7 @@ Provides an APIView class that is the base of all views in REST framework.
""" """
from django.conf import settings from django.conf import settings
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db import connection, models, transaction from django.db import connections, models
from django.http import Http404 from django.http import Http404
from django.http.response import HttpResponseBase from django.http.response import HttpResponseBase
from django.utils.cache import cc_delim_re, patch_vary_headers from django.utils.cache import cc_delim_re, patch_vary_headers
@ -63,9 +63,9 @@ def get_view_description(view, html=False):
def set_rollback(): def set_rollback():
atomic_requests = connection.settings_dict.get('ATOMIC_REQUESTS', False) for db in connections.all():
if atomic_requests and connection.in_atomic_block: if db.settings_dict['ATOMIC_REQUESTS'] and db.in_atomic_block:
transaction.set_rollback(True) db.set_rollback(True)
def exception_handler(exc, context): def exception_handler(exc, context):

View File

@ -24,6 +24,10 @@ def pytest_configure(config):
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:' 'NAME': ':memory:'
},
'secondary': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:'
} }
}, },
SITE_ID=1, SITE_ID=1,

View File

@ -130,6 +130,41 @@ class DBTransactionAPIExceptionTests(TestCase):
assert BasicModel.objects.count() == 0 assert BasicModel.objects.count() == 0
@unittest.skipUnless(
connection.features.uses_savepoints,
"'atomic' requires transactions and savepoints."
)
class MultiDBTransactionAPIExceptionTests(TestCase):
databases = '__all__'
def setUp(self):
self.view = APIExceptionView.as_view()
connections.databases['default']['ATOMIC_REQUESTS'] = True
connections.databases['secondary']['ATOMIC_REQUESTS'] = True
def tearDown(self):
connections.databases['default']['ATOMIC_REQUESTS'] = False
connections.databases['secondary']['ATOMIC_REQUESTS'] = False
def test_api_exception_rollback_transaction(self):
"""
Transaction is rollbacked by our transaction atomic block.
"""
request = factory.post('/')
num_queries = 4 if connection.features.can_release_savepoints else 3
with self.assertNumQueries(num_queries):
# 1 - begin savepoint
# 2 - insert
# 3 - rollback savepoint
# 4 - release savepoint
with transaction.atomic(), transaction.atomic(using='secondary'):
response = self.view(request)
assert transaction.get_rollback()
assert transaction.get_rollback(using='secondary')
assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
assert BasicModel.objects.count() == 0
@unittest.skipUnless( @unittest.skipUnless(
connection.features.uses_savepoints, connection.features.uses_savepoints,
"'atomic' requires transactions and savepoints." "'atomic' requires transactions and savepoints."