2 from django.core.management.color import no_style
3 from django.db import connection, transaction, models
4 from django.db.backends.util import truncate_name
5 from django.dispatch import dispatcher
6 from django.conf import settings
8 class DatabaseOperations(object):
11 Generic SQL implementation of the DatabaseOperations.
12 Some of this code comes from Django Evolution.
17 self.deferred_sql = []
20 def execute(self, sql, params=[]):
22 Executes the given SQL statement, with optional parameters.
23 If the instance's debug attribute is True, prints out what it executes.
25 cursor = connection.cursor()
27 print " = %s" % sql, params
28 cursor.execute(sql, params)
30 return cursor.fetchall()
35 def add_deferred_sql(self, sql):
37 Add a SQL statement to the deferred list, that won't be executed until
38 this instance's execute_deferred_sql method is run.
40 self.deferred_sql.append(sql)
43 def execute_deferred_sql(self):
45 Executes all deferred SQL, resetting the deferred_sql list
47 for sql in self.deferred_sql:
50 self.deferred_sql = []
53 def create_table(self, table_name, fields):
55 Creates the table 'table_name'. 'fields' is a tuple of fields,
56 each repsented by a 2-part tuple of field name and a
57 django.db.models.fields.Field object
59 qn = connection.ops.quote_name
61 self.column_sql(table_name, field_name, field)
62 for field_name, field in fields
65 self.execute('CREATE TABLE %s (%s);' % (qn(table_name), ', '.join([col for col in columns if col])))
67 add_table = create_table # Alias for consistency's sake
70 def rename_table(self, old_table_name, table_name):
72 Renames the table 'old_table_name' to 'table_name'.
74 if old_table_name == table_name:
77 qn = connection.ops.quote_name
78 params = (qn(old_table_name), qn(table_name))
79 self.execute('ALTER TABLE %s RENAME TO %s;' % params)
82 def delete_table(self, table_name):
84 Deletes the table 'table_name'.
86 qn = connection.ops.quote_name
87 params = (qn(table_name), )
88 self.execute('DROP TABLE %s;' % params)
90 drop_table = delete_table
93 def add_column(self, table_name, name, field):
95 Adds the column 'name' to the table 'table_name'.
96 Uses the 'field' paramater, a django.db.models.fields.Field instance,
97 to generate the necessary sql
99 @param table_name: The name of the table to add the column to
100 @param name: The name of the column to add
101 @param field: The field to use
103 qn = connection.ops.quote_name
104 sql = self.column_sql(table_name, name, field)
110 sql = 'ALTER TABLE %s ADD COLUMN %s;' % params
114 alter_string_set_type = 'ALTER COLUMN %(column)s TYPE %(type)s'
115 alter_string_set_null = 'ALTER COLUMN %(column)s SET NOT NULL'
116 alter_string_drop_null = 'ALTER COLUMN %(column)s DROP NOT NULL'
118 def alter_column(self, table_name, name, field):
120 Alters the given column name so it will match the given field.
121 Note that conversion between the two by the database must be possible.
123 @param table_name: The name of the table to add the column to
124 @param name: The name of the column to alter
125 @param field: The new field definition to use
128 # hook for the field to do any resolution prior to it's attributes being queried
129 if hasattr(field, 'south_init'):
132 qn = connection.ops.quote_name
134 # First, change the type
137 "type": field.db_type(),
139 sqls = [self.alter_string_set_type % params]
142 # Next, set any default
147 if not field.null and field.has_default():
148 default = field.get_default()
149 if isinstance(default, basestring):
150 default = "'%s'" % default
151 params += ("SET DEFAULT %s",)
153 params += ("DROP DEFAULT",)
155 sqls.append('ALTER COLUMN %s %s ' % params)
161 "type": field.db_type(),
164 sqls.append(self.alter_string_drop_null % params)
166 sqls.append(self.alter_string_set_null % params)
171 self.execute("ALTER TABLE %s %s;" % (qn(table_name), ", ".join(sqls)))
174 def column_sql(self, table_name, field_name, field, tablespace=''):
176 Creates the SQL snippet for a column. Used by add_column and add_table.
178 qn = connection.ops.quote_name
180 field.set_attributes_from_name(field_name)
182 # hook for the field to do any resolution prior to it's attributes being queried
183 if hasattr(field, 'south_init'):
186 sql = field.db_type()
188 field_output = [qn(field.column), sql]
189 field_output.append('%sNULL' % (not field.null and 'NOT ' or ''))
190 if field.primary_key:
191 field_output.append('PRIMARY KEY')
193 field_output.append('UNIQUE')
195 tablespace = field.db_tablespace or tablespace
196 if tablespace and connection.features.supports_tablespaces and field.unique:
197 # We must specify the index tablespace inline, because we
198 # won't be generating a CREATE INDEX statement for this field.
199 field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))
201 sql = ' '.join(field_output)
203 # if the field is "NOT NULL" and a default value is provided, create the column with it
204 # this allows the addition of a NOT NULL field to a table with existing rows
205 if not field.null and field.has_default():
206 default = field.get_default()
207 if isinstance(default, basestring):
208 default = "'%s'" % default.replace("'", "''")
210 sqlparams = (default)
213 self.add_deferred_sql(
214 self.foreign_key_sql(
217 field.rel.to._meta.db_table,
218 field.rel.to._meta.get_field(field.rel.field_name).column
222 if field.db_index and not field.unique:
223 self.add_deferred_sql(self.create_index_sql(table_name, [field.column]))
225 if hasattr(field, 'post_create_sql'):
227 for stmt in field.post_create_sql(style, table_name):
228 self.add_deferred_sql(stmt)
231 return sql % sqlparams
235 def foreign_key_sql(self, from_table_name, from_column_name, to_table_name, to_column_name):
237 Generates a full SQL statement to add a foreign key constraint
239 constraint_name = '%s_refs_%s_%x' % (from_column_name, to_column_name, abs(hash((from_table_name, to_table_name))))
240 return 'ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % (
242 truncate_name(constraint_name, connection.ops.max_name_length()),
246 connection.ops.deferrable_sql() # Django knows this
249 def create_index_name(self, table_name, column_names):
251 Generate a unique name for the index
253 index_unique_name = ''
254 if len(column_names) > 1:
255 index_unique_name = '_%x' % abs(hash((table_name, ','.join(column_names))))
257 return '%s_%s%s' % (table_name, column_names[0], index_unique_name)
259 def create_index_sql(self, table_name, column_names, unique=False, db_tablespace=''):
261 Generates a create index statement on 'table_name' for a list of 'column_names'
264 print "No column names supplied on which to create an index"
267 if db_tablespace and connection.features.supports_tablespaces:
268 tablespace_sql = ' ' + connection.ops.tablespace_sql(db_tablespace)
272 index_name = self.create_index_name(table_name, column_names)
273 qn = connection.ops.quote_name
274 return 'CREATE %sINDEX %s ON %s (%s)%s;' % (
275 unique and 'UNIQUE ' or '',
278 ','.join([qn(field) for field in column_names]),
282 def create_index(self, table_name, column_names, unique=False, db_tablespace=''):
283 """ Executes a create index statement """
284 sql = self.create_index_sql(table_name, column_names, unique, db_tablespace)
288 def delete_index(self, table_name, column_names, db_tablespace=''):
290 Deletes an index created with create_index.
291 This is possible using only columns due to the deterministic
292 index naming function which relies on column names.
294 name = self.create_index_name(table_name, column_names)
295 sql = "DROP INDEX %s" % name
299 def delete_column(self, table_name, name):
301 Deletes the column 'column_name' from the table 'table_name'.
303 qn = connection.ops.quote_name
304 params = (qn(table_name), qn(name))
305 self.execute('ALTER TABLE %s DROP COLUMN %s CASCADE;' % params, [])
308 def rename_column(self, table_name, old, new):
310 Renames the column 'old' from the table 'table_name' to 'new'.
312 raise NotImplementedError("rename_column has no generic SQL syntax")
315 def start_transaction(self):
317 Makes sure the following commands are inside a transaction.
318 Must be followed by a (commit|rollback)_transaction call.
320 transaction.commit_unless_managed()
321 transaction.enter_transaction_management()
322 transaction.managed(True)
325 def commit_transaction(self):
327 Commits the current transaction.
328 Must be preceded by a start_transaction call.
331 transaction.leave_transaction_management()
334 def rollback_transaction(self):
336 Rolls back the current transaction.
337 Must be preceded by a start_transaction call.
339 transaction.rollback()
340 transaction.leave_transaction_management()
343 def send_create_signal(self, app_label, model_names):
345 Sends a post_syncdb signal for the model specified.
347 If the model is not found (perhaps it's been deleted?),
350 TODO: The behavior of django.contrib.* apps seems flawed in that
351 they don't respect created_models. Rather, they blindly execute
352 over all models within the app sending the signal. This is a
353 patch we should push Django to make For now, this should work.
355 app = models.get_app(app_label)
360 for model_name in model_names:
361 model = models.get_model(app_label, model_name)
363 created_models.append(model)
366 # syncdb defaults -- perhaps take these as options?
370 if hasattr(dispatcher, "send"):
371 dispatcher.send(signal=models.signals.post_syncdb, sender=app,
372 app=app, created_models=created_models,
373 verbosity=verbosity, interactive=interactive)
375 models.signals.post_syncdb.send(sender=app,
376 app=app, created_models=created_models,
377 verbosity=verbosity, interactive=interactive)
379 def mock_model(self, model_name, db_table, db_tablespace='',
380 pk_field_name='id', pk_field_type=models.AutoField,
383 Generates a MockModel class that provides enough information
384 to be used by a foreign key/many-to-many relationship.
386 Migrations should prefer to use these rather than actual models
387 as models could get deleted over time, but these can remain in
388 migration files forever.
390 class MockOptions(object):
392 self.db_table = db_table
393 self.db_tablespace = db_tablespace or settings.DEFAULT_TABLESPACE
394 self.object_name = model_name
395 self.module_name = model_name.lower()
397 if pk_field_type == models.AutoField:
398 pk_field_kwargs['primary_key'] = True
400 self.pk = pk_field_type(**pk_field_kwargs)
401 self.pk.set_attributes_from_name(pk_field_name)
402 self.abstract = False
404 def get_field_by_name(self, field_name):
405 # we only care about the pk field
406 return (self.pk, self.model, True, False)
408 def get_field(self, name):
409 # we only care about the pk field
412 class MockModel(object):
415 # We need to return an actual class object here, not an instance
416 MockModel._meta = MockOptions()
417 MockModel._meta.model = MockModel