Use two models in generated migrations. Test column addition and removal.

This commit is contained in:
Gabriel 2011-07-07 19:12:18 +02:00
parent 15f571d296
commit a1968e7f7d
3 changed files with 93 additions and 49 deletions

View File

@ -42,14 +42,15 @@ class TestSchemaDiff(fixture.DB):
def assertDiff(isDiff, tablesMissingInDatabase, tablesMissingInModel, tablesWithDiff): def assertDiff(isDiff, tablesMissingInDatabase, tablesMissingInModel, tablesWithDiff):
diff = schemadiff.getDiffOfModelAgainstDatabase(self.meta, self.engine, excludeTables=['migrate_version']) diff = schemadiff.getDiffOfModelAgainstDatabase(self.meta, self.engine, excludeTables=['migrate_version'])
eq_(bool(diff), isDiff)
eq_( eq_(
(diff.tables_missing_from_B, (diff.tables_missing_from_B,
diff.tables_missing_from_A, diff.tables_missing_from_A,
diff.tables_different.keys()), diff.tables_different.keys(),
bool(diff)),
(tablesMissingInDatabase, (tablesMissingInDatabase,
tablesMissingInModel, tablesMissingInModel,
tablesWithDiff) tablesWithDiff,
isDiff)
) )
# Model is defined but database is empty. # Model is defined but database is empty.
@ -64,8 +65,9 @@ class TestSchemaDiff(fixture.DB):
if repr(String()) == 'String()': if repr(String()) == 'String()':
self.assertEqualsIgnoreWhitespace(decls, ''' self.assertEqualsIgnoreWhitespace(decls, '''
from migrate.changeset import schema from migrate.changeset import schema
meta = MetaData() pre_meta = MetaData()
tmp_schemadiff = Table('tmp_schemadiff', meta, post_meta = MetaData()
tmp_schemadiff = Table('tmp_schemadiff', post_meta,
Column('id', Integer, primary_key=True, nullable=False), Column('id', Integer, primary_key=True, nullable=False),
Column('name', UnicodeText), Column('name', UnicodeText),
Column('data', UnicodeText), Column('data', UnicodeText),
@ -74,19 +76,14 @@ class TestSchemaDiff(fixture.DB):
else: else:
self.assertEqualsIgnoreWhitespace(decls, ''' self.assertEqualsIgnoreWhitespace(decls, '''
from migrate.changeset import schema from migrate.changeset import schema
meta = MetaData() pre_meta = MetaData()
tmp_schemadiff = Table('tmp_schemadiff', meta, post_meta = MetaData()
tmp_schemadiff = Table('tmp_schemadiff', post_meta,
Column('id', Integer, primary_key=True, nullable=False), Column('id', Integer, primary_key=True, nullable=False),
Column('name', UnicodeText(length=None)), Column('name', UnicodeText(length=None)),
Column('data', UnicodeText(length=None)), Column('data', UnicodeText(length=None)),
) )
''') ''')
self.assertEqualsIgnoreWhitespace(upgradeCommands,
'''meta.bind = migrate_engine
tmp_schemadiff.create()''')
self.assertEqualsIgnoreWhitespace(downgradeCommands,
'''meta.bind = migrate_engine
tmp_schemadiff.drop()''')
# Create table in database, now model should match database. # Create table in database, now model should match database.
self._applyLatestModel() self._applyLatestModel()
@ -111,12 +108,14 @@ class TestSchemaDiff(fixture.DB):
else: else:
dataId = result.last_inserted_ids()[0] dataId = result.last_inserted_ids()[0]
# Modify table in model (by removing it and adding it back to model) -- drop column data and add column data2. # Modify table in model (by removing it and adding it back to model)
# Drop column data, add columns data2 and data3.
self.meta.remove(self.table) self.meta.remove(self.table)
self.table = Table(self.table_name,self.meta, self.table = Table(self.table_name,self.meta,
Column('id',Integer(),primary_key=True), Column('id',Integer(),primary_key=True),
Column('name',UnicodeText(length=None)), Column('name',UnicodeText(length=None)),
Column('data2',Integer(),nullable=True), Column('data2',Integer(),nullable=True),
Column('data3',Integer(),nullable=True),
) )
assertDiff(True, [], [], [self.table_name]) assertDiff(True, [], [], [self.table_name])
@ -124,6 +123,36 @@ class TestSchemaDiff(fixture.DB):
self._applyLatestModel() self._applyLatestModel()
assertDiff(False, [], [], []) assertDiff(False, [], [], [])
# Drop column data3, add data4
self.meta.remove(self.table)
self.table = Table(self.table_name,self.meta,
Column('id',Integer(),primary_key=True),
Column('name',UnicodeText(length=None)),
Column('data2',Integer(),nullable=True),
Column('data4',Float(),nullable=True),
)
assertDiff(True, [], [], [self.table_name])
diff = schemadiff.getDiffOfModelAgainstDatabase(
self.meta, self.engine, excludeTables=['migrate_version'])
decls, upgradeCommands, downgradeCommands = genmodel.ModelGenerator(diff,self.engine).genB2AMigration(indent='')
# decls have changed since genBDefinition
exec decls in locals()
# migration commands expect a namespace containing migrate_engine
migrate_engine = self.engine
# run the migration up and down
exec upgradeCommands in locals()
assertDiff(False, [], [], [])
exec decls in locals()
exec downgradeCommands in locals()
assertDiff(True, [], [], [self.table_name])
exec decls in locals()
exec upgradeCommands in locals()
assertDiff(False, [], [], [])
if not self.engine.name == 'oracle': if not self.engine.name == 'oracle':
# Make sure data is still present. # Make sure data is still present.
result = self.engine.execute(self.table.select(self.table.c.id==dataId)) result = self.engine.execute(self.table.select(self.table.c.id==dataId))

View File

@ -158,8 +158,8 @@ def upgrade(migrate_engine):
repository=self.repo_path, repository=self.repo_path,
) )
self.assertTrue('User.create()' in source_script) self.assertTrue("['User'].create()" in source_script)
self.assertTrue('User.drop()' in source_script) self.assertTrue("['User'].drop()" in source_script)
@fixture.usedb() @fixture.usedb()
def test_make_update_script_for_equal_models(self): def test_make_update_script_for_equal_models(self):
@ -196,9 +196,9 @@ def upgrade(migrate_engine):
self.assertTrue(0 self.assertTrue(0
< source_script.find('upgrade') < source_script.find('upgrade')
< source_script.find('User.create()') < source_script.find("['User'].create()")
< source_script.find('downgrade') < source_script.find('downgrade')
< source_script.find('User.drop()')) < source_script.find("['User'].drop()"))
def setup_model_params(self): def setup_model_params(self):
self.script_path = self.tmp_py() self.script_path = self.tmp_py()

View File

@ -96,7 +96,7 @@ class ModelGenerator(object):
else: else:
return """Column(%(name)r, %(commonStuff)s)""" % data return """Column(%(name)r, %(commonStuff)s)""" % data
def _getTableDefn(self, table): def _getTableDefn(self, table, metaName='meta'):
out = [] out = []
tableName = table.name tableName = table.name
if self.declarative: if self.declarative:
@ -107,8 +107,8 @@ class ModelGenerator(object):
out.append(" %s" % self.column_repr(col)) out.append(" %s" % self.column_repr(col))
out.append('\n') out.append('\n')
else: else:
out.append("%(table)s = Table('%(table)s', meta," % out.append("%(table)s = Table('%(table)s', %(meta)s," %
{'table': tableName}) {'table': tableName, 'meta': metaName})
for col in table.columns: for col in table.columns:
out.append(" %s," % self.column_repr(col)) out.append(" %s," % self.column_repr(col))
out.append(")\n") out.append(")\n")
@ -151,46 +151,61 @@ class ModelGenerator(object):
''' '''
decls = ['from migrate.changeset import schema', decls = ['from migrate.changeset import schema',
'meta = MetaData()'] 'pre_meta = MetaData()',
for table in self._get_tables( 'post_meta = MetaData()',
missingA=True,missingB=True,modified=True ]
): upgradeCommands = ['pre_meta.bind = migrate_engine',
decls.extend(self._getTableDefn(table)) 'post_meta.bind = migrate_engine']
downgradeCommands = list(upgradeCommands)
upgradeCommands, downgradeCommands = [], [] for tn in self.diff.tables_missing_from_A:
for tableName in self.diff.tables_missing_from_A: pre_table = self.diff.metadataB.tables[tn]
upgradeCommands.append("%(table)s.drop()" % {'table': tableName}) decls.extend(self._getTableDefn(pre_table, metaName='pre_meta'))
downgradeCommands.append("%(table)s.create()" % \ upgradeCommands.append(
{'table': tableName}) "pre_meta.tables[%(table)r].drop()" % {'table': tn})
for tableName in self.diff.tables_missing_from_B: downgradeCommands.append(
upgradeCommands.append("%(table)s.create()" % {'table': tableName}) "pre_meta.tables[%(table)r].create()" % {'table': tn})
downgradeCommands.append("%(table)s.drop()" % {'table': tableName})
for tn in self.diff.tables_missing_from_B:
post_table = self.diff.metadataA.tables[tn]
decls.extend(self._getTableDefn(post_table, metaName='post_meta'))
upgradeCommands.append(
"post_meta.tables[%(table)r].create()" % {'table': tn})
downgradeCommands.append(
"post_meta.tables[%(table)r].drop()" % {'table': tn})
for (tn, td) in self.diff.tables_different.iteritems():
if td.columns_missing_from_A or td.columns_different:
pre_table = self.diff.metadataB.tables[tn]
decls.extend(self._getTableDefn(
pre_table, metaName='pre_meta'))
if td.columns_missing_from_B or td.columns_different:
post_table = self.diff.metadataA.tables[tn]
decls.extend(self._getTableDefn(
post_table, metaName='post_meta'))
for tableName in self.diff.tables_different:
dbTable = self.diff.metadataB.tables[tableName]
td = self.diff.tables_different[tableName]
for col in td.columns_missing_from_A: for col in td.columns_missing_from_A:
upgradeCommands.append('%s.append_column(%s)' % ( upgradeCommands.append(
tableName, 'pre_meta.tables[%r].columns[%r].drop()' % (tn, col))
self.column_repr( downgradeCommands.append(
self.diff.metadataB.tables[tableName].columns[col]))) 'pre_meta.tables[%r].columns[%r].create()' % (tn, col))
downgradeCommands.append('%s.columns[%r].drop()' % (tableName, col))
for col in td.columns_missing_from_B: for col in td.columns_missing_from_B:
upgradeCommands.append('%s.columns[%r].drop()' % (tableName, col)) upgradeCommands.append(
downgradeCommands.append('%s.columns[%r].create()' % (tableName, col)) 'post_meta.tables[%r].columns[%r].create()' % (tn, col))
downgradeCommands.append(
'post_meta.tables[%r].columns[%r].drop()' % (tn, col))
for modelCol, databaseCol, modelDecl, databaseDecl in td.columns_different: for modelCol, databaseCol, modelDecl, databaseDecl in td.columns_different:
upgradeCommands.append( upgradeCommands.append(
'assert False, "Can\'t alter columns: %s:%s=>%s"' % ( 'assert False, "Can\'t alter columns: %s:%s=>%s"' % (
tableName, modelCol.name, databaseCol.name)) tn, modelCol.name, databaseCol.name))
downgradeCommands.append( downgradeCommands.append(
'assert False, "Can\'t alter columns: %s:%s=>%s"' % ( 'assert False, "Can\'t alter columns: %s:%s=>%s"' % (
tableName, modelCol.name, databaseCol.name)) tn, modelCol.name, databaseCol.name))
pre_command = ' meta.bind = migrate_engine'
return ( return (
'\n'.join(decls), '\n'.join(decls),
'\n'.join([pre_command] + ['%s%s' % (indent, line) for line in upgradeCommands]), '\n'.join('%s%s' % (indent, line) for line in upgradeCommands),
'\n'.join([pre_command] + ['%s%s' % (indent, line) for line in downgradeCommands])) '\n'.join('%s%s' % (indent, line) for line in downgradeCommands))
def _db_can_handle_this_change(self,td): def _db_can_handle_this_change(self,td):
"""Check if the database can handle going from B to A.""" """Check if the database can handle going from B to A."""