fix: validate bulk JPEG uploads only once

This commit is contained in:
2026-08-03 15:46:28 +02:00
parent d54fc646ee
commit e1153cfce4
6 changed files with 38 additions and 16 deletions
+2 -2
View File
@@ -34,7 +34,7 @@ Release-Tag oder ein bestimmter Commit verwendet werden:
```bash ```bash
/opt/netbox/venv/bin/pip install --upgrade --force-reinstall \ /opt/netbox/venv/bin/pip install --upgrade --force-reinstall \
"git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.6.0" "git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.6.1"
``` ```
Alternativ kann hinter dem `@` die vollständige Commit-ID stehen. Alternativ kann hinter dem `@` die vollständige Commit-ID stehen.
@@ -179,7 +179,7 @@ Einzel-Upload durch **Mehrere Bilder hochladen** ersetzt. Im Dateidialog können
bis zu 50 Bilder gemeinsam ausgewählt werden. Vor dem Speichern zeigt das bis zu 50 Bilder gemeinsam ausgewählt werden. Vor dem Speichern zeigt das
Plugin Vorschaubilder und die jeweiligen Dateinamen an. Plugin Vorschaubilder und die jeweiligen Dateinamen an.
Alle Dateien werden zunächst mit NetBox' eigener Bildvalidierung geprüft. Ist Jede Datei wird genau einmal mit NetBox' eigener Bildvalidierung geprüft. Ist
eine Datei ungültig, wird kein Bild aus dieser Auswahl gespeichert. Eine eine Datei ungültig, wird kein Bild aus dieser Auswahl gespeichert. Eine
optionale gemeinsame Beschreibung kann auf alle Bilder angewendet werden; als optionale gemeinsame Beschreibung kann auf alle Bilder angewendet werden; als
Anzeigename bleibt der jeweilige ursprüngliche Dateiname erhalten. Benötigt Anzeigename bleibt der jeweilige ursprüngliche Dateiname erhalten. Benötigt
+1 -1
View File
@@ -1,6 +1,6 @@
from netbox.plugins import PluginConfig, get_plugin_config from netbox.plugins import PluginConfig, get_plugin_config
__version__ = "0.6.0" __version__ = "0.6.1"
class NetBoxUtilitiesConfig(PluginConfig): class NetBoxUtilitiesConfig(PluginConfig):
+2 -2
View File
@@ -11,7 +11,7 @@ class MultipleImageInput(forms.ClearableFileInput):
allow_multiple_selected = True allow_multiple_selected = True
class MultipleImageField(forms.ImageField): class MultipleFileField(forms.FileField):
widget = MultipleImageInput widget = MultipleImageInput
def clean(self, data, initial=None): def clean(self, data, initial=None):
@@ -22,7 +22,7 @@ class MultipleImageField(forms.ImageField):
class BulkImageUploadForm(forms.Form): class BulkImageUploadForm(forms.Form):
images = MultipleImageField( images = MultipleFileField(
label="Bilder", label="Bilder",
widget=MultipleImageInput( widget=MultipleImageInput(
attrs={ attrs={
+21 -3
View File
@@ -3,6 +3,7 @@ from io import BytesIO
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from extras.forms import ImageAttachmentForm
from PIL import Image from PIL import Image
from netbox_utilities.forms import BulkImageUploadForm from netbox_utilities.forms import BulkImageUploadForm
@@ -10,10 +11,11 @@ from netbox_utilities.forms import BulkImageUploadForm
class BulkImageUploadFormTest(SimpleTestCase): class BulkImageUploadFormTest(SimpleTestCase):
@staticmethod @staticmethod
def _image(name): def _image(name, image_format="PNG"):
content = BytesIO() content = BytesIO()
Image.new("RGB", (2, 2), "white").save(content, format="PNG") Image.new("RGB", (2, 2), "white").save(content, format=image_format)
return SimpleUploadedFile(name, content.getvalue(), content_type="image/png") content_type = "image/jpeg" if image_format == "JPEG" else "image/png"
return SimpleUploadedFile(name, content.getvalue(), content_type=content_type)
def test_accepts_multiple_images(self): def test_accepts_multiple_images(self):
files = MultiValueDict({"images": [self._image("front.png"), self._image("rear.png")]}) files = MultiValueDict({"images": [self._image("front.png"), self._image("rear.png")]})
@@ -24,6 +26,22 @@ class BulkImageUploadFormTest(SimpleTestCase):
self.assertEqual(len(form.cleaned_data["images"]), 2) self.assertEqual(len(form.cleaned_data["images"]), 2)
self.assertEqual(form.cleaned_data["description"], "Dokumentation") self.assertEqual(form.cleaned_data["description"], "Dokumentation")
def test_jpegs_are_validated_once_by_netbox(self):
files = MultiValueDict(
{
"images": [
self._image("front.JPG", "JPEG"),
self._image("rear.jpg", "JPEG"),
]
}
)
form = BulkImageUploadForm(data={}, files=files)
self.assertTrue(form.is_valid(), form.errors)
for image in form.cleaned_data["images"]:
image.seek(0)
self.assertIsNotNone(ImageAttachmentForm.base_fields["image"].clean(image))
def test_requires_at_least_one_image(self): def test_requires_at_least_one_image(self):
form = BulkImageUploadForm(data={}, files={}) form = BulkImageUploadForm(data={}, files={})
+11 -7
View File
@@ -109,14 +109,18 @@ class BulkImageUploadView(ContentTypePermissionRequiredMixin, View):
@staticmethod @staticmethod
def _build_image_forms(parent, cleaned_data): def _build_image_forms(parent, cleaned_data):
return [ image_forms = []
ImageAttachmentForm( for image in cleaned_data["images"]:
data={"name": "", "description": cleaned_data["description"]}, if hasattr(image, "seek"):
files={"image": image}, image.seek(0)
instance=ImageAttachment(parent=parent), image_forms.append(
ImageAttachmentForm(
data={"name": "", "description": cleaned_data["description"]},
files={"image": image},
instance=ImageAttachment(parent=parent),
)
) )
for image in cleaned_data["images"] return image_forms
]
def _return_url(self, request, parent): def _return_url(self, request, parent):
candidate = request.POST.get("return_url") or request.GET.get("return_url") candidate = request.POST.get("return_url") or request.GET.get("return_url")
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "netbox-utilities" name = "netbox-utilities"
version = "0.6.0" version = "0.6.1"
description = "Navigation, tenant utilities, bulk image/module upload, and atomic rack reordering for NetBox 4.6" description = "Navigation, tenant utilities, bulk image/module upload, and atomic rack reordering for NetBox 4.6"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"