Closes #18045: Enable adding a new MAC to an interface via quick add

This commit is contained in:
Jeremy Stretch 2024-12-10 15:36:59 -05:00
parent 39ca3ce571
commit 790aed2688
6 changed files with 41 additions and 17 deletions

View File

@ -3,9 +3,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.choices import * from dcim.choices import *
from dcim.constants import * from dcim.constants import *
from dcim.models import MACAddress
from utilities.forms import get_field_value from utilities.forms import get_field_value
from utilities.forms.fields import DynamicModelChoiceField
__all__ = ( __all__ = (
'InterfaceCommonForm', 'InterfaceCommonForm',
@ -20,12 +18,6 @@ class InterfaceCommonForm(forms.Form):
max_value=INTERFACE_MTU_MAX, max_value=INTERFACE_MTU_MAX,
label=_('MTU') label=_('MTU')
) )
primary_mac_address = DynamicModelChoiceField(
queryset=MACAddress.objects.all(),
label=_('Primary MAC address'),
required=False,
quick_add=True
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@ -1410,6 +1410,13 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
required=False, required=False,
label=_('VRF') 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( wwn = forms.CharField(
empty_value=None, empty_value=None,
required=False, required=False,

View File

@ -93,6 +93,7 @@ class DynamicModelChoiceMixin:
context=None, context=None,
selector=False, selector=False,
quick_add=False, quick_add=False,
quick_add_params=None,
**kwargs **kwargs
): ):
self.model = queryset.model self.model = queryset.model
@ -103,6 +104,7 @@ class DynamicModelChoiceMixin:
self.context = context or {} self.context = context or {}
self.selector = selector self.selector = selector
self.quick_add = quick_add self.quick_add = quick_add
self.quick_add_params = quick_add_params or {}
super().__init__(queryset, **kwargs) super().__init__(queryset, **kwargs)
@ -125,12 +127,6 @@ class DynamicModelChoiceMixin:
if self.selector: if self.selector:
attrs['selector'] = self.model._meta.label_lower 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 return attrs
def get_bound_field(self, form, field_name): def get_bound_field(self, form, field_name):
@ -171,6 +167,19 @@ class DynamicModelChoiceMixin:
viewname = get_viewname(self.queryset.model, action='list', rest_api=True) viewname = get_viewname(self.queryset.model, action='list', rest_api=True)
widget.attrs['data-url'] = reverse(viewname) 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':
v = form.instance.pk
widget.quick_add_context['params'][k] = v
return bound_field return bound_field

View File

@ -22,6 +22,15 @@ class APISelect(forms.Select):
dynamic_params: Dict[str, str] dynamic_params: Dict[str, str]
static_params: Dict[str, List[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 set on 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): def __init__(self, api_url=None, full=False, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@ -15,7 +15,7 @@
<i class="mdi mdi-database-search-outline"></i> <i class="mdi mdi-database-search-outline"></i>
</button> </button>
{% endif %} {% endif %}
{% if widget.attrs.quick_add and not widget.attrs.disabled %} {% if quick_add and not widget.attrs.disabled %}
{# Opens the quick add modal #} {# Opens the quick add modal #}
<button <button
type="button" type="button"
@ -23,7 +23,7 @@
class="btn btn-outline-secondary ms-1" class="btn btn-outline-secondary ms-1"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#htmx-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" hx-target="#htmx-modal-content"
> >
<i class="mdi mdi-plus-circle"></i> <i class="mdi mdi-plus-circle"></i>

View File

@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.forms.common import InterfaceCommonForm from dcim.forms.common import InterfaceCommonForm
from dcim.forms.mixins import ScopedForm 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 extras.models import ConfigTemplate
from ipam.choices import VLANQinQRoleChoices from ipam.choices import VLANQinQRoleChoices
from ipam.models import IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF from ipam.models import IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
@ -298,6 +298,13 @@ class VMComponentForm(NetBoxModelForm):
class VMInterfaceForm(InterfaceCommonForm, VMComponentForm): 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( parent = DynamicModelChoiceField(
queryset=VMInterface.objects.all(), queryset=VMInterface.objects.all(),
required=False, required=False,