Fixed #27170 -- Added DatabaseWrapper class attributes to ease subclassing.

This commit is contained in:
Chris Jerdonek 2016-09-08 13:33:36 -07:00 committed by Tim Graham
parent 0bbab97c28
commit 7ca3b391b6
11 changed files with 87 additions and 70 deletions

View File

@ -9,9 +9,7 @@ from .schema import MySQLGISSchemaEditor
class DatabaseWrapper(MySQLDatabaseWrapper): class DatabaseWrapper(MySQLDatabaseWrapper):
SchemaEditorClass = MySQLGISSchemaEditor SchemaEditorClass = MySQLGISSchemaEditor
# Classes instantiated in __init__().
def __init__(self, *args, **kwargs): features_class = DatabaseFeatures
super(DatabaseWrapper, self).__init__(*args, **kwargs) introspection_class = MySQLIntrospection
self.features = DatabaseFeatures(self) ops_class = MySQLOperations
self.ops = MySQLOperations(self)
self.introspection = MySQLIntrospection(self)

View File

@ -9,9 +9,7 @@ from .schema import OracleGISSchemaEditor
class DatabaseWrapper(OracleDatabaseWrapper): class DatabaseWrapper(OracleDatabaseWrapper):
SchemaEditorClass = OracleGISSchemaEditor SchemaEditorClass = OracleGISSchemaEditor
# Classes instantiated in __init__().
def __init__(self, *args, **kwargs): features_class = DatabaseFeatures
super(DatabaseWrapper, self).__init__(*args, **kwargs) introspection_class = OracleIntrospection
self.features = DatabaseFeatures(self) ops_class = OracleOperations
self.ops = OracleOperations(self)
self.introspection = OracleIntrospection(self)

View File

@ -17,6 +17,11 @@ from .schema import SpatialiteSchemaEditor
class DatabaseWrapper(SQLiteDatabaseWrapper): class DatabaseWrapper(SQLiteDatabaseWrapper):
SchemaEditorClass = SpatialiteSchemaEditor SchemaEditorClass = SpatialiteSchemaEditor
# Classes instantiated in __init__().
client_class = SpatiaLiteClient
features_class = DatabaseFeatures
introspection_class = SpatiaLiteIntrospection
ops_class = SpatiaLiteOperations
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# Before we get too far, make sure pysqlite 2.5+ is installed. # Before we get too far, make sure pysqlite 2.5+ is installed.
@ -37,10 +42,6 @@ class DatabaseWrapper(SQLiteDatabaseWrapper):
'SPATIALITE_LIBRARY_PATH in your settings.' 'SPATIALITE_LIBRARY_PATH in your settings.'
) )
super(DatabaseWrapper, self).__init__(*args, **kwargs) super(DatabaseWrapper, self).__init__(*args, **kwargs)
self.features = DatabaseFeatures(self)
self.ops = SpatiaLiteOperations(self)
self.client = SpatiaLiteClient(self)
self.introspection = SpatiaLiteIntrospection(self)
def get_new_connection(self, conn_params): def get_new_connection(self, conn_params):
conn = super(DatabaseWrapper, self).get_new_connection(conn_params) conn = super(DatabaseWrapper, self).get_new_connection(conn_params)

View File

@ -8,6 +8,7 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db import DEFAULT_DB_ALIAS from django.db import DEFAULT_DB_ALIAS
from django.db.backends import utils from django.db.backends import utils
from django.db.backends.base.validation import BaseDatabaseValidation
from django.db.backends.signals import connection_created from django.db.backends.signals import connection_created
from django.db.transaction import TransactionManagementError from django.db.transaction import TransactionManagementError
from django.db.utils import DatabaseError, DatabaseErrorWrapper from django.db.utils import DatabaseError, DatabaseErrorWrapper
@ -36,6 +37,13 @@ class BaseDatabaseWrapper(object):
ops = None ops = None
vendor = 'unknown' vendor = 'unknown'
SchemaEditorClass = None SchemaEditorClass = None
# Classes instantiated in __init__().
client_class = None
creation_class = None
features_class = None
introspection_class = None
ops_class = None
validation_class = BaseDatabaseValidation
queries_limit = 9000 queries_limit = 9000
@ -88,6 +96,13 @@ class BaseDatabaseWrapper(object):
# is called? # is called?
self.run_commit_hooks_on_set_autocommit_on = False self.run_commit_hooks_on_set_autocommit_on = False
self.client = self.client_class(self)
self.creation = self.creation_class(self)
self.features = self.features_class(self)
self.introspection = self.introspection_class(self)
self.ops = self.ops_class(self)
self.validation = self.validation_class(self)
def ensure_timezone(self): def ensure_timezone(self):
""" """
Ensure the connection's timezone is set to `self.timezone_name` and Ensure the connection's timezone is set to `self.timezone_name` and

View File

