DataTalksClub / DataTalksClub/website

D3.1 phase 1: Additive extension schema (courses.LearnerProfile + accounts_ext)

Open
#390 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

auth courses data-migration enhancement P1
Dominant language
Python
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Part of the D3.1 umbrella (#334): phase 1 of 6. Implements playbook P7 DTC steps 1-2 (expand only) and step 3's schema, against current main, refreshed from #334's original 2026-09-08 grooming.

Normative sources

Source What it governs
community-base repo: docs/plan/phase-3.md, C3.1a/D3.1; docs/03-playbooks.md P7 DTC steps 1-3 Field table and migration procedure (unchanged from #334; not relitigated here)
accounts/models.py (current main) Source definitions this phase mirrors verbatim
_docs/specs/09-migration-rollout-roadmap.md Expand-and-contract rollout policy

Goal

Land the target extension schema as pure additions: no CustomUser field is read, written, or removed by this issue. manage.py check and the full suite pass before and after with zero behavior change. This is the "safe to merge and forget" foundation phase: later phases build on it without redoing it, even if they take a while against a fast-moving main.

Scope

  1. courses.LearnerProfile: new model, user OneToOneField (related_name="learner_profile"), mirroring today's CustomUser definitions verbatim (same max lengths, null/blank, choices, help text) for: role (CharField(10), choices student/instructor, default student), certificate_name, country, region, registration_role, github_url, linkedin_url, personal_website_url, about_me, dark_mode.
  2. accounts_ext app: new Django app, added to INSTALLED_APPS.
    • accounts_ext.IdentityState (user OneToOneField): normalized_email, identity_state, plus the conditional unique constraint accounts_active_normalized_email_unique, mirrored verbatim from accounts.models.CustomUser. Import accounts/identity_values.normalize_account_email rather than copying it.
    • Move six models from accounts to accounts_ext via SeparateDatabaseAndState with db_table pinned to their existing table names, so no table is renamed, rebuilt, or has rows touched: AccountIdentityAlias, AccountIdentityQuarantine, AccountReconciliationRun, CmpLearnerImportProgress, plus two models added to accounts/models.py after #334 was originally groomed — CmpLearnerImportBinding and CmpLearnerClaim (same script-owned import-provenance family as CmpLearnerImportProgress; verify current accounts/models.py for their exact current shape before writing the migration). MailchimpSubscriptionImportRun (also added since original grooming) stays in accounts — it is generic import provenance, not identity state; its disposition is an explicit non-goal here, not a silent decision.
    • Repoint AccountIdentityAlias.survivor's FK (and any other identity-model FK) at the auth-model setting, not a hardcoded accounts.CustomUser label.
  3. Data migrations: one LearnerProfile row and one IdentityState row per existing user, values copied verbatim (nulls stay null). Idempotent on re-run.

Non-goals

  • No reader is switched to the new models (phases 2-3). CustomUser keeps every field unchanged.
  • No removal of any CustomUser field or constraint (phase 4).
  • No RenameModel/AUTH_USER_MODEL change (phase 5).
  • No AISL-origin field additions (phase 6).
  • No decision on MailchimpSubscriptionImportRun's eventual home.

Dependencies

  • Depends on: none (first phase).
  • Blocks: #391, #392 (both need LearnerProfile/IdentityState to exist before any reader can be switched).

Acceptance criteria

Django and integration
  • make django-check passes.
  • make migrations-check passes.
  • make test (full suite) passes with zero behavior change (no test asserts against the new models beyond this issue's own).
  • courses.LearnerProfile exists with the ten fields listed above, user OneToOne, mirroring CustomUser's current definitions field-for-field.
  • accounts_ext app exists, is installed, and declares IdentityState plus the six moved models with their original table names (\d accounts_accountidentityalias etc. via a migration-state assertion, not a live table rename).
  • A migration test seeds users with varied values (null certificate_name/URLs, both roles, every IdentityState.choices member) and asserts exactly one LearnerProfile row and one IdentityState row per user afterward, with identical field values.
  • CustomUser still declares all twelve fields untouched; nothing reads or writes the new models outside this issue's own migration and tests.
  • manage.py migrate applies cleanly end to end on a fresh database; each new migration reverses, or documents why not.
Repository and operations
  • The versioned verification plan per _docs/ci/change-selective-ci.md is recorded in the engineer handoff.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading accounts/models.py and _docs/specs/09-migration-rollout-roadmap.md, then inspect the existing migration state and current CustomUser definitions. Implement the additive models, accounts_ext app, SeparateDatabaseAndState moves, and idempotent data migrations described in the issue. Done means make django-check, make migrations-check, and make test pass, with migration tests confirming copied values and preserved tables.

Written by the indexing model from the issue text.

Assessment

Tech stack
django, python
Domain
backend, databases, testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.