CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-alembic

A database migration tool for SQLAlchemy.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

autogeneration.mddocs/

Autogeneration

Automatic migration generation by comparing database schema with SQLAlchemy model definitions. The autogeneration system includes customizable comparison and rendering systems for generating migration scripts.

Core Imports

from alembic.autogenerate import compare_metadata, produce_migrations, render_python_code
from alembic.autogenerate.api import RevisionContext, AutogenContext, _render_migration_diffs
from alembic.autogenerate.compare import _produce_net_changes, comparators
from alembic.autogenerate.render import render_op_text, renderers

Capabilities

Metadata Comparison

Compare target metadata against database schema to identify differences.

def compare_metadata(context, metadata):
    """
    Compare metadata against database and return differences.
    
    Args:
        context (MigrationContext): Migration context with database connection
        metadata (MetaData): SQLAlchemy metadata to compare
    
    Returns:
        list: List of operation objects representing differences
    """

Usage Example:

from alembic.autogenerate import compare_metadata
from alembic.runtime.migration import MigrationContext
from sqlalchemy import create_engine

engine = create_engine('postgresql://user:pass@localhost/db')
with engine.connect() as conn:
    context = MigrationContext.configure(conn)
    diffs = compare_metadata(context, target_metadata)
    for diff in diffs:
        print(diff)

Migration Production

Generate migration operations from metadata comparison.

def produce_migrations(context, metadata):
    """
    Produce migration operations from metadata comparison.
    
    Args:
        context (MigrationContext): Migration context
        metadata (MetaData): Target metadata
    
    Returns:
        UpgradeOps: Container of upgrade operations
    """

Usage Example:

from alembic.autogenerate import produce_migrations

operations = produce_migrations(context, target_metadata)
for op in operations.ops:
    print(f"Operation: {op}")

Python Code Rendering

Render migration operations as Python code for migration scripts.

def render_python_code(up_or_down_op, sqlalchemy_module_prefix='sa.', alembic_module_prefix='op.', render_as_batch=False, imports=None, render_item=None):
    """
    Render operations as Python code.
    
    Args:
        up_or_down_op (OperationContainer): Operations to render
        sqlalchemy_module_prefix (str): Prefix for SQLAlchemy imports
        alembic_module_prefix (str): Prefix for Alembic operations
        render_as_batch (bool): Render as batch operations
        imports (set): Set to collect required imports
        render_item (callable): Custom rendering function
    
    Returns:
        str: Python code representation
    """

Usage Example:

from alembic.autogenerate import render_python_code

# Render upgrade operations
imports = set()
code = render_python_code(
    operations,
    sqlalchemy_module_prefix='sa.',
    alembic_module_prefix='op.',
    imports=imports
)
print("Required imports:", imports)
print("Migration code:")
print(code)

Internal Rendering Functions

Internal functions for advanced migration rendering and processing.

def _render_migration_diffs(autogen_context, template_args):
    """
    Render migration differences for template generation.
    
    Args:
        autogen_context (AutogenContext): Autogeneration context
        template_args (dict): Template arguments
    
    Returns:
        str: Rendered migration code
    """

def _produce_net_changes(autogen_context, metadata):
    """
    Produce net changes from metadata comparison.
    
    Args:
        autogen_context (AutogenContext): Autogeneration context
        metadata (MetaData): Target metadata
    
    Returns:
        UpgradeOps: Net change operations
    """

def render_op_text(autogen_context, op):
    """
    Render operation as text representation.
    
    Args:
        autogen_context (AutogenContext): Autogeneration context
        op (MigrateOperation): Operation to render
    
    Returns:
        str: Text representation of operation
    """

Revision Context

Context manager for revision generation with autogeneration support.

class RevisionContext:
    def __init__(self, config, script_directory, command_args, process_revision_directives=None):
        """
        Context for revision generation.
        
        Args:
            config (Config): Alembic configuration
            script_directory (ScriptDirectory): Script directory
            command_args (dict): Command arguments
            process_revision_directives (callable): Custom directive processing
        """

    def run_autogenerate(self, rev_id, context):
        """
        Run autogeneration process.
        
        Args:
            rev_id (str): Revision identifier
            context (MigrationContext): Migration context
        
        Returns:
            Script: Generated script object
        """

    def run_no_autogenerate(self, rev_id, context):
        """
        Create empty revision without autogeneration.
        
        Args:
            rev_id (str): Revision identifier
            context (MigrationContext): Migration context
        
        Returns:
            Script: Generated script object
        """

