From db79faa3285361e6f6778bfc003edd8844196f7e Mon Sep 17 00:00:00 2001 From: Derek Anderson Date: Fri, 3 Aug 2007 22:38:44 +0000 Subject: [PATCH] schema-evolution: added "default" support so when you add a not null column to a non-empty table you don't get an sql exception git-svn-id: http://code.djangoproject.com/svn/django/branches/schema-evolution@5794 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/schema_evolution.py | 4 +- django/db/backends/mysql/base.py | 9 ++++- django/db/backends/postgresql/base.py | 8 +++- .../db/backends/postgresql_psycopg2/base.py | 8 +++- django/db/backends/sqlite3/base.py | 7 +++- tests/modeltests/schema_evolution/models.py | 40 +++++++++++++++++-- 6 files changed, 62 insertions(+), 14 deletions(-) diff --git a/django/core/schema_evolution.py b/django/core/schema_evolution.py index 76660827d31db..55f3dea1c447b 100644 --- a/django/core/schema_evolution.py +++ b/django/core/schema_evolution.py @@ -25,7 +25,7 @@ def get_sql_evolution_check_for_new_fields(klass, new_table_name): data_type = f.get_internal_type() col_type = data_types.get(data_type) if col_type is not None: - output.extend( backend.get_add_column_sql( klass._meta.db_table, f.column, management.style.SQL_COLTYPE(col_type % rel_field.__dict__), f.null, f.unique, f.primary_key ) ) + output.extend( backend.get_add_column_sql( klass._meta.db_table, f.column, management.style.SQL_COLTYPE(col_type % rel_field.__dict__), f.null, f.unique, f.primary_key, f.default ) ) return output def get_sql_evolution_check_for_changed_model_name(klass): @@ -120,7 +120,7 @@ def get_sql_evolution_check_for_changed_field_flags(klass, new_table_name): # col_def += ' '+ style.SQL_KEYWORD('UNIQUE') # if f.primary_key: # col_def += ' '+ style.SQL_KEYWORD('PRIMARY KEY') - output.extend( backend.get_change_column_def_sql( klass._meta.db_table, cf, col_type_def, f.null, f.unique, f.primary_key ) ) + output.extend( backend.get_change_column_def_sql( klass._meta.db_table, cf, col_type_def, f.null, f.unique, f.primary_key, f.default ) ) #print db_table, cf, f.maxlength, introspection.get_known_column_flags(cursor, db_table, cf) return output diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 9676e09390051..9827ccce75702 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -256,17 +256,19 @@ def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, output.append( 'ALTER TABLE '+ quote_name(table_name) +' CHANGE COLUMN '+ quote_name(old_col_name) +' '+ quote_name(new_col_name) +' '+ col_def + ';' ) return output -def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key ): +def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key, default ): output = [] col_def = col_type +' '+ ('%sNULL' % (not null and 'NOT ' or '')) if unique: col_def += ' '+ 'UNIQUE' if primary_key: col_def += ' '+ 'PRIMARY KEY' + if default and str(default) != 'django.db.models.fields.NOT_PROVIDED': + col_def += ' '+ 'DEFAULT '+ quote_name(str(default)) output.append( 'ALTER TABLE '+ quote_name(table_name) +' MODIFY COLUMN '+ quote_name(col_name) +' '+ col_def + ';' ) return output -def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key ): +def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key, default ): output = [] field_output = [] field_output.append('ALTER TABLE') @@ -279,6 +281,9 @@ def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_ke field_output.append(('UNIQUE')) if primary_key: field_output.append(('PRIMARY KEY')) + if default and str(default) != 'django.db.models.fields.NOT_PROVIDED': + field_output.append(('DEFAULT')) + field_output.append((quote_name(str(default)))) output.append(' '.join(field_output) + ';') return output diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index e5ae6a6f45f69..b24f71b5edf42 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -297,12 +297,14 @@ def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, output.append( 'ALTER TABLE '+ quote_name(table_name) +' RENAME COLUMN '+ quote_name(old_col_name) +' TO '+ quote_name(new_col_name) +';' ) return output -def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key ): +def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key, default ): output = [] output.append( 'ALTER TABLE '+ quote_name(table_name) +' ADD COLUMN '+ quote_name(col_name+'_tmp') +' '+ col_type + ';' ) output.append( 'UPDATE '+ quote_name(table_name) +' SET '+ quote_name(col_name+'_tmp') +' = '+ quote_name(col_name) + ';' ) output.append( 'ALTER TABLE '+ quote_name(table_name) +' DROP COLUMN '+ quote_name(col_name) +';' ) output.append( 'ALTER TABLE '+ quote_name(table_name) +' RENAME COLUMN '+ quote_name(col_name+'_tmp') +' TO '+ quote_name(col_name) + ';' ) + if default and str(default) != 'django.db.models.fields.NOT_PROVIDED': + output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET DEFAULT '+ quote_name(str(default)) +';' ) if not null: output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET NOT NULL;' ) if unique: @@ -310,9 +312,11 @@ def get_change_column_def_sql( table_name, col_name, col_type, null, unique, pri return output -def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key ): +def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key, default ): output = [] output.append( 'ALTER TABLE '+ quote_name(table_name) +' ADD COLUMN '+ quote_name(col_name) +' '+ col_type + ';' ) + if default and str(default) != 'django.db.models.fields.NOT_PROVIDED': + output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET DEFAULT '+ quote_name(str(default)) +';' ) if not null: output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET NOT NULL;' ) if unique: diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index d86a5c6d49a8e..7e89d51cd0fdd 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -239,12 +239,14 @@ def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, output.append( 'ALTER TABLE '+ quote_name(table_name) +' RENAME COLUMN '+ quote_name(old_col_name) +' TO '+ quote_name(new_col_name) +';' ) return output -def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key ): +def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key, default ): output = [] output.append( 'ALTER TABLE '+ quote_name(table_name) +' ADD COLUMN '+ quote_name(col_name+'_tmp') +' '+ col_type + ';' ) output.append( 'UPDATE '+ quote_name(table_name) +' SET '+ quote_name(col_name+'_tmp') +' = '+ quote_name(col_name) + ';' ) output.append( 'ALTER TABLE '+ quote_name(table_name) +' DROP COLUMN '+ quote_name(col_name) +';' ) output.append( 'ALTER TABLE '+ quote_name(table_name) +' RENAME COLUMN '+ quote_name(col_name+'_tmp') +' TO '+ quote_name(col_name) + ';' ) + if default and str(default) != 'django.db.models.fields.NOT_PROVIDED': + output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET DEFAULT '+ quote_name(str(default)) +';' ) if not null: output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET NOT NULL;' ) if unique: @@ -252,9 +254,11 @@ def get_change_column_def_sql( table_name, col_name, col_type, null, unique, pri return output -def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key ): +def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key, default ): output = [] output.append( 'ALTER TABLE '+ quote_name(table_name) +' ADD COLUMN '+ quote_name(col_name) +' '+ col_type + ';' ) + if default and str(default) != 'django.db.models.fields.NOT_PROVIDED': + output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET DEFAULT '+ quote_name(str(default)) +';' ) if not null: output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET NOT NULL;' ) if unique: diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index d9bc82fe16473..8edac29420305 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -241,7 +241,7 @@ def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, return output -def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key ): +def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key, default ): # sqlite doesn't support column modifications, so we fake it model = get_model_from_table_name(table_name) @@ -262,7 +262,7 @@ def get_change_column_def_sql( table_name, col_name, col_type, null, unique, pri return output -def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key ): +def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key, default ): output = [] field_output = [] field_output.append('ALTER TABLE') @@ -275,6 +275,9 @@ def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_ke field_output.append(('UNIQUE')) if primary_key: field_output.append(('PRIMARY KEY')) + if default and str(default) != 'django.db.models.fields.NOT_PROVIDED': + field_output.append(('DEFAULT')) + field_output.append((quote_name(str(default)))) output.append(' '.join(field_output) + ';') return output diff --git a/tests/modeltests/schema_evolution/models.py b/tests/modeltests/schema_evolution/models.py index 4118f94625563..c0809055e66f2 100644 --- a/tests/modeltests/schema_evolution/models.py +++ b/tests/modeltests/schema_evolution/models.py @@ -22,7 +22,7 @@ class Meta: aka = ('PersonOld', 'OtherBadName') class Muebles(models.Model): - tipo = models.CharField(maxlength=40) + tipo = models.CharField(maxlength=40, default="woot") # new fields fecha_publicacion = models.DateTimeField('date published') @@ -97,13 +97,24 @@ class Muebles(models.Model): >>> cursor.execute('DROP TABLE schema_evolution_person;'); cursor.execute(create_table_sql[0]) 0L\n0L -# delete a datetime column pair, so it looks like we've recently added a datetime field +# delete a datetime column, so it looks like we've recently added a datetime field >>> for sql in backend.get_drop_column_sql( 'schema_evolution_muebles', 'fecha_publicacion' ): print sql; cursor.execute(sql) ALTER TABLE `schema_evolution_muebles` DROP COLUMN `fecha_publicacion`; 0L >>> management.get_sql_evolution(app) ['ALTER TABLE `schema_evolution_muebles` ADD COLUMN `fecha_publicacion` datetime NOT NULL;'] +# reset the db +>>> cursor.execute('DROP TABLE schema_evolution_muebles;'); cursor.execute(create_table_sql[1]) +0L\n0L + +# delete a column with a default value, so it looks like we've recently added a column +>>> for sql in backend.get_drop_column_sql( 'schema_evolution_muebles', 'tipo' ): print sql; cursor.execute(sql) +ALTER TABLE `schema_evolution_muebles` DROP COLUMN `tipo`; +0L +>>> management.get_sql_evolution(app) +['ALTER TABLE `schema_evolution_muebles` ADD COLUMN `tipo` varchar(40) NOT NULL DEFAULT `woot`;'] + """ if settings.DATABASE_ENGINE == 'postgresql' or settings.DATABASE_ENGINE == 'postgresql_psycopg2' : @@ -124,7 +135,7 @@ class Muebles(models.Model): >>> cursor.execute('DROP TABLE schema_evolution_person;'); cursor.execute(create_table_sql[0]) # add a column, so it looks like we've recently deleted a field ->>> for sql in backend.get_add_column_sql( 'schema_evolution_person', 'gender_nothere', 'varchar(1)', True, False, False ): cursor.execute(sql) +>>> for sql in backend.get_add_column_sql( 'schema_evolution_person', 'gender_nothere', 'varchar(1)', True, False, False, None ): cursor.execute(sql) >>> management.get_sql_evolution(app) ['-- warning: the following may cause data loss', u'ALTER TABLE "schema_evolution_person" DROP COLUMN "gender_nothere";', '-- end warning'] @@ -148,7 +159,7 @@ class Muebles(models.Model): >>> cursor.execute(create_table_sql[0]) # change column flags, so it looks like we've recently changed a column flag ->>> for sql in backend.get_change_column_def_sql( 'schema_evolution_person', 'name', 'varchar(10)', True, False, False ): cursor.execute(sql) +>>> for sql in backend.get_change_column_def_sql( 'schema_evolution_person', 'name', 'varchar(10)', True, False, False, None ): cursor.execute(sql) >>> management.get_sql_evolution(app) ['ALTER TABLE "schema_evolution_person" ADD COLUMN "name_tmp" varchar(20);', 'UPDATE "schema_evolution_person" SET "name_tmp" = "name";', 'ALTER TABLE "schema_evolution_person" DROP COLUMN "name";', 'ALTER TABLE "schema_evolution_person" RENAME COLUMN "name_tmp" TO "name";', 'ALTER TABLE "schema_evolution_person" ALTER COLUMN "name" SET NOT NULL;'] @@ -160,6 +171,15 @@ class Muebles(models.Model): ALTER TABLE "schema_evolution_muebles" DROP COLUMN "fecha_publicacion"; >>> management.get_sql_evolution(app) ['ALTER TABLE "schema_evolution_muebles" ADD COLUMN "fecha_publicacion" timestamp with time zone;', 'ALTER TABLE "schema_evolution_muebles" ALTER COLUMN "fecha_publicacion" SET NOT NULL;'] + +# reset the db +>>> cursor.execute('DROP TABLE schema_evolution_muebles;'); cursor.execute(create_table_sql[1]) + +# delete a column with a default value, so it looks like we've recently added a column +>>> for sql in backend.get_drop_column_sql( 'schema_evolution_muebles', 'tipo' ): print sql; cursor.execute(sql) +ALTER TABLE "schema_evolution_muebles" DROP COLUMN "tipo"; +>>> management.get_sql_evolution(app) +['ALTER TABLE "schema_evolution_muebles" ADD COLUMN "tipo" varchar(40);', 'ALTER TABLE "schema_evolution_muebles" ALTER COLUMN "tipo" SET DEFAULT "woot";', 'ALTER TABLE "schema_evolution_muebles" ALTER COLUMN "tipo" SET NOT NULL;'] """ if settings.DATABASE_ENGINE == 'sqlite3': @@ -276,6 +296,18 @@ class Muebles(models.Model): >>> management.get_sql_evolution(app) ['ALTER TABLE "schema_evolution_muebles" ADD COLUMN "fecha_publicacion" datetime NOT NULL;'] +# reset the db +>>> cursor.execute('DROP TABLE schema_evolution_muebles;').__class__ + +>>> cursor.execute(create_table_sql[1]).__class__ + + +# delete a column with a default value, so it looks like we've recently added a column +>>> for sql in ['DROP TABLE schema_evolution_muebles;','CREATE TABLE "schema_evolution_muebles" ("id" integer NOT NULL UNIQUE PRIMARY KEY,"fecha_publicacion" datetime NOT NULL);']: cursor.execute(sql).__class__ + + +>>> management.get_sql_evolution(app) +['ALTER TABLE "schema_evolution_muebles" ADD COLUMN "tipo" varchar(40) NOT NULL DEFAULT "woot";'] """