Skip to content

Commit bbc6fd7

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

File tree

2 files changed

+67
-10
lines changed

2 files changed

+67
-10
lines changed

releases/admin.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,33 @@
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+
# Extending _save_m2m() instead of save() lets us support both save(commit=True/False)
23+
def _save_m2m(self):
24+
super()._save_m2m()
25+
26+
# Delete any files from storage that might have been cleared
27+
for a in _ARTIFACTS:
28+
if self._previous_file_fields[a] and not getattr(self.instance, a):
29+
self._previous_file_fields[a].delete(save=False)
30+
531

632
@admin.register(Release)
733
class ReleaseAdmin(admin.ModelAdmin):
@@ -10,6 +36,7 @@ class ReleaseAdmin(admin.ModelAdmin):
1036
("Dates", {"fields": ["date", "eol_date"]}),
1137
("Artifacts", {"fields": ["tarball", "wheel", "checksum"]}),
1238
]
39+
form = ReleaseAdminForm
1340
list_display = (
1441
"version",
1542
"show_is_published",
@@ -25,16 +52,6 @@ class ReleaseAdmin(admin.ModelAdmin):
2552
list_filter = ("status", "is_lts", "is_active")
2653
ordering = ("-major", "-minor", "-micro", "-status", "-iteration")
2754

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-
3855
@admin.display(
3956
description="status",
4057
ordering="status",

releases/tests.py

Lines changed: 40 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,43 @@ 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, commit_save=True):
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(commit=commit_save)
449+
if not commit_save:
450+
for artifact, tmpfile in files.items():
451+
with self.subTest(artifact=artifact):
452+
self.assertTrue(tmpfile.exists())
453+
release.save()
454+
form.save_m2m()
455+
release.refresh_from_db()
456+
457+
for artifact, tmpfile in files.items():
458+
with self.subTest(artifact=artifact):
459+
self.assertFalse(getattr(release, artifact))
460+
self.assertFalse(tmpfile.exists())
461+
462+
def test_clearing_also_deletes_file_commit_false(self):
463+
self.test_clearing_also_deletes_file(commit_save=False)
464+
425465

426466
class RedirectViewTestCase(TestCase):
427467
def test_redirect(self):

0 commit comments

Comments
 (0)