SQLAlchemy
database
tables
Python
ORM

SQLAlchemy - Getting a list of tables

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Listing tables with SQLAlchemy is useful for diagnostics, migration checks, and admin tooling. The recommended approach in modern SQLAlchemy is inspector-based introspection, with explicit schema handling for deterministic output. A reliable implementation also accounts for permissions, multi-schema setups, and reflection cost.

Use Inspector for Table Discovery

Inspector is lightweight and intended for metadata lookup.

python
1from sqlalchemy import create_engine, inspect
2
3engine = create_engine("postgresql+psycopg2://app_user:change_me@localhost:5432/appdb")
4insp = inspect(engine)
5
6tables = insp.get_table_names(schema="public")
7print(tables)
8
9views = insp.get_view_names(schema="public")
10print(views)

Specify schema explicitly whenever possible to avoid environment-dependent defaults.

Reflect Metadata for Rich Details

If you need columns or constraints, reflect metadata rather than only listing names.

python
1from sqlalchemy import MetaData
2
3metadata = MetaData(schema="public")
4metadata.reflect(bind=engine)
5
6for name, table in metadata.tables.items():
7    print(name, [col.name for col in table.columns])

Reflection is heavier than inspector calls, so do not run it repeatedly in latency-sensitive paths.

Selective Reflection for One Table

For targeted inspection, reflect one table by name.

python
1from sqlalchemy import MetaData, Table
2
3metadata = MetaData(schema="public")
4orders = Table("orders", metadata, autoload_with=engine)
5print([c.name for c in orders.columns])

This avoids loading full schema metadata unnecessarily.

Multi-Schema Introspection

In multi-tenant or modular databases, iterate known schemas explicitly.

python
schemas = ["public", "audit", "billing"]
for schema in schemas:
    print(schema, insp.get_table_names(schema=schema))

Avoid relying on search-path assumptions in operational tooling.

Verify Connection Context and Permissions

Missing tables are often privilege issues. Inspect current identity and schema context.

python
1from sqlalchemy import text
2
3with engine.connect() as conn:
4    user = conn.execute(text("select current_user")).scalar_one()
5    schema = conn.execute(text("select current_schema()")).scalar_one()
6    print(user, schema)

If role lacks schema access, introspection output will be incomplete.

Async SQLAlchemy Pattern

For async applications, use run_sync to execute inspector safely.

python
1import asyncio
2from sqlalchemy import inspect
3from sqlalchemy.ext.asyncio import create_async_engine
4
5async def list_tables_async():
6    engine = create_async_engine("postgresql+asyncpg://app_user:change_me@localhost:5432/appdb")
7    async with engine.connect() as conn:
8        def inspect_sync(sync_conn):
9            return inspect(sync_conn).get_table_names(schema="public")
10
11        tables = await conn.run_sync(inspect_sync)
12        print(tables)
13
14    await engine.dispose()
15
16asyncio.run(list_tables_async())

This keeps async event-loop behavior clean.

Build Reusable Utility Function

A small helper reduces repeated boilerplate and standardizes schema behavior.

python
1from sqlalchemy import inspect
2
3
4def list_tables(engine, schema="public"):
5    inspector = inspect(engine)
6    return sorted(inspector.get_table_names(schema=schema))

Use this in migration preflight checks and internal diagnostics.

Compare Table Lists Across Environments

One high-value use case is drift detection between staging and production. You can snapshot table names and diff them during deployment checks.

python
1def diff_table_sets(engine_a, engine_b, schema="public"):
2    a = set(list_tables(engine_a, schema=schema))
3    b = set(list_tables(engine_b, schema=schema))
4    return {
5        "only_in_a": sorted(a - b),
6        "only_in_b": sorted(b - a),
7    }

This helps catch missing migrations before application traffic is switched.

Security and Operational Notes

Table names can reveal internal architecture. If exposed through admin endpoints:

  • Restrict access to operators.
  • Log introspection access with actor and timestamp.
  • Avoid returning full schema metadata to untrusted clients.

Operationally, cache metadata briefly in admin tools if repeated listing is frequent. For migration-heavy teams, include database revision identifier in introspection output so operators can correlate table lists with migration state quickly. In scheduled diagnostics, capture table counts per schema so sudden object creation or disappearance is visible without manual diffing. Store these snapshots for trend analysis across releases and migrations.

Common Pitfalls

  • Using outdated introspection patterns from old SQLAlchemy versions.
  • Forgetting schema argument and getting incomplete table sets.
  • Running full metadata reflection in hot request paths.
  • Assuming missing tables indicate SQLAlchemy bug, not permission constraints.
  • Neglecting engine disposal in short-lived scripts.

Summary

  • Use inspector APIs for fast and reliable table-name listing.
  • Reflect metadata only when structural detail is required.
  • Pass schema explicitly for deterministic behavior.
  • Validate connection context and permissions when output looks wrong.
  • Wrap introspection in reusable utilities for consistent operational tooling.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.