Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 5.2.10 on 2026-01-25 21:52

import django.db.models.deletion
from django.db import migrations, models
from django.db.migrations.operations.special import SeparateDatabaseAndState


class Migration(migrations.Migration):

dependencies = [
('contentstore', '0014_remove_componentlink_downstream_is_modified_and_more'),
('openedx_content', '0001_initial'),
]

operations = [
SeparateDatabaseAndState(
database_operations=[],
state_operations=[
migrations.AlterField(
model_name='componentlink',
name='upstream_block',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='links', to='openedx_content.component'),
),
migrations.AlterField(
model_name='containerlink',
name='upstream_container',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='links', to='openedx_content.container'),
),
]
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 5.2.10 on 2026-01-25 21:52

import django.db.models.deletion
from django.db import migrations, models
from django.db.migrations.operations.special import SeparateDatabaseAndState


class Migration(migrations.Migration):

dependencies = [
('modulestore_migrator', '0006_alter_modulestoreblocksource_forwarded_and_more'),
('openedx_content', '0001_initial'),
]

operations = [
SeparateDatabaseAndState(
database_operations=[],
state_operations=[
migrations.AlterField(
model_name='modulestoreblockmigration',
name='change_log_record',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='openedx_content.draftchangelogrecord'),
),
migrations.AlterField(
model_name='modulestoreblockmigration',
name='target',
field=models.ForeignKey(blank=True, help_text='The target entity of this block migration, set to null if it fails to migrate', null=True, on_delete=django.db.models.deletion.CASCADE, to='openedx_content.publishableentity'),
),
migrations.AlterField(
model_name='modulestoremigration',
name='change_log',
field=models.ForeignKey(help_text='Changelog entry in the target learning package which records this migration', null=True, on_delete=django.db.models.deletion.SET_NULL, to='openedx_content.draftchangelog'),
),
migrations.AlterField(
model_name='modulestoremigration',
name='target',
field=models.ForeignKey(help_text='Content will be imported into this library', on_delete=django.db.models.deletion.CASCADE, to='openedx_content.learningpackage'),
),
migrations.AlterField(
model_name='modulestoremigration',
name='target_collection',
field=models.ForeignKey(blank=True, help_text='Optional - Collection (within the target library) into which imported content will be grouped', null=True, on_delete=django.db.models.deletion.SET_NULL, to='openedx_content.collection'),
),
]
),
]
10 changes: 2 additions & 8 deletions cms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from datetime import timedelta

from django.utils.translation import gettext_lazy as _
from openedx_learning.api.django import openedx_learning_apps_to_install

from openedx.envs.common import * # pylint: disable=wildcard-import

Expand Down Expand Up @@ -897,14 +898,7 @@ def make_lms_template_path(settings):

'openedx_events',

# Learning Core Apps, used by v2 content libraries (content_libraries app)
"openedx_learning.apps.authoring.collections",
"openedx_learning.apps.authoring.components",
"openedx_learning.apps.authoring.contents",
"openedx_learning.apps.authoring.publishing",
"openedx_learning.apps.authoring.units",
"openedx_learning.apps.authoring.subsections",
"openedx_learning.apps.authoring.sections",
*openedx_learning_apps_to_install(),
]

### Apps only installed in some instances
Expand Down
18 changes: 10 additions & 8 deletions lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
PROVISIONING_PENDING_ENTERPRISE_CUSTOMER_ADMIN_ROLE,
DEFAULT_ENTERPRISE_ENROLLMENT_INTENTIONS_ROLE,
)
from openedx_learning.api.django import openedx_learning_apps_to_install

from openedx.core.lib.derived import Derived
from openedx.envs.common import * # pylint: disable=wildcard-import
Expand Down Expand Up @@ -2019,14 +2020,15 @@

'openedx_events',

# Learning Core Apps, used by v2 content libraries (content_libraries app)
"openedx_learning.apps.authoring.collections",
"openedx_learning.apps.authoring.components",
"openedx_learning.apps.authoring.contents",
"openedx_learning.apps.authoring.publishing",
"openedx_learning.apps.authoring.units",
"openedx_learning.apps.authoring.subsections",
"openedx_learning.apps.authoring.sections",
# The openedx_learning apps require contentstore, modulestore_migrator,
# content.search, and content_staging to be in INSTALLED_APPS. If they are
# not here and LMS migrations are run before CMS migrations, it will cause
# errors (certain openedx_learning apps )
*openedx_learning_apps_to_install(),
'cms.djangoapps.contentstore',
'cms.djangoapps.modulestore_migrator',
'openedx.core.djangoapps.content.search',
'openedx.core.djangoapps.content_staging',
Comment on lines +2023 to +2031
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you expand on this? First, adding these CMS apps to LMS seems like a big change and I'd prefer to avoid it for now. Second, how do the "openedx_learning apps require contentstore" ? It doesn't make sense to me that Learning Core has any dependencies on Platform, and I thought we very explicitly don't want that ever.

Copy link
Contributor Author

@ormsbee ormsbee Jan 28, 2026

Choose a reason for hiding this comment

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

Yeah, it's messy. I go over this in more detail in the revised ADR (relevant part pasted below):


The ordering of these migrations is important, and existing edx-platform migrations should remain unchanged. This is important to make sure that we do not introduce ordering inconsistencies for existing installations that are upgrading.

Therefore, the migrations will happen in the following order:

  1. All backcompat.* apps migrations except for the final ones that delete model state. This takes us up to where migrations would already be before we make any changes.
  2. The openedx_content app's 0001_intial migration that adds model state without changing the database. At this point, model state exists for the same models in all the old backcompat.* apps as well as the new openedx_content app.
  3. edx-platform apps that had foreign keys to old backcompat.* apps models will need to be switched to point to the new openedx_content app models. This will likewise be done without a database change, because they're still pointing to the same tables and columns.
  4. Now that edx-platform references have been updated, we can delete the model state from the old backcompat.* apps and rename the underlying tables (in either order).

The tricky part is to make sure that the old backcompat.* apps models still exist when the edx-platform migrations to move over the references runs. This is problematic because the edx-platform migrations can only specify that they run after the new openedx_content models are created. They cannot specify that they run before the old backcompat models are dropped.

So in order to enforce this ordering, we do the following:

  • The``openedx_contentmigration0001_initial` requires that all `backcompat.*` migrations except the last ones removing model state are run.
  • The openedx_content migration 0002_rename_tables_to_openedx_content migration requires that the edx-platform migrations changing references over run. This is important anyway, because we want to make sure those reference changes happen before we change any table names.
  • The final backcompat.* migrations that remove model field state will list openedx_content app's 0002_rename_tables_to_openedx_content as a dependency.

A further complication is that openedx_learning will often run its migrations without edx-platform present (e.g. for CI or standalone dev purposes), so we can't force 0002_rename_tables_to_openedx_content in the openedx_content app to have references to edx-platform migrations. To get around this, we dynamically inject those migration dependencies only if we detect those edx-platform apps exist in the currently loaded Django project. This injection happens in the apps.py initialization for the openedx_content app.

The final complication is that we want these migration dependencies to be the same regardless of whether you're running edx-platform migrations with the LMS or CMS (Studio) settings, or we run the risk of getting into an inconsistent state and dropping the old models before all the edx-platform apps can run their migrations to move their references. To do this, we have to make sure that the edx-platform apps that reference Learning Core models are present in the INSTALLED_APPS for both configurations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Honestly, this has convinced me even more that we need to unify everything into one INSTALLED_APPS configuration across CMS and LMS, though I'm not about to attempt that bit of yak shaving just yet.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, right. Thanks. I'm still trying to understand this a bit more so I'll ask questions on the ADR PR.

]

# Add LMS specific optional apps
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 5.2.9 on 2026-01-25 19:44

import django.db.models.deletion
from django.db import migrations, models
from django.db.migrations.operations.special import SeparateDatabaseAndState


class Migration(migrations.Migration):

dependencies = [
('content_libraries', '0011_remove_contentlibrary_bundle_uuid_and_more'),
('openedx_content', '0001_initial'),
]

operations = [
SeparateDatabaseAndState(
database_operations=[],
state_operations=[
migrations.AlterField(
model_name='contentlibrary',
name='learning_package',
field=models.OneToOneField(default=None, null=True, on_delete=django.db.models.deletion.RESTRICT, to='openedx_content.learningpackage'),
),
]
),
]
2 changes: 1 addition & 1 deletion requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ numpy<2.0.0
# Date: 2023-09-18
# pinning this version to avoid updates while the library is being developed
# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35269
openedx-learning==0.30.2
# openedx-learning==0.30.1

# Date: 2023-11-29
# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.4.0
# via -r requirements/edx/kernel.in
openedx-learning==0.30.2
git+https://github.com/ormsbee/openedx-learning.git@big-authoring#egg=openedx_learning
# via
# -c requirements/constraints.txt
# -r requirements/edx/kernel.in
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,7 @@ openedx-forum==0.4.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
openedx-learning==0.30.2
git+https://github.com/ormsbee/openedx-learning.git@big-authoring#egg=openedx_learning
# via
# -c requirements/constraints.txt
# -r requirements/edx/doc.txt
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.4.0
# via -r requirements/edx/base.txt
openedx-learning==0.30.2
git+https://github.com/ormsbee/openedx-learning.git@big-authoring#egg=openedx_learning
# via
# -c requirements/constraints.txt
# -r requirements/edx/base.txt
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.4.0
# via -r requirements/edx/base.txt
openedx-learning==0.30.2
git+https://github.com/ormsbee/openedx-learning.git@big-authoring#egg=openedx_learning
# via
# -c requirements/constraints.txt
# -r requirements/edx/base.txt
Expand Down
Loading