class MigrationContext(object): """Represent the database state made available to a migration script. :class:`.MigrationContext` is the front end to an actual database connection, or alternatively a string output stream given a particular database dialect, from an Alembic perspective. When inside the ``env.py`` script, the :class:`.MigrationContext` is available via the :meth:`.EnvironmentContext.get_context` method, which is available at ``alembic.context``:: # from within env.py script from alembic import context migration_context = context.get_context() For usage outside of an ``env.py`` script, such as for utility routines that want to check the current version in the database, the :meth:`.MigrationContext.configure` method to create new :class:`.MigrationContext` objects. For example, to get at the current revision in the database using :meth:`.MigrationContext.get_current_revision`:: # in any application, outside of an env.py script from alembic.migration import MigrationContext from sqlalchemy import create_engine engine = create_engine("postgresql://mydatabase") conn = engine.connect() context = MigrationContext.configure(conn) current_rev = context.get_current_revision() The above context can also be used to produce Alembic migration operations with an :class:`.Operations` instance:: # in any application, outside of the normal Alembic environment from alembic.operations import Operations op = Operations(context) op.alter_column("mytable", "somecolumn", nullable=True) """ def __init__(self, dialect, connection, opts): self.opts = opts self.dialect = dialect self.script = opts.get('script') as_sql=opts.get('as_sql', False) transactional_ddl=opts.get("transactional_ddl") if as_sql: self.connection = self._stdout_connection(connection) assert self.connection is not None else: self.connection = connection self._migrations_fn = opts.get('fn') self.as_sql = as_sql self.output_buffer = opts.get("output_buffer", sys.stdout) self._user_compare_type = opts.get('compare_type', False) self._user_compare_server_default = opts.get( 'compare_server_default', False) version_table = opts.get('version_table', 'alembic_version') if not self.dialect.name == "cqlengine": self._version = Table( version_table, MetaData(), Column('version_num', String(32), nullable=False)) else: from cqlengine import models from cqlengine import columns class VersionTable(models.Model): __table_name__ = version_table key = columns.Text(primary_key=True) # Always is 'alembic' version_num = columns.Text() self._version = VersionTable self._start_from_rev = opts.get("starting_rev") self.impl = ddl.DefaultImpl.get_by_dialect(dialect)( dialect, self.connection, self.as_sql, transactional_ddl, self.output_buffer, opts ) log.info("Context impl %s.", self.impl.__class__.__name__) if self.as_sql: log.info("Generating static SQL") log.info("Will assume %s DDL.", "transactional" if self.impl.transactional_ddl else "non-transactional") @classmethod def configure(cls, connection=None, url=None, dialect_name=None, opts={}, ): """Create a new :class:`.MigrationContext`. This is a factory method usually called by :meth:`.EnvironmentContext.configure`. :param connection: a :class:`~sqlalchemy.engine.base.Connection` to use for SQL execution in "online" mode. When present, is also used to determine the type of dialect in use. :param url: a string database url, or a :class:`sqlalchemy.engine.url.URL` object. The type of dialect to be used will be derived from this if ``connection`` is not passed. :param dialect_name: string name of a dialect, such as "postgresql", "mssql", etc. The type of dialect to be used will be derived from this if ``connection`` and ``url`` are not passed. :param opts: dictionary of options. Most other options accepted by :meth:`.EnvironmentContext.configure` are passed via this dictionary. """ if connection: dialect = connection.dialect elif url: url = sqla_url.make_url(url) dialect = url.get_dialect()() elif dialect_name: url = sqla_url.make_url("%s://" % dialect_name) dialect = url.get_dialect()() else: raise Exception("Connection, url, or dialect_name is required.") return MigrationContext(dialect, connection, opts) def get_current_revision(self): """Return the current revision, usually that which is present in the ``alembic_version`` table in the database. If this :class:`.MigrationContext` was configured in "offline" mode, that is with ``as_sql=True``, the ``starting_rev`` parameter is returned instead, if any. """ if self.as_sql: return self._start_from_rev else: if self._start_from_rev: raise util.CommandError( "Can't specify current_rev to context " "when using a database connection") if 'cqlengine' == self.dialect.name: from cqlengine.exceptions import CQLEngineException # Try to load the data from the DB. # if the CF doesn't exist, create it, if the row doesn't exist # initialize it. try: return self._version.get(key='alembic').version_num except CQLEngineException, e: from cqlengine import management, models if 'Keyspace' in e.message and 'does not exist' in e.message: management.create_keyspace(models.DEFAULT_KEYSPACE) management.sync_table(self._version) self._version.create(key='alembic') return self.get_current_revision() elif "Bad Request: unconfigured columnfamily alembic_version" in e.message: management.sync_table(self._version) self._version.create(key='alembic') return self.get_current_revision() elif type(e).__name__ == 'DoesNotExist': self._version.create(key='alembic') return self.get_current_revision() else: