-
Notifications
You must be signed in to change notification settings - Fork 23
feat: Reinstate support for Unicode component_codes and container_codes #558
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| # Generated by Django 5.2.13 on 2026-04-23 14:15 | ||
|
|
||
| import re | ||
|
|
||
| import django.core.validators | ||
| import django.db.models.lookups | ||
| from django.conf import settings | ||
| from django.db import migrations, models | ||
|
|
||
| import openedx_django_lib.fields | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('openedx_content', '0012_rename_componentversionmedia_key_to_path'), | ||
| migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
| ] | ||
|
|
||
| operations = [ | ||
| # For the vast majority of sites, re-creating these constraints is redundant with | ||
| # 0009-0010. But, a handful of developers had applied 0009 and 0010 when they contained | ||
| # ascii-only constraints. Re-creating the constraints here ensure that everyone's on | ||
| # the same page with identical, unicode-friendly constraints. | ||
| migrations.RemoveConstraint( | ||
| model_name='component', | ||
| name='oel_component_code_regex', | ||
| ), | ||
| migrations.RemoveConstraint( | ||
| model_name='container', | ||
| name='oel_container_code_regex', | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='collection', | ||
| name='collection_code', | ||
| field=openedx_django_lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}, max_length=255, validators=[django.core.validators.RegexValidator(re.compile('^[a-zA-Z0-9_.-]+\\Z'), 'Enter a valid "code name" consisting of latin letters (A-Z, a-z), numbers, underscores, hyphens, or periods.', 'invalid')]), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='component', | ||
| name='component_code', | ||
| field=openedx_django_lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}, max_length=255, validators=[django.core.validators.RegexValidator(re.compile('^[\\w.-]+\\Z'), 'Enter a valid "code name" consisting of any letters, numbers, underscores, hyphens, or periods.', 'invalid')]), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='container', | ||
| name='container_code', | ||
| field=openedx_django_lib.fields.MultiCollationCharField(db_collations={'mysql': 'utf8mb4_bin', 'sqlite': 'BINARY'}, max_length=255, validators=[django.core.validators.RegexValidator(re.compile('^[\\w.-]+\\Z'), 'Enter a valid "code name" consisting of any letters, numbers, underscores, hyphens, or periods.', 'invalid')]), | ||
| ), | ||
| migrations.AddConstraint( | ||
| model_name='component', | ||
| constraint=models.CheckConstraint(condition=django.db.models.lookups.Regex(models.F('component_code'), '^[\\w.-]+\\Z'), name='oel_component_code_regex', violation_error_message='Enter a valid "code name" consisting of any letters, numbers, underscores, hyphens, or periods.'), | ||
| ), | ||
| migrations.AddConstraint( | ||
| model_name='container', | ||
| constraint=models.CheckConstraint(condition=django.db.models.lookups.Regex(models.F('container_code'), '^[\\w.-]+\\Z'), name='oel_container_code_regex', violation_error_message='Enter a valid "code name" consisting of any letters, numbers, underscores, hyphens, or periods.'), | ||
| ), | ||
| migrations.AlterConstraint( | ||
| model_name='collection', | ||
| name='oel_coll_collection_code_regex', | ||
| constraint=models.CheckConstraint(condition=django.db.models.lookups.Regex(models.F('collection_code'), '^[a-zA-Z0-9_.-]+\\Z'), name='oel_coll_collection_code_regex', violation_error_message='Enter a valid "code name" consisting of latin letters (A-Z, a-z), numbers, underscores, hyphens, or periods.'), | ||
| ), | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,4 +6,4 @@ | |
| """ | ||
|
|
||
| # The version for the entire repository | ||
| __version__ = "0.43.0" | ||
| __version__ = "0.44.0" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -119,15 +119,21 @@ def immutable_uuid_field() -> models.UUIDField: | |
|
|
||
|
|
||
| # Alphanumeric, hyphens, underscores, periods | ||
| CODE_REGEX = re.compile(r"^[a-zA-Z0-9_.-]+\Z") | ||
| CODE_REGEX_ASCII = re.compile(r"^[a-zA-Z0-9_.-]+\Z") | ||
|
|
||
| # Anything which passes isalnum(), plus underscores, hyphens, and periods | ||
| CODE_REGEX_UNICODE = re.compile(r"^[\w.-]+\Z", flags=re.UNICODE) | ||
|
|
||
| _CODE_VIOLATION_MSG = _( | ||
| 'Enter a valid "code name" consisting of letters, numbers, underscores, hyphens, or periods.' | ||
| _CODE_VIOLATION_MSG_ASCII = _( | ||
| 'Enter a valid "code name" consisting of latin letters (A-Z, a-z), numbers, underscores, hyphens, or periods.' | ||
| ) | ||
|
|
||
| _CODE_VIOLATION_MSG_UNICODE = _( | ||
| 'Enter a valid "code name" consisting of any letters, numbers, underscores, hyphens, or periods.' | ||
| ) | ||
|
|
||
|
|
||
| def code_field(**kwargs) -> MultiCollationCharField: | ||
| def code_field(unicode: bool, **kwargs) -> MultiCollationCharField: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that this is mandatory.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same. I'd rather force developers to think about which kind of code they're defining.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe we can make it default to true after doing openedx/openedx-platform#38413 |
||
| """ | ||
| Field to hold a 'code', i.e. a slug-like local identifier. | ||
|
|
||
|
|
@@ -139,19 +145,18 @@ def code_field(**kwargs) -> MultiCollationCharField: | |
| blank=False, | ||
| validators=[ | ||
| RegexValidator( | ||
| CODE_REGEX, | ||
| # Translators: "letters" means latin letters: a-z and A-Z. | ||
| _CODE_VIOLATION_MSG, | ||
| CODE_REGEX_UNICODE if unicode else CODE_REGEX_ASCII, | ||
| _CODE_VIOLATION_MSG_UNICODE if unicode else _CODE_VIOLATION_MSG_ASCII, | ||
| "invalid", | ||
| ), | ||
| ], | ||
| **kwargs, | ||
| ) | ||
|
|
||
|
|
||
| def code_field_check(field_name: str, *, name: str) -> models.CheckConstraint: | ||
| def code_field_check(field_name: str, *, name: str, unicode: bool) -> models.CheckConstraint: | ||
| """ | ||
| Return a ``CheckConstraint`` that enforces :data:`CODE_REGEX` at the DB level. | ||
| Return a ``CheckConstraint`` that enforces :data:`CODE_REGEX_UNICODE` or :data:`CODE_REGEX_ASCII` at the DB level. | ||
|
|
||
| Django validators (used by :func:`code_field`) are not called on ``.save()`` | ||
| or ``.update()``. Adding this constraint ensures the regex is also enforced | ||
|
|
@@ -162,13 +167,22 @@ def code_field_check(field_name: str, *, name: str) -> models.CheckConstraint: | |
|
|
||
| class Meta: | ||
| constraints = [ | ||
| code_field_check("my_code_field", name="myapp_mymodel_my_code_field_regex"), | ||
| code_field_check( | ||
| "my_code_field", | ||
| name="myapp_mymodel_my_code_field_regex", | ||
| unicode=True/False, # Make sure this matches the code_field! | ||
| ), | ||
| ] | ||
| """ | ||
| return models.CheckConstraint( | ||
| condition=Regex(models.F(field_name), CODE_REGEX.pattern), | ||
| condition=Regex( | ||
| models.F(field_name), | ||
| (CODE_REGEX_UNICODE if unicode else CODE_REGEX_ASCII).pattern, | ||
| ), | ||
| name=name, | ||
| violation_error_message=_CODE_VIOLATION_MSG, | ||
| violation_error_message=( | ||
| _CODE_VIOLATION_MSG_UNICODE if unicode else _CODE_VIOLATION_MSG_ASCII | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This updated constraint is already added in migration 0009, so why does it need to get re-created in this migration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to spell this out in the PR description. It's for those of us developers who already installed 0.43.0 into platform and applied 0009 and 0010, getting the ascii-only constraints.
I guess I could omit this, and instead just send out a slack blast to the handful of devs who might be in this situation, telling them to back-migrate to 0008. I think I'd also yank 0.43 from PyPI in that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bradenmacdonald let me know if you'd prefer this :
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, sorry I didn't notice the PR description. Since your platform PR was not merged I expect you can count the number of affected developers on one hand. But it is a nice idea to resolve it automatically; if you're keeping it, maybe mention that in a comment in the migration itself too? Either way is good with me!
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great, I added comments!