@ -13,7 +13,6 @@ from django.db.backends.base.client import BaseDatabaseClient
from django.db.backends.base.creation import BaseDatabaseCreation from django.db.backends.base.creation import BaseDatabaseCreation
from django.db.backends.base.introspection import BaseDatabaseIntrospection from django.db.backends.base.introspection import BaseDatabaseIntrospection
from django.db.backends.base.operations import BaseDatabaseOperations from django.db.backends.base.operations import BaseDatabaseOperations
from django.db.backends.base.validation import BaseDatabaseValidation
from django.db.backends.dummy.features import DummyDatabaseFeatures from django.db.backends.dummy.features import DummyDatabaseFeatures
@ -71,16 +70,12 @@ class DatabaseWrapper(BaseDatabaseWrapper):
_savepoint_commit = complain _savepoint_commit = complain
_savepoint_rollback = ignore _savepoint_rollback = ignore
_set_autocommit = complain _set_autocommit = complain
# Classes instantiated in __init__().
def __init__(self, *args, **kwargs): client_class = DatabaseClient
super(DatabaseWrapper, self).__init__(*args, **kwargs) creation_class = DatabaseCreation
features_class = DummyDatabaseFeatures
self.features = DummyDatabaseFeatures(self) introspection_class = DatabaseIntrospection
self.ops = DatabaseOperations(self) ops_class = DatabaseOperations
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
self.introspection = DatabaseIntrospection(self)
self.validation = BaseDatabaseValidation(self)
def is_usable(self): def is_usable(self):
return True return True

View File

