Parcourir la source

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
Jeremy Stretch il y a 1 an
Parent
commit
aa56b99566

+ 0 - 8
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)

+ 7 - 0
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,

+ 20 - 6
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
 
 

+ 9 - 0
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)
 

+ 2 - 2
netbox/utilities/templates/widgets/apiselect.html

@@ -15,7 +15,7 @@
       <i class="mdi mdi-database-search-outline"></i>
     </button>
   {% endif %}
-  {% if widget.attrs.quick_add and not widget.attrs.disabled %}
+  {% if quick_add and not widget.attrs.disabled %}
     {# Opens the quick add modal #}
     <button
       type="button"
@@ -23,7 +23,7 @@
       class="btn btn-outline-secondary ms-1"
       data-bs-toggle="modal"
       data-bs-target="#htmx-modal"
-      hx-get="{{ widget.attrs.quick_add }}?_quickadd=True&target={{ widget.attrs.id }}"
+      hx-get="{{ quick_add.url }}?_quickadd=True&target={{ widget.attrs.id }}{% for k, v in quick_add.params.items %}&{{ k }}={{ v }}{% endfor %}"
       hx-target="#htmx-modal-content"
     >
       <i class="mdi mdi-plus-circle"></i>

+ 8 - 1
netbox/virtualization/forms/model_forms.py

@@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
 
 from dcim.forms.common import InterfaceCommonForm
 from dcim.forms.mixins import ScopedForm
-from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site, SiteGroup
+from dcim.models import Device, DeviceRole, MACAddress, Platform, Rack, Region, Site, SiteGroup
 from extras.models import ConfigTemplate
 from ipam.choices import VLANQinQRoleChoices
 from ipam.models import IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
@@ -298,6 +298,13 @@ class VMComponentForm(NetBoxModelForm):
 
 
 class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
+    primary_mac_address = DynamicModelChoiceField(
+        queryset=MACAddress.objects.all(),
+        label=_('Primary MAC address'),
+        required=False,
+        quick_add=True,
+        quick_add_params={'vminterface': '$pk'}
+    )
     parent = DynamicModelChoiceField(
         queryset=VMInterface.objects.all(),
         required=False,