Skip to content

Commit

Permalink
Implemented BaseDatabaseFeatures and changed all code to access it --…
Browse files Browse the repository at this point in the history
… connection.features.foo instead of backend.foo

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5974 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
adrianholovaty committed Aug 20, 2007
1 parent 4f82250 commit 1a8f9b2
Show file tree
Hide file tree
Showing 12 changed files with 69 additions and 91 deletions.
2 changes: 1 addition & 1 deletion django/core/management/commands/syncdb.py
Expand Up @@ -34,7 +34,7 @@ def handle_noargs(self, **options):
# Get a list of all existing database tables,
# so we know what needs to be added.
table_list = table_list()
if backend.uses_case_insensitive_names:
if connection.features.uses_case_insensitive_names:
table_name_converter = str.upper
else:
table_name_converter = lambda x: x
Expand Down
24 changes: 12 additions & 12 deletions django/core/management/sql.py
Expand Up @@ -15,12 +15,12 @@ def table_list():

def installed_models(table_list):
"Returns a set of all models that are installed, given a list of existing table names."
from django.db import backend, models
from django.db import connection, models
all_models = []
for app in models.get_apps():
for model in models.get_models(app):
all_models.append(model)
if backend.uses_case_insensitive_names:
if connection.features.uses_case_insensitive_names:
converter = lambda x: x.upper()
else:
converter = lambda x: x
Expand Down Expand Up @@ -110,7 +110,7 @@ def sql_delete(app, style):
table_names = introspection.get_table_list(cursor)
else:
table_names = []
if backend.uses_case_insensitive_names:
if connection.features.uses_case_insensitive_names:
table_name_converter = str.upper
else:
table_name_converter = lambda x: x
Expand Down Expand Up @@ -138,7 +138,7 @@ def sql_delete(app, style):
# Drop the table now
output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
style.SQL_TABLE(qn(model._meta.db_table))))
if backend.supports_constraints and model in references_to_delete:
if connection.features.supports_constraints and model in references_to_delete:
for rel_class, f in references_to_delete[model]:
table = rel_class._meta.db_table
col = f.column
Expand Down Expand Up @@ -232,11 +232,11 @@ def sql_model_create(model, style, known_models=set()):
field_output = [style.SQL_FIELD(qn(f.column)),
style.SQL_COLTYPE(col_type)]
field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
if f.unique and (not f.primary_key or backend.allows_unique_and_pk):
if f.unique and (not f.primary_key or connection.features.allows_unique_and_pk):
field_output.append(style.SQL_KEYWORD('UNIQUE'))
if f.primary_key:
field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
if tablespace and backend.supports_tablespaces and (f.unique or f.primary_key) and backend.autoindexes_primary_keys:
if tablespace and connection.features.supports_tablespaces and (f.unique or f.primary_key) and connection.features.autoindexes_primary_keys:
# We must specify the index tablespace inline, because we
# won't be generating a CREATE INDEX statement for this field.
field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))
Expand Down Expand Up @@ -264,7 +264,7 @@ def sql_model_create(model, style, known_models=set()):
for i, line in enumerate(table_output): # Combine and add commas.
full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
full_statement.append(')')
if opts.db_tablespace and backend.supports_tablespaces:
if opts.db_tablespace and connection.features.supports_tablespaces:
full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace))
full_statement.append(';')
final_output.append('\n'.join(full_statement))
Expand All @@ -287,7 +287,7 @@ def sql_for_pending_references(model, style, pending_references):

qn = connection.ops.quote_name
final_output = []
if backend.supports_constraints:
if connection.features.supports_constraints:
opts = model._meta
if model in pending_references:
for rel_class, f in pending_references[model]:
Expand Down Expand Up @@ -316,7 +316,7 @@ def many_to_many_sql_for_model(model, style):
for f in opts.many_to_many:
if not isinstance(f.rel, generic.GenericRel):
tablespace = f.db_tablespace or opts.db_tablespace
if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys:
if tablespace and connection.features.supports_tablespaces and connection.features.autoindexes_primary_keys:
tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace, inline=True)
else:
tablespace_sql = ''
Expand Down Expand Up @@ -347,7 +347,7 @@ def many_to_many_sql_for_model(model, style):
style.SQL_FIELD(qn(f.m2m_reverse_name())),
tablespace_sql))
table_output.append(')')
if opts.db_tablespace and backend.supports_tablespaces:
if opts.db_tablespace and connection.features.supports_tablespaces:
# f.db_tablespace is only for indices, so ignore its value here.
table_output.append(connection.ops.tablespace_sql(opts.db_tablespace))
table_output.append(';')
Expand Down Expand Up @@ -395,10 +395,10 @@ def sql_indexes_for_model(model, style):

qn = connection.ops.quote_name
for f in model._meta.fields:
if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys):
if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
unique = f.unique and 'UNIQUE ' or ''
tablespace = f.db_tablespace or model._meta.db_tablespace
if tablespace and backend.supports_tablespaces:
if tablespace and connection.features.supports_tablespaces:
tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace)
else:
tablespace_sql = ''
Expand Down
10 changes: 10 additions & 0 deletions django/db/backends/__init__.py
Expand Up @@ -39,6 +39,16 @@ def make_debug_cursor(self, cursor):
from django.db.backends import util
return util.CursorDebugWrapper(cursor, self)

class BaseDatabaseFeatures(object):
allows_group_by_ordinal = True
allows_unique_and_pk = True
autoindexes_primary_keys = True
needs_datetime_string_cast = True
needs_upper_for_iops = False
supports_constraints = True
supports_tablespaces = False
uses_case_insensitive_names = False

class BaseDatabaseOperations(object):
"""
This class encapsulates all backend-specific differences, such as the way
Expand Down
15 changes: 5 additions & 10 deletions django/db/backends/ado_mssql/base.py
Expand Up @@ -4,7 +4,7 @@
Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
"""

from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
try:
import adodbapi as Database
except ImportError, e:
Expand Down Expand Up @@ -48,6 +48,9 @@ def variantToPython(variant, adType):
return res
Database.convertVariantToPython = variantToPython

class DatabaseFeatures(BaseDatabaseFeatures):
supports_tablespaces = True

class DatabaseOperations(BaseDatabaseOperations):
def date_extract_sql(self, lookup_type, field_name):
return "DATEPART(%s, %s)" % (lookup_type, field_name)
Expand Down Expand Up @@ -79,6 +82,7 @@ def tablespace_sql(self, tablespace, inline=False):
return "ON %s" % self.quote_name(tablespace)

class DatabaseWrapper(BaseDatabaseWrapper):
features = DatabaseFeatures()
ops = DatabaseOperations()

def _cursor(self, settings):
Expand All @@ -93,15 +97,6 @@ def _cursor(self, settings):
self.connection = Database.connect(conn_string)
return self.connection.cursor()

allows_group_by_ordinal = True
allows_unique_and_pk = True
autoindexes_primary_keys = True
needs_datetime_string_cast = True
needs_upper_for_iops = False
supports_constraints = True
supports_tablespaces = True
uses_case_insensitive_names = False

