Playground

The playground/ directory is a local development scratchpad for manually testing codegen_database against a real PostgreSQL database. It is not part of the distributed package — it exists purely for iterative development and experimentation.

It includes:

  • A SQLAlchemy model (models.py) you can extend freely

  • An Alembic setup for managing the local database schema

  • A justfile with commands wrapping common database operations

Getting the code

Clone the repository and install dependencies:

git clone https://github.com/FSHTech/codegen-database.git
cd codegen_database
uv sync --all-groups

The playground/ directory is inside the repository root.

Prerequisites

You need a running PostgreSQL server. The easiest options are:

Docker (recommended)

docker run -d \
    --name codegen_database-db \
    -p 5432:5432 \
    -e POSTGRES_DB=codegen_database \
    -e POSTGRES_HOST_AUTH_METHOD=trust \
    postgres:18

System install (Linux — Ubuntu/Debian)

See the PostgreSQL downloads page for the most up-to-date instructions for your distribution. On Ubuntu/Debian:

sudo apt install -y postgresql
sudo systemctl start postgresql
sudo systemctl enable postgresql   # start automatically on boot

Then create a local superuser so you can connect without switching to the postgres system user:

sudo -u postgres createuser --superuser $USER

By default, PostgreSQL requires a password for TCP connections. To avoid this, connect via a Unix socket instead — peer auth is passwordless for your local user with no extra configuration:

Warning

Passwordless authentication is only appropriate for local development. Production databases should always require credentials.

Configuration

Copy the example env and set your database URL:

cp playground/.env.example playground/.env

Edit playground/.env with your connection details:

# System postgres via Unix socket (passwordless, recommended):
DATABASE_URL=postgresql+psycopg:///codegen_database

# Docker or system postgres via TCP with no password:
DATABASE_URL=postgresql+psycopg://localhost/codegen_database

# System postgres with a user/password:
DATABASE_URL=postgresql+psycopg://user:password@localhost/codegen_database

playground/.env is gitignored and will never be committed.

Setup

From the playground/ directory, run:

just init

This creates the database (safe to re-run if it already exists) and applies all pending migrations.

Commands

All commands are run from the playground/ directory.

just check

Fail if migrations have multiple heads or migration-hash.txt is stale.

just current

Current migration state.

just db-shell

Open a psql shell using DATABASE_URL.

just downgrade <rev>

Downgrade to a specific revision: just downgrade abc123.

just format-revision <FILE>

they resolve against the codegen_database package regardless of cwd.

just fresh-revision <msg>

Reset and generate a fresh revision: just fresh-revision "...". [alias: fr]

just history

Migration history.

just init

Create the database and apply all migrations.

just migrate

Apply all pending migrations.

just migrate-revision <msg>

Apply migrations then generate a new one. [alias: mr]

just relinearize <base="origin/main">

Run to resolve a migration-hash.txt merge conflict.

just reset

Wipe migrations and drop the database. [alias: r]

just revision <msg>

New autogenerated migration: just revision "add users table".

just rollback

Roll back the most recent migration.

just wipe-db

Drop and recreate the database, cleaning up non-system roles. [alias: wd]

just wipe-migrations

Delete all migration version files. [alias: wm]

Typical workflow

  1. Edit models.py to add or change a model

  2. Generate a migration:

    just revision "add email_verified to users"
    
  3. Review the generated file in migrations/versions/

  4. Apply it:

    just migrate
    
  5. Write a script or use db-shell to verify the result:

    just db-shell
    
  6. If something is wrong, roll back and adjust:

    just rollback
    

Adding models

Define new models in playground/models.py. The playground uses the declarative style — subclass Base and declare columns as class attributes:

class Widgets(Base):
    __tablename__ = "widgets"
    __table_args__ = {"schema": "public"}

    name = Column(String, nullable=False)
    weight = Column(Float)

See playground/models.py for a full example covering all dimension types.

For a comparison of the declarative and imperative styles, and for choosing between them, see Declarative style.

Then generate and apply a migration:

just revision "add widgets table"
just migrate

Alembic autogenerate compares your models against the live database schema, so it will pick up additions, removals, and column changes automatically.