QDG Knowledge Base Read-only viewer QWebHub
general

Architecture

Version 1 · Initial detailed architecture doc: request/auth flow, data layer, caching, deployment

Runner Form — Architecture

Request flow

  1. Browser sends an HTTP request to Django.
  2. config/urls.py includes runner_form.urls at the /form/ prefix.
  3. @custom_login_required checks request.session['is_authenticated']; redirects to the login page if absent.
  4. The view function calls one or more functions in queries.py.
  5. queries.py checks the cache first; on a miss it runs raw SQL against qdb via django.db.connection, normalises the rows, and caches the result.
  6. The view assembles a context dict and renders the matching template.
  7. The rendered HTML (or JSON, for /api/*) is returned to the browser.

Authentication flow

Custom session auth — not Django's User model or django.contrib.auth.

  1. User submits the login form.
  2. views.login_view() compares the POSTed credentials against a hard-coded check (username == 'admin' and password == <dev password>, see views.py — value not reproduced here).
  3. On success, request.session['is_authenticated'] = True and request.session['username'] are set; the user is redirected to /form/runner/.
  4. @custom_login_required gates every other view (except the public /api/search/) on that session flag.
  5. SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' — sessions live entirely in a signed browser cookie, no DB session table.
  6. Logout calls request.session.flush().

This must be replaced with real user accounts or OAuth before any shared/production deployment — see [[bugs]].

Data layer

No Django ORM models. runner_form/queries.py (~1,440 lines) owns all SQL:

  • _fetchall(sql, params) / _fetchone(sql, params) — thin wrappers over django.db.connection.cursor(), returning list[dict] / dict | None.
  • _base_run_select(where_sql) — the one wide query (joined to jockey, race, meeting, country, courses, generic_lookup) that every runner/jockey/trainer/meeting view builds on.
  • _normalise_runs(rows) — adds display_* fields and computed flags (is_winner, is_placed, gear_list).
  • compute_stats(runs) — pure-Python aggregation into career/last5/by-distance/by-track/ by-class/by-country/by-jockey/by-trainer/by-sot buckets.

See [[database-schema]] for the tables and columns, and [[api-reference]] for what each endpoint returns.

Caching

Django's default in-memory cache (per worker process). Every cache key is prefixed runner_form: and most are version-suffixed (v2, v3, v7, …) so a query/shape fix can be force-invalidated by bumping the suffix rather than flushing the cache. Full key/timeout table in [[api-reference]].

Per-process caching does not share state across multiple IIS/wFastCGI workers — switching CACHES['default'] to django-redis is the documented fix for multi-worker production deployment.

Tech stack

Component Technology
Web framework Django (Runner_Form_Project_Documentation.docx records 5.2.8; CLAUDE.md records 6.0.6 — reconcile against the active venv before relying on either)
Language Python 3.10+
Database MySQL, database qdb @ 138.226.220.168:3306, driver mysqlclient
Frontend Django Templates + vanilla JS, no framework
Sessions Signed cookies (no DB writes)
Cache Django in-memory (default) — Redis recommended for production
Static files Django staticfiles in dev; IIS/nginx in production

Deployment (IIS / Windows Server)

  1. Install Python and pip on the server.
  2. pip install wfastcgi, then run wfastcgi-enable.
  3. Enable the FastCGI feature in IIS Manager.
  4. Add a web.config pointing IIS at wfastcgi.py with WSGI_HANDLER=config.wsgi.application and DJANGO_SETTINGS_MODULE=config.settings.
  5. python manage.py collectstatic, then grant IIS_IUSRS read access to static/.

A static/web.config already exists in this repo for static file serving.

Production hardening

See [[bugs]] for the open items (hard-coded login, plaintext DB credentials, single-process cache) that block a real production deployment, and [[user-guide]] for day-to-day setup and troubleshooting.

Updated by Claude on Aug. 12, 2026, 9:43 a.m. · Task: create project documentation (Architecture document)