Skip to content

feat: Adding archived and deletion-locked flags to tagging models - #856

Open
tbain wants to merge 2 commits into
openedx:mainfrom
tbain:tbain/776_archived_deletion_locked
Open

tbain wants to merge 2 commits into
openedx:mainfrom
tbain:tbain/776_archived_deletion_locked

Conversation

@tbain

@tbain tbain commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds archived = models.BooleanField(default=False) to Taxonomy, Tag, and ObjectTag, and
deletion_locked = models.BooleanField(default=False) to ObjectTag only, in one schema-only
migration (oel_tagging 0023_archived_and_deletion_locked).

Today these three models can only be hard-deleted, which would destroy the learner mastery history
that points at them. archived records that a row has been retired: hidden from active use, but
kept. deletion_locked records that something outside the tagging app depends on an association,
so a future delete must archive it instead of removing it. Tag and Taxonomy have no lock field
of their own; whether one is locked is derived from the associations beneath it, which #777 adds.

This PR makes no behavior changes. Nothing sets, reads, or filters on either field. #777 adds the
lock API, #778 makes archived records inert on read and write paths, and #779 through #781 add the
delete branches that use both fields.

Impacted roles: Developer only. This is a schema-only change with no reachable behavior yet.

Choices that differ from the ticket's suggestions

Supporting information

Testing instructions

  1. Apply the migration: python manage.py migrate oel_tagging
  2. Confirm no migration is missing: python manage.py makemigrations --check --dry-run
  3. Run the new and updated tests:
    pytest tests/openedx_tagging/test_models.py tests/openedx_tagging/test_migrations.py --no-cov
    • test_archived_and_deletion_locked_default_to_false_for_existing_rows runs the real migration
      over a taxonomy, tag, and association created on 0022 and checks they come out unarchived
      and unlocked.
    • test_cast_copies_archived_and_deletion_locked checks that ObjectTag.cast() carries both
      flags over.
  4. Manual check in a Django shell: create a Taxonomy, a Tag, and an ObjectTag, and confirm
    archived and deletion_locked are False. Set deletion_locked=True on the ObjectTag,
    save it, reload it, and confirm archived is still False.
  5. Confirm the fields are inert: grep -rn "archived\|deletion_locked" src/openedx_tagging
    matches only the model definitions, the migration, and ObjectTag.copy().

Other information

  • PII: all three models are already listed as no_pii in .annotation_safe_list.yml, so the new
    fields are covered without new annotations. make pii_check currently fails on main for
    reasons unrelated to this PR: openedx_content.Draft and openedx_content.PublishableEntityVersion
    are both annotated and in the safe list, and 20 models in other apps are uncovered. None of
    those failures involve the tagging models.
  • pydocstyle reports the same pre-existing errors in src/openedx_tagging/models/base.py on
    main and on this branch; this PR adds none.
  • Migration rollback is trivially reversible (four AddField operations, no data migration).
  • AI usage: this PR was drafted with Claude (Anthropic) in Cowork. The developer reviewed the
    change and ran all tests and checks locally.

Generated with Claude Code

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

Copy link
Copy Markdown

Thanks for the pull request, @tbain!

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.

Claude and I collaborated on this code review. Claude wrote this comment:

Thanks for this, the model and migration changes look good. The ObjectTag.copy() fix is a good catch, and I confirmed that test_cast_copies_archived_and_deletion_locked fails when those two copy() lines are reverted, so that test is doing real work. I also ran the tagging model and migration tests, makemigrations --check, pylint, pycodestyle, isort, mypy, and lint-imports against the PR head, and everything passes.

My requests below are all about the tests. Most of the new tests check things that the schema already guarantees, so they can't fail.

1. Make the test_migrations.py module docstring clearer (line 2). "Run against existing data" is vague. What the tests actually do is create rows on the schema from before a migration, run the real migration, and then check those rows. Something like "Tests that run openedx_tagging migrations over rows created on the earlier schema." would say that directly.

2. In test_archived_and_deletion_locked_default_to_false_for_existing_rows, remove lines 140-147, and with them the line 121 comment and the explicit _export_id/_value arguments on line 123. Lines 140-147 check that the migration left every other field unchanged. Migration 0023 only adds four columns with a default, and adding a column can't change the values in other columns, so these assertions have no way to fail. Lines 146-147 in particular only check the test's own setup. I tried removing the _export_id/_value arguments from line 123: the only failure was assert '' == 'flags_test' on line 146, which comes from the test's setup, not from the migration. With lines 140-147, the comment, and those two arguments all removed, the test still passes. It still checks what matters, lines 135-138: pre-existing rows come out unarchived and unlocked.

3. Remove test_archived_defaults_to_false (test_models.py, line 158) and test_archived_and_deletion_locked_default_to_false (test_models.py, line 611). Both tests check that a newly created row has the default=False declared on the field, so they test the schema itself rather than any behavior. The defaults for pre-existing rows, which are the case #776 actually asks about, are already covered by the migration test.

4. Remove test_setting_one_flag_leaves_the_other_and_identity_unchanged (test_models.py, line 623). archived and deletion_locked are separate columns, so saving one can't change the other, and neither can change object_id, taxonomy_id, tag_id, or the cached fields. I know #776's acceptance criteria list "can be set independently ... with no other field affected", but not every acceptance criterion needs a unit test, and the schema already guarantees this one.

5. Nit, not required: the docstrings on the new tests in test_models.py could be dropped. Pylint doesn't require docstrings on tests here, and each one mostly restates the test name. Keeping them is fine too, since the file has both styles today. This applies to whichever of those tests remain after items 3 and 4, which would be only test_cast_copies_archived_and_deletion_locked.

@tbain

tbain commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Updated to address all the code review comments, handled with Claude 5.5 Opus. I also updated Step 3 of the testing instructions since we changed/removed some of the tests.

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: Needs Triage

Development

Successfully merging this pull request may close these issues.

[BE] Add an archived field to Tag, Taxonomy, and ObjectTag, and a deletion_locked field to ObjectTag

3 participants