# sqlacodegen: generating SQLAlchemy models from an existing database

> sqlacodegen reads a live database schema and writes SQLAlchemy model code, with generators for plain tables, declarative classes, dataclasses and SQLModel. It is a code generator, not a migration tool, and its output is meant to be edited.

**agronholm/sqlacodegen** — Automatic model code generator for SQLAlchemy

- Repository: https://github.com/agronholm/sqlacodegen
- Stars: 2,371 · Forks: 283
- Language: Python
- License: NOASSERTION
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/agronholm-sqlacodegen

## The problem: a database that already exists and no models for it

Most SQLAlchemy projects start from Python classes and generate the schema. sqlacodegen handles the opposite direction. The README describes it as a tool that reads the structure of an existing database and generates the appropriate SQLAlchemy model code, using the declarative style if possible. The audience is the engineer handed a legacy PostgreSQL, MySQL or Oracle instance, or a schema owned by another team, who needs to query it through the ORM without hand-transcribing dozens of tables. The repository positions it as a replacement for sqlautocode, which the README says was incompatible with Python 3 and with newer SQLAlchemy versions. The current package targets SQLAlchemy 2.x and requires Python 3.10 or newer, so it is not a drop-in for projects still on SQLAlchemy 1.4 or Python 3.8.

## How sqlacodegen reads a schema and turns it into Python

The database URL is passed straight to SQLAlchemy's create_engine(), so anything SQLAlchemy can connect to is a candidate. From the reflected schema, sqlacodegen builds tables, columns, constraints and foreign keys, then hands that structure to a generator. Generators are registered as entry points under the sqlacodegen.generators group in pyproject.toml, and four ship in the box: tables, declarative, dataclasses and sqlmodels. The choice of generator decides what the output looks like, and each one accepts its own set of --options.

The class-based generators do not produce a class for every table. Two cases fall back to a plain Table object: a table with no primary key constraint, because SQLAlchemy requires one for a model class, and an association table between two other tables. That second rule is a judgement call baked into the tool, and it is the reason a generated module can mix classes and Table definitions in the same file. Relationship detection is driven by the foreign key constraints present in the database, so a schema with foreign keys that were never declared will produce models with no relationships at all.

Naming is deterministic rather than clever. Table names are converted to PEP 8 compliant class names by replacing characters unsuitable for Python identifiers with underscores, then title casing the underscore-separated parts and joining them, so example_name becomes ExampleName. The optional use_inflect flag runs names through the inflect library to singularise them, turning sales_invoices into SalesInvoice. The README is explicit that inflection is off by default because table names are not always English and the process is imperfect. That is the right default.

## Installing sqlacodegen and generating your first models

The base install pulls in SQLAlchemy 2.0.29 or newer and inflect. Optional extras cover dialect-specific types that would otherwise be adapted or dropped.

```bash
pip install sqlacodegen
```

Database drivers are not bundled, so a PostgreSQL run needs a driver such as psycopg or pg8000 installed alongside. The minimum invocation is a database URL and nothing else. The default generator is declarative, which emits classes inheriting from declarative_base().

```bash
sqlacodegen postgresql:///some_local_db
```

Output goes to stdout, so redirect it into a module you intend to keep. To get plain Table objects instead, for a project that uses the Core rather than the ORM, name the tables generator. The README gives this exact example with a MySQL URL.

```bash
sqlacodegen --generator tables mysql+pymysql://user:password@localhost/dbname
```

For SQLModel classes, use the sqlmodels generator. This requires the sqlmodel extra because sqlmodel is an optional dependency.

```bash
pip install sqlacodegen[sqlmodel]
sqlacodegen --generator sqlmodels sqlite:///database.db
```

Generator options are passed as a comma-delimited list to --options. The README shows noconstraints and nobidi combined this way, and the same syntax applies to nocomments, noindexes, nojoined and the rest. Engine arguments are parsed with ast.literal_eval, which matters for drivers that need extra keyword arguments, as in the README's Oracle example.

```bash
sqlacodegen oracle+oracledb://user:pass@127.0.0.1:1521/XE --engine-arg thick_mode=True
```

Run sqlacodegen --help to see the generic options. Expect a single Python module containing every table in the schema, which for a large database means a file you will want to split by hand.

## Where the generated code stops being useful

The output is a snapshot of one schema at one moment. sqlacodegen has no incremental mode described in the README, so re-running it against a changed database produces a fresh module rather than a diff. Teams that adopt it usually generate once, commit the result, and then maintain the models by hand, which means the tool's value decays the moment the schema moves. If your database changes weekly, the generated file is stale weekly.

