Skip to content

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 web container starts, however the stack is started. A migration you create while it runs needs make manage cmd=migrate (or a restart of web).
  • No .env needed: the defaults work. Compose reads .env if there is one (.env.example lists 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, put STAFF_REQUIRE_2FA=false in .env and run make 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 example make 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 pytest and docker 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), run make 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.

uv sync
uv run python manage.py migrate
uv run python manage.py runserver

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:

make docs         # preview at http://localhost:8001, reloads on save
deploy/docs.sh    # build and publish

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.