From aa56b99566b5ef0603cd4fb2454eb58433d97ae5 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 16 Dec 2024 10:57:09 -0500 Subject: [PATCH] Closes #18045: Enable adding a new MAC to an interface via quick add (#18200) * Closes #18045: Enable adding a new MAC to an interface via quick add * Misc cleanup --- netbox/dcim/forms/common.py | 8 ------ netbox/dcim/forms/model_forms.py | 7 +++++ netbox/utilities/forms/fields/dynamic.py | 26 ++++++++++++++----- netbox/utilities/forms/widgets/apiselect.py | 9 +++++++ .../templates/widgets/apiselect.html | 4 +-- netbox/virtualization/forms/model_forms.py | 9 ++++++- 6 files changed, 46 insertions(+), 17 deletions(-) diff --git a/netbox/dcim/forms/common.py b/netbox/dcim/forms/common.py index 65d0d0f23..8ca258f34 100644 --- a/netbox/dcim/forms/common.py +++ b/netbox/dcim/forms/common.py @@ -3,9 +3,7 @@ from django.utils.translation import gettext_lazy as _ from dcim.choices import * from dcim.constants import * -from dcim.models import MACAddress from utilities.forms import get_field_value -from utilities.forms.fields import DynamicModelChoiceField __all__ = ( 'InterfaceCommonForm', @@ -20,12 +18,6 @@ class InterfaceCommonForm(forms.Form): max_value=INTERFACE_MTU_MAX, label=_('MTU') ) - primary_mac_address = DynamicModelChoiceField( - queryset=MACAddress.objects.all(), - label=_('Primary MAC address'), - required=False, - quick_add=True - ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index d6fdb21e2..9bc69e991 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1410,6 +1410,13 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): required=False, label=_('VRF') ) + primary_mac_address = DynamicModelChoiceField( + queryset=MACAddress.objects.all(), + label=_('Primary MAC address'), + required=False, + quick_add=True, + quick_add_params={'interface': '$pk'} + ) wwn = forms.CharField( empty_value=None, required=False, diff --git a/netbox/utilities/forms/fields/dynamic.py b/netbox/utilities/forms/fields/dynamic.py index 13d5ffc70..793494b4b 100644 --- a/netbox/utilities/forms/fields/dynamic.py +++ b/netbox/utilities/forms/fields/dynamic.py @@ -68,6 +68,8 @@ class DynamicModelChoiceMixin: selector: Include an advanced object selection widget to assist the user in identifying the desired object quick_add: Include a widget to quickly create a new related object for assignment. NOTE: Nested usage of quick-add fields is not currently supported. + quick_add_params: A dictionary of initial data to include when launching the quick-add form (optional). The + token string "$pk" will be replaced with the primary key of the form's instance, if any. Context keys: value: The name of the attribute which contains the option's value (default: 'id') @@ -93,6 +95,7 @@ class DynamicModelChoiceMixin: context=None, selector=False, quick_add=False, + quick_add_params=None, **kwargs ): self.model = queryset.model @@ -103,6 +106,7 @@ class DynamicModelChoiceMixin: self.context = context or {} self.selector = selector self.quick_add = quick_add + self.quick_add_params = quick_add_params or {} super().__init__(queryset, **kwargs) @@ -125,12 +129,6 @@ class DynamicModelChoiceMixin: if self.selector: attrs['selector'] = self.model._meta.label_lower - # Include quick add? - if self.quick_add: - app_label = self.model._meta.app_label - model_name = self.model._meta.model_name - attrs['quick_add'] = reverse_lazy(f'{app_label}:{model_name}_add') - return attrs def get_bound_field(self, form, field_name): @@ -171,6 +169,22 @@ class DynamicModelChoiceMixin: viewname = get_viewname(self.queryset.model, action='list', rest_api=True) widget.attrs['data-url'] = reverse(viewname) + # Include quick add? + if self.quick_add: + app_label = self.model._meta.app_label + model_name = self.model._meta.model_name + widget.quick_add_context = { + 'url': reverse_lazy(f'{app_label}:{model_name}_add'), + 'params': {}, + } + for k, v in self.quick_add_params.items(): + if v == '$pk': + # Replace "$pk" token with the primary key of the form's instance (if any) + if getattr(form.instance, 'pk', None): + widget.quick_add_context['params'][k] = form.instance.pk + else: + widget.quick_add_context['params'][k] = v + return bound_field diff --git a/netbox/utilities/forms/widgets/apiselect.py b/netbox/utilities/forms/widgets/apiselect.py index 278371de6..7e9122922 100644 --- a/netbox/utilities/forms/widgets/apiselect.py +++ b/netbox/utilities/forms/widgets/apiselect.py @@ -22,6 +22,15 @@ class APISelect(forms.Select): dynamic_params: Dict[str, str] static_params: Dict[str, List[str]] + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + + # Add quick-add context data, if enabled for the widget + if hasattr(self, 'quick_add_context'): + context['quick_add'] = self.quick_add_context + + return context + def __init__(self, api_url=None, full=False, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/netbox/utilities/templates/widgets/apiselect.html b/netbox/utilities/templates/widgets/apiselect.html index 51ee79e6a..2d5c9e493 100644 --- a/netbox/utilities/templates/widgets/apiselect.html +++ b/netbox/utilities/templates/widgets/apiselect.html @@ -15,7 +15,7 @@ {% endif %} - {% if widget.attrs.quick_add and not widget.attrs.disabled %} + {% if quick_add and not widget.attrs.disabled %} {# Opens the quick add modal #}