Backend-first project · Django REST API

Dependency-aware workflows with explicit permissions and auditable state transitions.

QuestBoard is a Python and Django backend API for collaborative work with dependencies and review. Unlike basic task CRUD, it enforces what work can start, who can approve it, and keeps meaningful changes consistent and auditable.

  • Python
  • Django
  • Django REST Framework
  • PostgreSQL
  • Docker
  • Gunicorn

The project concentrates on backend rules that ordinary create, update and delete endpoints do not express well: workflow eligibility, project-scoped authority, dependency integrity, review separation and durable audit history.

Quest state changes use a dedicated transition operation rather than unrestricted PATCH behavior. Legal transitions carry their own permissions and preconditions, including a controlled review return to IN_PROGRESS.

  1. BACKLOG
  2. READY
  3. IN_PROGRESS
  4. REVIEW
  5. DONE

Dependencies form a validated project-local graph. Planning operations reject invalid edges before insertion, while PostgreSQL constraints backstop duplicate and self-referential relationships. Prerequisites directly gate whether a BACKLOG quest may become READY.

  • Self-dependencies are rejected
  • Dependencies must remain within one project
  • Duplicate edges and cycles are rejected
  • Every prerequisite must be DONE before READY

Authorization is evaluated against project membership, role, assignment and workflow state. OWNER and REVIEWER authority is separated from CONTRIBUTOR execution, and an assignee cannot approve their own work.

Project roles

  • OWNER
  • REVIEWER
  • CONTRIBUTOR

Protected business invariants

  • Assignees must belong to the project
  • Assignment is frozen from IN_PROGRESS
  • Self-approval is forbidden
  • DONE is terminal
  • Protected deletion preserves valid relationships
  • Every project retains at least one OWNER

QuestEvent persists significant creation, assignment, dependency and state mutations with actor, timestamp and structured context. A quest_id_snapshot keeps the historical identifier available after a legal quest deletion.

Domain mutations use transaction.atomic. Quest rows are locked for concurrent state changes, while project-level select_for_update locks serialize graph-sensitive dependency writes and membership changes whose invariants span multiple rows.

  1. transaction.atomic
  2. Quest row · select_for_update
  3. Project row · graph and owner invariants

A PostgreSQL-specific failure exposed an invalid SELECT FOR UPDATE shape through the nullable assignee relation. The fix preserved locking: transition_quest now locks the Quest row only and resolves the nullable relation separately, keeping the transaction boundary and workflow invariants intact.

GitHub Actions exercises the backend against PostgreSQL 17 and validates code quality, migrations, Django configuration, the OpenAPI contract, domain/API tests and Docker image construction. The same Docker runtime is deployed through Gunicorn on Render with managed PostgreSQL.

  • PostgreSQL 17
  • Ruff lint and format
  • Migration drift
  • Django system checks
  • OpenAPI validation
  • Backend/API tests
  • Docker image build