Database and seeding¶
First-run initialisation¶
app/initial_data.py prepares a database for first use:
python -m app.initial_data
- It decides whether to create the tables. It does if none of the app's tables exist yet, or if
INITIAL_DATA_RESET_DBis set to1,true,yesoron. - When creating,
init_dbdrops all tables in the metadata and creates them again from the SQLModel models. - It creates the default tenant, then the
FIRST_SUPERUSERaccount (roleadmin, statusactive) if it is missing, ensures a default workspace in that tenant owned by that account, and adds the membership.
Re-running it on a populated database is safe: it only fills in what is missing. INITIAL_DATA_RESET_DB is destructive. Never set it in production. .env-test sets it to True, so anything started with that file (the Docker image does this, see Testing and deployment) drops and recreates all tables each time initial_data runs unless the variable is overridden.
app/update_data.py (python -m app.update_data) only runs create_all, so it adds tables for new models but does not alter existing ones.
This matters when you move an existing database to this version of the template. Workspaces now belong to a tenant, so an older database lacks the tenant table and the workspace.tenant_id column, and neither initial_data nor update_data adds them. Add a revision for them before starting the app on that database.
Migrations¶
alembic/env.py is configured and imports app.models and settings, but the repository contains no revisions (alembic/versions/ does not exist). Until you generate your first one, alembic upgrade head finds no revisions, prints nothing and exits successfully (checked with alembic 1.13.3 and 1.20.0, with and without an empty versions/ directory), and initial_data creates the tables from the models. The command does connect to PostgreSQL first, see below.
Things to know before relying on Alembic:
alembicis a dependency of the template (pyproject.toml), andprestart.shcallsalembic upgrade head.env.pybuilds a PostgreSQL URL from thePOSTGRES_*settings and ignoresUSE_SQLITE.- Generate a revision after model changes with
alembic revision --autogenerate -m "message", and review it before applying.
The common commands (upgrade head, downgrade -1, history) are listed in alembic/README.md.
Start-up script¶
prestart.sh is the container entry step. It waits until the database answers (app/backend_pre_start.py retries for up to five minutes), runs alembic upgrade head, and runs app/initial_data.py.
Seeding¶
app/seed/ holds the template's setup for the MSFLib seeding tool. It loads sample data through the app's actions, so passwords are hashed and events fire as in normal use. How the YAML, factories and custom seeders work is described in Seeding. The template's pieces are:
| File | Role |
|---|---|
seeders.yml |
Four seeders: accounts (from data/accounts.csv), profiles (inline record, linked to the seeded account), workspaces and users |
data/accounts.csv |
One admin account, seed-admin@example.com |
factories.py |
Return the app's account_action and profile_action for the YAML |
seeders/ |
WorkspaceSeeder and UserSeeder, custom classes for the two seeders that need more than a plain create |
runner.py |
The command line entry point |
Run it with the wrapper script, which uses ../.venv/bin/python when it exists and python otherwise:
./run_seeder.sh --dry-run # validate the YAML and data files
./run_seeder.sh # seed the configured database
The flags and the safety prompt for non-local databases are described in the library page above. Seeding is not idempotent: run it against an empty database.
To add your own data, append a seeder to seeders.yml, put its records under app/seed/data/, and import its action in app/actions/__init__.py.