Selaa lähdekoodia

Added an API to control database-level autocommit.

Aymeric Augustin 12 vuotta sitten
vanhempi
commit
f515619494

+ 14 - 0
django/db/backends/__init__.py

@@ -44,6 +44,7 @@ class BaseDatabaseWrapper(object):
         self.savepoint_state = 0
 
         # Transaction management related attributes
+        self.autocommit = False
         self.transaction_state = []
         # Tracks if the connection is believed to be in transaction. This is
         # set somewhat aggressively, as the DBAPI doesn't make it easy to
@@ -232,6 +233,12 @@ class BaseDatabaseWrapper(object):
         """
         pass
 
+    def _set_autocommit(self, autocommit):
+        """
+        Backend-specific implementation to enable or disable autocommit.
+        """
+        raise NotImplementedError
+
     ##### Generic transaction management methods #####
 
     def enter_transaction_management(self, managed=True, forced=False):
@@ -274,6 +281,13 @@ class BaseDatabaseWrapper(object):
             raise TransactionManagementError(
                 "Transaction managed block ended with pending COMMIT/ROLLBACK")
 
+    def set_autocommit(self, autocommit=True):
+        """
+        Enable or disable autocommit.
+        """
+        self._set_autocommit(autocommit)
+        self.autocommit = autocommit
+
     def abort(self):
         """
         Roll back any ongoing transaction and clean the transaction state

+ 5 - 1
django/db/backends/creation.py

@@ -1,6 +1,7 @@
 import hashlib
 import sys
 import time
+import warnings
 
 from django.conf import settings
 from django.db.utils import load_backend
@@ -466,7 +467,10 @@ class BaseDatabaseCreation(object):
         anymore by Django code. Kept for compatibility with user code that
         might use it.
         """
-        pass
+        warnings.warn(
+            "set_autocommit was moved from BaseDatabaseCreation to "
+            "BaseDatabaseWrapper.", PendingDeprecationWarning, stacklevel=2)
+        return self.connection.set_autocommit()
 
     def _prepare_for_test_db_ddl(self):
         """

+ 1 - 0
django/db/backends/dummy/base.py

@@ -57,6 +57,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
     _savepoint_rollback = ignore
     _enter_transaction_management = complain
     _leave_transaction_management = ignore
+    _set_autocommit = complain
     set_dirty = complain
     set_clean = complain
     commit_unless_managed = complain

+ 3 - 0
django/db/backends/mysql/base.py

@@ -445,6 +445,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
         except Database.NotSupportedError:
             pass
 
+    def _set_autocommit(self, autocommit):
+        self.connection.autocommit(autocommit)
+
     def disable_constraint_checking(self):
         """
         Disables foreign key checks, primarily for use in adding rows with forward references. Always returns True,

+ 3 - 0
django/db/backends/oracle/base.py

@@ -612,6 +612,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
     def _savepoint_commit(self, sid):
         pass
 
+    def _set_autocommit(self, autocommit):
+        self.connection.autocommit = autocommit
+
     def check_constraints(self, table_names=None):
         """
         To check constraints, we set constraints to immediate. Then, when, we're done we must ensure they

+ 0 - 3
django/db/backends/oracle/creation.py

@@ -273,6 +273,3 @@ class DatabaseCreation(BaseDatabaseCreation):
             settings_dict['NAME'],
             self._test_database_user(),
         )
-
-    def set_autocommit(self):
-        self.connection.connection.autocommit = True

+ 8 - 0
django/db/backends/postgresql_psycopg2/base.py

@@ -201,6 +201,14 @@ class DatabaseWrapper(BaseDatabaseWrapper):
             self.isolation_level = level
             self.features.uses_savepoints = bool(level)
 
+    def _set_autocommit(self, autocommit):
+        if autocommit:
+            level = psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT
+        else:
+            level = self.settings_dict["OPTIONS"].get('isolation_level',
+                psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
+        self._set_isolation_level(level)
+
     def set_dirty(self):
         if ((self.transaction_state and self.transaction_state[-1]) or
                 not self.features.uses_autocommit):

+ 0 - 3
django/db/backends/postgresql_psycopg2/creation.py

@@ -78,9 +78,6 @@ class DatabaseCreation(BaseDatabaseCreation):
                                             ' text_pattern_ops'))
         return output
 
