Skip to content

fix: match openedx-platform's SIMPLE_HISTORY_DATE_INDEX in test settings - #853

Merged
ormsbee merged 4 commits into
openedx:mainfrom
ufedaseyeuconsultant:ufedaseyeu/fix-simple-history-date-index
Oct 9, 2026
Merged

ormsbee merged 4 commits into
openedx:mainfrom
ufedaseyeuconsultant:ufedaseyeu/fix-simple-history-date-index

Conversation

@ufedaseyeuconsultant

@ufedaseyeuconsultant ufedaseyeuconsultant commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Description

Fixes a migration/model drift that only surfaces once this library's models are installed into
openedx-platform: every HistoricalRecords() table (CompetencyCriteriaGroup,
CompetencyCriterion, CompetencyRuleProfile) shipped a migration recording history_date = models.DateTimeField(db_index=True), but django-simple-history derives that field's
db_index from the SIMPLE_HISTORY_DATE_INDEX Django setting at the time the historical model
class is built ("history_date": models.DateTimeField(db_index=self._date_indexing is True),
confirmed identical in django-simple-history 3.12.0 and 3.13.0 source, so this is not a
library-version issue). This library's own test_settings.py never set that setting, so its
migrations were generated against the django-simple-history default (indexed). openedx-platform
sets SIMPLE_HISTORY_DATE_INDEX = False globally in openedx/envs/common.py, so the moment these
models load there, the live model state (db_index=False) disagrees with what this library's own
migrations recorded, and openedx-platform's MigrationTests.test_migrations_are_in_sync fails
with a pending AlterField on history_date for all three historical tables.

Sets SIMPLE_HISTORY_DATE_INDEX = False in this library's own test_settings.py, matching
openedx-platform's setting (the primary consumer these models are built for), and adds the
migration makemigrations generates as a result, dropping the now-mismatched index on
history_date for all three affected tables.

This change impacts the Developer and Operator roles (a migration-correctness fix with no
behavioral effect on authoring or learning functionality). It has no UI effect.

Supporting information

  • Found and diagnosed while preparing an openedx-platform PR to bump this library to 1.6.0,
    which failed CI on this exact test_migrations_are_in_sync check once 1.6.0 was installed
    there.

Testing instructions

  1. DJANGO_SETTINGS_MODULE=test_settings pytest --no-cov — full suite passes (914 passed, 1
    skipped locally).
  2. DJANGO_SETTINGS_MODULE=test_settings python manage.py makemigrations --check --dry-run and
    the same under mysql_test_settings — both report "No changes detected".
  3. Cross-repo verification against the actual failure this fixes: in a local openedx-platform
    checkout, add a temporary [tool.uv.sources] override pointing openedx-core at this branch
    ({ path = "<path-to-this-checkout>", editable = true }), uv lock, uv sync --group testing --frozen --python 3.12 (match CI's Python version; a newer local default can pick an
    interpreter xmlsec has no prebuilt wheel for, an unrelated build failure), then:
    pytest --ds=cms.envs.test common/djangoapps/util/tests/test_db.py::MigrationTests::test_migrations_are_in_sync
    
    This test fails against the currently-published 1.6.0 and passes against this branch. Revert
    the temporary pyproject.toml/uv.lock changes afterward (git checkout --); they're a local
    verification aid, not meant to be committed there.

Deadline

