feat: bump opaque-keys to get case-sensitivity support + default max_length (#38044)
refactor: remove some 'max_length=255' to be more DRY feat: example of making an OpaqueKeyField case_sensitive (modulestore_migrator) test: update test now that we're using case-insensitive collation on SQLite
This commit is contained in:
committed by
Braden MacDonald
parent
ef783a1bca
commit
3e522d5272
@@ -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"),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -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.'),
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user