@ -227,16 +227,13 @@ class DatabaseWrapper(BaseDatabaseWrapper):
Database = Database Database = Database
SchemaEditorClass = DatabaseSchemaEditor SchemaEditorClass = DatabaseSchemaEditor
# Classes instantiated in __init__().
def __init__(self, *args, **kwargs): client_class = DatabaseClient
super(DatabaseWrapper, self).__init__(*args, **kwargs) creation_class = DatabaseCreation
features_class = DatabaseFeatures
self.features = DatabaseFeatures(self) introspection_class = DatabaseIntrospection
self.ops = DatabaseOperations(self) ops_class = DatabaseOperations
self.client = DatabaseClient(self) validation_class = DatabaseValidation
self.creation = DatabaseCreation(self)
self.introspection = DatabaseIntrospection(self)
self.validation = DatabaseValidation(self)
def get_connection_params(self): def get_connection_params(self):
kwargs = { kwargs = {

View File

@ -15,7 +15,6 @@ import warnings
from django.conf import settings from django.conf import settings
from django.db import utils from django.db import utils
from django.db.backends.base.base import BaseDatabaseWrapper from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.base.validation import BaseDatabaseValidation
from django.utils import six, timezone from django.utils import six, timezone
from django.utils.deprecation import RemovedInDjango20Warning from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.duration import duration_string from django.utils.duration import duration_string
@ -179,18 +178,17 @@ class DatabaseWrapper(BaseDatabaseWrapper):
Database = Database Database = Database
SchemaEditorClass = DatabaseSchemaEditor SchemaEditorClass = DatabaseSchemaEditor
# Classes instantiated in __init__().
client_class = DatabaseClient
creation_class = DatabaseCreation
features_class = DatabaseFeatures
introspection_class = DatabaseIntrospection
ops_class = DatabaseOperations
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(DatabaseWrapper, self).__init__(*args, **kwargs) super(DatabaseWrapper, self).__init__(*args, **kwargs)
self.features = DatabaseFeatures(self)
use_returning_into = self.settings_dict["OPTIONS"].get('use_returning_into', True) use_returning_into = self.settings_dict["OPTIONS"].get('use_returning_into', True)
self.features.can_return_id_from_insert = use_returning_into self.features.can_return_id_from_insert = use_returning_into
self.ops = DatabaseOperations(self)
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
self.introspection = DatabaseIntrospection(self)
self.validation = BaseDatabaseValidation(self)
def _connect_string(self): def _connect_string(self):
settings_dict = self.settings_dict settings_dict = self.settings_dict

View File

@ -10,7 +10,6 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db import DEFAULT_DB_ALIAS from django.db import DEFAULT_DB_ALIAS
from django.db.backends.base.base import BaseDatabaseWrapper from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.base.validation import BaseDatabaseValidation
from django.db.utils import DatabaseError as WrappedDatabaseError from django.db.utils import DatabaseError as WrappedDatabaseError
from django.utils import six from django.utils import six
from django.utils.encoding import force_str from django.utils.encoding import force_str
@ -141,16 +140,12 @@ class DatabaseWrapper(BaseDatabaseWrapper):
Database = Database Database = Database
SchemaEditorClass = DatabaseSchemaEditor SchemaEditorClass = DatabaseSchemaEditor
# Classes instantiated in __init__().
def __init__(self, *args, **kwargs): client_class = DatabaseClient
super(DatabaseWrapper, self).__init__(*args, **kwargs) creation_class = DatabaseCreation
features_class = DatabaseFeatures
self.features = DatabaseFeatures(self) introspection_class = DatabaseIntrospection
self.ops = DatabaseOperations(self) ops_class = DatabaseOperations
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
self.introspection = DatabaseIntrospection(self)
self.validation = BaseDatabaseValidation(self)
def get_connection_params(self): def get_connection_params(self):
settings_dict = self.settings_dict settings_dict = self.settings_dict

View File

@ -15,7 +15,6 @@ from django.conf import settings
from django.db import utils from django.db import utils
from django.db.backends import utils as backend_utils from django.db.backends import utils as backend_utils
from django.db.backends.base.base import BaseDatabaseWrapper from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.base.validation import BaseDatabaseValidation
from django.utils import six, timezone from django.utils import six, timezone
from django.utils.dateparse import ( from django.utils.dateparse import (
parse_date, parse_datetime, parse_duration, parse_time, parse_date, parse_datetime, parse_duration, parse_time,
@ -163,16 +162,12 @@ class DatabaseWrapper(BaseDatabaseWrapper):
Database = Database Database = Database
SchemaEditorClass = DatabaseSchemaEditor SchemaEditorClass = DatabaseSchemaEditor
# Classes instantiated in __init__().
def __init__(self, *args, **kwargs): client_class = DatabaseClient
super(DatabaseWrapper, self).__init__(*args, **kwargs) creation_class = DatabaseCreation
features_class = DatabaseFeatures
self.features = DatabaseFeatures(self) introspection_class = DatabaseIntrospection
self.ops = DatabaseOperations(self) ops_class = DatabaseOperations
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
self.introspection = DatabaseIntrospection(self)
self.validation = BaseDatabaseValidation(self)
def get_connection_params(self): def get_connection_params(self):
settings_dict = self.settings_dict settings_dict = self.settings_dict

View File

@ -32,6 +32,31 @@ from django.utils.six.moves import range
from . import models from . import models
class DatabaseWrapperTests(SimpleTestCase):
def test_initialization_class_attributes(self):
"""
The "initialization" class attributes like client_class and
creation_class should be set on the class and reflected in the
corresponding instance attributes of the instantiated backend.
"""
conn = connections[DEFAULT_DB_ALIAS]
conn_class = type(conn)
attr_names = [
('client_class', 'client'),
('creation_class', 'creation'),
('features_class', 'features'),
('introspection_class', 'introspection'),
('ops_class', 'ops'),
('validation_class', 'validation'),
]
for class_attr_name, instance_attr_name in attr_names:
class_attr_value = getattr(conn_class, class_attr_name)
self.assertIsNotNone(class_attr_value)
instance_attr_value = getattr(conn, instance_attr_name)
self.assertIsInstance(instance_attr_value, class_attr_value)
class DummyBackendTest(SimpleTestCase): class DummyBackendTest(SimpleTestCase):
def test_no_databases(self): def test_no_databases(self):

View File

@ -281,7 +281,7 @@ class SetupDatabasesTests(unittest.TestCase):
} }
}) })
with mock.patch('django.db.backends.dummy.base.DatabaseCreation') as mocked_db_creation: with mock.patch('django.db.backends.dummy.base.DatabaseWrapper.creation_class') as mocked_db_creation:
with mock.patch('django.test.utils.connections', new=tested_connections): with mock.patch('django.test.utils.connections', new=tested_connections):
old_config = self.runner_instance.setup_databases() old_config = self.runner_instance.setup_databases()
self.runner_instance.teardown_databases(old_config) self.runner_instance.teardown_databases(old_config)
@ -306,7 +306,7 @@ class SetupDatabasesTests(unittest.TestCase):
'ENGINE': 'django.db.backends.dummy', 'ENGINE': 'django.db.backends.dummy',
}, },
}) })
with mock.patch('django.db.backends.dummy.base.DatabaseCreation') as mocked_db_creation: with mock.patch('django.db.backends.dummy.base.DatabaseWrapper.creation_class') as mocked_db_creation:
with mock.patch('django.test.utils.connections', new=tested_connections): with mock.patch('django.test.utils.connections', new=tested_connections):
self.runner_instance.setup_databases() self.runner_instance.setup_databases()
mocked_db_creation.return_value.create_test_db.assert_called_once_with( mocked_db_creation.return_value.create_test_db.assert_called_once_with(
@ -320,7 +320,7 @@ class SetupDatabasesTests(unittest.TestCase):
'TEST': {'SERIALIZE': False}, 'TEST': {'SERIALIZE': False},
}, },
}) })
with mock.patch('django.db.backends.dummy.base.DatabaseCreation') as mocked_db_creation: with mock.patch('django.db.backends.dummy.base.DatabaseWrapper.creation_class') as mocked_db_creation:
with mock.patch('django.test.utils.connections', new=tested_connections): with mock.patch('django.test.utils.connections', new=tested_connections):
self.runner_instance.setup_databases() self.runner_instance.setup_databases()
mocked_db_creation.return_value.create_test_db.assert_called_once_with( mocked_db_creation.return_value.create_test_db.assert_called_once_with(