Autogeneration Context

Context object containing autogeneration configuration and state.

class AutogenContext:
    def __init__(self, migration_context, metadata=None, opts=None, autogenerate=True):
        """
        Context for autogeneration operations.
        
        Args:
            migration_context (MigrationContext): Migration context
            metadata (MetaData): Target metadata
            opts (dict): Configuration options
            autogenerate (bool): Enable autogeneration
        """
        
    # Properties and methods available on AutogenContext
    migration_context: MigrationContext
    metadata: MetaData
    connection: Connection
    dialect: Dialect
    imports: Set[str]

Comparison Customization

Custom Comparators

Register custom comparison functions for specific object types.

from alembic.autogenerate import comparators

@comparators.dispatch_for("schema")
def compare_schema(autogen_context, upgrade_ops, schemas):
    """
    Custom schema comparison.
    
    Args:
        autogen_context (AutogenContext): Autogeneration context
        upgrade_ops (UpgradeOps): Container for upgrade operations
        schemas (set): Set of schema names
    """

@comparators.dispatch_for("table")  
def compare_table(autogen_context, upgrade_ops, schema, table_name, conn_table, metadata_table):
    """
    Custom table comparison.
    
    Args:
        autogen_context (AutogenContext): Autogeneration context
        upgrade_ops (UpgradeOps): Container for upgrade operations
        schema (str): Schema name
        table_name (str): Table name
        conn_table (Table): Database table reflection
        metadata_table (Table): Metadata table definition
    """

Example Usage:

@comparators.dispatch_for("table")
def ignore_temp_tables(autogen_context, upgrade_ops, schema, table_name, conn_table, metadata_table):
    """Ignore temporary tables during comparison."""
    if table_name.startswith('temp_'):
        return
    # Continue with default comparison
    return comparators.dispatch("table")(autogen_context, upgrade_ops, schema, table_name, conn_table, metadata_table)

Type Comparison

Customize how column types are compared during autogeneration.

def compare_type(context, inspected_column, metadata_column, inspected_type, metadata_type):
    """
    Compare column types for differences.
    
    Args:
        context (MigrationContext): Migration context
        inspected_column (dict): Database column information
        metadata_column (Column): Metadata column
        inspected_type (TypeEngine): Database column type
        metadata_type (TypeEngine): Metadata column type
    
    Returns:
        bool: True if types are different
    """
    # Custom type comparison logic
    if isinstance(metadata_type, sa.String) and isinstance(inspected_type, sa.Text):
        return False  # Consider String and Text as equivalent
    return None  # Use default comparison

# Configure in context.configure()
context.configure(
    connection=connection,
    target_metadata=target_metadata,
    compare_type=compare_type
)

Server Default Comparison

Customize server default comparison logic.

def compare_server_default(context, inspected_column, metadata_column, inspected_default, metadata_default, rendered_metadata_default):
    """
    Compare server defaults for differences.
    
    Args:
        context (MigrationContext): Migration context
        inspected_column (dict): Database column information
        metadata_column (Column): Metadata column
        inspected_default (str): Database default value
        metadata_default: Metadata default value
        rendered_metadata_default (str): Rendered metadata default
    
    Returns:
        bool: True if defaults are different
    """
    # Custom server default comparison
    if inspected_default == "''" and metadata_default is None:
        return False  # Empty string equals NULL
    return None  # Use default comparison

context.configure(
    connection=connection,
    target_metadata=target_metadata,
    compare_server_default=compare_server_default
)

Rendering Customization

Custom Renderers

Register custom rendering functions for specific operations.

from alembic.autogenerate import renderers

@renderers.dispatch_for(CreateTableOp)
def render_create_table(autogen_context, op):
    """
    Custom rendering for CREATE TABLE operations.
    
    Args:
        autogen_context (AutogenContext): Autogeneration context
        op (CreateTableOp): Create table operation
    
    Returns:
        str: Python code for the operation
    """
    return f"op.create_table({op.table_name!r}, ...)"

Include/Exclude Filtering

Filter objects during autogeneration.

def include_object(object, name, type_, reflected, compare_to):
    """
    Determine whether to include an object in autogeneration.
    
    Args:
        object: Database object
        name (str): Object name
        type_ (str): Object type ('table', 'column', 'index', etc.)
        reflected (bool): True if object was reflected from database
        compare_to: Corresponding metadata object
    
    Returns:
        bool: True to include object in comparison
    """
    # Skip temporary tables
    if type_ == "table" and name.startswith("temp_"):
        return False
    
    # Skip certain indexes
    if type_ == "index" and name.startswith("_"):
        return False
        
    return True

