Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Implemented BaseDatabaseFeatures and changed all code to access it --…

… 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...
commit 1a8f9b2b97d2549fe28c2d9090fece3f29f029fa 1 parent 4f82250
Adrian Holovaty authored
2  django/core/management/commands/syncdb.py
@@ -34,7 +34,7 @@ def handle_noargs(self, **options):
34 34
         # Get a list of all existing database tables,
35 35
         # so we know what needs to be added.
36 36
         table_list = table_list()
37  
-        if backend.uses_case_insensitive_names:
  37
+        if connection.features.uses_case_insensitive_names:
38 38
             table_name_converter = str.upper
39 39
         else:
40 40
             table_name_converter = lambda x: x
24  django/core/management/sql.py
@@ -15,12 +15,12 @@ def table_list():
15 15
 
16 16
 def installed_models(table_list):
17 17
     "Returns a set of all models that are installed, given a list of existing table names."
18  
-    from django.db import backend, models
  18
+    from django.db import connection, models
19 19
     all_models = []
20 20
     for app in models.get_apps():
21 21
         for model in models.get_models(app):
22 22
             all_models.append(model)
23  
-    if backend.uses_case_insensitive_names:
  23
+    if connection.features.uses_case_insensitive_names:
24 24
         converter = lambda x: x.upper()
25 25
     else:
26 26
         converter = lambda x: x
@@ -110,7 +110,7 @@ def sql_delete(app, style):
110 110
         table_names = introspection.get_table_list(cursor)
111 111
     else:
112 112
         table_names = []
113  
-    if backend.uses_case_insensitive_names:
  113
+    if connection.features.uses_case_insensitive_names:
114 114
         table_name_converter = str.upper
115 115
     else:
116 116
         table_name_converter = lambda x: x
@@ -138,7 +138,7 @@ def sql_delete(app, style):
138 138
             # Drop the table now
139 139
             output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
140 140
                 style.SQL_TABLE(qn(model._meta.db_table))))
141  
-            if backend.supports_constraints and model in references_to_delete:
  141
+            if connection.features.supports_constraints and model in references_to_delete:
142 142
                 for rel_class, f in references_to_delete[model]:
143 143
                     table = rel_class._meta.db_table
144 144
                     col = f.column
@@ -232,11 +232,11 @@ def sql_model_create(model, style, known_models=set()):
232 232
         field_output = [style.SQL_FIELD(qn(f.column)),
233 233
             style.SQL_COLTYPE(col_type)]
234 234
         field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
235  
-        if f.unique and (not f.primary_key or backend.allows_unique_and_pk):
  235
+        if f.unique and (not f.primary_key or connection.features.allows_unique_and_pk):
236 236
             field_output.append(style.SQL_KEYWORD('UNIQUE'))
237 237
         if f.primary_key:
238 238
             field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
239  
-        if tablespace and backend.supports_tablespaces and (f.unique or f.primary_key) and backend.autoindexes_primary_keys:
  239
+        if tablespace and connection.features.supports_tablespaces and (f.unique or f.primary_key) and connection.features.autoindexes_primary_keys:
240 240
             # We must specify the index tablespace inline, because we
241 241
             # won't be generating a CREATE INDEX statement for this field.
242 242
             field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))
@@ -264,7 +264,7 @@ def sql_model_create(model, style, known_models=set()):
264 264
     for i, line in enumerate(table_output): # Combine and add commas.
265 265
         full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
266 266
     full_statement.append(')')
267  
-    if opts.db_tablespace and backend.supports_tablespaces:
  267
+    if opts.db_tablespace and connection.features.supports_tablespaces:
268 268
         full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace))
269 269
     full_statement.append(';')
270 270
     final_output.append('\n'.join(full_statement))
@@ -287,7 +287,7 @@ def sql_for_pending_references(model, style, pending_references):
287 287
 
288 288
     qn = connection.ops.quote_name
289 289
     final_output = []
290  
-    if backend.supports_constraints:
  290
+    if connection.features.supports_constraints:
291 291
         opts = model._meta
292 292
         if model in pending_references:
293 293
             for rel_class, f in pending_references[model]:
@@ -316,7 +316,7 @@ def many_to_many_sql_for_model(model, style):
316 316
     for f in opts.many_to_many:
317 317
         if not isinstance(f.rel, generic.GenericRel):
318 318
             tablespace = f.db_tablespace or opts.db_tablespace
