make migrate.changeset.databases PEP-8 clean and add it to the API docs

This commit is contained in:
jan.dittberner 2009-01-25 12:52:33 +00:00
parent 728b677e56
commit eb00570991
7 changed files with 233 additions and 93 deletions

View File

@ -9,13 +9,54 @@ Module :mod:`migrate.changeset`
:members:
:synopsis: Database changeset management
Module :mod:`migrate.changeset.ansisql`
---------------------------------------
Module :mod:`ansisql <migrate.changeset.ansisql>`
-------------------------------------------------
.. automodule:: migrate.changeset.ansisql
:members:
:synopsis: Standard SQL implementation for altering database schemas
Module :mod:`databases <migrate.changeset.databases>`
-----------------------------------------------------
.. automodule:: migrate.changeset.databases
:members:
:synopsis: Database specific changeset implementations
Module :mod:`mysql <migrate.changeset.databases.mysql>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.mysql
:members:
:synopsis: MySQL database specific changeset implementations
Module :mod:`oracle <migrate.changeset.databases.oracle>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.oracle
:members:
:synopsis: Oracle database specific changeset implementations
Module :mod:`postgres <migrate.changeset.databases.postgres>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.postgres
:members:
:synopsis: PostgreSQL database specific changeset implementations
Module :mod:`sqlite <migrate.changeset.databases.slite>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.sqlite
:members:
:synopsis: SQLite database specific changeset implementations
Module :mod:`visitor <migrate.changeset.databases.visitor>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.visitor
:members:
Module :mod:`migrate.versioning`
================================

View File

@ -1,3 +1,7 @@
"""
This module contains database dialect specific changeset
implementations.
"""
__all__=[
'postgres',
'sqlite',

View File

@ -1,20 +1,32 @@
"""
MySQL database specific implementations of changeset classes.
"""
from migrate.changeset import ansisql, exceptions
from sqlalchemy.databases import mysql as sa_base
#import sqlalchemy as sa
MySQLSchemaGenerator = sa_base.MySQLSchemaGenerator
class MySQLColumnGenerator(MySQLSchemaGenerator, ansisql.ANSIColumnGenerator):
def _do_quote_table_identifier(self, identifier):
return '%s'%identifier
pass
class MySQLColumnDropper(ansisql.ANSIColumnDropper):
def _do_quote_table_identifier(self, identifier):
return '%s'%identifier
def _do_quote_column_identifier(self, identifier):
return '%s'%identifier
class MySQLSchemaChanger(MySQLSchemaGenerator, ansisql.ANSISchemaChanger):
def visit_column(self, delta):
keys = delta.keys()
if 'type' in keys or 'nullable' in keys or 'name' in keys:
@ -22,31 +34,41 @@ class MySQLSchemaChanger(MySQLSchemaGenerator,ansisql.ANSISchemaChanger):
if 'server_default' in keys:
# Column name might have changed above
col_name = delta.get('name', delta.current_name)
self._run_subvisit(delta,self._visit_column_default,col_name=col_name)
self._run_subvisit(delta, self._visit_column_default,
col_name=col_name)
def _visit_column_change(self, table_name, col_name, delta):
if not hasattr(delta, 'result_column'):
# Mysql needs the whole column definition, not just a lone name/type
# Mysql needs the whole column definition, not just a lone
# name/type
raise exceptions.NotSupportedError(
"A column object is required to do this")
column = delta.result_column
if not column.table: column.table = delta.table # needed by get_column_specification
# needed by get_column_specification
if not column.table:
column.table = delta.table
colspec = self.get_column_specification(column)
self.start_alter_table(table_name)
self.append("CHANGE COLUMN ")
self.append(col_name)
self.append(' ')
self.append(colspec)
def visit_index(self, param):
# If MySQL can do this, I can't find how
raise exceptions.NotSupportedError("MySQL cannot rename indexes")
def _do_quote_table_identifier(self, identifier):
return '%s'%identifier
class MySQLConstraintGenerator(ansisql.ANSIConstraintGenerator):
def _do_quote_table_identifier(self, identifier):
return '%s'%identifier
class MySQLConstraintDropper(ansisql.ANSIConstraintDropper):
#def visit_constraint(self,constraint):
# if isinstance(constraint,sqlalchemy.schema.PrimaryKeyConstraint):
@ -54,6 +76,7 @@ class MySQLConstraintDropper(ansisql.ANSIConstraintDropper):
# elif isinstance(constraint,sqlalchemy.schema.ForeignKeyConstraint):
# return self._visit_constraint_fk(constraint)
# return super(MySQLConstraintDropper,self).visit_constraint(constraint)
def visit_migrate_primary_key_constraint(self, constraint):
self.start_alter_table(constraint)
self.append("DROP PRIMARY KEY")
@ -68,6 +91,7 @@ class MySQLConstraintDropper(ansisql.ANSIConstraintDropper):
def _do_quote_table_identifier(self, identifier):
return '%s'%identifier
class MySQLDialect(ansisql.ANSIDialect):
columngenerator = MySQLColumnGenerator
columndropper = MySQLColumnDropper

View File

@ -1,54 +1,74 @@
"""
Oracle database specific implementations of changeset classes.
"""
from migrate.changeset import ansisql, exceptions
from sqlalchemy.databases import oracle as sa_base
import sqlalchemy as sa
OracleSchemaGenerator = sa_base.OracleSchemaGenerator
class OracleColumnGenerator(OracleSchemaGenerator,ansisql.ANSIColumnGenerator):
class OracleColumnGenerator(OracleSchemaGenerator,
ansisql.ANSIColumnGenerator):
pass
class OracleColumnDropper(ansisql.ANSIColumnDropper):
pass
class OracleSchemaChanger(OracleSchemaGenerator, ansisql.ANSISchemaChanger):
def get_column_specification(self, column, **kwargs):
# Ignore the NOT NULL generated
override_nullable = kwargs.pop('override_nullable', None)
if override_nullable:
orig = column.nullable
column.nullable = True
ret=super(OracleSchemaChanger,self).get_column_specification(column,**kwargs)
ret = super(OracleSchemaChanger, self).get_column_specification(
column, **kwargs)
if override_nullable:
column.nullable = orig
return ret
def visit_column(self, delta):
keys = delta.keys()
if 'type' in keys or 'nullable' in keys or 'default' in keys or 'server_default' in keys:
if 'type' in keys or 'nullable' in keys or 'default' in keys \
or 'server_default' in keys:
self._run_subvisit(delta, self._visit_column_change)
if 'name' in keys:
self._run_subvisit(delta, self._visit_column_name)
def _visit_column_change(self, table_name, col_name, delta):
if not hasattr(delta, 'result_column'):
# Oracle needs the whole column definition, not just a lone name/type
# Oracle needs the whole column definition, not just a
# lone name/type
raise exceptions.NotSupportedError(
"A column object is required to do this")
column = delta.result_column
# Oracle cannot drop a default once created, but it can set it to null.
# We'll do that if default=None
# http://forums.oracle.com/forums/message.jspa?messageID=1273234#1273234
dropdefault_hack = (column.server_default is None and 'server_default' in delta.keys())
# Oracle apparently doesn't like it when we say "not null" if the
# column's already not null. Fudge it, so we don't need a new function
notnull_hack = ((not column.nullable) and ('nullable' not in delta.keys()))
# We need to specify NULL if we're removing a NOT NULL constraint
# Oracle cannot drop a default once created, but it can set it
# to null. We'll do that if default=None
# http://forums.oracle.com/forums/message.jspa?\
# messageID=1273234#1273234
dropdefault_hack = (column.server_default is None \
and 'server_default' in delta.keys())
# Oracle apparently doesn't like it when we say "not null" if
# the column's already not null. Fudge it, so we don't need a
# new function
notnull_hack = ((not column.nullable) \
and ('nullable' not in delta.keys()))
# We need to specify NULL if we're removing a NOT NULL
# constraint
null_hack = (column.nullable and ('nullable' in delta.keys()))
if dropdefault_hack:
column.server_default = sa.PassiveDefault(sa.sql.null())
if notnull_hack:
column.nullable = True
colspec=self.get_column_specification(column,override_nullable=null_hack)
colspec=self.get_column_specification(column,
override_nullable=null_hack)
if null_hack:
colspec += ' NULL'
if notnull_hack:
@ -59,18 +79,28 @@ class OracleSchemaChanger(OracleSchemaGenerator,ansisql.ANSISchemaChanger):
self.start_alter_table(table_name)
self.append("MODIFY ")
self.append(colspec)
class OracleConstraintCommon(object):
def get_constraint_name(self, cons):
# Oracle constraints can't guess their name like other DBs
if not cons.name:
raise exceptions.NotSupportedError(
"Oracle constraint names must be explicitly stated")
return cons.name
class OracleConstraintGenerator(OracleConstraintCommon,ansisql.ANSIConstraintGenerator):
class OracleConstraintGenerator(OracleConstraintCommon,
ansisql.ANSIConstraintGenerator):
pass
class OracleConstraintDropper(OracleConstraintCommon,ansisql.ANSIConstraintDropper):
class OracleConstraintDropper(OracleConstraintCommon,
ansisql.ANSIConstraintDropper):
pass
class OracleDialect(ansisql.ANSIDialect):
columngenerator = OracleColumnGenerator
columndropper = OracleColumnDropper

View File

@ -1,44 +1,53 @@
"""
`PostgreSQL`_ database specific implementations of changeset classes.
.. _`PostgreSQL`: http://www.postgresql.org/
"""
from migrate.changeset import ansisql
from sqlalchemy.databases import postgres as sa_base
#import sqlalchemy as sa
PGSchemaGenerator = sa_base.PGSchemaGenerator
class PGSchemaGeneratorMixin(object):
"""Common code used by the PostgreSQL specific classes."""
def _do_quote_table_identifier(self, identifier):
return identifier
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
class PGColumnGenerator(PGSchemaGenerator,ansisql.ANSIColumnGenerator, PGSchemaGeneratorMixin):
def _do_quote_table_identifier(self, identifier):
return identifier
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
class PGColumnGenerator(PGSchemaGenerator, ansisql.ANSIColumnGenerator,
PGSchemaGeneratorMixin):
"""PostgreSQL column generator implementation."""
pass
class PGColumnDropper(ansisql.ANSIColumnDropper, PGSchemaGeneratorMixin):
def _do_quote_table_identifier(self, identifier):
return identifier
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
"""PostgreSQL column dropper implementation."""
pass
class PGSchemaChanger(ansisql.ANSISchemaChanger, PGSchemaGeneratorMixin):
def _do_quote_table_identifier(self, identifier):
return identifier
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
"""PostgreSQL schema changer implementation."""
pass
class PGConstraintGenerator(ansisql.ANSIConstraintGenerator, PGSchemaGeneratorMixin):
def _do_quote_table_identifier(self, identifier):
return identifier
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
class PGConstraintDropper(ansisql.ANSIConstraintDropper, PGSchemaGeneratorMixin):
def _do_quote_table_identifier(self, identifier):
return identifier
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
class PGConstraintGenerator(ansisql.ANSIConstraintGenerator,
PGSchemaGeneratorMixin):
"""PostgreSQL constraint generator implementation."""
pass
class PGConstraintDropper(ansisql.ANSIConstraintDropper,
PGSchemaGeneratorMixin):
"""PostgreSQL constaint dropper implementation."""
pass
class PGDialect(ansisql.ANSIDialect):
columngenerator = PGColumnGenerator

View File

@ -1,3 +1,8 @@
"""
`SQLite`_ database specific implementations of changeset classes.
.. _`SQLite`: http://www.sqlite.org/
"""
from migrate.changeset import ansisql, constraint, exceptions
from sqlalchemy.databases import sqlite as sa_base
from sqlalchemy import Table, MetaData
@ -5,7 +10,9 @@ from sqlalchemy import Table, MetaData
SQLiteSchemaGenerator = sa_base.SQLiteSchemaGenerator
class SQLiteHelper(object):
def visit_column(self, param):
try:
table = self._to_table(param.table)
@ -24,16 +31,22 @@ class SQLiteHelper(object):
self.append('DROP TABLE migration_tmp')
self.execute()
class SQLiteColumnGenerator(SQLiteSchemaGenerator,ansisql.ANSIColumnGenerator):
class SQLiteColumnGenerator(SQLiteSchemaGenerator,
ansisql.ANSIColumnGenerator):
pass
class SQLiteColumnDropper(SQLiteHelper, ansisql.ANSIColumnDropper):
def _modify_table(self, table, column):
del table.columns[column.name]
columns = ','.join([c.name for c in table.columns])
return 'INSERT INTO %(table_name)s SELECT ' + columns + ' from migration_tmp'
return 'INSERT INTO %(table_name)s SELECT %s from migration_tmp' % \
','.join([c.name for c in table.columns])
class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger):
def _not_supported(self, op):
raise exceptions.NotSupportedError("SQLite does not support "
"%s; see http://www.sqlite.org/lang_altertable.html"%op)
@ -50,7 +63,9 @@ class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger):
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
class SQLiteConstraintGenerator(ansisql.ANSIConstraintGenerator):
def visit_migrate_primary_key_constraint(self, constraint):
tmpl = "CREATE UNIQUE INDEX %s ON %s ( %s )"
cols = ','.join([c.name for c in constraint.columns])
@ -60,7 +75,9 @@ class SQLiteConstraintGenerator(ansisql.ANSIConstraintGenerator):
self.append(msg)
self.execute()
class SQLiteConstraintDropper(ansisql.ANSIColumnDropper):
def visit_migrate_primary_key_constraint(self, constraint):
tmpl = "DROP INDEX %s "
name = constraint.name
@ -68,6 +85,7 @@ class SQLiteConstraintDropper(ansisql.ANSIColumnDropper):
self.append(msg)
self.execute()
class SQLiteDialect(ansisql.ANSIDialect):
columngenerator = SQLiteColumnGenerator
columndropper = SQLiteColumnDropper

View File

@ -1,3 +1,6 @@
"""
Module for visitor class mapping.
"""
import sqlalchemy as sa
from migrate.changeset.databases import sqlite, postgres, mysql, oracle
from migrate.changeset import ansisql
@ -11,10 +14,21 @@ dialects = {
sa.databases.oracle.OracleDialect: oracle.OracleDialect,
}
def get_engine_visitor(engine, name):
"""
Get the visitor implementation for the given database engine.
"""
return get_dialect_visitor(engine.dialect, name)
def get_dialect_visitor(sa_dialect, name):
"""
Get the visitor implementation for the given dialect.
Finds the visitor implementation based on the dialect class and
returns and instance initialized with the given name.
"""
sa_dialect_cls = sa_dialect.__class__
migrate_dialect_cls = dialects[sa_dialect_cls]
return migrate_dialect_cls.visitor(name)