Skip to content

feat!: Package and Entity keys are now opaque refs#546

Open
kdmccormick wants to merge 1 commit intokdmccormick/keys-containerfrom
kdmccormick/keys-ref
Open

feat!: Package and Entity keys are now opaque refs#546
kdmccormick wants to merge 1 commit intokdmccormick/keys-containerfrom
kdmccormick/keys-ref

Conversation

@kdmccormick
Copy link
Copy Markdown
Member

@kdmccormick kdmccormick commented Apr 15, 2026

Description

The {LearningPackage,PublishableEntity}.key fields were always meant to be externally-supplied refs whose format does not matter. However, they were effectively being used as parseable psuedo-keys for identifying containers and components, with more assumptions to their structure being baked in over time, both within and outside openedx-core.

This commit renames them to {package,entity}_ref, and removes all parsing of them from the API and from the internals (except for where required for backcompat with Ulmo ZIP backups). Instead, the pairings of (component_type,component_code) and (container_type,container_code) are used to identify components and containers; their respective entity_refs are derived from those by convention but the publishing app has no conception of this convention.

BREAKING CHANGES to openedx_django_lib:

  • Renamed key_field(...) -> ref_field(...) in openedx_django_lib.

BREAKING CHANGES to models API:

  • Renamed LearningPackage.key -> LearningPackage.package_ref.
  • Renamed PublishableEntity.key -> PublishableEntityKey.entity_ref
  • Renamed PublishableEntityMixin.key -> PublishableEntityMixin.entity_ref

BREAKING CHANGES to publishing API:

  • In create_learning_package(...), renamed param key -> package_ref
  • In update_learning_package(...), renamed param key -> package_ref
  • In learning_package_exists(...), renamed param key -> package_ref
  • In create_publishable_entity(...), renamed param key -> entity_ref
  • Renamed get_publishable_entity_by_key(...) -> get_publishable_entity_by_ref(...), and renamed param key -> entity_ref
  • Renamed get_learning_package_by_key(...) -> get_learning_package_by_ref(...), and renamed param key -> package_ref

BREAKING CHANGES to components APIs:

  • In create_component(...):
    • Positional key argument removed
    • Entity key (now "entity_ref") is derived as "{namespace}:{type_name}:{component_code}"
  • Function get_or_create_component_type_by_entity_key() removed.

BREAKING CHANGES to container APIs:

  • Renamed get_container_by_key(...) -> get_container_by_code(...), and renamed param key -> container_code
  • Rename get_container_children_entities_keys(...) -> get_container_children_entity_refs(...)
  • Renamed get_container_children_entities_keys(...) -> get_container_children_entity_refs(...)
  • In create_container(...):
    • Positional key argument removed
    • Entity key (now "entity_ref") is derived as "{namespace}:{type_name}:{container_code}"

BREAKING CHANGES to collections API:

  • In get_entity_collections(...), renamed param entity_key -> entity_ref

BREAKING CHANGES to media API:

  • In look_up_component_version_media(...):
    • Renamed param learning_package_key -> learning_package_ref
    • Renamed param component_key -> entity_ref
  • In add_assets_to_component management command:
    • Renamed argument learning_package_key -> learning_package_ref
    • Renamed argument component_key -> entity_ref

BREAKING CHANGES to backup_restore API:

  • BREAKING CHANGE In load_learning_package:
    • Renamed param key -> package_ref
    • In return dict, within sub-dict ["lp_restored_data"]:
      • Renamed ["key"] -> ["package_ref"]
      • Renamed ["archive_lp_key"] -> ["archive_package_ref"]
      • Renamed ["archive_org_key"] -> ["archive_org_code"]
      • Renamed ["archive_slug"] -> ["archive_package_code"]
    • If archive_package_ref cannot be parsed into "{_prefix}:{org_code}:{package_code}", then archvie_org_code and archive_package_code will both be None. Callers should handle this case. (Previously, a ValueError would be raised by backup-restore code.)

Backup-restore still write and reads entity_ref and package_ref to/from TOML files as key
for backwards compatibility. This may change in a future "v2" restore format.

Part of: #322

Full series of PRs:

  1. feat!: Collection.key -> Collection.collection_code #542
  2. feat!: Component.local_key -> Component.component_code #544
  3. feat!: Add Container.container_code field #545
  4. feat!: Package and Entity keys are now opaque refs #546
  5. feat!: ComponentVersionMedia.key -> ComponentVersionMedia.path #547

Testing, AI Usage, and Merge Considerations

See #322

Comment thread src/openedx_content/applets/subsections/api.py
@kdmccormick kdmccormick force-pushed the kdmccormick/keys-container branch from 266d7f3 to 5283857 Compare April 16, 2026 20:47
@kdmccormick kdmccormick force-pushed the kdmccormick/keys-ref branch from 52fe205 to b9d11db Compare April 16, 2026 20:48
@kdmccormick kdmccormick force-pushed the kdmccormick/keys-container branch 2 times, most recently from c32f78c to 94bd0e5 Compare April 17, 2026 16:36
@kdmccormick kdmccormick force-pushed the kdmccormick/keys-ref branch from 09bbd04 to 3f23389 Compare April 17, 2026 16:43
@kdmccormick kdmccormick force-pushed the kdmccormick/keys-container branch from 8434cbf to 64f0eeb Compare April 17, 2026 16:56
@kdmccormick kdmccormick force-pushed the kdmccormick/keys-ref branch from 29580c7 to 3a446d5 Compare April 17, 2026 17:01
@kdmccormick kdmccormick marked this pull request as ready for review April 17, 2026 17:58
@kdmccormick kdmccormick force-pushed the kdmccormick/keys-ref branch from fd76848 to 28197b9 Compare April 17, 2026 18:27
@kdmccormick kdmccormick force-pushed the kdmccormick/keys-container branch from 64f0eeb to f67afa4 Compare April 17, 2026 18:47
The `{LearningPackage,PublishableEntity}.key` fields were always
meant to be externally-supplied refs whose format does not matter.
However, they were effectively being used as parseable psuedo-keys
for identifying containers and components, with more assumptions to their
structure being baked in over time, both within and outside openedx-core.

