Exemple #1
0
def render_python_code(
        up_or_down_op,
        sqlalchemy_module_prefix="sa.",
        alembic_module_prefix="op.",
        render_as_batch=False,
        imports=(),
        render_item=None,
):
    """Render Python code given an :class:`.UpgradeOps` or
    :class:`.DowngradeOps` object.

    This is a convenience function that can be used to test the
    autogenerate output of a user-defined :class:`.MigrationScript` structure.

    """
    opts = {
        "sqlalchemy_module_prefix": sqlalchemy_module_prefix,
        "alembic_module_prefix": alembic_module_prefix,
        "user_module_prefix": None,
        "render_item": render_item,
        "render_as_batch": render_as_batch,
    }

    autogen_context = AutogenContext(None, opts=opts)
    autogen_context.imports = set(imports)
    return render._indent(
        render._render_cmd_body(up_or_down_op, autogen_context))
Exemple #2
0
def include_entity(
    entity: ReplaceableEntity, autogen_context: AutogenContext, reflected: bool
) -> bool:
    """The functions on the AutogenContext object
    are described here:
    https://alembic.sqlalchemy.org/en/latest/api/autogenerate.html#alembic.autogenerate.api.AutogenContext.run_name_filters

    The meaning of the function parameters are explained in the corresponding
    definitions in the EnvironmentContext object:
    https://alembic.sqlalchemy.org/en/latest/api/runtime.html#alembic.runtime.environment.EnvironmentContext.configure.params.include_name

    This will only have an impact for projects which set include_object and/or include_name in the configuration
    of their Alembic env.
    """
    name = f"{entity.schema}.{entity.signature}"
    parent_names = {
        "schema_name": entity.schema,
        # At the time of writing, the implementation of `run_name_filters` in Alembic assumes that every type of object
        # will either be a table or have a table_name in its `parent_names` dict. This is true for columns and indexes,
        # but not true for the type of objects supported in this library such as views as functions. Nevertheless, to avoid
        # a KeyError when calling `run_name_filters`, we have to set some value.
        "table_name": f"Not applicable for type {entity.type_}",
    }
    # According to the Alembic docs, the name filter is only relevant for reflected objects
    if reflected:
        name_result = autogen_context.run_name_filters(name, entity.type_, parent_names)
    else:
        name_result = True

    # Object filters should be applied to object from local metadata and to reflected objects
    object_result = autogen_context.run_object_filters(
        entity, name, entity.type_, reflected=reflected, compare_to=None
    )
    return name_result and object_result
Exemple #3
0
 def get_upgrade_steps(self, migration_context) -> typing.List:
     content_metadata = self.context.content_metadata()
     content_metadata.clear()
     idx_dc = self.context.dataclass()
     tbl = dc2pgsqla.convert(idx_dc, content_metadata)
     upgrade_ops = UpgradeOps([])
     autogen_context = AutogenContext(migration_context, content_metadata)
     schemas = [content_metadata.schema]
     comparators.dispatch("schema", autogen_context.dialect.name)(
         autogen_context, upgrade_ops, schemas
     )
     return upgrade_ops
Exemple #4
0
def generate_migration(metadata, engine):
    """Create MigrationContext and MigrationScript"""
    mc = MigrationContext.configure(engine.connect())
    ms = produce_migrations(mc, metadata)
    ac = AutogenContext(None,
                        opts={
                            'sqlalchemy_module_prefix': 'sa.',
                            'alembic_module_prefix': 'op.',
                        })
    lines = []
    for x in ms.upgrade_ops.ops:
        lines.append(render_op_text(ac, x))
    return mc, lines
Exemple #5
0
 def get_upgrade_steps(self, migration_context) -> typing.List:
     content_metadata = self.context.content_metadata()
     content_metadata.clear()
     dmcol = get_dm_collection(self.request)
     for dm in dmcol.search(
             rulez.field["schema_uuid"] == self.context["schema_uuid"]):
         dc = dm.dataclass()
         tbl = dataclass_to_pgsqla(dc, content_metadata)
     upgrade_ops = UpgradeOps([])
     autogen_context = AutogenContext(migration_context, content_metadata)
     schemas = [content_metadata.schema]
     comparators.dispatch("schema",
                          autogen_context.dialect.name)(autogen_context,
                                                        upgrade_ops,
                                                        schemas)
     return upgrade_ops
    def verify_is_empty_revision(migration_context, __, directives):
        """Adapted from pytest-alembic test_model_definitions_match_ddl"""
        script = directives[0]

        migration_is_empty = script.upgrade_ops.is_empty()
        if not migration_is_empty:
            autogen_context = AutogenContext(migration_context)
            rendered_upgrade = _render_cmd_body(script.upgrade_ops,
                                                autogen_context)
            pytest.fail(
                "The models decribing the DDL of your database are out of sync with the set of "
                "steps described in the revision history. This usually means that someone has "
                "made manual changes to the database's DDL, or some model has been changed "
                "without also generating a migration to describe that change. The upgrade which "
                f"would have been generated would look like:\n {rendered_upgrade}"
            )
        else:
            directives[:] = []
Exemple #7
0
    def verify_is_empty_revision(migration_context, __, directives):
        script = directives[0]

        migration_is_empty = script.upgrade_ops.is_empty()
        if not migration_is_empty:
            autogen_context = AutogenContext(migration_context)
            rendered_upgrade = _render_cmd_body(script.upgrade_ops,
                                                autogen_context)

            if not migration_is_empty:
                raise AlembicTestFailure(
                    "The models decribing the DDL of your database are out of sync with the set of "
                    "steps described in the revision history. This usually means that someone has "
                    "made manual changes to the database's DDL, or some model has been changed "
                    "without also generating a migration to describe that change.",
                    context=[(
                        "The upgrade which would have been generated would look like",
                        rendered_upgrade,
                    )],
                )