319  
-            if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys:
  319
+            if tablespace and connection.features.supports_tablespaces and connection.features.autoindexes_primary_keys:
320 320
                 tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace, inline=True)
321 321
             else:
322 322
                 tablespace_sql = ''
@@ -347,7 +347,7 @@ def many_to_many_sql_for_model(model, style):
347 347
                 style.SQL_FIELD(qn(f.m2m_reverse_name())),
348 348
                 tablespace_sql))
349 349
             table_output.append(')')
350  
-            if opts.db_tablespace and backend.supports_tablespaces:
  350
+            if opts.db_tablespace and connection.features.supports_tablespaces:
351 351
                 # f.db_tablespace is only for indices, so ignore its value here.
352 352
                 table_output.append(connection.ops.tablespace_sql(opts.db_tablespace))
353 353
             table_output.append(';')
@@ -395,10 +395,10 @@ def sql_indexes_for_model(model, style):
395 395
 
396 396
     qn = connection.ops.quote_name
397 397
     for f in model._meta.fields:
398  
-        if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys):
  398
+        if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
399 399
             unique = f.unique and 'UNIQUE ' or ''
400 400
             tablespace = f.db_tablespace or model._meta.db_tablespace
401  
-            if tablespace and backend.supports_tablespaces:
  401
+            if tablespace and connection.features.supports_tablespaces:
402 402
                 tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace)
403 403
             else:
404 404
                 tablespace_sql = ''
10  django/db/backends/__init__.py
@@ -39,6 +39,16 @@ def make_debug_cursor(self, cursor):
39 39
         from django.db.backends import util
40 40
         return util.CursorDebugWrapper(cursor, self)
41 41
 
  42
+class BaseDatabaseFeatures(object):
  43
+    allows_group_by_ordinal = True
  44
+    allows_unique_and_pk = True
  45
+    autoindexes_primary_keys = True
  46
+    needs_datetime_string_cast = True
  47
+    needs_upper_for_iops = False
  48
+    supports_constraints = True
  49
+    supports_tablespaces = False
  50
+    uses_case_insensitive_names = False
  51
+
42 52
 class BaseDatabaseOperations(object):
43 53
     """
44 54
     This class encapsulates all backend-specific differences, such as the way
15  django/db/backends/ado_mssql/base.py
@@ -4,7 +4,7 @@
4 4
 Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
5 5
 """
6 6
 
7  
-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
  7
+from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
8 8
 try:
9 9
     import adodbapi as Database
10 10
 except ImportError, e:
@@ -48,6 +48,9 @@ def variantToPython(variant, adType):
48 48
     return res
49 49
 Database.convertVariantToPython = variantToPython
50 50
 
  51
+class DatabaseFeatures(BaseDatabaseFeatures):
  52
+    supports_tablespaces = True
  53
+
51 54
 class DatabaseOperations(BaseDatabaseOperations):
52 55
     def date_extract_sql(self, lookup_type, field_name):
53 56
         return "DATEPART(%s, %s)" % (lookup_type, field_name)
@@ -79,6 +82,7 @@ def tablespace_sql(self, tablespace, inline=False):
79 82
         return "ON %s" % self.quote_name(tablespace)
80 83
 
81 84
 class DatabaseWrapper(BaseDatabaseWrapper):
  85
+    features = DatabaseFeatures()
82 86
     ops = DatabaseOperations()
83 87
 
84 88
     def _cursor(self, settings):
@@ -93,15 +97,6 @@ def _cursor(self, settings):
93 97
             self.connection = Database.connect(conn_string)
94 98
         return self.connection.cursor()
95 99
 
96  
-allows_group_by_ordinal = True
97  
-allows_unique_and_pk = True
98  
-autoindexes_primary_keys = True
99  
-needs_datetime_string_cast = True
100  
-needs_upper_for_iops = False
101  
-supports_constraints = True
102  
-supports_tablespaces = True
103  
-uses_case_insensitive_names = False
104  
-
105 100
 OPERATOR_MAPPING = {
106 101
     'exact': '= %s',
107 102
     'iexact': 'LIKE %s',
8  django/db/backends/dummy/base.py
@@ -21,12 +21,13 @@ class DatabaseError(Exception):
21 21
 class IntegrityError(DatabaseError):
22 22
     pass
23 23
 
24  
-class DatabaseOperations(object):
  24
+class ComplainOnGetattr(object):
25 25
     def __getattr__(self, *args, **kwargs):
26 26
         complain()
27 27
 
28 28
 class DatabaseWrapper(object):
29  
-    ops = DatabaseOperations()
  29
+    ops = ComplainOnGetattr()
  30
+    features = ComplainOnGetattr()
30 31
     cursor = complain
31 32
     _commit = complain
32 33
     _rollback = ignore
@@ -37,7 +38,4 @@ def __init__(self, **kwargs):
37 38
     def close(self):
38 39
         pass # close()
39 40
 
40  
-supports_constraints = False
41  
-supports_tablespaces = False
42  
-
43 41
 OPERATOR_MAPPING = {}
15  django/db/backends/mysql/base.py
@@ -4,7 +4,7 @@
4 4
 Requires MySQLdb: http://sourceforge.net/projects/mysql-python
5 5
 """
6 6
 
7  
-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
  7
+from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
8 8
 try:
9 9
     import MySQLdb as Database
10 10
 except ImportError, e:
@@ -53,6 +53,9 @@
53 53
 # standard util.CursorDebugWrapper can be used. Also, using sql_mode
54 54
 # TRADITIONAL will automatically cause most warnings to be treated as errors.
55 55
 
  56
+class DatabaseFeatures(BaseDatabaseFeatures):
  57
+    autoindexes_primary_keys = False
  58
+
56 59
 class DatabaseOperations(BaseDatabaseOperations):
57 60
     def date_extract_sql(self, lookup_type, field_name):
58 61
         # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
@@ -116,6 +119,7 @@ def sql_flush(self, style, tables, sequences):
116 119
             return []
117 120
 
118 121
 class DatabaseWrapper(BaseDatabaseWrapper):
  122
+    features = DatabaseFeatures()
119 123
     ops = DatabaseOperations()
120 124
 
121 125
     def __init__(self, **kwargs):
@@ -175,15 +179,6 @@ def get_server_version(self):
175 179
             self.server_version = tuple([int(x) for x in m.groups()])
176 180
         return self.server_version
177 181
 
178  
-allows_group_by_ordinal = True
179  
-allows_unique_and_pk = True
180  
-autoindexes_primary_keys = False
181  
-needs_datetime_string_cast = True     # MySQLdb requires a typecast for dates
182  
-needs_upper_for_iops = False
183  
-supports_constraints = True
184  
-supports_tablespaces = False
185  
-uses_case_insensitive_names = False
186  
-
187 182
 OPERATOR_MAPPING = {
188 183
     'exact': '= %s',
189 184
     'iexact': 'LIKE %s',
15  django/db/backends/mysql_old/base.py
@@ -4,7 +4,7 @@
4 4
 Requires MySQLdb: http://sourceforge.net/projects/mysql-python
5 5
 """
6 6
 
7  
-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
  7
+from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
8 8
 from django.utils.encoding import force_unicode
9 9
 try:
10 10
     import MySQLdb as Database
@@ -63,6 +63,9 @@ def __getattr__(self, attr):
63 63
         else:
64 64
             return getattr(self.cursor, attr)
65 65
 
  66
+class DatabaseFeatures(BaseDatabaseFeatures):
  67
+    autoindexes_primary_keys = False
  68
+
66 69
 class DatabaseOperations(BaseDatabaseOperations):
67 70
     def date_extract_sql(self, lookup_type, field_name):
68 71
         # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
@@ -126,6 +129,7 @@ def sql_flush(self, style, tables, sequences):
126 129
             return []
127 130
 
128 131
 class DatabaseWrapper(BaseDatabaseWrapper):
  132
+    features = DatabaseFeatures()
129 133
     ops = DatabaseOperations()
130 134
 
131 135
     def __init__(self, **kwargs):
@@ -194,15 +198,6 @@ def get_server_version(self):
194 198
             self.server_version = tuple([int(x) for x in m.groups()])
195 199
         return self.server_version
196 200
 
197  
-allows_group_by_ordinal = True
198  
-allows_unique_and_pk = True
199  
-autoindexes_primary_keys = False
200  
-needs_datetime_string_cast = True     # MySQLdb requires a typecast for dates
201  
-needs_upper_for_iops = False
202  
-supports_constraints = True
203  
-supports_tablespaces = False
204  
-uses_case_insensitive_names = False
205  
-
206 201
 OPERATOR_MAPPING = {
207 202
     'exact': '= %s',
208 203
     'iexact': 'LIKE %s',
20  django/db/backends/oracle/base.py
@@ -4,7 +4,7 @@
4 4
 Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
5 5
 """
6 6
 
7  
-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
  7
+from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
8 8
 from django.utils.datastructures import SortedDict
9 9
 from django.utils.encoding import smart_str, force_unicode
10 10
 import datetime
@@ -21,6 +21,14 @@
21 21
 DatabaseError = Database.Error
22 22
 IntegrityError = Database.IntegrityError
23 23
 
  24
+class DatabaseFeatures(BaseDatabaseFeatures):
  25
+    allows_group_by_ordinal = False
  26
+    allows_unique_and_pk = False        # Suppress UNIQUE/PK for Oracle (ORA-02259)
  27
+    needs_datetime_string_cast = False
  28
+    needs_upper_for_iops = True
  29
+    supports_tablespaces = True
  30
+    uses_case_insensitive_names = True
  31
+
24 32
 class DatabaseOperations(BaseDatabaseOperations):
25 33
     def autoinc_sql(self, table):
26 34
         # To simulate auto-incrementing primary keys in Oracle, we have to
@@ -128,6 +136,7 @@ def tablespace_sql(self, tablespace, inline=False):
128 136
         return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace))
129 137
 
130 138
 class DatabaseWrapper(BaseDatabaseWrapper):
  139
+    features = DatabaseFeatures()
131 140
     ops = DatabaseOperations()
132 141
 
133 142
     def _valid_connection(self):
@@ -151,15 +160,6 @@ def _cursor(self, settings):
151 160
         cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
152 161
         return cursor
153 162
 
154  
-allows_group_by_ordinal = False
155  
-allows_unique_and_pk = False        # Suppress UNIQUE/PK for Oracle (ORA-02259)
156  
-autoindexes_primary_keys = True
157  
-needs_datetime_string_cast = False
158  
-needs_upper_for_iops = True
159  
-supports_constraints = True
160  
-supports_tablespaces = True
161  
-uses_case_insensitive_names = True
162  
-
163 163
 class FormatStylePlaceholderCursor(Database.Cursor):
164 164
     """
165 165
     Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
15  django/db/backends/postgresql/base.py
@@ -5,7 +5,7 @@
5 5
 """
6 6
 
7 7
 from django.utils.encoding import smart_str, smart_unicode
8  
-from django.db.backends import BaseDatabaseWrapper, util
  8
+from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util
9 9
 from django.db.backends.postgresql.operations import DatabaseOperations
10 10
 try:
11 11
     import psycopg as Database
@@ -56,7 +56,11 @@ def __getattr__(self, attr):
56 56
         else:
57 57
             return getattr(self.cursor, attr)
58 58
 
  59
+class DatabaseFeatures(BaseDatabaseFeatures):
  60
+    pass # This backend uses all the defaults.
  61
+
59 62
 class DatabaseWrapper(BaseDatabaseWrapper):
  63
+    features = DatabaseFeatures()
60 64
     ops = DatabaseOperations()
61 65
 
62 66
     def _cursor(self, settings):
@@ -87,15 +91,6 @@ def _cursor(self, settings):
87 91
             self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
88 92
         return cursor
89 93
 
90  
-allows_group_by_ordinal = True
91  
-allows_unique_and_pk = True
92  
-autoindexes_primary_keys = True
93  
-needs_datetime_string_cast = True
94  
-needs_upper_for_iops = False
95  
-supports_constraints = True
96  
-supports_tablespaces = False
97  
-uses_case_insensitive_names = False
98  
-
99 94
 def typecast_string(s):
100 95
     """
101 96
     Cast all returned strings to unicode strings.
15  django/db/backends/postgresql_psycopg2/base.py
@@ -4,7 +4,7 @@
4 4
 Requires psycopg 2: http://initd.org/projects/psycopg2
5 5
 """
6 6
 
7  
-from django.db.backends import BaseDatabaseWrapper
  7
+from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
8 8
 from django.db.backends.postgresql.operations import DatabaseOperations
9 9
 try:
10 10
     import psycopg2 as Database
@@ -18,7 +18,11 @@
18 18
 
19 19
 psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
20 20
 
  21
+class DatabaseFeatures(BaseDatabaseFeatures):
  22
+    needs_datetime_string_cast = False
  23
+
21 24
 class DatabaseWrapper(BaseDatabaseWrapper):
  25
+    features = DatabaseFeatures()
22 26
     ops = DatabaseOperations()
23 27
 
24 28
     def _cursor(self, settings):
@@ -49,15 +53,6 @@ def _cursor(self, settings):
49 53
             self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
50 54
         return cursor
51 55
 
52  
-allows_group_by_ordinal = True
53  
-allows_unique_and_pk = True
54  
-autoindexes_primary_keys = True
55  
-needs_datetime_string_cast = False
56  
-needs_upper_for_iops = False
57  
-supports_constraints = True
58  
-supports_tablespaces = False
59  
-uses_case_insensitive_names = False
60  
-
61 56
 OPERATOR_MAPPING = {
62 57
     'exact': '= %s',
63 58
     'iexact': 'ILIKE %s',
15  django/db/backends/sqlite3/base.py
@@ -2,7 +2,7 @@
2 2
 SQLite3 backend for django.  Requires pysqlite2 (http://pysqlite.org/).
3 3
 """
4 4
 
5  
-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
  5
+from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
6 6
 try:
7 7
     try:
8 8
         from sqlite3 import dbapi2 as Database
@@ -34,6 +34,9 @@
34 34
 Database.register_converter("decimal", util.typecast_decimal)
35 35
 Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal)
36 36
 
  37
+class DatabaseFeatures(BaseDatabaseFeatures):
  38
+    supports_constraints = False
  39
+
37 40
 class DatabaseOperations(BaseDatabaseOperations):
38 41
     def date_extract_sql(self, lookup_type, field_name):
39 42
         # sqlite doesn't support extract, so we fake it with the user-defined
@@ -70,6 +73,7 @@ def sql_flush(self, style, tables, sequences):
70 73
         return sql
71 74
 
72 75
 class DatabaseWrapper(BaseDatabaseWrapper):
  76
+    features = DatabaseFeatures()
73 77
     ops = DatabaseOperations()
74 78
 
75 79
     def _cursor(self, settings):
@@ -111,15 +115,6 @@ def executemany(self, query, param_list):
111 115
     def convert_query(self, query, num_params):
112 116
         return query % tuple("?" * num_params)
113 117
 
114  
-allows_group_by_ordinal = True
115  
-allows_unique_and_pk = True
116  
-autoindexes_primary_keys = True
117  
-needs_datetime_string_cast = True
118  
-needs_upper_for_iops = False
119  
-supports_constraints = False
120  
-supports_tablespaces = False
121  
-uses_case_insensitive_names = False
122  
-
123 118
 def _sqlite_extract(lookup_type, dt):
124 119
     try:
125 120
         dt = util.typecast_timestamp(dt)
6  django/db/models/query.py
@@ -651,7 +651,7 @@ def iterator(self):
651 651
         table_name = qn(self.model._meta.db_table)
652 652
         field_name = qn(self._field.column)
653 653
 
654  
-        if backend.allows_group_by_ordinal:
  654
+        if connection.features.allows_group_by_ordinal:
655 655
             group_by = '1'
656 656
         else:
657 657
             group_by = connection.ops.date_trunc_sql(self._kind, '%s.%s' % (table_name, field_name))
@@ -663,7 +663,7 @@ def iterator(self):
663 663
         cursor.execute(sql, params)
664 664
 
665 665
         has_resolve_columns = hasattr(self, 'resolve_columns')
666  
-        needs_datetime_string_cast = backend.needs_datetime_string_cast
  666
+        needs_datetime_string_cast = connection.features.needs_datetime_string_cast
667 667
         dates = []
668 668
         # It would be better to use self._field here instead of DateTimeField(),
669 669
         # but in Oracle that will result in a list of datetime.date instead of
@@ -796,7 +796,7 @@ def get_where_clause(lookup_type, table_prefix, field_name, value, db_type):
796 796
     else:
797 797
         field_cast_sql = '%s%s'
798 798
     field_sql = field_cast_sql % (table_prefix, field_name)
799  
-    if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops:
  799
+    if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and connection.features.needs_upper_for_iops:
800 800
         format = 'UPPER(%s) %s'
801 801
     else:
802 802
         format = '%s %s'

0 notes on commit 1a8f9b2

Please sign in to comment.
Something went wrong with that request. Please try again.