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 freelyAn Alembic setup for managing the local database schema
A
justfilewith 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 checkFail if migrations have multiple heads or migration-hash.txt is stale.
just currentCurrent migration state.
just db-shellOpen 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 historyMigration history.
just initCreate the database and apply all migrations.
just migrateApply 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 resetWipe migrations and drop the database. [alias: r]
just revision <msg>New autogenerated migration:
just revision "add users table".just rollbackRoll back the most recent migration.
just wipe-dbDrop and recreate the database, cleaning up non-system roles. [alias: wd]
just wipe-migrationsDelete all migration version files. [alias: wm]
Typical workflow¶
Edit
models.pyto add or change a modelGenerate a migration:
just revision "add email_verified to users"
Review the generated file in
migrations/versions/Apply it:
just migrate
Write a script or use
db-shellto verify the result:just db-shell
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.