Unblocks an in-progress openedx-platform PR bumping this library to 1.6.0 for QA(openedx/openedx-platform#39199)

Other information

  • Does this change depend on other changes elsewhere? Conceptually yes, in that the "correct"
    value here is whatever SIMPLE_HISTORY_DATE_INDEX openedx-platform sets — if that setting
    value ever changes there, this library's migrations would need a matching follow-up. No code
    dependency on this PR, though.
  • No ADR: this is a narrow bugfix (aligning a test setting with this library's only real
    consumer), not an architectural decision.
  • No manual version bump or changelog entry needed: this repo uses python-semantic-release
    driven by conventional commit prefixes; the fix: commit here triggers an automatic patch
    release once merged.
  • Known limitation, not fixed here: any other consumer of this library that does not set
    SIMPLE_HISTORY_DATE_INDEX = False (i.e. relies on django-simple-history's own default) will
    now see the inverse drift. django-simple-history has no per-model override for history_date
    specifically (no_db_index only affects fields copied from the tracked model, not this
    synthetic one), so there is no way to make this field setting-independent via its current public
    API. Pinning this library's own test settings to its actual primary consumer's choice is the
    available option here, not a complete fix for every possible consumer.
  • Database migration: a simple AlterField dropping an index (DROP INDEX under the hood on
    most backends). Reversible, no data loss, no backfill required.

🤖 Generated with help of Claude Code

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Oct 7, 2026
@openedx-webhooks

Copy link
Copy Markdown

Thanks for the pull request, @ufedaseyeuconsultant!

This repository is currently maintained by @axim-engineering.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@mgwozdz-unicon mgwozdz-unicon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is a collaboration between Claude and me.

@ufedaseyeuconsultant Thanks for tracking this down. Since the point of this PR is getting openedx/openedx-platform#39199 green, there are a few more things to fold in before it's ready, plus one change on the platform PR itself.

1. Migration number conflict. #852 merged this morning and added 0008_competency_mastery_status, 0009, and 0010, and its 0008 also depends on 0007_alter_criterion_override_help_text. That means this PR's 0008 forks the migration graph, and Django will refuse to migrate with a "Conflicting migrations detected" error once both are on main. GitHub still shows this as mergeable because the filenames are different. Could you rebase on main, delete this PR's 0008, and rerun makemigrations so it comes out as 0011 depending on 0010_studentcompetencystatus? Heads up that #854 also adds a 0011 (and 0012), so whichever of the two PRs merges second will need to renumber.

2. Lint Python Imports failure on #39199. src/openedx_learning/applets/cbe/models/criteria.py:18 does from openedx_catalog.models import CourseRun. openedx-platform lists both openedx_catalog and openedx_learning as isolated apps in its import-linter config, which only allows other code to import from an app's api, models_api, data, and tests modules. openedx_catalog/models_api.py already re-exports CourseRun for exactly this purpose (as a foreign key target), so changing that line to from openedx_catalog.models_api import CourseRun should clear it. The foreign key still points at the same model, so this doesn't need a migration.

3. Compile requirements failure on #39199 (that PR, not this one). That check regenerates requirements/edx/base.txt and requirements/edx/development.txt and fails because the regenerated files don't match what's committed. Once this PR merges and semantic-release cuts the new patch version, #39199 will need to bump to that version instead of 1.6.0 anyway, so rerunning make upgrade-package package=openedx-core and committing the regenerated files should take care of both. I haven't confirmed exactly why the files differ today, so let me know if it still fails after a clean regenerate.

@ormsbee Could you add this one to your review queue alongside #854? It's blocking QA on #758, which was also part of our Oct 2 goal. I also wanted to check one thing with you: with this change, openedx-core's committed migrations only stay in sync if SIMPLE_HISTORY_DATE_INDEX in its test settings matches whatever openedx-platform sets, because django-simple-history bakes that setting into the history_date index at migration time. Should openedx-core's test settings mirror openedx-platform's for this case?

@ufedaseyeuconsultant

Copy link
Copy Markdown
Contributor Author

@mgwozdz-unicon done

@mgwozdz-unicon mgwozdz-unicon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@mgwozdz-unicon
mgwozdz-unicon requested a review from ormsbee October 7, 2026 20:20

@ormsbee ormsbee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracking this down, and for the writeup. The diagnosis checks out: django-simple-history reads SIMPLE_HISTORY_DATE_INDEX while it's building each historical model class (models.py#L568 and #L594-L606 in 3.13.0), so whatever that setting is when someone runs makemigrations is what ends up in the migration file. Claude ran the CBE tests and makemigrations --check on this branch for me, and both are clean under test_settings.

I also wanted to check one thing with you: with this change, openedx-core's committed migrations only stay in sync if SIMPLE_HISTORY_DATE_INDEX in its test settings matches whatever openedx-platform sets, because django-simple-history bakes that setting into the history_date index at migration time. Should openedx-core's test settings mirror openedx-platform's for this case?

@mgwozdz-unicon: Yes. Any setting that feeds into model state needs to match openedx-platform, since that's where the migrations we ship actually run. It's a frustrating bit of coupling, but it shows up in other repos too (e.g. edx-organizations). 😞

Only one blocking item: Please set it in projects/dev.py as well (inline). That's the settings module manage.py defaults to, and running makemigrations under it on this branch puts the index right back. I would definitely run into this personally.

I also belatedly realized that the migrations will have to be renumbered, since I just merged #854.

Thank you.


Claude drafted the first pass of this review and ran the CBE tests and makemigrations --check for me against test_settings, projects.dev, and plain main.

Comment thread test_settings.py
# must set it to whatever its actual consumer uses, or installing those migrations into that
# consumer's project drifts out of sync with its live model state. openedx-platform (this
# library's primary consumer) sets this to False in openedx/envs/common.py.
SIMPLE_HISTORY_DATE_INDEX = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add this to projects/dev.py too, since that's what manage.py uses when DJANGO_SETTINGS_MODULE isn't set. Claude ran makemigrations --check --dry-run on this branch under projects.dev, and it generates a 0012 that re-adds the index on all three tables (plus one for organizations). A one-line comment there pointing back at this one is enough–no need to repeat the explanation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread test_settings.py Outdated
Comment on lines +131 to +136
# django-simple-history bakes this setting's value into the db_index it generates for every
# HistoricalRecords() model's history_date field, so a library that ships committed migrations
# must set it to whatever its actual consumer uses, or installing those migrations into that
# consumer's project drifts out of sync with its live model state. openedx-platform (this
# library's primary consumer) sets this to False in openedx/envs/common.py.
SIMPLE_HISTORY_DATE_INDEX = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This explains the mechanism, but not why openedx-platform made the choice, which is the first thing the next person is going to ask when they wonder whether it can be flipped back. Maybe something like?

Suggested change
# django-simple-history bakes this setting's value into the db_index it generates for every
# HistoricalRecords() model's history_date field, so a library that ships committed migrations
# must set it to whatever its actual consumer uses, or installing those migrations into that
# consumer's project drifts out of sync with its live model state. openedx-platform (this
# library's primary consumer) sets this to False in openedx/envs/common.py.
SIMPLE_HISTORY_DATE_INDEX = False
# django-simple-history bakes this setting into the history_date field of every
# HistoricalRecords() model, and therefore into any migration we generate for one.
# openedx-platform has had this set to False since its 2023 django-simple-history
# upgrade (openedx/openedx-platform#32880), to avoid adding an index to every
# historical table in the platform at once. Our migrations run there, so this has
# to match.
SIMPLE_HISTORY_DATE_INDEX = False

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

from simple_history.models import HistoricalRecords

from openedx_catalog.models import CourseRun
from openedx_catalog.models_api import CourseRun

@ormsbee ormsbee Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This is the right fix, but it bothers me a little that openedx-platform's import-linter had to be the one to catch it. Our .importlinter only layers the top-level packages and says nothing about which modules of openedx_catalog the layers above may reach into, which is the whole reason models_api.py exists. We should add the equivalent of openedx-platform's isolated_apps contract (pyproject.toml, the one with allowed_modules = ["api", "models_api", "data", "tests"]) on the openedx-core side, so the next one of these fails here instead.

Not required to merge, but I would appreciate it if you could add it here or to a follow-up PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking on this more, the contract stuff should definitely be a follow-up PR, so we unblock the upgrade as soon as possible. Thank you.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be changed in the follow-up PR

ufedaseyeuconsultant and others added 4 commits October 9, 2026 19:49
django-simple-history bakes the SIMPLE_HISTORY_DATE_INDEX setting's value
into the db_index it generates for every HistoricalRecords() model's
history_date field. This library's test settings never set it, so its
committed migrations recorded db_index=True (the django-simple-history
default) for CompetencyCriteriaGroup, CompetencyCriterion, and
CompetencyRuleProfile's historical tables. openedx-platform, the primary
consumer these models are built for, sets SIMPLE_HISTORY_DATE_INDEX = False
globally in openedx/envs/common.py, so installing this library there makes
the live model state disagree with the shipped migrations: Django detects a
missing migration for history_date on every one of these three tables the
moment openedx-platform's own test suite loads them, failing its
MigrationTests.test_migrations_are_in_sync check.

Sets the same value in test_settings.py so this library's own migrations
match what its actual consumer produces, and adds the migration that
`makemigrations` generates as a result.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rebasing onto upstream/main (not this fork's lagging origin/main) put
this branch's own 0008 migration side by side with upstream's unrelated
0008_competency_mastery_status, 0009, and 0010 (all added after this
branch forked), forking the migration graph since both chains depend on
0007. Deletes this branch's old 0008 and regenerates it via
makemigrations so it lands as 0011, depending on
0010_studentcompetencystatus; its operations are otherwise identical to
the deleted file.

Also switches criteria.py's CourseRun import from openedx_catalog.models
to openedx_catalog.models_api. openedx-platform's own import-linter
config treats openedx_catalog as an isolated app, reachable only through
its api/models_api/data/tests modules, so the direct .models import
fails that check once openedx-platform installs this library, even
though this repo's own, more permissive layering contract never caught
it. models_api already re-exports CourseRun for exactly this kind of
foreign-key reference; the FK still targets the same model, so no
migration is needed for this part.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
manage.py defaults to projects.dev when DJANGO_SETTINGS_MODULE isn't
set, and running makemigrations under it (the path a contributor is
most likely to hit locally) regenerated the history_date index on all
three CompetencyCriteria* historical tables, undoing this fix.
projects/dev.py now sets the same value, pointing back at
test_settings.py's comment instead of repeating it.

Also replaces that comment with one that explains why
openedx-platform chose False, not just the baking mechanism: it's been
set since openedx-platform's 2023 django-simple-history upgrade
(openedx/openedx-platform#32880), to avoid adding an index to every
historical table in the platform at once.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rebased onto upstream/main, which merged two more migrations
(0011_studentcompetencycriterionstatus, 0012_studentcompetencycriteriagroupstatus)
since this branch's migration was last renumbered, forking the graph
again. Deletes the old 0011 and regenerates via makemigrations, landing
as 0013 depending on 0012_studentcompetencycriteriagroupstatus; the
field operations are otherwise identical to the deleted file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ufedaseyeuconsultant
ufedaseyeuconsultant force-pushed the ufedaseyeu/fix-simple-history-date-index branch from cd51ceb to f42e6b2 Compare October 9, 2026 16:54
@ufedaseyeuconsultant

Copy link
Copy Markdown
Contributor Author

@ormsbee Migration updated, attr added

@ormsbee
ormsbee merged commit b6a58bf into openedx:main Oct 9, 2026
7 checks passed
ufedaseyeuconsultant added a commit to ufedaseyeuconsultant/openedx-core that referenced this pull request Oct 9, 2026
Rebased onto upstream/main, which merged openedx#853 since this branch was
last renumbered, adding its own 0013 migration depending on the same
0012_studentcompetencycriteriagroupstatus this branch's migration also
depended on. Deletes the old 0013 and regenerates via makemigrations,
landing as 0014; the field operations are otherwise identical to the
deleted file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ufedaseyeuconsultant added a commit to ufedaseyeuconsultant/openedx-core that referenced this pull request Oct 9, 2026
Drops the commit hash and the "copied back and forth" sentence from
the module docstring (nothing copies this file back to
openedx-platform, so that process doesn't exist), removes the two
method docstrings now that the file is closer to openedx-platform's
original, and cuts the list() workaround's annotations and
three-line comment down to one line (the annotations did nothing:
mypy skips the untyped check() method's body, and pylint passes
without them either way).

Also shortens the comment above the new contract in .importlinter:
drops the clause about how the bad import happened, since that
history belongs in openedx#853 and openedx#860, not in the config.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

open-source-contribution PR author is not from Axim or 2U

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants