fix: expose rack width fields on device edit form
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# NetBox Utilities
|
||||
|
||||
Plugin für **NetBox 4.6.5** mit neun Funktionen:
|
||||
Plugin für **NetBox 4.6.5 bis 4.6.7** mit neun Funktionen:
|
||||
|
||||
- Jeder Benutzer kann die Menüs der linken Navigation verschieben oder ausblenden.
|
||||
- Ein Dropdown in der Kopfleiste setzt einen sitzungsweiten Filter für einen Mandanten oder eine Mandantengruppe.
|
||||
@@ -36,7 +36,7 @@ Release-Tag oder ein bestimmter Commit verwendet werden:
|
||||
|
||||
```bash
|
||||
/opt/netbox/venv/bin/pip install --upgrade --force-reinstall \
|
||||
"git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.8.0"
|
||||
"git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.8.1"
|
||||
```
|
||||
|
||||
Alternativ kann hinter dem `@` die vollständige Commit-ID stehen.
|
||||
@@ -135,8 +135,9 @@ empfohlen.
|
||||
|
||||
### Mehrere Geräte nebeneinander in derselben HE
|
||||
|
||||
Im normalen NetBox-Geräteformular stehen direkt nach **Rackseite** zwei neue
|
||||
optionale Felder zur Verfügung:
|
||||
Auf der normalen **Bearbeitungsseite eines Geräts** stehen direkt nach
|
||||
**Rackseite** zwei neue optionale Felder zur Verfügung. Dafür wird kein
|
||||
separates Plugin-Menü benötigt:
|
||||
|
||||
- **Rackbreite**: volle, halbe, Drittel- oder Viertelbreite;
|
||||
- **Breitenposition**: Position 1 bis 4, von links gezählt.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from netbox.plugins import PluginConfig, get_plugin_config
|
||||
|
||||
__version__ = "0.8.0"
|
||||
__version__ = "0.8.1"
|
||||
|
||||
|
||||
class NetBoxUtilitiesConfig(PluginConfig):
|
||||
|
||||
@@ -306,84 +306,81 @@ def _install_device_validation():
|
||||
|
||||
|
||||
def _install_device_form():
|
||||
from dcim.forms import DeviceForm
|
||||
from dcim.views import DeviceEditView
|
||||
|
||||
if getattr(DeviceForm, "_netbox_utilities_rack_width_installed", False):
|
||||
if getattr(DeviceEditView, "_netbox_utilities_rack_width_installed", False):
|
||||
return
|
||||
|
||||
original_init = DeviceForm.__init__
|
||||
original_clean = DeviceForm.clean
|
||||
original_save = DeviceForm.save
|
||||
base_device_form = DeviceEditView.form
|
||||
|
||||
@wraps(original_init)
|
||||
def width_aware_init(form, *args, **kwargs):
|
||||
original_init(form, *args, **kwargs)
|
||||
width, horizontal_position = _stored_width_position(form.instance)
|
||||
form.fields["utilities_rack_width"] = forms.ChoiceField(
|
||||
class RackWidthDeviceForm(base_device_form):
|
||||
utilities_rack_width = forms.ChoiceField(
|
||||
label="Rackbreite",
|
||||
choices=WIDTH_CHOICES,
|
||||
required=False,
|
||||
initial=width,
|
||||
initial=FULL_WIDTH,
|
||||
help_text="Optional: Geräte können sich eine HE nebeneinander teilen.",
|
||||
)
|
||||
form.fields["utilities_horizontal_position"] = forms.ChoiceField(
|
||||
utilities_horizontal_position = forms.ChoiceField(
|
||||
label="Breitenposition",
|
||||
choices=POSITION_CHOICES,
|
||||
required=False,
|
||||
initial=horizontal_position,
|
||||
initial=1,
|
||||
help_text="Position von links; bei halber Breite sind Position 1 und 2 möglich.",
|
||||
)
|
||||
if not form.is_bound:
|
||||
form.initial["utilities_rack_width"] = width
|
||||
form.initial["utilities_horizontal_position"] = horizontal_position
|
||||
|
||||
reordered = {}
|
||||
for name, field in form.fields.items():
|
||||
if name in {"utilities_rack_width", "utilities_horizontal_position"}:
|
||||
continue
|
||||
reordered[name] = field
|
||||
if name == "face":
|
||||
reordered["utilities_rack_width"] = form.fields["utilities_rack_width"]
|
||||
reordered["utilities_horizontal_position"] = form.fields["utilities_horizontal_position"]
|
||||
form.fields = reordered
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
width, horizontal_position = _stored_width_position(self.instance)
|
||||
if not self.is_bound:
|
||||
self.initial["utilities_rack_width"] = width
|
||||
self.initial["utilities_horizontal_position"] = horizontal_position
|
||||
|
||||
@wraps(original_clean)
|
||||
def width_aware_form_clean(form):
|
||||
cleaned_data = original_clean(form)
|
||||
try:
|
||||
width, horizontal_position = normalize_width_position(
|
||||
cleaned_data.get("utilities_rack_width"),
|
||||
cleaned_data.get("utilities_horizontal_position"),
|
||||
)
|
||||
except ValidationError as error:
|
||||
form.add_error("utilities_horizontal_position", error)
|
||||
reordered = {}
|
||||
for name, field in self.fields.items():
|
||||
if name in {"utilities_rack_width", "utilities_horizontal_position"}:
|
||||
continue
|
||||
reordered[name] = field
|
||||
if name == "face":
|
||||
reordered["utilities_rack_width"] = self.fields["utilities_rack_width"]
|
||||
reordered["utilities_horizontal_position"] = self.fields["utilities_horizontal_position"]
|
||||
self.fields = reordered
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
try:
|
||||
width, horizontal_position = normalize_width_position(
|
||||
cleaned_data.get("utilities_rack_width"),
|
||||
cleaned_data.get("utilities_horizontal_position"),
|
||||
)
|
||||
except ValidationError as error:
|
||||
self.add_error("utilities_horizontal_position", error)
|
||||
return cleaned_data
|
||||
if not cleaned_data.get("rack") or not cleaned_data.get("position"):
|
||||
width, horizontal_position = FULL_WIDTH, 1
|
||||
cleaned_data["utilities_rack_width"] = width
|
||||
cleaned_data["utilities_horizontal_position"] = horizontal_position
|
||||
stage_width_position(self.instance, width, horizontal_position)
|
||||
return cleaned_data
|
||||
if not cleaned_data.get("rack") or not cleaned_data.get("position"):
|
||||
width, horizontal_position = FULL_WIDTH, 1
|
||||
cleaned_data["utilities_rack_width"] = width
|
||||
cleaned_data["utilities_horizontal_position"] = horizontal_position
|
||||
stage_width_position(form.instance, width, horizontal_position)
|
||||
return cleaned_data
|
||||
|
||||
@wraps(original_save)
|
||||
def width_aware_form_save(form, commit=True):
|
||||
device = original_save(form, commit=commit)
|
||||
if not commit:
|
||||
def save(self, commit=True):
|
||||
device = super().save(commit=commit)
|
||||
if not commit:
|
||||
return device
|
||||
width, horizontal_position = get_width_position(device)
|
||||
if width == FULL_WIDTH or not device.rack_id or not device.position:
|
||||
DeviceRackPlacement.objects.filter(device=device).delete()
|
||||
else:
|
||||
DeviceRackPlacement.objects.update_or_create(
|
||||
device=device,
|
||||
defaults={"width": width, "horizontal_position": horizontal_position},
|
||||
)
|
||||
return device
|
||||
width, horizontal_position = get_width_position(device)
|
||||
if width == FULL_WIDTH or not device.rack_id or not device.position:
|
||||
DeviceRackPlacement.objects.filter(device=device).delete()
|
||||
else:
|
||||
DeviceRackPlacement.objects.update_or_create(
|
||||
device=device,
|
||||
defaults={"width": width, "horizontal_position": horizontal_position},
|
||||
)
|
||||
return device
|
||||
|
||||
DeviceForm.__init__ = width_aware_init
|
||||
DeviceForm.clean = width_aware_form_clean
|
||||
DeviceForm.save = width_aware_form_save
|
||||
DeviceForm._netbox_utilities_rack_width_installed = True
|
||||
RackWidthDeviceForm.__module__ = __name__
|
||||
RackWidthDeviceForm.__qualname__ = "RackWidthDeviceForm"
|
||||
DeviceEditView.form = RackWidthDeviceForm
|
||||
DeviceEditView._netbox_utilities_rack_width_installed = True
|
||||
|
||||
|
||||
def _install_rack_svg():
|
||||
|
||||
@@ -18,6 +18,13 @@ from netbox_utilities.rack_width import (
|
||||
|
||||
|
||||
class RackWidthTest(SimpleTestCase):
|
||||
def test_device_edit_view_uses_explicit_rack_width_form(self):
|
||||
from dcim.views import DeviceEditView
|
||||
|
||||
self.assertEqual(DeviceEditView.form.__name__, "RackWidthDeviceForm")
|
||||
self.assertIn("utilities_rack_width", DeviceEditView.form.base_fields)
|
||||
self.assertIn("utilities_horizontal_position", DeviceEditView.form.base_fields)
|
||||
|
||||
def test_normalizes_full_width_to_first_position(self):
|
||||
self.assertEqual(normalize_width_position(1, 4), (1, 1))
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "netbox-utilities"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
description = "Navigation, tenant utilities, partial-width rack devices, bulk uploads, and rack reordering for NetBox 4.6"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
Reference in New Issue
Block a user