diff --git a/downloads/managers.py b/downloads/managers.py index b529dcdd4..56040d2bb 100644 --- a/downloads/managers.py +++ b/downloads/managers.py @@ -23,12 +23,18 @@ def python2(self): def python3(self): return self.filter(version=3, is_published=True) + def pymanager(self): + return self.filter(version=100, is_published=True) + def latest_python2(self): return self.python2().filter(is_latest=True) def latest_python3(self): return self.python3().filter(is_latest=True) + def latest_pymanager(self): + return self.pymanager().filter(is_latest=True) + def pre_release(self): return self.filter(pre_release=True) @@ -50,3 +56,10 @@ def latest_python3(self): return qs[0] else: return None + + def latest_pymanager(self): + qs = self.get_queryset().latest_pymanager() + if qs: + return qs[0] + else: + return None diff --git a/downloads/migrations/0012_alter_release_version.py b/downloads/migrations/0012_alter_release_version.py new file mode 100644 index 000000000..e6aea4d1f --- /dev/null +++ b/downloads/migrations/0012_alter_release_version.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.20 on 2025-04-24 19:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('downloads', '0011_alter_os_creator_alter_os_last_modified_by_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='release', + name='version', + field=models.IntegerField(choices=[(3, 'Python 3.x.x'), (2, 'Python 2.x.x'), (1, 'Python 1.x.x'), (100, 'Python install manager')], default=3), + ), + ] diff --git a/downloads/models.py b/downloads/models.py index 415804b6e..3c0a74f97 100644 --- a/downloads/models.py +++ b/downloads/models.py @@ -45,10 +45,12 @@ class Release(ContentManageable, NameSlugModel): PYTHON1 = 1 PYTHON2 = 2 PYTHON3 = 3 + PYMANAGER = 100 PYTHON_VERSION_CHOICES = ( (PYTHON3, 'Python 3.x.x'), (PYTHON2, 'Python 2.x.x'), (PYTHON1, 'Python 1.x.x'), + (PYMANAGER, 'Python install manager'), ) version = models.IntegerField(default=PYTHON3, choices=PYTHON_VERSION_CHOICES) is_latest = models.BooleanField( @@ -146,6 +148,10 @@ def is_version_at_least_3_5(self): def is_version_at_least_3_9(self): return self.is_version_at_least((3, 9)) + @property + def is_version_at_least_3_14(self): + return self.is_version_at_least((3, 14)) + def update_supernav(): latest_python3 = Release.objects.latest_python3() diff --git a/downloads/urls.py b/downloads/urls.py index eddef7a10..5b6b3fda0 100644 --- a/downloads/urls.py +++ b/downloads/urls.py @@ -5,6 +5,7 @@ urlpatterns = [ re_path(r'latest/python2/?$', views.DownloadLatestPython2.as_view(), name='download_latest_python2'), re_path(r'latest/python3/?$', views.DownloadLatestPython3.as_view(), name='download_latest_python3'), + re_path(r'latest/pymanager/?$', views.DownloadLatestPyManager.as_view(), name='download_latest_pymanager'), re_path(r'latest/?$', views.DownloadLatestPython3.as_view(), name='download_latest_python3'), path('operating-systems/', views.DownloadFullOSList.as_view(), name='download_full_os_list'), path('release//', views.DownloadReleaseDetail.as_view(), name='download_release_detail'), diff --git a/downloads/views.py b/downloads/views.py index 2e48cb2f3..816ad3b2e 100644 --- a/downloads/views.py +++ b/downloads/views.py @@ -45,6 +45,22 @@ def get_redirect_url(self, **kwargs): return reverse('download') +class DownloadLatestPyManager(RedirectView): + """ Redirect to latest Python install manager release """ + permanent = False + + def get_redirect_url(self, **kwargs): + try: + latest_pymanager = Release.objects.latest_pymanager() + except Release.DoesNotExist: + latest_pymanager = None + + if latest_pymanager: + return latest_pymanager.get_absolute_url() + else: + return reverse('download') + + class DownloadBase: """ Include latest releases in all views """ def get_context_data(self, **kwargs): @@ -52,6 +68,7 @@ def get_context_data(self, **kwargs): context.update({ 'latest_python2': Release.objects.latest_python2(), 'latest_python3': Release.objects.latest_python3(), + 'latest_pymanager': Release.objects.latest_pymanager(), }) return context @@ -71,6 +88,8 @@ def get_context_data(self, **kwargs): except Release.DoesNotExist: latest_python3 = None + latest_pymanager = context.get('latest_pymanager') + python_files = [] for o in OS.objects.all(): data = { @@ -80,6 +99,8 @@ def get_context_data(self, **kwargs): data['python2'] = latest_python2.download_file_for_os(o.slug) if latest_python3 is not None: data['python3'] = latest_python3.download_file_for_os(o.slug) + if latest_pymanager is not None: + data['pymanager'] = latest_pymanager.download_file_for_os(o.slug) python_files.append(data) context.update({ diff --git a/templates/downloads/index.html b/templates/downloads/index.html index 4cd8155e1..f527be031 100644 --- a/templates/downloads/index.html +++ b/templates/downloads/index.html @@ -17,11 +17,20 @@

Download the latest source release

{% else %}

Download the latest version for {{ data.os.name }}

{% endif %} + {% if data.pymanager %} +

+ Download Python install manager +

+ {% if data.python3 %} +

Or get the standalone installer for {{ data.python3.release.name }}

+ {% endif %} + {% else %}

{% if data.python3 %} Download {{ data.python3.release.name }} {% endif %}

+ {% endif %} {% endfor %}
diff --git a/templates/downloads/os_list.html b/templates/downloads/os_list.html index 1e0177dca..ffd524e5f 100644 --- a/templates/downloads/os_list.html +++ b/templates/downloads/os_list.html @@ -29,6 +29,10 @@

Python Releases for {{ os.name }}

@@ -39,7 +43,13 @@

Stable Releases

  • {{ r.name }} - {{ r.release_date|date }} {% if os.slug == 'windows' %} - {% if r.is_version_at_least_3_9 %} + {% if latest_pymanager and r.is_version_at_least_3_14 %} + {% if latest_pymanager %} +

    Download using the Python install manager.

    + {% else %} +

    Download using the Python install manager.

    + {% endif %} + {% elif r.is_version_at_least_3_9 %}

    Note that {{ r.name }} cannot be used on Windows 7 or earlier.

    {% elif r.is_version_at_least_3_5 %}

    Note that {{ r.name }} cannot be used on Windows XP or earlier.

    diff --git a/templates/downloads/supernav.html b/templates/downloads/supernav.html index fb0a1e629..12568eadb 100644 --- a/templates/downloads/supernav.html +++ b/templates/downloads/supernav.html @@ -7,10 +7,16 @@

    Python Source

    {% else %}

    Download for {{ data.os.name }}

    {% endif %} + + {% if data.pymanager %} +

    Python install manager

    +

    Or get the standalone installer for {{ data.python3.release.name }}

    + {% else %}

    {{ data.python3.release.name }}

    {% if data.os.slug == 'windows' %}

    Note that Python 3.9+ cannot be used on Windows 7 or earlier.

    {% endif %} + {% endif %}

    Not the OS you are looking for? Python can be used on many operating systems and environments. View the full list of downloads.

  • {% endfor %}