diff --git a/cms/djangoapps/contentstore/models.py b/cms/djangoapps/contentstore/models.py index 9baa3799ed..0685b93f02 100644 --- a/cms/djangoapps/contentstore/models.py +++ b/cms/djangoapps/contentstore/models.py @@ -97,9 +97,9 @@ class EntityLinkBase(models.Model): ) # A downstream entity can only link to single upstream entity # whereas an entity can be upstream for multiple downstream entities. - downstream_usage_key = UsageKeyField(max_length=255, unique=True) + downstream_usage_key = UsageKeyField(unique=True) # Search by course/downstream key - downstream_context_key = CourseKeyField(max_length=255, db_index=True) + downstream_context_key = CourseKeyField(db_index=True) # This is present if the creation of this link is a consequence of # importing a container that has one or more levels of children. # This represents the parent (container) in the top level @@ -152,7 +152,6 @@ class ComponentLink(EntityLinkBase): blank=True, ) upstream_usage_key = UsageKeyField( - max_length=255, help_text=_( "Upstream block usage key, this value cannot be null" " and useful to track upstream library blocks that do not exist yet" @@ -324,7 +323,6 @@ class ContainerLink(EntityLinkBase): blank=True, ) upstream_container_key = ContainerKeyField( - max_length=255, help_text=_( "Upstream block key (e.g. lct:...), this value cannot be null " "and is useful to track upstream library blocks that do not exist yet " @@ -564,7 +562,6 @@ class LearningContextLinksStatus(models.Model): course or a learning context. """ context_key = CourseKeyField( - max_length=255, # Single entry for a learning context or course unique=True, help_text=_("Linking status for course context key"), diff --git a/cms/djangoapps/modulestore_migrator/migrations/0008_key_case_sensitive.py b/cms/djangoapps/modulestore_migrator/migrations/0008_key_case_sensitive.py new file mode 100644 index 0000000000..49dbc90140 --- /dev/null +++ b/cms/djangoapps/modulestore_migrator/migrations/0008_key_case_sensitive.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.11 on 2026-02-23 23:41 + +import opaque_keys.edx.django.models +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("modulestore_migrator", "0001_squashed_0007_switch_to_openedx_content"), + ] + + operations = [ + migrations.AlterField( + model_name="modulestoreblocksource", + name="key", + field=opaque_keys.edx.django.models.UsageKeyField( + case_sensitive=True, + help_text="Original usage key of the XBlock that has been imported.", + max_length=255, + unique=True, + ), + ), + migrations.AlterField( + model_name="modulestoresource", + name="key", + field=opaque_keys.edx.django.models.LearningContextKeyField( + case_sensitive=True, + help_text="Key of the content source (a course or a legacy library)", + max_length=255, + unique=True, + ), + ), + ] diff --git a/cms/djangoapps/modulestore_migrator/models.py b/cms/djangoapps/modulestore_migrator/models.py index 17da8e1870..6571a28137 100644 --- a/cms/djangoapps/modulestore_migrator/models.py +++ b/cms/djangoapps/modulestore_migrator/models.py @@ -47,8 +47,8 @@ class ModulestoreSource(models.Model): control whether `forwarded` is set to any given migration. """ key = LearningContextKeyField( - max_length=255, unique=True, + case_sensitive=True, help_text=_('Key of the content source (a course or a legacy library)'), ) forwarded = models.OneToOneField( @@ -189,7 +189,7 @@ class ModulestoreBlockSource(TimeStampedModel): related_name="blocks", ) key = UsageKeyField( - max_length=255, + case_sensitive=True, unique=True, help_text=_('Original usage key of the XBlock that has been imported.'), ) diff --git a/common/djangoapps/course_modes/models.py b/common/djangoapps/course_modes/models.py index 2e711a1081..a739a9076f 100644 --- a/common/djangoapps/course_modes/models.py +++ b/common/djangoapps/course_modes/models.py @@ -943,7 +943,7 @@ class CourseModesArchive(models.Model): app_label = "course_modes" # the course that this mode is attached to - course_id = CourseKeyField(max_length=255, db_index=True) + course_id = CourseKeyField(db_index=True) # the reference to this mode that can be used by Enrollments to generate # similar behavior for the same slug across courses diff --git a/common/djangoapps/status/models.py b/common/djangoapps/status/models.py index 2d37faedb8..6685643432 100644 --- a/common/djangoapps/status/models.py +++ b/common/djangoapps/status/models.py @@ -64,7 +64,7 @@ class CourseMessage(models.Model): .. no_pii: """ global_message = models.ForeignKey(GlobalStatusMessage, on_delete=models.CASCADE) - course_key = CourseKeyField(max_length=255, blank=True, db_index=True) + course_key = CourseKeyField(blank=True, db_index=True) message = models.TextField(blank=True, null=True) def __str__(self): diff --git a/common/djangoapps/student/models/user.py b/common/djangoapps/student/models/user.py index 8d10db4b0f..a9055d5247 100644 --- a/common/djangoapps/student/models/user.py +++ b/common/djangoapps/student/models/user.py @@ -93,7 +93,7 @@ class AnonymousUserId(models.Model): user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE) anonymous_user_id = models.CharField(unique=True, max_length=32) - course_id = LearningContextKeyField(db_index=True, max_length=255, blank=True) + course_id = LearningContextKeyField(db_index=True, blank=True) def anonymous_id_for_user(user, course_id): @@ -1058,7 +1058,7 @@ class CourseAccessRole(models.Model): # blank org is for global group based roles such as course creator (may be deprecated) org = models.CharField(max_length=64, db_index=True, blank=True) # blank course_id implies org wide role - course_id = CourseKeyField(max_length=255, db_index=True, blank=True) + course_id = CourseKeyField(db_index=True, blank=True) role = models.CharField(max_length=64, db_index=True) class Meta: @@ -1116,7 +1116,7 @@ class CourseAccessRoleHistory(TimeStampedModel): user = models.ForeignKey(User, on_delete=models.CASCADE) org = models.CharField(max_length=64, db_index=True, blank=True) - course_id = CourseKeyField(max_length=255, db_index=True, blank=True) + course_id = CourseKeyField(db_index=True, blank=True) role = models.CharField(max_length=64, db_index=True) action_type = models.CharField(max_length=10, choices=ACTION_CHOICES, db_index=True) changed_by = models.ForeignKey( @@ -1493,7 +1493,7 @@ class EntranceExamConfiguration(models.Model): """ user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE) - course_id = CourseKeyField(max_length=255, db_index=True) + course_id = CourseKeyField(db_index=True) created = models.DateTimeField(auto_now_add=True, null=True, db_index=True) updated = models.DateTimeField(auto_now=True, db_index=True) diff --git a/lms/djangoapps/bulk_email/models.py b/lms/djangoapps/bulk_email/models.py index b1a7aa5744..4ee71be039 100644 --- a/lms/djangoapps/bulk_email/models.py +++ b/lms/djangoapps/bulk_email/models.py @@ -255,7 +255,7 @@ class CourseEmail(Email): class Meta: app_label = "bulk_email" - course_id = CourseKeyField(max_length=255, db_index=True) + course_id = CourseKeyField(db_index=True) # to_option is deprecated and unused, but dropping db columns is hard so it's still here for legacy reasons to_option = models.CharField(max_length=64, choices=[("deprecated", "deprecated")]) targets = models.ManyToManyField(Target) @@ -314,7 +314,7 @@ class Optout(models.Model): # We need to first create the 'user' column with some sort of default in order to run the data migration, # and given the unique index, 'null' is the best default value. user = models.ForeignKey(User, db_index=True, null=True, on_delete=models.CASCADE) - course_id = CourseKeyField(max_length=255, db_index=True) + course_id = CourseKeyField(db_index=True) class Meta: app_label = "bulk_email" @@ -430,7 +430,7 @@ class CourseAuthorization(models.Model): app_label = "bulk_email" # The course that these features are attached to. - course_id = CourseKeyField(max_length=255, db_index=True, unique=True) + course_id = CourseKeyField(db_index=True, unique=True) # Whether or not to enable instructor email email_enabled = models.BooleanField(default=False) @@ -462,7 +462,7 @@ class DisabledCourse(models.Model): class Meta: app_label = "bulk_email" - course_id = CourseKeyField(max_length=255, db_index=True, unique=True) + course_id = CourseKeyField(db_index=True, unique=True) @classmethod def instructor_email_disabled_for_course(cls, course_id): diff --git a/lms/djangoapps/ccx/models.py b/lms/djangoapps/ccx/models.py index 21f0136f07..7341922cec 100644 --- a/lms/djangoapps/ccx/models.py +++ b/lms/djangoapps/ccx/models.py @@ -26,7 +26,7 @@ class CustomCourseForEdX(models.Model): .. no_pii: """ - course_id = CourseKeyField(max_length=255, db_index=True) + course_id = CourseKeyField(db_index=True) display_name = models.CharField(max_length=255) coach = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE) # if not empty, this field contains a json serialized list of @@ -112,7 +112,7 @@ class CcxFieldOverride(models.Model): .. no_pii: """ ccx = models.ForeignKey(CustomCourseForEdX, db_index=True, on_delete=models.CASCADE) - location = UsageKeyField(max_length=255, db_index=True) + location = UsageKeyField(db_index=True) field = models.CharField(max_length=255) class Meta: diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py index ac674167a2..985e4ea6ea 100644 --- a/lms/djangoapps/certificates/models.py +++ b/lms/djangoapps/certificates/models.py @@ -60,7 +60,7 @@ class CertificateAllowlist(TimeStampedModel): objects = NoneToEmptyManager() user = models.ForeignKey(User, on_delete=models.CASCADE) - course_id = CourseKeyField(max_length=255, blank=True, default=None) + course_id = CourseKeyField(blank=True, default=None) allowlist = models.BooleanField(default=0) notes = models.TextField(default=None, null=True) @@ -219,7 +219,7 @@ class GeneratedCertificate(models.Model): ] user = models.ForeignKey(User, on_delete=models.CASCADE) - course_id = CourseKeyField(max_length=255, blank=True, default=None) + course_id = CourseKeyField(blank=True, default=None) verify_uuid = models.CharField(max_length=32, blank=True, default='', db_index=True) grade = models.CharField(max_length=5, blank=True, default='') key = models.CharField(max_length=32, blank=True, default='') @@ -554,7 +554,7 @@ class CertificateGenerationHistory(TimeStampedModel): .. no_pii: """ - course_id = CourseKeyField(max_length=255) + course_id = CourseKeyField() generated_by = models.ForeignKey(User, on_delete=models.CASCADE) instructor_task = models.ForeignKey(InstructorTask, on_delete=models.CASCADE) is_regeneration = models.BooleanField(default=False) @@ -714,7 +714,7 @@ class ExampleCertificateSet(TimeStampedModel): .. no_pii: """ - course_key = CourseKeyField(max_length=255, db_index=True) + course_key = CourseKeyField(db_index=True) class Meta: get_latest_by = 'created' @@ -975,7 +975,7 @@ class CertificateGenerationCourseSetting(TimeStampedModel): .. no_pii: """ - course_key = CourseKeyField(max_length=255, db_index=True) + course_key = CourseKeyField(db_index=True) self_generation_enabled = models.BooleanField( default=False, diff --git a/lms/djangoapps/course_api/tests/test_views.py b/lms/djangoapps/course_api/tests/test_views.py index d0f50c5818..2839b21c4c 100644 --- a/lms/djangoapps/course_api/tests/test_views.py +++ b/lms/djangoapps/course_api/tests/test_views.py @@ -446,8 +446,8 @@ class CourseListSearchViewTest(CourseApiTestViewMixin, ModuleStoreTestCase, Sear self.setup_user(self.audit_user) # These query counts were found empirically - query_counts = [52, 46, 46, 46, 46, 46, 46, 46, 46, 46, 16] - ordered_course_ids = sorted([str(cid) for cid in (course_ids + [c.id for c in self.courses])]) + query_counts = [57, 46, 46, 46, 46, 46, 46, 46, 46, 43, 12] + ordered_course_ids = sorted([str(cid) for cid in (course_ids + [c.id for c in self.courses])], key=str.lower) self.clear_caches() diff --git a/lms/djangoapps/lti_provider/models.py b/lms/djangoapps/lti_provider/models.py index f763321748..621d2bb8d1 100644 --- a/lms/djangoapps/lti_provider/models.py +++ b/lms/djangoapps/lti_provider/models.py @@ -129,8 +129,8 @@ class GradedAssignment(models.Model): .. no_pii: """ user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE) - course_key = CourseKeyField(max_length=255, db_index=True) - usage_key = UsageKeyField(max_length=255, db_index=True) + course_key = CourseKeyField(db_index=True) + usage_key = UsageKeyField(db_index=True) outcome_service = models.ForeignKey(OutcomeService, on_delete=models.CASCADE) lis_result_sourcedid = models.CharField(max_length=255, db_index=True) version_number = models.IntegerField(default=0) diff --git a/lms/djangoapps/program_enrollments/models.py b/lms/djangoapps/program_enrollments/models.py index 2abec16d93..b837c26e59 100644 --- a/lms/djangoapps/program_enrollments/models.py +++ b/lms/djangoapps/program_enrollments/models.py @@ -128,7 +128,7 @@ class ProgramCourseEnrollment(TimeStampedModel): blank=True, on_delete=models.CASCADE ) - course_key = CourseKeyField(max_length=255) + course_key = CourseKeyField() status = models.CharField(max_length=9, choices=STATUS_CHOICES) historical_records = HistoricalRecords() diff --git a/lms/djangoapps/support/models.py b/lms/djangoapps/support/models.py index d8f6e2b9a1..99dca53937 100644 --- a/lms/djangoapps/support/models.py +++ b/lms/djangoapps/support/models.py @@ -24,7 +24,7 @@ class CourseResetCourseOptIn(TimeStampedModel): .. no_pii: """ - course_id = CourseKeyField(max_length=255, unique=True) + course_id = CourseKeyField(unique=True) active = BooleanField() def __str__(self): diff --git a/lms/djangoapps/teams/models.py b/lms/djangoapps/teams/models.py index d58fe9801d..ff53871f22 100644 --- a/lms/djangoapps/teams/models.py +++ b/lms/djangoapps/teams/models.py @@ -130,7 +130,7 @@ class CourseTeam(models.Model): team_id = models.SlugField(max_length=255, unique=True) discussion_topic_id = models.SlugField(max_length=255, unique=True) name = models.CharField(max_length=255, db_index=True) - course_id = CourseKeyField(max_length=255, db_index=True) + course_id = CourseKeyField(db_index=True) topic_id = models.CharField(default='', max_length=255, db_index=True, blank=True) date_created = models.DateTimeField(auto_now_add=True) description = models.CharField(max_length=300) diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py index cee18ca8d3..2c9edf9b6d 100644 --- a/lms/djangoapps/verify_student/models.py +++ b/lms/djangoapps/verify_student/models.py @@ -1091,7 +1091,6 @@ class VerificationDeadline(TimeStampedModel): app_label = "verify_student" course_key = CourseKeyField( - max_length=255, db_index=True, unique=True, help_text=gettext_lazy("The course for which this deadline applies"), diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 99b34b47b2..05c9a594b7 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -485,7 +485,7 @@ edx-i18n-tools==1.9.0 # xblocks-contrib edx-milestones==1.1.0 # via -r requirements/edx/kernel.in -edx-opaque-keys[django]==3.0.0 +edx-opaque-keys[django]==3.1.0 # via # -r requirements/edx/kernel.in # edx-bulk-grades diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index e176170e9c..8e3f7bb879 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -777,7 +777,7 @@ edx-milestones==1.1.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -edx-opaque-keys[django]==3.0.0 +edx-opaque-keys[django]==3.1.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 48e5eac571..e46f772e58 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -574,7 +574,7 @@ edx-i18n-tools==1.9.0 # xblocks-contrib edx-milestones==1.1.0 # via -r requirements/edx/base.txt -edx-opaque-keys[django]==3.0.0 +edx-opaque-keys[django]==3.1.0 # via # -r requirements/edx/base.txt # edx-bulk-grades diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index ee113180e3..6cb004cc17 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -598,7 +598,7 @@ edx-lint==5.6.0 # via -r requirements/edx/testing.in edx-milestones==1.1.0 # via -r requirements/edx/base.txt -edx-opaque-keys[django]==3.0.0 +edx-opaque-keys[django]==3.1.0 # via # -r requirements/edx/base.txt # edx-bulk-grades