def test_mark_for_rollback_on_error_in_transaction(self): with transaction.atomic(savepoint=False): # Swallow the intentional error raised. with self.assertRaisesMessage(Exception, "Oops"): # Wrap in `mark_for_rollback_on_error` to check if the # transaction is marked broken. with transaction.mark_for_rollback_on_error(): # Ensure that we are still in a good state. self.assertFalse(transaction.get_rollback()) raise Exception("Oops") # mark_for_rollback_on_error marked the transaction as broken … self.assertTrue(transaction.get_rollback()) # … and further queries fail. msg = "You can't execute queries until the end of the 'atomic' block." with self.assertRaisesMessage( transaction.TransactionManagementError, msg): Reporter.objects.create() # Transaction errors are reset at the end of an transaction, so this # should just work. Reporter.objects.create()
def post(self, request, title=None, content_pk=None): error_page = self.get_render(request) if self.content_form.is_valid() is True: self.content_instance = self.content_form.save() save_image_form(self.content_instance, self.image_form, transaction) if transaction.get_rollback() is True: return error_page # last_url used for next page for javascript to scroll to the a tag that has that last_url last_url = str( reverse_lazy("content:edit", kwargs={ "title": self.post_instance.slug_title, "content_pk": self.content_instance.pk })) return HttpResponseRedirect( reverse_lazy("post:edit", kwargs={"title": self.post_instance.slug_title}) + f"?last=" + last_url) else: return error_page
def _ctit_db_wrapper(trans_safe=False): ''' Wrapper to avoid undesired actions by Django ORM when managing settings if only getting a setting, can use trans_safe=True, which will avoid throwing errors if the prior context was a broken transaction. Any database errors will be logged, but exception will be suppressed. ''' rollback_set = None is_atomic = None try: if trans_safe: is_atomic = connection.in_atomic_block if is_atomic: rollback_set = transaction.get_rollback() if rollback_set: logger.debug('Obtaining database settings in spite of broken transaction.') transaction.set_rollback(False) yield except DBError: if trans_safe: if 'migrate' not in sys.argv and 'check_migrations' not in sys.argv: logger.exception('Database settings are not available, using defaults.') else: logger.exception('Error modifying something related to database settings.') finally: if trans_safe and is_atomic and rollback_set: transaction.set_rollback(rollback_set)
def test_force_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") # atomic block shouldn't rollback, but force it. self.assertFalse(transaction.get_rollback()) transaction.set_rollback(True) self.assertQuerysetEqual(Reporter.objects.all(), [])
def test_force_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") # atomic block shouldn't rollback, but force it. self.assertFalse(transaction.get_rollback()) transaction.set_rollback(True) self.assertSequenceEqual(Reporter.objects.all(), [])
def post(self, request, title=None): context = self.get_context(request) post_to_be_translated = context.get("post_to_be_translated") translation_already_existing_page = self.check_if_translation_exists(post_to_be_translated, request, context) if translation_already_existing_page: return translation_already_existing_page error_page = self.get_render(request, context) if self.form.is_valid() is True and self.content_form.is_valid() is True: new_post_instance = self.form.save() content_instance = self.content_form.save(post=new_post_instance) save_image_form(content_instance, self.image_form, transaction) self.marry_multi_language_posts(new_post_instance, post_to_be_translated) if transaction.get_rollback() is True: return error_page return HttpResponseRedirect( reverse_lazy("blog:post-detail", kwargs={"title": new_post_instance.title_slugified()})) else: return error_page
def atomic_9(resquest): g1 = Group() s_info = 'atomic_9 save 1' g1.name = '%s %s' % (app_prefix, s_info) g1.save() with transaction.atomic(): g2 = Group() s_info = 'atomic_9 save 2' g2.name = '%s %s' % (app_prefix, s_info) g2.save() g3 = Group() s_info = 'atomic_9 save 3' g3.name = '%s %s' % (app_prefix, s_info) g3.save() auto_commit = transaction.get_autocommit() #transaction.set_autocommit(auto_commit) # raising an exception, it is forbidden when atomic block is active #transaction.rollback() # raising an exception, it is forbidden when atomic block is active print("function get_autocommit() : %s" % (auto_commit, )) sid = transaction.savepoint() # test print("save point id %s" % (sid, )) # test transaction.savepoint_rollback(sid) transaction.clean_savepoints() rollback_cond = transaction.get_rollback() print("rollback condition is %s" % (rollback_cond, )) transaction.set_rollback(rollback_cond) raise Exception("Error 2")
def test_prevent_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") sid = transaction.savepoint() # trigger a database error inside an inner atomic without savepoint with self.assertRaises(DatabaseError): with transaction.atomic(savepoint=False): connection.cursor().execute( "SELECT no_such_col FROM transactions_reporter" ) # prevent atomic from rolling back since we're recovering manually self.assertTrue(transaction.get_rollback()) transaction.set_rollback(False) transaction.savepoint_rollback(sid) self.assertQuerysetEqual(Reporter.objects.all(), ['<Reporter: Tintin>']) self.assertAtomicSignalCalls( # Enter atomic transaction block. enter_block_atomic_signal_call_sequence(True) + # Create Reporter. create_model_atomic_signal_call_sequence() + # Enter and leave atomic transaction block. enter_block_atomic_signal_call_sequence(False, savepoint=False) + leave_block_atomic_signal_call_sequence(False, False, savepoint=False) + # Leave atomic transaction with recovered rollback. leave_block_atomic_signal_call_sequence(True, True) )
def test_no_exception_commit_transaction(self): request = factory.post('/') with self.assertNumQueries(1): response = self.view(request) assert not transaction.get_rollback() assert response.status_code == status.HTTP_200_OK assert BasicModel.objects.count() == 1
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
def test_no_exception_commit_transaction(self): request = factory.post("/") with self.assertNumQueries(1): response = self.view(request) self.assertFalse(transaction.get_rollback()) self.assertEqual(response.status_code, status.HTTP_200_OK) assert BasicModel.objects.count() == 1
def test_no_exception_conmmit_transaction(self): request = factory.post('/') with self.assertNumQueries(1): response = self.view(request) self.assertFalse(transaction.get_rollback()) self.assertEqual(response.status_code, status.HTTP_200_OK) assert BasicModel.objects.count() == 1
def _ctit_db_wrapper(trans_safe=False): ''' Wrapper to avoid undesired actions by Django ORM when managing settings if only getting a setting, can use trans_safe=True, which will avoid throwing errors if the prior context was a broken transaction. Any database errors will be logged, but exception will be suppressed. ''' rollback_set = None is_atomic = None try: if trans_safe: is_atomic = connection.in_atomic_block if is_atomic: rollback_set = transaction.get_rollback() if rollback_set: logger.debug('Obtaining database settings in spite of broken transaction.') transaction.set_rollback(False) yield except DBError: # We want the _full_ traceback with the context # First we get the current call stack, which constitutes the "top", # it has the context up to the point where the context manager is used top_stack = StringIO() traceback.print_stack(file=top_stack) top_lines = top_stack.getvalue().strip('\n').split('\n') top_stack.close() # Get "bottom" stack from the local error that happened # inside of the "with" block this wraps exc_type, exc_value, exc_traceback = sys.exc_info() bottom_stack = StringIO() traceback.print_tb(exc_traceback, file=bottom_stack) bottom_lines = bottom_stack.getvalue().strip('\n').split('\n') # Glue together top and bottom where overlap is found bottom_cutoff = 0 for i, line in enumerate(bottom_lines): if line in top_lines: # start of overlapping section, take overlap from bottom top_lines = top_lines[:top_lines.index(line)] bottom_cutoff = i break bottom_lines = bottom_lines[bottom_cutoff:] tb_lines = top_lines + bottom_lines tb_string = '\n'.join( ['Traceback (most recent call last):'] + tb_lines + ['{}: {}'.format(exc_type.__name__, str(exc_value))] ) bottom_stack.close() # Log the combined stack if trans_safe: if 'check_migrations' not in sys.argv: logger.debug('Database settings are not available, using defaults, error:\n{}'.format(tb_string)) else: logger.debug('Error modifying something related to database settings.\n{}'.format(tb_string)) finally: if trans_safe and is_atomic and rollback_set: transaction.set_rollback(rollback_set)
def savepoint_2(request): autocommit_flag = transaction.get_autocommit() print("autocommit state : %s" % (autocommit_flag, )) rollback_stat = transaction.get_rollback() print("rollback status : %s" % (rollback_stat, )) sid1 = transaction.savepoint() g1 = Group() s_info = 'savepoint_2 a' g1.name = '%s %s' % (app_prefix, s_info) g1.save() g2 = Group() s_info = 'savepoint_2 b' g2.name = '%s %s' % (app_prefix, s_info) g2.save() transaction.savepoint_commit(sid1) sid2 = transaction.savepoint() g3 = Group() s_info = 'savepoint_2 c' g3.name = '%s %s' % (app_prefix, s_info) g3.save() g4 = Group() s_info = 'savepoint_2 d' g4.name = '%s %s' % (app_prefix, s_info) g4.save() transaction.savepoint_rollback(sid2) sid3 = transaction.savepoint() g5 = Group() s_info = 'savepoint_2 e' g5.name = '%s %s' % (app_prefix, s_info) g5.save() g6 = Group() s_info = 'savepoint_2 f' g6.name = '%s %s' % (app_prefix, s_info) g6.save() transaction.savepoint_commit(sid3) transaction.clean_savepoints() raise Exception("Error")
def test_prevent_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") sid = transaction.savepoint() # trigger a database error inside an inner atomic without savepoint with self.assertRaises(DatabaseError): with transaction.atomic(savepoint=False): connection.cursor().execute("SELECT no_such_col FROM transactions_reporter") # prevent atomic from rolling back since we're recovering manually self.assertTrue(transaction.get_rollback()) transaction.set_rollback(False) transaction.savepoint_rollback(sid) self.assertQuerysetEqual(Reporter.objects.all(), ["<Reporter: Tintin>"])
def test_prevent_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") sid = transaction.savepoint() # trigger a database error inside an inner atomic without savepoint with self.assertRaises(DatabaseError), transaction.atomic(savepoint=False): connection.cursor().execute( "SELECT no_such_col FROM transactions_reporter") transaction.savepoint_rollback(sid) # atomic block should rollback, but prevent it, as we just did it. self.assertTrue(transaction.get_rollback()) transaction.set_rollback(False) self.assertQuerysetEqual(Reporter.objects.all(), ['<Reporter: Tintin>'])
def get_ticket(self): try: return self._internal_get_ticket() except exceptions: connection = connections[self.db] # If the transaction is in an `atomic` block, and needs a rollback, # we should mark the database as rolled back. This is because the # database backend may not prevent running SQL queries in broken # transactions, in which case we need to say that we have handled # the rollback so we can try one more time. if (connection.in_atomic_block and transaction.get_rollback(using=self.db)): transaction.set_rollback(False, using=self.db) return self._internal_get_ticket()
def post(self, request, title=None): error_page = self.get_render(request) if self.content_form.is_valid() is True: self.content_instance = self.content_form.save(post=self.post_instance) save_image_form(self.content_instance, self.image_form, transaction) if transaction.get_rollback() is True: return error_page return HttpResponseRedirect(reverse_lazy("post:edit", kwargs={"title": self.post_instance.slug_title})) else: return error_page
def test_prevent_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") sid = transaction.savepoint() # trigger a database error inside an inner atomic without savepoint with self.assertRaises(DatabaseError): with transaction.atomic(savepoint=False): connection.cursor().execute( "SELECT no_such_col FROM transactions_reporter") # prevent atomic from rolling back since we're recovering manually self.assertTrue(transaction.get_rollback()) transaction.set_rollback(False) transaction.savepoint_rollback(sid) self.assertQuerysetEqual(Reporter.objects.all(), ['<Reporter: Tintin>'])
def test_prevent_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") sid = transaction.savepoint() # trigger a database error inside an inner atomic without savepoint with self.assertRaises(DatabaseError): with transaction.atomic(savepoint=False): connection.cursor().execute( "SELECT no_such_col FROM transactions_reporter") transaction.savepoint_rollback(sid) # atomic block should rollback, but prevent it, as we just did it. self.assertTrue(transaction.get_rollback()) transaction.set_rollback(False) self.assertQuerysetEqual(Reporter.objects.all(), ['<Reporter: Tintin>'])
def test_merged_outer_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") with transaction.atomic(savepoint=False): Reporter.objects.create(first_name="Archibald", last_name="Haddock") with six.assertRaisesRegex(self, Exception, "Oops"): with transaction.atomic(savepoint=False): Reporter.objects.create(first_name="Calculus") raise Exception("Oops, that's his last name") # The third insert couldn't be roll back. Temporarily mark the # connection as not needing rollback to check it. self.assertTrue(transaction.get_rollback()) transaction.set_rollback(False) self.assertEqual(Reporter.objects.count(), 3) transaction.set_rollback(True) # The second insert couldn't be roll back. Temporarily mark the # connection as not needing rollback to check it. self.assertTrue(transaction.get_rollback()) transaction.set_rollback(False) self.assertEqual(Reporter.objects.count(), 3) transaction.set_rollback(True) # The first block has a savepoint and must roll back. self.assertQuerysetEqual(Reporter.objects.all(), [])
def test_mark_for_rollback_on_error_in_transaction(self): with transaction.atomic(savepoint=False): # Swallow the intentional error raised. with self.assertRaisesMessage(Exception, "Oops"): # Wrap in `mark_for_rollback_on_error` to check if the transaction is marked broken. with transaction.mark_for_rollback_on_error(): # Ensure that we are still in a good state. self.assertFalse(transaction.get_rollback()) raise Exception("Oops") # Ensure that `mark_for_rollback_on_error` marked the transaction as broken … self.assertTrue(transaction.get_rollback()) # … and further queries fail. msg = "You can't execute queries until the end of the 'atomic' block." with self.assertRaisesMessage(transaction.TransactionManagementError, msg): Reporter.objects.create() # Transaction errors are reset at the end of an transaction, so this should just work. Reporter.objects.create()
def test_api_exception_rollback_transaction(self): """ Transaction is rollbacked by our transaction atomic block. """ request = factory.post("/") num_queries = 4 if getattr(connection.features, "can_release_savepoints", False) else 3 with self.assertNumQueries(num_queries): # 1 - begin savepoint # 2 - insert # 3 - rollback savepoint # 4 - release savepoint (django>=1.8 only) with transaction.atomic(): response = self.view(request) self.assertTrue(transaction.get_rollback()) self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) assert BasicModel.objects.count() == 0
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(): response = self.view(request) assert transaction.get_rollback() assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR assert BasicModel.objects.count() == 0
def test_generic_exception_delegate_transaction_management(self): """ Transaction is eventually managed by outer-most transaction atomic block. DRF do not try to interfere here. We let django deal with the transaction when it will catch the Exception. """ request = factory.post('/') with self.assertNumQueries(3): # 1 - begin savepoint # 2 - insert # 3 - release savepoint with transaction.atomic(): self.assertRaises(Exception, self.view, request) assert not transaction.get_rollback() assert BasicModel.objects.count() == 1
def test_generic_exception_delegate_transaction_management(self): """ Transaction is eventually managed by outer-most transaction atomic block. DRF do not try to interfere here. We let django deal with the transaction when it will catch the Exception. """ request = factory.post("/") with self.assertNumQueries(3): # 1 - begin savepoint # 2 - insert # 3 - release savepoint with transaction.atomic(): self.assertRaises(Exception, self.view, request) self.assertFalse(transaction.get_rollback()) assert BasicModel.objects.count() == 1
def test_api_exception_rollback_transaction(self): """ Transaction is rollbacked by our transaction atomic block. """ request = factory.post('/') num_queries = (4 if getattr(connection.features, 'can_release_savepoints', False) else 3) with self.assertNumQueries(num_queries): # 1 - begin savepoint # 2 - insert # 3 - rollback savepoint # 4 - release savepoint (django>=1.8 only) with transaction.atomic(): response = self.view(request) assert transaction.get_rollback() assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR assert BasicModel.objects.count() == 0
def test_force_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") # atomic block shouldn't rollback, but force it. self.assertFalse(transaction.get_rollback()) transaction.set_rollback(True) self.assertQuerysetEqual(Reporter.objects.all(), []) self.assertAtomicSignalCalls( # Enter atomic transaction block. enter_block_atomic_signal_call_sequence(True) + # Create Reporter. create_model_atomic_signal_call_sequence() + # Leave atomic transaction with forced rollback. leave_block_atomic_signal_call_sequence(True, False) )
def _ctit_db_wrapper(trans_safe=False): """ Wrapper to avoid undesired actions by Django ORM when managing settings if only getting a setting, can use trans_safe=True, which will avoid throwing errors if the prior context was a broken transaction. Any database errors will be logged, but exception will be suppressed. """ rollback_set = None is_atomic = None try: if trans_safe: is_atomic = connection.in_atomic_block if is_atomic: rollback_set = transaction.get_rollback() if rollback_set: logger.debug( 'Obtaining database settings in spite of broken transaction.' ) transaction.set_rollback(False) yield except DBError as exc: if trans_safe: level = logger.warning if isinstance(exc, ProgrammingError): if 'relation' in str(exc) and 'does not exist' in str(exc): # this generally means we can't fetch Tower configuration # because the database hasn't actually finished migrating yet; # this is usually a sign that a service in a container (such as ws_broadcast) # has come up *before* the database has finished migrating, and # especially that the conf.settings table doesn't exist yet level = logger.debug level( f'Database settings are not available, using defaults. error: {str(exc)}' ) else: logger.exception( 'Error modifying something related to database settings.') finally: if trans_safe and is_atomic and rollback_set: transaction.set_rollback(rollback_set)
def view(request): data = {} if request.method == 'POST': if 'user' in request.session: if 'action' in request.POST: action = request.POST['action'] if action == 'add_factura': try: vali = FacturaForm(request.POST) if vali.is_valid(): factura = Factura( ruccliente=vali.cleaned_data['ruccliente'], nombrecliente=vali. cleaned_data['nombrecliente']) factura.save() datos = json.loads(request.POST['lista_items1']) for p in datos: det = FacturaProducto(factura=factura, producto=p['producto'], precio=p['precio'], cantidad=p['cantidad']) det.save() return JsonResponse({"result": "ok"}) else: return JsonResponse({ "result": "error", "mensaje": 'datos erroneos en el formulario.' }) except Exception as ex: transaction.get_rollback() return JsonResponse({ "result": "error", "mensaje": 'Ocurrio un problema contacte con el administrador.' }) else: return JsonResponse({"result": "session"}) # return HttpResponseRedirect("/") else: if 'action' in request.GET: action = request.GET['action'] if action == 'add_factura': data['title'] = u'Agregar Factura' data['form'] = FacturaForm() return render(request, "factura/add_factura.html", data) if action == 'edit_pais': data['title'] = u'Editar Pais' data['pais'] = pais = Pais.objects.get( pk=int(request.GET['id'])) data['form'] = PaisForm(initial={ 'nombre': pais.nombre, 'estado': pais.estado }) return render(request, "add_pais.html", data) else: data['user'] = request.session['user'] data['factura'] = Factura.objects.all() return render(request, "factura.html", data)