Complete worked example
python
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, inspect, text
from alembic.migration import MigrationContext
from alembic.operations import Operations
def inspect_example(score):
engine = create_engine("sqlite+pysqlite://", connect_args={"autocommit": False})
try:
original = MetaData()
Table("roles", original, Column("id", Integer, primary_key=True), Column("score", Integer))
original.create_all(engine)
proposed = MetaData()
Table("roles", proposed, Column("id", Integer, primary_key=True),
Column("score", Integer), Column("label", String(30), nullable=True))
proposed.create_all(engine) # existing tables are not altered
with engine.begin() as connection:
connection.execute(text("INSERT INTO roles VALUES (1, :score)"), {"score": score})
context = MigrationContext.configure(connection)
operation = Operations(context)
operation.add_column("roles", Column("label", String(30), nullable=True))
columns = [column["name"] for column in inspect(connection).get_columns("roles")]
row = list(connection.execute(text("SELECT id, score, label FROM roles")).one())
return {"columns": columns, "row": row, "revision": context.get_current_revision()}
finally:
engine.dispose()- Reasoning
- The program inserts one owned row, creates a MigrationContext from the active connection, and uses Operations.add_column for the reviewed nullable addition. The final report contains columns [id, score, label] and a row whose new label is None. The original score remains unchanged. This low-level Operations object does not by itself establish a complete revision environment. The report’s revision remains None because this experiment has no version-table history. That distinction is deliberate: it isolates an actual schema change before the next lesson introduces named revision scripts and command-driven upgrades.