Review before using
SQL to SQLAlchemy models: FAQ
Which SQL does it understand?
CREATE TABLE, CREATE INDEX,
ALTER TABLE … ADD CONSTRAINT and PostgreSQL
CREATE TYPE … AS ENUM, in PostgreSQL, MySQL / MariaDB, SQL Server,
SQLite, Oracle, CockroachDB, ClickHouse, DuckDB, Snowflake, Redshift and Db2 syntax. Every other statement (SET, GRANT, functions, views…) is skipped, so you can
paste a full pg_dump --schema-only or mysqldump --no-data as-is.
What does the generated code look like?
Modern SQLAlchemy 2.0 declarative models: a DeclarativeBase, typed
Mapped[...] attributes with mapped_column(),
Optional for nullable columns, foreign keys, unique and check constraints, indexes, enums, server defaults, identity and computed columns.
The SQL type is only written out when SQLAlchemy cannot infer it from the annotation. To connect the models to
your database, build the engine with the
SQLAlchemy connection string generator.
Does it keep database-specific types?
It detects the source database from the SQL (shown in the Source menu, which you can override) and
uses portable SQLAlchemy types wherever they describe the column exactly. Only when a generic type would lose
information does it use the dialect type: mysql.BIGINT(unsigned=True), mssql.MONEY,
postgresql.TIMESTAMP(precision=3), oracle.NUMBER(10, asdecimal=False), ClickHouse
MergeTree engines and so on. Choose Generic types only for models that ignore those details.
Every database is checked by recreating real dumps from the models and comparing the schemas.
How are relationships named?
Every single-column foreign key between two tables in your SQL gets a relationship() on both sides.
The many-to-one side is named after the column without _id (author_id →
author) and the one-to-many side after the referencing table (posts). When two
tables are linked by more than one foreign key, foreign_keys= is added so SQLAlchemy knows
which one each relationship uses; self-references become parent / children.
Why is my column called date_?
A class attribute named date, text or uuid would hide the import with
the same name inside the class, so the attribute gets a trailing underscore and the real column name is kept
with mapped_column("date"). The database schema does not change.
What happens to tables without a primary key?
The SQLAlchemy ORM can only map tables that have a primary key, so those become plain
Table(...) objects on the same metadata. Unknown column types, such as
PostGIS geometry, are kept verbatim so the generated DDL still matches your database.
Limitations
- Views, triggers, stored procedures and PostgreSQL domains are not turned into models; they are skipped.
- Storage details have no SQLAlchemy equivalent and are left out: partitions, tablespaces, LOB storage, CockroachDB hash-sharded indexes and column families, Redshift distribution and sort keys.
- Many-to-many association tables become regular classes with their two relationships, not
secondary=links. - Class and relationship names come from table and column names (
categories→Category,author_id→author); rename them freely. - Types SQLAlchemy has no class for (PostGIS
geometry, SQL Serverhierarchyid, DuckDBSTRUCT...) are kept verbatim through a smallRawSQLTypeand listed in the warnings. - Tables without a primary key become
Table(...)objects, because the ORM can only map tables that have one.