Reflection is also only as good as the schema it reads. Views, triggers, stored procedures and application-level invariants have no representation in the generated models. A column that is nullable in the database but never actually null in practice will be generated as Optional. A foreign key that exists logically but was never declared as a constraint will not become a relationship.

The dialect extras carry their own warning. The README says CITEXT support should be considered as tested only under a few environments, and the same phrasing appears for the geoalchemy2 types. Treat those paths as less proven than the core reflection code. The keep_dialect_types option exists precisely because the default behaviour adapts dialect-specific column types to generic SQLAlchemy types, which is lossy; if you need StarRocks partition options or PostgreSQL-specific types preserved, you have to ask for it. Finally, the package metadata declares the licence as MIT while the repository's licence field reads NOASSERTION, so confirm the terms yourself before shipping the generated file in a commercial product.

## sqlacodegen against Alembic autogenerate

Alembic autogenerate is the obvious comparison, and the two solve different problems despite both reading a database. Alembic compares a live database against your existing model metadata and emits migration scripts that move one toward the other. It assumes you already have models and a migration history. sqlacodegen assumes you have neither and produces the models themselves. There is no revision history, no upgrade path, no downgrade function.

The practical difference shows up on day one versus day one hundred. On day one, sqlacodegen is faster: one command against a legacy schema and you have a working module. Alembic has nothing to compare against until models exist. On day one hundred, Alembic is the tool that keeps the database and the models in step, and sqlacodegen has nothing to contribute unless you are generating a second set of models for a database you do not control. Some teams run sqlacodegen first to bootstrap, then adopt Alembic for everything after. The README does not describe that workflow, but the two tools do not overlap.

## The sqlmodels generator and link tables

SQLModel output is the most opinionated path. The sqlmodels generator inherits every declarative option and adds nolinktables, which controls how many-to-many association tables are rendered. By default they become link model classes passed to Relationship(link_model=...). With nolinktables they are rendered as plain Table objects referenced through secondary instead. Which you want depends on whether your application needs to attach data to the association itself. If the junction table has only two foreign keys, the default link model adds a class you will never query directly; if it has extra columns, the link model is the only way to reach them through SQLModel. The README does not say which the maintainers prefer, and the default is not obviously better than the alternative.

The nofknames option is worth knowing about for schemas with several foreign keys pointing at the same target. By default sqlacodegen names one-to-many relationships after the foreign key column, producing names like simple_items_parent_container, and many-to-many relationships after the junction table, producing names like students_enrollments. Turning on nofknames reverts to underscore suffixes such as simple_items_ and student_. The longer default names are more informative and more verbose; the option exists for people who find them unwieldy.

## Frequently asked questions

The questions below cover the parts of sqlacodegen that are not obvious from the command line help, including which generators exist, what the options change, and where the tool's responsibility ends.

## Conclusion

Adopt sqlacodegen when you have a database you did not design and you want a starting set of models rather than a blank file. Skip it if your schema changes constantly, because the generated module is a snapshot, not a live mapping. Before trusting the output, run it against a staging database with the generator you intend to use, then check that every foreign key you care about produced a relationship and that association tables came out as Table objects rather than classes.

## FAQ

### How do I use sqlacodegen to generate SQLAlchemy models?

Install it with pip install sqlacodegen, then pass a database URL on the command line. The default generator is declarative and writes classes inheriting from declarative_base(); use --generator tables, --generator dataclasses or --generator sqlmodels to change the output style.

### Which generators does sqlacodegen provide?

Four are registered as entry points: tables, which emits only Table objects; declarative, the default; dataclasses; and sqlmodels for SQLModel classes. The dataclasses and sqlmodels generators accept all the declarative options.

### Does sqlacodegen generate a class for every table?

No. A table with no primary key constraint is emitted as a plain Table object, because SQLAlchemy requires a primary key for a model class. Association tables between two other tables are also emitted as Table objects.

### Why does sqlacodegen not singularise my table names by default?

The use_inflect option runs names through the inflect library to singularise them, but it is disabled by default. The README states that table names are not always in English and that the inflection process is far from perfect.

### Can sqlacodegen keep my models in sync when the database changes?

The README documents no incremental or diff mode. Each run produces a fresh module from the current schema, so keeping models in step with a changing database is outside what the tool does.

## Sources

- [agronholm/sqlacodegen on GitHub](https://github.com/agronholm/sqlacodegen)
- [Issues](https://github.com/agronholm/sqlacodegen/issues)
- [README](https://github.com/agronholm/sqlacodegen/blob/master/README.md)
- [Releases](https://github.com/agronholm/sqlacodegen/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/agronholm-sqlacodegen
