Complete worked example
python
from sqlalchemy import create_engine, select, text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
class Base(DeclarativeBase):
pass
class Role(Base):
__tablename__ = "roles"
id: Mapped[int] = mapped_column(primary_key=True)
score: Mapped[int]
def inspect_example(scores):
engine = create_engine("sqlite+pysqlite://", connect_args={"autocommit": False})
try:
Base.metadata.create_all(engine)
with Session(engine) as session, session.begin():
session.add_all(Role(id=i, score=value) for i, value in enumerate(scores, 1))
with Session(engine) as session:
statement = select(Role.id, Role.score).order_by(Role.id)
return [list(row) for row in session.execute(statement)]
finally:
engine.dispose()- Reasoning
- The example stores scores with explicit IDs, constructs select(Role.id, Role.score), and orders by ID. Iterating session.execute yields two-column rows, converted to ordinary lists in the returned report. For scores [4, 9], the expected result is [[1, 4], [2, 9]]. The returned lists are detached data rather than live ORM instances. SQL row objects have their own access interfaces, and ORM identity behavior is a separate concern. Converting at the boundary makes it clear which fields the caller receives and avoids relying on a serialization library to guess what a SQL result means.