Development¶
With Docker¶
All you need is Docker with Compose:
Docker Desktop on macOS
and Windows (one installer, Compose included), or Docker Engine with the
Compose plugin on Linux. compose.yaml runs the whole stack: PostgreSQL 17,
Redis, the Django development server, and a Celery worker and beat that
fetch the feeds every 30 minutes. The source is mounted into the
containers, so the server reloads when you edit code.
The Makefile wraps the usual commands (macOS has make once the Xcode
command line tools are installed: xcode-select --install):
make build # build and start the stack
make manage cmd=createsuperuser
make manage cmd="add_feed https://example.org/feed.xml"
The site is at http://localhost:8000 and the admin at http://localhost:8000/admin/.
Postgres (postgres/postgres) and the site are only published on
127.0.0.1.
| Command | Does |
|---|---|
make up / make build |
start the stack / rebuild the image first, then start |
make down |
stop it (docker compose down -v also deletes the database) |
make logs / make logs service=web |
follow the logs of all services / one |
make manage cmd="..." |
any manage.py command, in the web container |
make shell |
a Django shell |
Without make, run the same through Docker Compose: docker compose up -d --build,
docker compose exec web python manage.py createsuperuser, and so on.
- Migrations are applied when the
webcontainer starts, however the stack is started. A migration you create while it runs needsmake manage cmd=migrate(or a restart ofweb). - No
.envneeded: the defaults work. Compose reads.envif there is one (.env.examplelists every setting; see Configuration). - Signing in: every account must confirm its email address first, the
superuser included. In development emails aren't sent but printed: find
the link with
make logs service=web. Staff must then set up two-factor sign-in before the admin opens; to skip that locally, putSTAFF_REQUIRE_2FA=falsein.envand runmake up. - Signup is closed by default. Open it under Site settings in the admin, or invite people there.
- Feeds: run any command from Managing feeds with
make manage cmd="...", for examplemake manage cmd=update_feeds. For a ready-made dataset:make manage cmd="seed_demo --entries 2000". - Tests and lint run in the container too (it includes the dev
dependencies):
docker compose exec web pytestanddocker compose exec web ruff check .. - After changing code used by Celery, restart it:
docker compose restart worker beat. After changing dependencies (pyproject.toml/uv.lock), runmake build.
Without Docker¶
You need uv and PostgreSQL 17 (SQLite won't
do: search relies on PostgreSQL's full-text search). manage.py uses the
development settings. They come from environment variables only; .env is
just for Docker, so export any you need in your shell. DATABASE_URL defaults to
postgres://postgres:postgres@localhost:5432/postgres, which is the
Postgres from compose.yaml: docker compose up -d db gives you one.
The site works without Redis. Only Celery (the scheduled fetches, digests
and clean-ups) needs it, at REDIS_URL (default redis://localhost:6379/0):
uv run celery -A core worker -l info
uv run celery -A core beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
Without Celery, fetch feeds by hand with update_feeds and update_content.
Tests and lint¶
make test # pytest (needs a reachable Postgres, see above)
make lint # ruff check + ruff format --check
make format # apply ruff fixes and formatting
These run on your machine with uv. Inside Docker, use
docker compose exec web pytest (or ruff) instead. pytest always uses
core.settings.test, whatever DJANGO_SETTINGS_MODULE says. CI runs the
tests on Python 3.12 and 3.13 against PostgreSQL 17, checks for missing
migrations, and builds and boots the production image.
Settings modules live in core/settings/ (development, production, test).
Documentation¶
This site is built with Zensical from docs/ and
zensical.toml:
Zensical is in the docs dependency group, which a plain uv sync and the
Docker image leave out; both commands install it on first use. The preview
uses port 8001, because the development stack has 8000.