Fix and test issue 118. Clarify genmodel transformations.
This commit is contained in:
parent
5cfc74959f
commit
3fe1b4a3be
@ -35,7 +35,7 @@ class TestSchemaDiff(fixture.DB):
|
||||
|
||||
def _applyLatestModel(self):
|
||||
diff = schemadiff.getDiffOfModelAgainstDatabase(self.meta, self.engine, excludeTables=['migrate_version'])
|
||||
genmodel.ModelGenerator(diff,self.engine).applyModel()
|
||||
genmodel.ModelGenerator(diff,self.engine).runB2A()
|
||||
|
||||
@fixture.usedb()
|
||||
def test_functional(self):
|
||||
@ -57,16 +57,30 @@ class TestSchemaDiff(fixture.DB):
|
||||
|
||||
# Check Python upgrade and downgrade of database from updated model.
|
||||
diff = schemadiff.getDiffOfModelAgainstDatabase(self.meta, self.engine, excludeTables=['migrate_version'])
|
||||
decls, upgradeCommands, downgradeCommands = genmodel.ModelGenerator(diff,self.engine).toUpgradeDowngradePython()
|
||||
self.assertEqualsIgnoreWhitespace(decls, '''
|
||||
from migrate.changeset import schema
|
||||
meta = MetaData()
|
||||
tmp_schemadiff = Table('tmp_schemadiff', meta,
|
||||
Column('id', Integer(), primary_key=True, nullable=False),
|
||||
Column('name', UnicodeText(length=None)),
|
||||
Column('data', UnicodeText(length=None)),
|
||||
)
|
||||
''')
|
||||
decls, upgradeCommands, downgradeCommands = genmodel.ModelGenerator(diff,self.engine).genB2AMigration()
|
||||
|
||||
# Feature test for a recent SQLa feature;
|
||||
# expect different output in that case.
|
||||
if repr(String()) == 'String()':
|
||||
self.assertEqualsIgnoreWhitespace(decls, '''
|
||||
from migrate.changeset import schema
|
||||
meta = MetaData()
|
||||
tmp_schemadiff = Table('tmp_schemadiff', meta,
|
||||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('name', UnicodeText),
|
||||
Column('data', UnicodeText),
|
||||
)
|
||||
''')
|
||||
else:
|
||||
self.assertEqualsIgnoreWhitespace(decls, '''
|
||||
from migrate.changeset import schema
|
||||
meta = MetaData()
|
||||
tmp_schemadiff = Table('tmp_schemadiff', meta,
|
||||
Column('id', Integer(), primary_key=True, nullable=False),
|
||||
Column('name', UnicodeText(length=None)),
|
||||
Column('data', UnicodeText(length=None)),
|
||||
)
|
||||
''')
|
||||
self.assertEqualsIgnoreWhitespace(upgradeCommands,
|
||||
'''meta.bind = migrate_engine
|
||||
tmp_schemadiff.create()''')
|
||||
@ -80,7 +94,7 @@ class TestSchemaDiff(fixture.DB):
|
||||
|
||||
# Check Python code gen from database.
|
||||
diff = schemadiff.getDiffOfModelAgainstDatabase(MetaData(), self.engine, excludeTables=['migrate_version'])
|
||||
src = genmodel.ModelGenerator(diff,self.engine).toPython()
|
||||
src = genmodel.ModelGenerator(diff,self.engine).genBDefinition()
|
||||
|
||||
exec src in locals()
|
||||
|
||||
|
@ -161,23 +161,44 @@ def upgrade(migrate_engine):
|
||||
self.assertTrue('User.create()' in source_script)
|
||||
self.assertTrue('User.drop()' in source_script)
|
||||
|
||||
#@fixture.usedb()
|
||||
#def test_make_update_script_for_model_equals(self):
|
||||
# """Try to make update script from two identical models"""
|
||||
@fixture.usedb()
|
||||
def test_make_update_script_for_equal_models(self):
|
||||
"""Try to make update script from two identical models"""
|
||||
|
||||
# self.setup_model_params()
|
||||
# self.write_file(self.first_model_path, self.base_source + self.model_source)
|
||||
# self.write_file(self.second_model_path, self.base_source + self.model_source)
|
||||
self.setup_model_params()
|
||||
self.write_file(self.first_model_path, self.base_source + self.model_source)
|
||||
self.write_file(self.second_model_path, self.base_source + self.model_source)
|
||||
|
||||
# source_script = self.pyscript.make_update_script_for_model(
|
||||
# engine=self.engine,
|
||||
# oldmodel=load_model('testmodel_first:meta'),
|
||||
# model=load_model('testmodel_second:meta'),
|
||||
# repository=self.repo_path,
|
||||
# )
|
||||
source_script = self.pyscript.make_update_script_for_model(
|
||||
engine=self.engine,
|
||||
oldmodel=load_model('testmodel_first:meta'),
|
||||
model=load_model('testmodel_second:meta'),
|
||||
repository=self.repo_path,
|
||||
)
|
||||
|
||||
# self.assertFalse('User.create()' in source_script)
|
||||
# self.assertFalse('User.drop()' in source_script)
|
||||
self.assertFalse('User.create()' in source_script)
|
||||
self.assertFalse('User.drop()' in source_script)
|
||||
|
||||
@fixture.usedb()
|
||||
def test_make_update_script_direction(self):
|
||||
"""Check update scripts go in the right direction"""
|
||||
|
||||
self.setup_model_params()
|
||||
self.write_file(self.first_model_path, self.base_source)
|
||||
self.write_file(self.second_model_path, self.base_source + self.model_source)
|
||||
|
||||
source_script = self.pyscript.make_update_script_for_model(
|
||||
engine=self.engine,
|
||||
oldmodel=load_model('testmodel_first:meta'),
|
||||
model=load_model('testmodel_second:meta'),
|
||||
repository=self.repo_path,
|
||||
)
|
||||
|
||||
self.assertTrue(0
|
||||
< source_script.find('upgrade')
|
||||
< source_script.find('User.create()')
|
||||
< source_script.find('downgrade')
|
||||
< source_script.find('User.drop()'))
|
||||
|
||||
def setup_model_params(self):
|
||||
self.script_path = self.tmp_py()
|
||||
@ -195,6 +216,8 @@ User = Table('User', meta,
|
||||
|
||||
self.repo = repository.Repository.create(self.repo_path, 'repo')
|
||||
self.pyscript = PythonScript.create(self.script_path)
|
||||
sys.modules.pop('testmodel_first', None)
|
||||
sys.modules.pop('testmodel_second', None)
|
||||
|
||||
def write_file(self, path, contents):
|
||||
f = open(path, 'w')
|
||||
|
@ -1,9 +1,9 @@
|
||||
"""
|
||||
Code to generate a Python model from a database or differences
|
||||
between a model and database.
|
||||
Code to generate a Python model from a database or differences
|
||||
between a model and database.
|
||||
|
||||
Some of this is borrowed heavily from the AutoCode project at:
|
||||
http://code.google.com/p/sqlautocode/
|
||||
Some of this is borrowed heavily from the AutoCode project at:
|
||||
http://code.google.com/p/sqlautocode/
|
||||
"""
|
||||
|
||||
import sys
|
||||
@ -34,6 +34,13 @@ Base = declarative.declarative_base()
|
||||
|
||||
|
||||
class ModelGenerator(object):
|
||||
"""Various transformations from an A, B diff.
|
||||
|
||||
In the implementation, A tends to be called the model and B
|
||||
the database (although this is not true of all diffs).
|
||||
The diff is directionless, but transformations apply the diff
|
||||
in a particular direction, described in the method name.
|
||||
"""
|
||||
|
||||
def __init__(self, diff, engine, declarative=False):
|
||||
self.diff = diff
|
||||
@ -89,7 +96,7 @@ class ModelGenerator(object):
|
||||
else:
|
||||
return """Column(%(name)r, %(commonStuff)s)""" % data
|
||||
|
||||
def getTableDefn(self, table):
|
||||
def _getTableDefn(self, table):
|
||||
out = []
|
||||
tableName = table.name
|
||||
if self.declarative:
|
||||
@ -118,8 +125,14 @@ class ModelGenerator(object):
|
||||
for name in names:
|
||||
yield metadata.tables.get(name)
|
||||
|
||||
def toPython(self):
|
||||
"""Assume database is current and model is empty."""
|
||||
def genBDefinition(self):
|
||||
"""Generates the source code for a definition of B.
|
||||
|
||||
Assumes a diff where A is empty.
|
||||
|
||||
Was: toPython. Assume database (B) is current and model (A) is empty.
|
||||
"""
|
||||
|
||||
out = []
|
||||
if self.declarative:
|
||||
out.append(DECLARATIVE_HEADER)
|
||||
@ -127,17 +140,22 @@ class ModelGenerator(object):
|
||||
out.append(HEADER)
|
||||
out.append("")
|
||||
for table in self._get_tables(missingA=True):
|
||||
out.extend(self.getTableDefn(table))
|
||||
out.extend(self._getTableDefn(table))
|
||||
return '\n'.join(out)
|
||||
|
||||
def toUpgradeDowngradePython(self, indent=' '):
|
||||
''' Assume model is most current and database is out-of-date. '''
|
||||
def genB2AMigration(self, indent=' '):
|
||||
'''Generate a migration from B to A.
|
||||
|
||||
Was: toUpgradeDowngradePython
|
||||
Assume model (A) is most current and database (B) is out-of-date.
|
||||
'''
|
||||
|
||||
decls = ['from migrate.changeset import schema',
|
||||
'meta = MetaData()']
|
||||
for table in self._get_tables(
|
||||
missingA=True,missingB=True,modified=True
|
||||
):
|
||||
decls.extend(self.getTableDefn(table))
|
||||
decls.extend(self._getTableDefn(table))
|
||||
|
||||
upgradeCommands, downgradeCommands = [], []
|
||||
for tableName in self.diff.tables_missing_from_A:
|
||||
@ -175,16 +193,21 @@ class ModelGenerator(object):
|
||||
'\n'.join([pre_command] + ['%s%s' % (indent, line) for line in downgradeCommands]))
|
||||
|
||||
def _db_can_handle_this_change(self,td):
|
||||
"""Check if the database can handle going from B to A."""
|
||||
|
||||
if (td.columns_missing_from_B
|
||||
and not td.columns_missing_from_A
|
||||
and not td.columns_different):
|
||||
# Even sqlite can handle this.
|
||||
# Even sqlite can handle column additions.
|
||||
return True
|
||||
else:
|
||||
return not self.engine.url.drivername.startswith('sqlite')
|
||||
|
||||
def applyModel(self):
|
||||
"""Apply model to current database."""
|
||||
def runB2A(self):
|
||||
"""Goes from B to A.
|
||||
|
||||
Was: applyModel. Apply model (A) to current database (B).
|
||||
"""
|
||||
|
||||
meta = sqlalchemy.MetaData(self.engine)
|
||||
|
||||
|
@ -117,7 +117,7 @@ class ControlledSchema(object):
|
||||
diff = schemadiff.getDiffOfModelAgainstDatabase(
|
||||
model, self.engine, excludeTables=[self.repository.version_table]
|
||||
)
|
||||
genmodel.ModelGenerator(diff,self.engine).applyModel()
|
||||
genmodel.ModelGenerator(diff,self.engine).runB2A()
|
||||
|
||||
self.update_repository_table(self.version, int(self.repository.latest))
|
||||
|
||||
@ -217,4 +217,4 @@ class ControlledSchema(object):
|
||||
diff = schemadiff.getDiffOfModelAgainstDatabase(
|
||||
MetaData(), engine, excludeTables=[repository.version_table]
|
||||
)
|
||||
return genmodel.ModelGenerator(diff, engine, declarative).toPython()
|
||||
return genmodel.ModelGenerator(diff, engine, declarative).genBDefinition()
|
||||
|
@ -61,12 +61,12 @@ class PythonScript(base.BaseScript):
|
||||
|
||||
# Compute differences.
|
||||
diff = schemadiff.getDiffOfModelAgainstModel(
|
||||
oldmodel,
|
||||
model,
|
||||
oldmodel,
|
||||
excludeTables=[repository.version_table])
|
||||
# TODO: diff can be False (there is no difference?)
|
||||
decls, upgradeCommands, downgradeCommands = \
|
||||
genmodel.ModelGenerator(diff,engine).toUpgradeDowngradePython()
|
||||
genmodel.ModelGenerator(diff,engine).genB2AMigration()
|
||||
|
||||
# Store differences into file.
|
||||
src = Template(opts.pop('templates_path', None)).get_script(opts.pop('templates_theme', None))
|
||||
|
Loading…
x
Reference in New Issue
Block a user