sqlacodegen

repository·master·Indexed 25 days ago

https://github.com/agronholm/sqlacodegen

An automatic model code generator for SQLAlchemy that reads an existing database schema to generate model code. It supports multiple styles including declarative, dataclass, and SQLModel, and provides a CLI for specifying database URLs, schemas, and generator options.

Tokens
4.1K
Snippets
5
Records
25
Agent score
82%

What's inside sqlacodegen

  1. Available code generators

    master

    You can select different generators using the --generator flag. The available built-in generators are:

    • tables: Only generates Table objects (useful if you don't want to use the ORM).
    • declarative (default): Generates classes inheriting from declarative_base().
    • dataclasses: Generates dataclass-based models (requires SQLAlchemy 1.4+).
    • sqlmodels: Generates model classes for SQLModel.
  2. How model class and relationship naming works

    master

    Model Class Naming

    By default, table names are converted to PEP 8 compliant class names (e.g., example_name becomes ExampleName). If the use_inflect option is enabled, table names are converted to their singular form (e.g., sales_invoices becomes SalesInvoice).

    Relationship Naming

    Relationships are typically named after the opposite class's table name.

    • Special Case: If a column is named employer_id, the relationship is named employer (stripping the _id suffix).
    • Self-referential: The reverse side gets a _reverse suffix.
    • Disambiguation: When multiple FKs or junction tables connect to the same target:
      • One-to-many uses FK column names (e.g., simple_items_parent_container).
      • Many-to-many uses junction table names (e.g., students_enrollments).
      • Use the nofknames option to revert to simpler underscore suffixes (e.g., simple_items_).
  3. Quickstart with sqlacodegen

    master

    To use sqlacodegen, provide a database URL (compatible with SQLAlchemy's create_engine()). The tool reads the database structure and generates SQLAlchemy model code.

    Basic usage examples:

    # Basic usage with a local PostgreSQL database
    sqlacodegen postgresql:///some_local_db
    
    # Using a specific generator (e.g., tables) with MySQL
    sqlacodegen --generator tables mysql+pymysql://user:password@localhost/dbname
    
    # Generating dataclass-based models
    sqlacodegen --generator dataclasses sqlite:///database.db
    
    # Passing engine arguments (parsed via ast.literal_eval)
    sqlacodegen oracle+oracledb://user:pass@127.0.0.1:1521/XE --engine-arg thick_mode=True
    
    # Passing complex engine arguments like connect_args
    sqlacodegen oracle+oracledb://user:pass@127.0.0.1:1521/XE --engine-arg thick_mode=True --engine-arg connect_args='{"user": "user", "dsn": "..."}'
    sqlacodegen postgresql:///some_local_db
  4. Customize code generation logic

    master

    To implement custom generation logic, subclass one of the existing code generator classes and override the necessary methods.

    To make your custom generator available via the CLI:

    1. Implement your class.
    2. Register it as an entry point in the sqlacodegen.generators namespace.
    3. Install your package (e.g., via pip install .).
    4. Invoke it using the --generator flag:
    sqlacodegen --generator <your_entry_point_name> <database_url>
  5. Install sqlacodegen

    master

    Install the base package using pip:

    pip install sqlacodegen

    Depending on your database requirements, you may need to install specific extras for PostgreSQL support:

    • CITEXT extension: pip install sqlacodegen[citext]
    • PostgreSQL Geometry/Raster: pip install sqlacodegen[geoalchemy2]
    • PGVECTOR extension: pip install sqlacodegen[pgvector]
    pip install sqlacodegen
  6. Configure generator options

    master

    You can customize code generation by passing options via the --options flag. Multiple options must be comma-delimited (e.g., --options noconstraints,nobidi).

    tables options

    • noconstraints: Ignore constraints (foreign key, unique, etc.).
    • nocomments: Ignore table/column comments.
    • noindexes: Ignore indexes.
    • nonativeenums: Use plain string mapping instead of Python enum classes for native DB ENUMs.
    • nosyntheticenums: Don't generate Python enum classes from CHECK constraints with IN clauses.
    • noidsuffix: Prevent special naming logic for single column many-to-one and one-to-one relationships.
    • include_dialect_options: Render dialect-specific table options.
    • keep_dialect_types: Preserve dialect-specific column types instead of adapting to generic SQLAlchemy types.

    declarative options

    • All options from tables are available.
    • use_inflect: Use the inflect library to turn plural names into singular for classes and relationships.
    • nojoined: Do not attempt to detect joined-class inheritance.
    • nobidi: Generate relationships in a unidirectional fashion (only many-to-one or first side of many-to-many).
    • nofknames: Disable improved relationship naming. Reverts to underscore suffixes (e.g., simple_items_) instead of using FK column names or junction table names.

    dataclasses options

    • All options from declarative are available.

    sqlmodels options

    • All options from declarative are available.
  7. How DeclarativeGenerator produces SQLAlchemy 2.0 models

    master

    The DeclarativeGenerator is designed to produce modern SQLAlchemy 2.0 style code using DeclarativeBase, Mapped, and mapped_column.

    Key behaviors include:

    • Base Class: It generates a base class inheriting from sqlalchemy.orm.DeclarativeBase.
    • Type Annotations: It uses Mapped[Type] for both columns and relationships.
    • Imports: It automatically collects necessary imports such as from sqlalchemy.orm import Mapped, mapped_column, relationship and from typing import Optional.
    • Relationships: It automatically detects and generates ONE_TO_ONE, MANY_TO_ONE, ONE_TO_MANY, and MANY_TO_MANY relationships based on foreign key constraints.
    • Inheritance: If joined table inheritance is detected and nojoined is not specified, it nests child classes under their parent classes.
    • Naming: It handles name collisions by finding free names and can use inflect to singularize class names.
  8. How TablesGenerator works

    master

    The TablesGenerator is the primary engine for producing SQLAlchemy model code from database metadata. Its lifecycle follows these steps:

    1. Base Generation: Creates a MetaData object and necessary imports.
    2. Metadata Cleaning: Removes tables that should be ignored (like alembic_version) and applies requested exclusions (indexes, constraints, or comments).
    3. Type Fixing: Adjusts reflected column types, such as converting integer columns with IN (0, 1) check constraints into Boolean types, or VARCHAR columns with specific IN constraints into Enum types.
    4. Model Generation: Creates Model objects for each table and determines unique Python attribute names to avoid collisions.
    5. Import Collection: Automatically gathers all necessary imports for types, constraints, and indexes.
    6. Rendering: Produces a string containing module-level variables, Python enum.Enum classes, and the SQLAlchemy model definitions.
  9. Understand relationship naming logic

    master

    The generator uses several strategies to name relationship attributes in the resulting Python classes:

    1. Default: Uses the target table name or a derived name.
    2. ID Stripping: If noidsuffix is not set, it attempts to strip _id from foreign key column names (e.g., user_id becomes user).
    3. FK Qualification: If nofknames is not set, it may use foreign key column names to qualify relationships, especially for multi-column foreign keys.
    4. M2M Qualification: For many-to-many relationships with multiple junction tables, it uses the junction table name as a qualifier.
    5. Inflection: If use_inflect is enabled, it will singularize ONE_TO_ONE and MANY_TO_ONE relationships and pluralize ONE_TO_MANY and MANY_TO_MANY relationships.
  10. Automatic Enum generation from CHECK constraints

    master

    By default, sqlacodegen can detect CHECK constraints on String/VARCHAR columns and convert them into Python enum.Enum classes.

    For example, a column with a constraint like CHECK (status IN ('active', 'inactive')) will be transformed into:

    class Status(str, enum.Enum):
        ACTIVE = 'active'
        INACTIVE = 'inactive'
    
    # ... in the model
    status = mapped_column(Enum(Status, ...))

    To disable this behavior and keep the column as a standard string, use the nosyntheticenums option.

  11. How column type adaptation works

    master

    The generator attempts to adapt database-specific types to standard SQLAlchemy types unless keep_dialect_types is enabled.

    Special handling is included for:

    • Enums: Handles native database ENUM types (like PostgreSQL ENUM) and ensures they are correctly represented.
    • Arrays: Handles ARRAY columns, including those containing Enums (e.g., ARRAY(ENUM) in PostgreSQL), while preserving dialect-specific ARRAY subclasses to maintain functionality like .contains().
    • PostgreSQL Sequences: Detects sequences from server_default in PostgreSQL and adds explicit sqlalchemy.Sequence objects.
    • Domains: Includes specific logic to adapt PostgreSQL DOMAIN types.
  12. Use the sqlacodegen CLI to generate SQLAlchemy models

    master

    The sqlacodegen command-line interface generates SQLAlchemy model code from an existing database using a specified SQLAlchemy URL. By default, it outputs the generated code to stdout, but you can specify an output file using --outfile.

    Basic usage pattern:

    sqlcodegen <DATABASE_URL> [OPTIONS]