-    def set_autocommit(self):
-        self._prepare_for_test_db_ddl()
-
     def _prepare_for_test_db_ddl(self):
         """Rollback and close the active transaction."""
         # Make sure there is an open connection.

+ 11 - 0
django/db/backends/sqlite3/base.py

@@ -355,6 +355,17 @@ class DatabaseWrapper(BaseDatabaseWrapper):
         if self.settings_dict['NAME'] != ":memory:":
             BaseDatabaseWrapper.close(self)
 
+    def _set_autocommit(self, autocommit):
+        if autocommit:
+            level = None
+        else:
+            # sqlite3's internal default is ''. It's different from None.
+            # See Modules/_sqlite/connection.c.
+            level = ''
+        # 'isolation_level' is a misleading API.
+        # SQLite always runs at the SERIALIZABLE isolation level.
+        self.connection.isolation_level = level
+
     def check_constraints(self, table_names=None):
         """
         Checks each table name in `table_names` for rows with invalid foreign key references. This method is

+ 0 - 3
django/db/backends/sqlite3/creation.py

@@ -72,9 +72,6 @@ class DatabaseCreation(BaseDatabaseCreation):
             # Remove the SQLite database file
             os.remove(test_database_name)
 
-    def set_autocommit(self):
-        self.connection.connection.isolation_level = None
-
     def test_db_signature(self):
         """
         Returns a tuple that uniquely identifies a test database.

+ 12 - 0
django/db/transaction.py

@@ -39,6 +39,18 @@ def get_connection(using=None):
         using = DEFAULT_DB_ALIAS
     return connections[using]
 
+def get_autocommit(using=None):
+    """
+    Get the autocommit status of the connection.
+    """
+    return get_connection(using).autocommit
+
+def set_autocommit(using=None, autocommit=True):
+    """
+    Set the autocommit status of the connection.
+    """
+    return get_connection(using).set_autocommit(autocommit)
+
 def abort(using=None):
     """
     Roll back any ongoing transactions and clean the transaction management

+ 3 - 0
django/test/testcases.py

@@ -63,6 +63,7 @@ def to_list(value):
         value = [value]
     return value
 
+real_set_autocommit = transaction.set_autocommit
 real_commit = transaction.commit
 real_rollback = transaction.rollback
 real_enter_transaction_management = transaction.enter_transaction_management
@@ -73,6 +74,7 @@ def nop(*args, **kwargs):
     return
 
 def disable_transaction_methods():
+    transaction.set_autocommit = nop
     transaction.commit = nop
     transaction.rollback = nop
     transaction.enter_transaction_management = nop
@@ -80,6 +82,7 @@ def disable_transaction_methods():
     transaction.abort = nop
 
 def restore_transaction_methods():
+    transaction.set_autocommit = real_set_autocommit
     transaction.commit = real_commit
     transaction.rollback = real_rollback
     transaction.enter_transaction_management = real_enter_transaction_management

+ 4 - 3
docs/internals/deprecation.txt

@@ -348,9 +348,10 @@ these changes.
 * Remove the backward compatible shims introduced to rename the attributes
   ``ChangeList.root_query_set`` and ``ChangeList.query_set``.
 
-* The private API ``django.db.close_connection`` will be removed.
-
-* The private API ``django.transaction.managed`` will be removed.
+* The following private APIs will be removed:
+  - ``django.db.close_connection()``
+  - ``django.db.backends.creation.BaseDatabaseCreation.set_autocommit()``
+  - ``django.db.transaction.managed()``
 
 2.0
 ---

+ 17 - 0
docs/topics/db/transactions.txt

@@ -208,6 +208,23 @@ This applies to all database operations, not just write operations. Even
 if your transaction only reads from the database, the transaction must
 be committed or rolled back before you complete a request.
 
+.. _managing-autocommit:
+
+Managing autocommit
+===================
+
+.. versionadded:: 1.6
+
+Django provides a straightforward API to manage the autocommit state of each
+database connection, if you need to.
+
+.. function:: get_autocommit(using=None)
+
+.. function:: set_autocommit(using=None, autocommit=True)
+
+These functions take a ``using`` argument which should be the name of a
+database. If it isn't provided, Django uses the ``"default"`` database.
+
 .. _deactivate-transaction-management:
 
 How to globally deactivate transaction management