Skip to content

fix: point SITE_ID at the LMS domain during init (supersedes #1389) - #1431

Open
ahmed-arb wants to merge 1 commit into
mainfrom
fix/site-id-1182
Open

fix: point SITE_ID at the LMS domain during init (supersedes #1389)#1431
ahmed-arb wants to merge 1 commit into
mainfrom
fix/site-id-1182

Conversation

@ahmed-arb

Copy link
Copy Markdown
Collaborator

Fixes #1182
Supersedes #1389 (and replaces the reverted #1323).

Problem

Django auto-creates an example.com Site at id=SITE_ID during the first migration. Code paths that run outside of an HTTP request — e.g. bulk emails sent from Celery workers — cannot infer the site from the request host, so Django falls back to the SITE_ID setting and renders links/branding for example.com instead of the real domain.

Approach

This takes the init-layer, data-fix approach discussed on #1389 (rather than the reverted SITE_ID=1 change, or a runtime django.setup monkeypatch):

  • jobs/init/lms.sh (after migrate): renames the auto-created example.com site in place to the LMS domain (settings.LMS_BASE). This is Django's recommended pattern — there is then a single Site, and SITE_ID (still a plain = 2) naturally points at it. Idempotent.
  • Ambiguous installations — where both an example.com site and a separate site for the domain already exist — are left untouched, with a warning printing both site IDs and how to consolidate. This addresses @kdmccormick's concern (fix: fixed wrong site in django shell #1389 (comment)) that data may be tied to either site.
  • jobs/init/cms.sh: ensures a Site exists for the CMS domain. The shared SITE_ID row is owned by the LMS, since the LMS and CMS share one django_site table.

SITE_ID remains declarative (= 2); no runtime mutation, no django.setup patching, no per-process DB writes.

Why not #1389's approach

#1389 wraps django.setup() from the settings template to rewrite settings.SITE_ID on every process boot. As raised in review, that is fragile (patches a core Django bootstrap hook, DB write on every start), racy with import-time reads of SITE_ID, and treats a data-state problem as a runtime workaround. Fixing the data once, in the init step, avoids all of that.

Testing

Verified on the Teak (openedx-dev) image via tutor dev:

  • First-time launch (only example.com@2): renamed in place → single site (2, <LMS domain>), Site.objects.get_current() returns the real site.
  • Existing data (example.com@2 + real site@3): warning printed, database left unchanged.
  • Idempotent re-run → no-op; fresh migrate unaffected by the step; core lms.sh runs before Indigo's settheme (so the fresh+theme case consolidates cleanly).
  • Added unit tests (tests/commands/test_jobs.py) asserting the rendered init scripts; unittest, mypy --strict, black, isort pass.

🤖 Generated with Claude Code

Django auto-creates an "example.com" Site at id=SITE_ID during the first
migration. Code paths that run outside of an HTTP request (e.g. bulk emails
sent from Celery workers) cannot infer the site from the request and fall
back to SITE_ID, so they render links and branding for "example.com".

Fix this in the init layer instead of at runtime: the LMS init job renames
the "example.com" site in place to the LMS domain (Django's recommended
pattern), so there is a single Site and SITE_ID naturally points at it.
Installations that already have both an "example.com" site and a site for
their domain are left untouched, with a warning explaining how to
consolidate them. The CMS init job ensures a Site exists for the CMS domain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ahmed-arb
ahmed-arb requested a review from kdmccormick July 21, 2026 09:06
@ahmed-arb ahmed-arb moved this from Pending Triage to In review in Tutor project management Jul 21, 2026
@ahmed-arb
ahmed-arb requested a review from Faraz32123 July 21, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

2 participants