From 86994c01ce18fd56ad1af8609464f6e3a7e50c0a Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Tue, 6 May 2025 10:18:10 -0500 Subject: [PATCH 1/5] feat: sanitze release names upon submission --- downloads/models.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/downloads/models.py b/downloads/models.py index 415804b6e..ea1c5156a 100644 --- a/downloads/models.py +++ b/downloads/models.py @@ -146,6 +146,20 @@ def is_version_at_least_3_5(self): def is_version_at_least_3_9(self): return self.is_version_at_least((3, 9)) + def clean(self): + super().clean() + if not re.match(r'^Python\s[\d.]+%', self.name): + raise ValidationError({ + "name": "Release name must be in the format 'Python X.Y.Z' (e.g., 'Python 3.14.0')" + }) + + # def clean(self): + # super().clean() + # if not re.match(r'^Python\s[\d.]+$', self.name): + # raise ValidationError({ + # 'name': 'Name must be in the format "Python X.Y.Z" (e.g., "Python 3.9.0")' + # }) + def update_supernav(): latest_python3 = Release.objects.latest_python3() From 565b4cc87c7713377ed3fa2e55d5f2e83d7f16b0 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Tue, 6 May 2025 10:18:39 -0500 Subject: [PATCH 2/5] fix: adjust regex Closes #2724 --- downloads/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/downloads/models.py b/downloads/models.py index ea1c5156a..d24d65fe2 100644 --- a/downloads/models.py +++ b/downloads/models.py @@ -148,7 +148,7 @@ def is_version_at_least_3_9(self): def clean(self): super().clean() - if not re.match(r'^Python\s[\d.]+%', self.name): + if not re.match(r'^Python\s[\d.]+$', self.name): raise ValidationError({ "name": "Release name must be in the format 'Python X.Y.Z' (e.g., 'Python 3.14.0')" }) From 40c448b9b366c00ffd57b7af4b0c9083790c74ea Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Tue, 6 May 2025 10:20:39 -0500 Subject: [PATCH 3/5] chore: clean copypasta from issue submission --- downloads/models.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/downloads/models.py b/downloads/models.py index d24d65fe2..e7af57706 100644 --- a/downloads/models.py +++ b/downloads/models.py @@ -153,13 +153,6 @@ def clean(self): "name": "Release name must be in the format 'Python X.Y.Z' (e.g., 'Python 3.14.0')" }) - # def clean(self): - # super().clean() - # if not re.match(r'^Python\s[\d.]+$', self.name): - # raise ValidationError({ - # 'name': 'Name must be in the format "Python X.Y.Z" (e.g., "Python 3.9.0")' - # }) - def update_supernav(): latest_python3 = Release.objects.latest_python3() From 2325b526a4fb157d7de208d512cd5a2d1fee3c6e Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Tue, 6 May 2025 10:31:36 -0500 Subject: [PATCH 4/5] feat: use validators instead of clean, override NameSlugModel name field --- downloads/models.py | 10 ++-------- downloads/validators.py | 9 +++++++++ 2 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 downloads/validators.py diff --git a/downloads/models.py b/downloads/models.py index e7af57706..984613fd4 100644 --- a/downloads/models.py +++ b/downloads/models.py @@ -17,7 +17,7 @@ from pages.models import Page from .managers import ReleaseManager - +from .validators import is_valid_python_release DEFAULT_MARKUP_TYPE = getattr(settings, 'DEFAULT_MARKUP_TYPE', 'restructuredtext') @@ -50,6 +50,7 @@ class Release(ContentManageable, NameSlugModel): (PYTHON2, 'Python 2.x.x'), (PYTHON1, 'Python 1.x.x'), ) + name = models.CharField(max_length=200, validators=[is_valid_python_release]) version = models.IntegerField(default=PYTHON3, choices=PYTHON_VERSION_CHOICES) is_latest = models.BooleanField( verbose_name='Is this the latest release?', @@ -146,13 +147,6 @@ def is_version_at_least_3_5(self): def is_version_at_least_3_9(self): return self.is_version_at_least((3, 9)) - def clean(self): - super().clean() - if not re.match(r'^Python\s[\d.]+$', self.name): - raise ValidationError({ - "name": "Release name must be in the format 'Python X.Y.Z' (e.g., 'Python 3.14.0')" - }) - def update_supernav(): latest_python3 = Release.objects.latest_python3() diff --git a/downloads/validators.py b/downloads/validators.py new file mode 100644 index 000000000..bfd8097b1 --- /dev/null +++ b/downloads/validators.py @@ -0,0 +1,9 @@ +"""Model valdators for the DOwnloads app.""" + +from django.core.exceptions import ValidationError +from django.core import validators + +is_valid_python_release = validators.RegexValidator( + regex=r'^Python\s[\d.]+$', + message="Release name must be in the format 'Python X.Y.Z' (e.g., 'Python 3.14.0')" +) From 07c9b36532b8ca05e1ca69b08ecbf42ac0ff5b93 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Tue, 6 May 2025 10:33:09 -0500 Subject: [PATCH 5/5] Update downloads/validators.py --- downloads/validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/downloads/validators.py b/downloads/validators.py index bfd8097b1..a40fc6c76 100644 --- a/downloads/validators.py +++ b/downloads/validators.py @@ -1,4 +1,4 @@ -"""Model valdators for the DOwnloads app.""" +"""Model validators for the Downloads app.""" from django.core.exceptions import ValidationError from django.core import validators