コード例 #1
0
ファイル: migration.py プロジェクト: Lifto/alembic
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: