Skip to content

Commit 95f50d7

Browse files
authored
Add /downloads/latest/prerelease redirect (#2823)
1 parent 578c624 commit 95f50d7

File tree

7 files changed

+63
-4
lines changed

7 files changed

+63
-4
lines changed

downloads/managers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def latest_python3(self, minor_version: int | None = None):
3535
pattern = rf"^Python 3\.{minor_version}\."
3636
return self.python3().filter(name__regex=pattern).order_by("-release_date")
3737

38+
def latest_prerelease(self):
39+
return self.python3().filter(pre_release=True).order_by("-release_date")
40+
3841
def latest_pymanager(self):
3942
return self.pymanager().filter(is_latest=True)
4043

@@ -52,5 +55,8 @@ def latest_python2(self):
5255
def latest_python3(self, minor_version: int | None = None):
5356
return self.get_queryset().latest_python3(minor_version).first()
5457

58+
def latest_prerelease(self):
59+
return self.get_queryset().latest_prerelease().first()
60+
5561
def latest_pymanager(self):
5662
return self.get_queryset().latest_pymanager().first()

downloads/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ def purge_fastly_download_pages(sender, instance, **kwargs):
298298
match = re.match(r'^3\.(\d+)', version)
299299
if match:
300300
purge_url(f'/downloads/latest/python3.{match.group(1)}/')
301+
purge_url('/downloads/latest/prerelease/')
301302
purge_url('/downloads/latest/pymanager/')
302303
purge_url('/downloads/macos/')
303304
purge_url('/downloads/source/')

downloads/tests/test_models.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,28 @@ def test_latest_python3(self):
7474
latest_3_99 = Release.objects.latest_python3(minor_version=99)
7575
self.assertIsNone(latest_3_99)
7676

77+
def test_latest_prerelease(self):
78+
latest_prerelease = Release.objects.latest_prerelease()
79+
self.assertEqual(latest_prerelease, self.pre_release)
80+
81+
# Create a newer prerelease with a future date
82+
newer_prerelease = Release.objects.create(
83+
version=Release.PYTHON3,
84+
name="Python 3.9.99",
85+
is_published=True,
86+
pre_release=True,
87+
release_date=self.pre_release.release_date + dt.timedelta(days=1),
88+
)
89+
latest_prerelease = Release.objects.latest_prerelease()
90+
self.assertEqual(latest_prerelease, newer_prerelease)
91+
self.assertNotEqual(latest_prerelease, self.pre_release)
92+
93+
def test_latest_prerelease_when_no_prerelease(self):
94+
# Delete the prerelease
95+
self.pre_release.delete()
96+
latest_prerelease = Release.objects.latest_prerelease()
97+
self.assertIsNone(latest_prerelease)
98+
7799
def test_get_version(self):
78100
self.assertEqual(self.release_275.name, 'Python 2.7.5')
79101
self.assertEqual(self.release_275.get_version(), '2.7.5')

downloads/tests/test_views.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,18 @@ def test_latest_python3x_redirects(self):
9595
response = self.client.get(url)
9696
self.assertRedirects(response, reverse("download:download"))
9797

98+
def test_latest_prerelease_redirect(self):
99+
url = reverse("download:download_latest_prerelease")
100+
response = self.client.get(url)
101+
self.assertRedirects(response, self.pre_release.get_absolute_url())
102+
103+
def test_latest_prerelease_redirect_when_no_prerelease(self):
104+
# Delete the prerelease to test fallback
105+
self.pre_release.delete()
106+
url = reverse("download:download_latest_prerelease")
107+
response = self.client.get(url)
108+
self.assertRedirects(response, reverse("download:download"))
109+
98110
def test_redirect_page_object_to_release_detail_page(self):
99111
self.release_275.release_page = None
100112
self.release_275.save()

downloads/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
re_path(r'latest/python2/?$', views.DownloadLatestPython2.as_view(), name='download_latest_python2'),
77
re_path(r'latest/python3/?$', views.DownloadLatestPython3.as_view(), name='download_latest_python3'),
88
re_path(r'latest/python3\.(?P<minor>\d+)/?$', views.DownloadLatestPython3.as_view(), name='download_latest_python3x'),
9+
re_path(r'latest/prerelease/?$', views.DownloadLatestPrerelease.as_view(), name='download_latest_prerelease'),
910
re_path(r'latest/pymanager/?$', views.DownloadLatestPyManager.as_view(), name='download_latest_pymanager'),
1011
re_path(r'latest/?$', views.DownloadLatestPython3.as_view(), name='download_latest_python3'),
1112
path('operating-systems/', views.DownloadFullOSList.as_view(), name='download_full_os_list'),

downloads/views.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,23 @@ def get_redirect_url(self, **kwargs):
4747
return reverse("downloads:download")
4848

4949

50+
class DownloadLatestPrerelease(RedirectView):
51+
"""Redirect to latest Python 3 prerelease"""
52+
53+
permanent = False
54+
55+
def get_redirect_url(self, **kwargs):
56+
try:
57+
latest_prerelease = Release.objects.latest_prerelease()
58+
except Release.DoesNotExist:
59+
latest_prerelease = None
60+
61+
if latest_prerelease:
62+
return latest_prerelease.get_absolute_url()
63+
else:
64+
return reverse("downloads:download")
65+
66+
5067
class DownloadLatestPyManager(RedirectView):
5168
""" Redirect to latest Python install manager release """
5269
permanent = False

0 commit comments

Comments
 (0)