OPERATOR_MAPPING = {
'exact': '= %s',
'iexact': 'LIKE %s',
Expand Down
8 changes: 3 additions & 5 deletions django/db/backends/dummy/base.py
Expand Up @@ -21,12 +21,13 @@ class DatabaseError(Exception):
class IntegrityError(DatabaseError):
pass

class DatabaseOperations(object):
class ComplainOnGetattr(object):
def __getattr__(self, *args, **kwargs):
complain()

class DatabaseWrapper(object):
ops = DatabaseOperations()
ops = ComplainOnGetattr()
features = ComplainOnGetattr()
cursor = complain
_commit = complain
_rollback = ignore
Expand All @@ -37,7 +38,4 @@ def __init__(self, **kwargs):
def close(self):
pass # close()

supports_constraints = False
supports_tablespaces = False

OPERATOR_MAPPING = {}
15 changes: 5 additions & 10 deletions django/db/backends/mysql/base.py
Expand Up @@ -4,7 +4,7 @@
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
"""

from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
try:
import MySQLdb as Database
except ImportError, e:
Expand Down Expand Up @@ -53,6 +53,9 @@
# standard util.CursorDebugWrapper can be used. Also, using sql_mode
# TRADITIONAL will automatically cause most warnings to be treated as errors.

class DatabaseFeatures(BaseDatabaseFeatures):
autoindexes_primary_keys = False

class DatabaseOperations(BaseDatabaseOperations):
def date_extract_sql(self, lookup_type, field_name):
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
Expand Down Expand Up @@ -116,6 +119,7 @@ def sql_flush(self, style, tables, sequences):
return []

class DatabaseWrapper(BaseDatabaseWrapper):
features = DatabaseFeatures()
ops = DatabaseOperations()

def __init__(self, **kwargs):
Expand Down Expand Up @@ -175,15 +179,6 @@ def get_server_version(self):
self.server_version = tuple([int(x) for x in m.groups()])
return self.server_version

allows_group_by_ordinal = True
allows_unique_and_pk = True
autoindexes_primary_keys = False
needs_datetime_string_cast = True # MySQLdb requires a typecast for dates
needs_upper_for_iops = False
supports_constraints = True
supports_tablespaces = False
uses_case_insensitive_names = False

OPERATOR_MAPPING = {
'exact': '= %s',
'iexact': 'LIKE %s',
Expand Down
15 changes: 5 additions & 10 deletions django/db/backends/mysql_old/base.py
Expand Up @@ -4,7 +4,7 @@
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
"""

from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
from django.utils.encoding import force_unicode
try:
import MySQLdb as Database
Expand Down Expand Up @@ -63,6 +63,9 @@ def __getattr__(self, attr):
else:
return getattr(self.cursor, attr)

class DatabaseFeatures(BaseDatabaseFeatures):
autoindexes_primary_keys = False

class DatabaseOperations(BaseDatabaseOperations):
def date_extract_sql(self, lookup_type, field_name):
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
Expand Down Expand Up @@ -126,6 +129,7 @@ def sql_flush(self, style, tables, sequences):
return []

class DatabaseWrapper(BaseDatabaseWrapper):
features = DatabaseFeatures()
ops = DatabaseOperations()

def __init__(self, **kwargs):
Expand Down Expand Up @@ -194,15 +198,6 @@ def get_server_version(self):
self.server_version = tuple([int(x) for x in m.groups()])
return self.server_version

allows_group_by_ordinal = True
allows_unique_and_pk = True
autoindexes_primary_keys = False
needs_datetime_string_cast = True # MySQLdb requires a typecast for dates
needs_upper_for_iops = False
supports_constraints = True
supports_tablespaces = False
uses_case_insensitive_names = False

OPERATOR_MAPPING = {
'exact': '= %s',
'iexact': 'LIKE %s',
Expand Down
20 changes: 10 additions & 10 deletions django/db/backends/oracle/base.py
Expand Up @@ -4,7 +4,7 @@
Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
"""

from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
from django.utils.datastructures import SortedDict
from django.utils.encoding import smart_str, force_unicode
import datetime
Expand All @@ -21,6 +21,14 @@
DatabaseError = Database.Error
IntegrityError = Database.IntegrityError

class DatabaseFeatures(BaseDatabaseFeatures):
allows_group_by_ordinal = False
allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259)
needs_datetime_string_cast = False
needs_upper_for_iops = True
supports_tablespaces = True
uses_case_insensitive_names = True

class DatabaseOperations(BaseDatabaseOperations):
def autoinc_sql(self, table):
# To simulate auto-incrementing primary keys in Oracle, we have to
Expand Down Expand Up @@ -128,6 +136,7 @@ def tablespace_sql(self, tablespace, inline=False):
return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace))

class DatabaseWrapper(BaseDatabaseWrapper):
features = DatabaseFeatures()
ops = DatabaseOperations()

def _valid_connection(self):
Expand All @@ -151,15 +160,6 @@ def _cursor(self, settings):
cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
return cursor

allows_group_by_ordinal = False
allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259)
autoindexes_primary_keys = True
needs_datetime_string_cast = False
needs_upper_for_iops = True
supports_constraints = True
supports_tablespaces = True
uses_case_insensitive_names = True

class FormatStylePlaceholderCursor(Database.Cursor):
"""
Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
Expand Down
15 changes: 5 additions & 10 deletions django/db/backends/postgresql/base.py
Expand Up @@ -5,7 +5,7 @@
"""

from django.utils.encoding import smart_str, smart_unicode
from django.db.backends import BaseDatabaseWrapper, util
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util
from django.db.backends.postgresql.operations import DatabaseOperations
try:
import psycopg as Database
Expand Down Expand Up @@ -56,7 +56,11 @@ def __getattr__(self, attr):
else:
return getattr(self.cursor, attr)

class DatabaseFeatures(BaseDatabaseFeatures):
pass # This backend uses all the defaults.

class DatabaseWrapper(BaseDatabaseWrapper):
features = DatabaseFeatures()
ops = DatabaseOperations()

def _cursor(self, settings):
Expand Down Expand Up @@ -87,15 +91,6 @@ def _cursor(self, settings):
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
return cursor

allows_group_by_ordinal = True
allows_unique_and_pk = True
autoindexes_primary_keys = True
needs_datetime_string_cast = True
needs_upper_for_iops = False
supports_constraints = True
supports_tablespaces = False
uses_case_insensitive_names = False

def typecast_string(s):
"""
Cast all returned strings to unicode strings.
Expand Down
15 changes: 5 additions & 10 deletions django/db/backends/postgresql_psycopg2/base.py
Expand Up @@ -4,7 +4,7 @@
Requires psycopg 2: http://initd.org/projects/psycopg2
"""

from django.db.backends import BaseDatabaseWrapper
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
from django.db.backends.postgresql.operations import DatabaseOperations
try:
import psycopg2 as Database
Expand All @@ -18,7 +18,11 @@

psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)

class DatabaseFeatures(BaseDatabaseFeatures):
needs_datetime_string_cast = False

class DatabaseWrapper(BaseDatabaseWrapper):
features = DatabaseFeatures()
ops = DatabaseOperations()

def _cursor(self, settings):
Expand Down Expand Up @@ -49,15 +53,6 @@ def _cursor(self, settings):
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
return cursor

allows_group_by_ordinal = True
allows_unique_and_pk = True
autoindexes_primary_keys = True
needs_datetime_string_cast = False
needs_upper_for_iops = False
supports_constraints = True
supports_tablespaces = False
uses_case_insensitive_names = False

OPERATOR_MAPPING = {
'exact': '= %s',
'iexact': 'ILIKE %s',
Expand Down

0 comments on commit 1a8f9b2

Please sign in to comment.