def include_name(name, type_, parent_names):
    """
    Filter object names during reflection.
    
    Args:
        name (str): Object name
        type_ (str): Object type
        parent_names (dict): Parent object names
    
    Returns:
        bool: True to include object
    """
    # Skip system schemas
    if type_ == "schema" and name.startswith("information_"):
        return False
    return True

# Configure filtering
context.configure(
    connection=connection,
    target_metadata=target_metadata,
    include_object=include_object,
    include_name=include_name
)

Advanced Features

Process Revision Directives

Customize the revision generation process.

def process_revision_directives(context, revision, directives):
    """
    Process and modify revision directives.
    
    Args:
        context (MigrationContext): Migration context
        revision (tuple): Revision information
        directives (list): List of MigrationScript directives
    """
    # Skip empty migrations
    script = directives[0]
    if script.upgrade_ops.is_empty():
        directives[:] = []
        print("Skipping empty migration")
        return

    # Add custom header comment
    script.upgrade_ops.ops.insert(0, 
        ExecuteSQLOp("-- Auto-generated migration"))

# Use in context configuration
context.configure(
    connection=connection,
    target_metadata=target_metadata,
    process_revision_directives=process_revision_directives
)

Template Arguments

Pass custom variables to migration templates.

def process_revision_directives(context, revision, directives):
    """Add custom template variables."""
    script = directives[0]
    script.template_args = {
        'author': 'AutoGen System',
        'created_at': datetime.now().isoformat()
    }

# In migration template
"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
Author: ${author}
Created At: ${created_at}
"""

Batch Rendering

Render operations suitable for SQLite batch mode.

# Configure batch rendering
context.configure(
    connection=connection,
    target_metadata=target_metadata,
    render_as_batch=True  # Use batch operations
)

# Or in rendering
code = render_python_code(
    operations,
    render_as_batch=True
)

Integration Examples

Flask-SQLAlchemy Integration

from flask import current_app
from alembic import context
from flask_sqlalchemy import SQLAlchemy

def get_metadata():
    """Get metadata from Flask-SQLAlchemy."""
    with current_app.app_context():
        return current_app.extensions['sqlalchemy'].db.metadata

def run_migrations_online():
    """Run migrations with Flask app context."""
    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix='sqlalchemy.',
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(
            connection=connection,
            target_metadata=get_metadata(),
            include_object=include_object,
            compare_type=True,
            compare_server_default=True
        )

        with context.begin_transaction():
            context.run_migrations()

Django Integration

def run_autogenerate_with_django():
    """Run autogeneration with Django models."""
    import django
    from django.conf import settings
    
    django.setup()
    
    # Convert Django models to SQLAlchemy metadata
    metadata = convert_django_models_to_sqlalchemy()
    
    engine = create_engine(get_database_url())
    with engine.connect() as connection:
        context = MigrationContext.configure(
            connection,
            opts={
                'target_metadata': metadata,
                'compare_type': True,
                'include_object': include_object
            }
        )
        
        diffs = compare_metadata(context, metadata)
        return diffs

Types

# Autogeneration context types
class AutogenContext:
    migration_context: MigrationContext
    metadata: Optional[MetaData]
    connection: Connection
    dialect: Dialect
    imports: Set[str]
    
class RevisionContext:
    config: Config
    script_directory: ScriptDirectory
    command_args: Dict[str, Any]
    
# Operation containers
class UpgradeOps:
    ops: List[MigrateOperation]
    
class DowngradeOps:
    ops: List[MigrateOperation]

# Comparison function types
CompareTypeFunc = Callable[[MigrationContext, Column, Column, TypeEngine, TypeEngine], Optional[bool]]
CompareServerDefaultFunc = Callable[[MigrationContext, Column, Column, Any, Any, str], Optional[bool]]
IncludeObjectFunc = Callable[[Any, str, str, bool, Any], bool]
ProcessRevisionDirectivesFunc = Callable[[MigrationContext, Tuple, List], None]

Install with Tessl CLI

npx tessl i tessl/pypi-alembic

docs

autogeneration.md

cli-commands.md

configuration.md

index.md

migration-context.md

migration-operations.md

runtime.md

script-management.md

tile.json