Skip to content

Commit 9e8395a

Browse files
committed
Deleted release artifact files when they're cleared in admin
1 parent 5479b9e commit 9e8395a

File tree

2 files changed

+60
-10
lines changed

2 files changed

+60
-10
lines changed

releases/admin.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,35 @@
1+
from django import forms
12
from django.contrib import admin
23

34
from .models import Release
45

6+
_ARTIFACTS = ["checksum", "tarball", "wheel"]
7+
8+
9+
class ReleaseAdminForm(forms.ModelForm):
10+
def __init__(self, *args, **kwargs):
11+
super().__init__(*args, **kwargs)
12+
13+
# Add `accept` attributes to the artifact file fields to make it a bit
14+
# easier to pick the right files in the browser's 'filepicker
15+
extensions = {"tarball": ".tar.gz", "wheel": ".whl", "checksum": ".asc,.txt"}
16+
for field, accept in extensions.items():
17+
widget = self.fields[field].widget
18+
widget.attrs.setdefault("accept", accept)
19+
20+
self._previous_file_fields = {a: getattr(self.instance, a) for a in _ARTIFACTS}
21+
22+
def save(self, commit=True):
23+
if not commit:
24+
raise ValueError("ReleaseAdminForm.save() doesn't support commit=True")
25+
instance = super().save(commit=commit)
26+
27+
# Delete any files from storage that might have been cleared
28+
for a in _ARTIFACTS:
29+
if self._previous_file_fields[a] and not getattr(instance, a):
30+
self._previous_file_fields[a].delete(save=False)
31+
return instance
32+
533

634
@admin.register(Release)
735
class ReleaseAdmin(admin.ModelAdmin):
@@ -10,6 +38,7 @@ class ReleaseAdmin(admin.ModelAdmin):
1038
("Dates", {"fields": ["date", "eol_date"]}),
1139
("Artifacts", {"fields": ["tarball", "wheel", "checksum"]}),
1240
]
41+
form = ReleaseAdminForm
1342
list_display = (
1443
"version",
1544
"show_is_published",
@@ -25,16 +54,6 @@ class ReleaseAdmin(admin.ModelAdmin):
2554
list_filter = ("status", "is_lts", "is_active")
2655
ordering = ("-major", "-minor", "-micro", "-status", "-iteration")
2756

28-
def get_form(self, request, obj=None, change=False, **kwargs):
29-
form_class = super().get_form(request, obj=obj, change=change, **kwargs)
30-
# Add `accept` attributes to the artifact file fields to make it a bit
31-
# easier to pick the right files in the browser's 'filepicker
32-
extensions = {"tarball": ".tar.gz", "wheel": ".whl", "checksum": ".asc,.txt"}
33-
for field, accept in extensions.items():
34-
widget = form_class.base_fields[field].widget
35-
widget.attrs.setdefault("accept", accept)
36-
return form_class
37-
3857
@admin.display(
3958
description="status",
4059
ordering="status",

releases/tests.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import datetime
22
import re
3+
import shutil
4+
import tempfile
5+
from pathlib import Path
36

47
from django.contrib import admin
58
from django.core.exceptions import ValidationError
@@ -422,6 +425,34 @@ def test_file_upload_renames_correctly(self):
422425
)
423426
self.assertEqual(release.checksum.name, "pgp/Django-1.2.3.checksum.txt")
424427

428+
def test_clearing_also_deletes_file(self):
429+
tempdir = Path(tempfile.mkdtemp(prefix="djangoprojectcom_"))
430+
self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True)
431+
self.enterContext(override_settings(MEDIA_ROOT=tempdir))
432+
433+
files = {
434+
"checksum": tempdir / "checksum.txt",
435+
"tarball": tempdir / "tarball.tar.gz",
436+
"wheel": tempdir / "wheel.whl",
437+
}
438+
# Create the files on disk:
439+
for f in files.values():
440+
f.touch()
441+
442+
release = Release.objects.create(
443+
version="1.0", **{a: f.name for a, f in files.items()}
444+
)
445+
data = {"version": "2.0", **{f"{a}-clear": True for a in files.keys()}}
446+
form = self.form_class(instance=release, data=data)
447+
self.assertTrue(form.is_valid(), form.errors)
448+
form.save()
449+
release.refresh_from_db()
450+
451+
for artifact, tmpfile in files.items():
452+
with self.subTest(artifact=artifact):
453+
self.assertFalse(getattr(release, artifact))
454+
self.assertFalse(tmpfile.exists())
455+
425456

426457
class RedirectViewTestCase(TestCase):
427458
def test_redirect(self):

0 commit comments

Comments
 (0)