This commit renames them to `{package,entity}_ref`, and removes all
parsing of them from the API and from the internals (except for where
required for backcompat with Ulmo ZIP backups). Instead, the pairings
of (component_type,component_code) and (container_type,container_code)
are used to identify components and containers; their respective
entity_refs are derived from those *by conventional default* but also
are customizable by callers.

BREAKING CHANGE: Renamed key_field(...) -> ref_field(...) in openedx_django_lib.

BREAKING CHANGE: Renamed PublishableEntity.key -> PublishableEntityKey.entity_ref
BREAKING CHANGE: Renamed PublishableEntityMixin.key -> PublishableEntityMixin.entity_ref
BREAKING CHANGE: In create_publishable_entity(...), renamed param key -> entity_ref
BREAKING CHANGE: Renamed get_publishable_entity_by_key(...) ->
  get_publishable_entity_by_ref(...), and renamed param key -> entity_ref
BREAKING CHANGE: In get_entity_collections(...), renamed param entity_key -> entity_ref
BREAKING CHANGE: In create_component(...)/create_container(...):
  * Positional key argument removed
  * Optional entity_ref argument added
  * Defaults to defaults to "{namespace}:{type_name}:{component_code}" if unspecified
BREAKING CHANGE: Function get_or_create_component_type_by_entity_key() removed.
BREAKING CHANGE: Rename get_container_children_entities_keys(...) ->
  get_container_children_entity_refs(...)
BREAKING CHANGE: Renamed get_container_by_key(...) -> get_container_by_ref(..)
  and renamed param key -> entity_ref.
BREAKING CHANGE: Renamed get_container_children_entities_keys(...) ->
  get_container_children_entity_refs(...)

BRAEKING CHANGE: Renamed LearningPackage.key -> LearningPackage.package_ref.
BREAKING CHANGE: In create_learning_package(...), renamed param key -> package_ref
BREAKING CHANGE: In update_learning_package(...), renamed param key -> package_ref
BREAKING CHANGE: Renamed get_learning_package_by_key(...) ->
  get_learning_package_by_ref(...), and renamed param key -> package_ref
BREAKING CHANGE: In learning_package_exists(...), renamed param key -> package_ref

BREAKING CHANGE: In look_up_component_version_media(...):
  * Renamed param learning_package_key -> learning_package_ref
  * Renamed param component_key -> entity_ref

BREAKING CHANGE: In add_assets_to_component management command:
  * Renamed argument learning_package_key -> learning_package_ref
  * Renamed argument component_key -> entity_ref

BREAKING CHANGE In load_learning_package:
  * Renamed  param key -> package_ref
  * In return dict, within sub-dict ["lp_restored_data"]:
    * Renamed ["key"] -> ["package_ref"]
    * Renamed ["archive_lp_key"] -> ["archive_package_ref"]
    * Renamed ["archive_org_key"] -> ["archive_org_code"]
    * Renamed ["archive_slug"] -> ["archive_package_code"]
  * If archive_package_ref cannot be parsed into "{_prefix}:{org_code}:{package_code}", then
    archvie_org_code and archive_package_code will both be None. Callers should handle this case.
    (Previously, a ValueError would be raised by backup-restore code.)

Backup/Restore format changes (non-breaking):
* In [learning_package], key field is renamed -> package_ref.
* In [entity], key field is renamed -> entity_ref.
* For compatibility with Ulmo instances, both key and {package,entity}_ref
  will be written in all new archives.
* For compatibility with Ulmo archives, both key and {package,entity}_ref
  will be read, with priority given to the latter.

Part of: #322

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kdmccormick kdmccormick force-pushed the kdmccormick/keys-ref branch from 28197b9 to 9d0ed28 Compare April 17, 2026 18:48
Comment on lines -532 to +520
"X-Open-edX-Component-Key": component.publishable_entity.key,
"X-Open-edX-Component-Key": component.publishable_entity.entity_ref,
"X-Open-edX-Component-Uuid": component.uuid,
# Component Version
"X-Open-edX-Component-Version-Uuid": component_version.uuid,
"X-Open-edX-Component-Version-Num": str(component_version.version_num),
# Learning Package
"X-Open-edX-Learning-Package-Key": learning_package.key,
"X-Open-edX-Learning-Package-Key": learning_package.package_ref,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@ormsbee I should probably fix these header names, right?

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.

@kdmccormick: Ah, right. Yeah, it'd be good to fix. It's not like anyone's relying on these values as they are now--they're just debug information at the moment.

Copy link
Copy Markdown
Contributor

@ormsbee ormsbee Apr 17, 2026

Choose a reason for hiding this comment

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

I mean, clearly not a blocker and doesn't need to happen for Verawood, but it makes sense to update.

learning_package_id: LearningPackage.ID,
key: str,
*,
container_code: str,
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.

Since key was before the *, won't it help ease compatibility and consistency if we move container_code to be where key was, and allow it to be a positional argument?

]
list_filter = ("component_type", "learning_package")
search_fields = ["publishable_entity__uuid", "publishable_entity__key"]
search_fields = ["publishable_entity__uuid", "publishable_entity__entity_ref"]
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.

Optional: Shouldn't this just be component_code for searching? Also, unrelated, but do we need to search by UUID?

Suggested change
search_fields = ["publishable_entity__uuid", "publishable_entity__entity_ref"]
search_fields = ["component_code"]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants