1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Replace custom form templates with TabbedFieldGroups

This commit is contained in:
Jeremy Stretch
2024-03-13 10:59:00 -04:00
parent 8f03a19b5f
commit 2aaa552067
12 changed files with 87 additions and 369 deletions

View File

@ -7,6 +7,7 @@ from ipam.models import ASN
from netbox.forms import NetBoxModelForm from netbox.forms import NetBoxModelForm
from tenancy.forms import TenancyForm from tenancy.forms import TenancyForm
from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
from utilities.forms.rendering import TabbedFieldGroups
from utilities.forms.widgets import DatePicker, NumberWithOptions from utilities.forms.widgets import DatePicker, NumberWithOptions
__all__ = ( __all__ = (
@ -146,6 +147,21 @@ class CircuitTerminationForm(NetBoxModelForm):
selector=True selector=True
) )
fieldsets = (
(_('Circuit Termination'), (
'circuit',
'term_side',
'description',
'tags',
TabbedFieldGroups(
(_('Site'), 'site'),
(_('Provider Network'), 'provider_network'),
),
'mark_connected',
)),
(_('Termination Details'), ('port_speed', 'upstream_speed', 'xconnect_id', 'pp_info')),
)
class Meta: class Meta:
model = CircuitTermination model = CircuitTermination
fields = [ fields = [

View File

@ -412,7 +412,6 @@ class CircuitContactsView(ObjectContactsView):
class CircuitTerminationEditView(generic.ObjectEditView): class CircuitTerminationEditView(generic.ObjectEditView):
queryset = CircuitTermination.objects.all() queryset = CircuitTermination.objects.all()
form = forms.CircuitTerminationForm form = forms.CircuitTerminationForm
template_name = 'circuits/circuittermination_edit.html'
@register_model_view(CircuitTermination, 'delete') @register_model_view(CircuitTermination, 'delete')

View File

@ -16,7 +16,7 @@ from utilities.forms.fields import (
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField, CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
SlugField, SlugField,
) )
from utilities.forms.rendering import ObjectAttribute from utilities.forms.rendering import InlineFields, ObjectAttribute, TabbedFieldGroups
from utilities.forms.widgets import DatePicker from utilities.forms.widgets import DatePicker
from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
@ -308,6 +308,20 @@ class IPAddressForm(TenancyForm, NetBoxModelForm):
) )
comments = CommentField() comments = CommentField()
fieldsets = (
(_('IP Address'), ('address', 'status', 'role', 'vrf', 'dns_name', 'description', 'tags')),
(_('Tenancy'), ('tenant_group', 'tenant')),
(_('Assignment'), (
TabbedFieldGroups(
(_('Device'), 'interface'),
(_('Virtual Machine'), 'vminterface'),
(_('FHRP Group'), 'fhrpgroup'),
),
'primary_for_parent',
)),
(_('NAT IP (Inside)'), ('nat_inside',)),
)
class Meta: class Meta:
model = IPAddress model = IPAddress
fields = [ fields = [
@ -709,6 +723,20 @@ class ServiceForm(NetBoxModelForm):
) )
comments = CommentField() comments = CommentField()
fieldsets = (
(_('Service'), (
TabbedFieldGroups(
(_('Device'), 'device'),
(_('Virtual Machine'), 'virtual_machine'),
),
'name',
InlineFields(_('Port(s)'), 'protocol', 'ports'),
'ipaddresses',
'description',
'tags',
)),
)
class Meta: class Meta:
model = Service model = Service
fields = [ fields = [
@ -723,6 +751,22 @@ class ServiceCreateForm(ServiceForm):
required=False required=False
) )
fieldsets = (
(_('Service'), (
TabbedFieldGroups(
(_('Device'), 'device'),
(_('Virtual Machine'), 'virtual_machine'),
),
TabbedFieldGroups(
(_('From Template'), 'service_template'),
(_('Custom'), 'name', 'protocol', 'ports'),
),
'ipaddresses',
'description',
'tags',
)),
)
class Meta(ServiceForm.Meta): class Meta(ServiceForm.Meta):
fields = [ fields = [
'device', 'virtual_machine', 'service_template', 'name', 'protocol', 'ports', 'ipaddresses', 'description', 'device', 'virtual_machine', 'service_template', 'name', 'protocol', 'ports', 'ipaddresses', 'description',

View File

@ -781,7 +781,6 @@ class IPAddressView(generic.ObjectView):
class IPAddressEditView(generic.ObjectEditView): class IPAddressEditView(generic.ObjectEditView):
queryset = IPAddress.objects.all() queryset = IPAddress.objects.all()
form = forms.IPAddressForm form = forms.IPAddressForm
template_name = 'ipam/ipaddress_edit.html'
def alter_object(self, obj, request, url_args, url_kwargs): def alter_object(self, obj, request, url_args, url_kwargs):
@ -1235,14 +1234,12 @@ class ServiceView(generic.ObjectView):
class ServiceCreateView(generic.ObjectEditView): class ServiceCreateView(generic.ObjectEditView):
queryset = Service.objects.all() queryset = Service.objects.all()
form = forms.ServiceCreateForm form = forms.ServiceCreateForm
template_name = 'ipam/service_create.html'
@register_model_view(Service, 'edit') @register_model_view(Service, 'edit')
class ServiceEditView(generic.ObjectEditView): class ServiceEditView(generic.ObjectEditView):
queryset = Service.objects.all() queryset = Service.objects.all()
form = forms.ServiceForm form = forms.ServiceForm
template_name = 'ipam/service_edit.html'
@register_model_view(Service, 'delete') @register_model_view(Service, 'delete')

View File

@ -1,58 +0,0 @@
{% extends 'generic/object_edit.html' %}
{% load static %}
{% load form_helpers %}
{% load i18n %}
{% block form %}
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "Circuit Termination" %}</h5>
</div>
{% render_field form.circuit %}
{% render_field form.term_side %}
{% render_field form.tags %}
{% render_field form.mark_connected %}
{% with providernetwork_tab_active=form.initial.provider_network %}
<div class="row">
<div class="col-9 offset-3">
<ul class="nav nav-pills mb-1" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link{% if not providernetwork_tab_active %} active{% endif %}" role="tab" type="button" data-bs-target="#site" data-bs-toggle="tab">{% trans "Site" %}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link{% if providernetwork_tab_active %} active{% endif %}" role="tab" type="button" data-bs-toggle="tab" data-bs-target="#providernetwork">{% trans "Provider Network" %}</button>
</li>
</ul>
</div>
</div>
<div class="tab-content p-0 border-0">
<div class="tab-pane{% if not providernetwork_tab_active %} active{% endif %}" id="site">
{% render_field form.site %}
</div>
<div class="tab-pane{% if providernetwork_tab_active %} active{% endif %}" id="providernetwork">
{% render_field form.provider_network %}
</div>
</div>
{% endwith %}
</div>
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "Termination Details" %}</h5>
</div>
{% render_field form.port_speed %}
{% render_field form.upstream_speed %}
{% render_field form.xconnect_id %}
{% render_field form.pp_info %}
{% render_field form.description %}
</div>
{% if form.custom_fields %}
<div class="field-group mb-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "Custom Fields" %}</h5>
</div>
{% render_custom_fields form %}
</div>
{% endif %}
{% endblock %}

View File

@ -1,93 +0,0 @@
{% extends 'generic/object_edit.html' %}
{% load static %}
{% load form_helpers %}
{% load helpers %}
{% load i18n %}
{% block tabs %}
{% include 'ipam/inc/ipaddress_edit_header.html' with active_tab='add' %}
{% endblock tabs %}
{% block form %}
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "IP Address" %}</h5>
</div>
{% render_field form.address %}
{% render_field form.status %}
{% render_field form.role %}
{% render_field form.vrf %}
{% render_field form.dns_name %}
{% render_field form.description %}
{% render_field form.tags %}
</div>
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "Tenancy" %}</h5>
</div>
{% render_field form.tenant_group %}
{% render_field form.tenant %}
</div>
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "Interface Assignment" %}</h5>
</div>
<div class="row">
<div class="col-9 offset-3">
<ul class="nav nav-pills mb-1" role="tablist">
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="device_tab" data-bs-toggle="tab" aria-controls="device" data-bs-target="#device" class="nav-link {% if not form.initial.vminterface and not form.initial.fhrpgroup %}active{% endif %}">
{% trans "Device" %}
</button>
</li>
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="vm_tab" data-bs-toggle="tab" aria-controls="vm" data-bs-target="#vm" class="nav-link {% if form.initial.vminterface %}active{% endif %}">
{% trans "Virtual Machine" %}
</button>
</li>
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="fhrpgroup_tab" data-bs-toggle="tab" aria-controls="fhrpgroup" data-bs-target="#fhrpgroup" class="nav-link {% if form.initial.fhrpgroup %}active{% endif %}">
{% trans "FHRP Group" %}
</button>
</li>
</ul>
</div>
</div>
<div class="tab-content p-0 border-0">
<div class="tab-pane {% if not form.initial.vminterface and not form.initial.fhrpgroup %}active{% endif %}" id="device" role="tabpanel" aria-labeled-by="device_tab">
{% render_field form.interface %}
</div>
<div class="tab-pane {% if form.initial.vminterface %}active{% endif %}" id="vm" role="tabpanel" aria-labeled-by="vm_tab">
{% render_field form.vminterface %}
</div>
<div class="tab-pane {% if form.initial.fhrpgroup %}active{% endif %}" id="fhrpgroup" role="tabpanel" aria-labeled-by="fhrpgroup_tab">
{% render_field form.fhrpgroup %}
</div>
{% render_field form.primary_for_parent %}
</div>
</div>
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "NAT IP (Inside" %})</h5>
</div>
<div class="row">
{% render_field form.nat_inside %}
</div>
</div>
<div class="field-group my-5">
{% render_field form.comments %}
</div>
{% if form.custom_fields %}
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "Custom Fields" %}</h5>
</div>
{% render_custom_fields form %}
</div>
{% endif %}
{% endblock %}

View File

@ -1,79 +0,0 @@
{% extends 'generic/object_edit.html' %}
{% load form_helpers %}
{% load i18n %}
{% block form %}
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "Service" %}</h5>
</div>
{# Device/VM selection #}
<div class="row">
<div class="col-9 offset-3">
<ul class="nav nav-pills mb-1" role="tablist">
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="device_tab" data-bs-toggle="tab" aria-controls="device" data-bs-target="#device" class="nav-link {% if not form.initial.virtual_machine %}active{% endif %}">
{% trans "Device" %}
</button>
</li>
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="vm_tab" data-bs-toggle="tab" aria-controls="vm" data-bs-target="#vm" class="nav-link {% if form.initial.virtual_machine %}active{% endif %}">
{% trans "Virtual Machine" %}
</button>
</li>
</ul>
</div>
</div>
<div class="tab-content p-0 border-0">
<div class="tab-pane {% if not form.initial.virtual_machine %}active{% endif %}" id="device" role="tabpanel" aria-labeled-by="device_tab">
{% render_field form.device %}
</div>
<div class="tab-pane {% if form.initial.virtual_machine %}active{% endif %}" id="vm" role="tabpanel" aria-labeled-by="vm_tab">
{% render_field form.virtual_machine %}
</div>
</div>
{# Template or custom #}
<div class="row">
<div class="col-9 offset-3">
<ul class="nav nav-pills mb-1" role="tablist">
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="template_tab" data-bs-toggle="tab" data-bs-target="#template" class="nav-link active">
{% trans "From Template" %}
</button>
</li>
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="custom_tab" data-bs-toggle="tab" data-bs-target="#custom" class="nav-link">
{% trans "Custom" %}
</button>
</li>
</ul>
</div>
</div>
<div class="tab-content p-0 border-0">
<div class="tab-pane active" id="template" role="tabpanel" aria-labeled-by="template_tab">
{% render_field form.service_template %}
</div>
<div class="tab-pane" id="custom" role="tabpanel" aria-labeled-by="custom_tab">
{% render_field form.name %}
{% render_field form.protocol %}
{% render_field form.ports %}
</div>
</div>
{% render_field form.ipaddresses %}
{% render_field form.description %}
{% render_field form.tags %}
</div>
<div class="field-group my-5">
{% render_field form.comments %}
</div>
{% if form.custom_fields %}
<div class="row">
<h5 class="col-9 offset-3">{% trans "Custom Fields" %}</h5>
</div>
{% render_custom_fields form %}
{% endif %}
{% endblock %}

View File

@ -1,66 +0,0 @@
{% extends 'generic/object_edit.html' %}
{% load form_helpers %}
{% load i18n %}
{% block form %}
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "Service" %}</h5>
</div>
<div class="row">
<div class="col-9 offset-3">
<ul class="nav nav-pills mb-1" role="tablist">
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="device_tab" data-bs-toggle="tab" aria-controls="device" data-bs-target="#device" class="nav-link {% if not form.initial.virtual_machine %}active{% endif %}">
{% trans "Device" %}
</button>
</li>
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="vm_tab" data-bs-toggle="tab" aria-controls="vm" data-bs-target="#vm" class="nav-link {% if form.initial.virtual_machine %}active{% endif %}">
{% trans "Virtual Machine" %}
</button>
</li>
</ul>
</div>
</div>
<div class="tab-content p-0 border-0">
<div class="tab-pane {% if not form.initial.virtual_machine %}active{% endif %}" id="device" role="tabpanel" aria-labeled-by="device_tab">
{% render_field form.device %}
</div>
<div class="tab-pane {% if form.initial.virtual_machine %}active{% endif %}" id="vm" role="tabpanel" aria-labeled-by="vm_tab">
{% render_field form.virtual_machine %}
</div>
</div>
{% render_field form.name %}
<div class="row">
<label class="col-sm-3 col-form-label text-lg-end">{% trans "Port(s)" %}</label>
<div class="col-3">
{{ form.protocol }}
</div>
<div class="col-6">
{{ form.ports }}
</div>
</div>
<div class="row mb-3">
<div class="col-3"></div>
<div class="col-9">
<span class="form-text">{{ form.ports.help_text }}</span>
</div>
</div>
{% render_field form.ipaddresses %}
{% render_field form.description %}
{% render_field form.tags %}
</div>
<div class="field-group my-5">
{% render_field form.comments %}
</div>
{% if form.custom_fields %}
<div class="row">
<h5 class="col-9 offset-3">{% trans "Custom Fields" %}</h5>
</div>
{% render_custom_fields form %}
{% endif %}
{% endblock %}

View File

@ -1,56 +0,0 @@
{% extends 'generic/object_edit.html' %}
{% load helpers %}
{% load form_helpers %}
{% load i18n %}
{% block form %}
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "L2VPN Termination" %}</h5>
</div>
{% render_field form.l2vpn %}
<div class="row">
<div class="col-9 offset-3">
<ul class="nav nav-pills mb-1" role="tablist">
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="vlan_tab" data-bs-toggle="tab" aria-controls="vlan" data-bs-target="#vlan" class="nav-link {% if not form.initial.interface and not form.initial.vminterface %}active{% endif %}">
{% trans "VLAN" %}
</button>
</li>
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="interface_tab" data-bs-toggle="tab" aria-controls="interface" data-bs-target="#interface" class="nav-link {% if form.initial.interface %}active{% endif %}">
{% trans "Device" %}
</button>
</li>
<li role="presentation" class="nav-item">
<button role="tab" type="button" id="vminterface_tab" data-bs-toggle="tab" aria-controls="vminterface" data-bs-target="#vminterface" class="nav-link {% if form.initial.vminterface %}active{% endif %}">
{% trans "Virtual Machine" %}
</button>
</li>
</ul>
</div>
</div>
<div class="row mb-3">
<div class="tab-content p-0 border-0">
<div class="tab-pane {% if not form.initial.interface and not form.initial.vminterface %}active{% endif %}" id="vlan" role="tabpanel" aria-labeled-by="vlan_tab">
{% render_field form.vlan %}
</div>
<div class="tab-pane {% if form.initial.interface %}active{% endif %}" id="interface" role="tabpanel" aria-labeled-by="interface_tab">
{% render_field form.interface %}
</div>
<div class="tab-pane {% if form.initial.vminterface %}active{% endif %}" id="vminterface" role="tabpanel" aria-labeled-by="vminterface_tab">
{% render_field form.vminterface %}
</div>
{% render_field form.tags %}
</div>
</div>
</div>
{% if form.custom_fields %}
<div class="field-group my-5">
<div class="row">
<h5 class="col-9 offset-3">{% trans "Custom Fields" %}</h5>
</div>
{% render_custom_fields form %}
</div>
{% endif %}
{% endblock %}

View File

@ -26,7 +26,7 @@
{% elif layout == 'inline' %} {% elif layout == 'inline' %}
{# Multiple form fields on the same line #} {# Multiple form fields on the same line #}
<div class="row mb-3"> <div class="row mb-3">
<label class="col col-form-label text-lg-end">{{ title|default:'' }}</label> <label class="col col-3 col-form-label text-lg-end">{{ title|default:'' }}</label>
{% for field in items %} {% for field in items %}
<div class="col mb-1"> <div class="col mb-1">
{{ field }} {{ field }}
@ -37,16 +37,18 @@
{% elif layout == 'tabs' %} {% elif layout == 'tabs' %}
{# Tabbed groups of fields #} {# Tabbed groups of fields #}
<div class="row offset-sm-3"> <div class="row">
<ul class="nav nav-pills mb-1" role="tablist"> <div class="col offset-3">
{% for tab in items %} <ul class="nav nav-pills mb-1" role="tablist">
<li role="presentation" class="nav-item"> {% for tab in items %}
<button role="tab" type="button" id="{{ tab.id }}_tab" data-bs-toggle="tab" aria-controls="{{ tab.id }}" data-bs-target="#{{ tab.id }}" class="nav-link {% if tab.active %}active{% endif %}"> <li role="presentation" class="nav-item">
{% trans tab.title %} <button role="tab" type="button" id="{{ tab.id }}_tab" data-bs-toggle="tab" aria-controls="{{ tab.id }}" data-bs-target="#{{ tab.id }}" class="nav-link {% if tab.active %}active{% endif %}">
</button> {% trans tab.title %}
</li> </button>
{% endfor %} </li>
</ul> {% endfor %}
</ul>
</div>
</div> </div>
<div class="tab-content p-0 border-0"> <div class="tab-content p-0 border-0">
{% for tab in items %} {% for tab in items %}

View File

@ -7,6 +7,7 @@ from ipam.models import IPAddress, RouteTarget, VLAN
from netbox.forms import NetBoxModelForm from netbox.forms import NetBoxModelForm
from tenancy.forms import TenancyForm from tenancy.forms import TenancyForm
from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
from utilities.forms.rendering import TabbedFieldGroups
from utilities.forms.utils import add_blank_choice, get_field_value from utilities.forms.utils import add_blank_choice, get_field_value
from utilities.forms.widgets import HTMXSelect from utilities.forms.widgets import HTMXSelect
from virtualization.models import VirtualMachine, VMInterface from virtualization.models import VirtualMachine, VMInterface
@ -444,6 +445,18 @@ class L2VPNTerminationForm(NetBoxModelForm):
label=_('Interface') label=_('Interface')
) )
fieldsets = (
(None, (
'l2vpn',
TabbedFieldGroups(
(_('VLAN'), 'vlan'),
(_('Device'), 'interface'),
(_('Virtual Machine'), 'vminterface'),
),
'tags',
)),
)
class Meta: class Meta:
model = L2VPNTermination model = L2VPNTermination
fields = ('l2vpn', 'tags') fields = ('l2vpn', 'tags')

View File

@ -479,7 +479,6 @@ class L2VPNTerminationView(generic.ObjectView):
class L2VPNTerminationEditView(generic.ObjectEditView): class L2VPNTerminationEditView(generic.ObjectEditView):
queryset = L2VPNTermination.objects.all() queryset = L2VPNTermination.objects.all()
form = forms.L2VPNTerminationForm form = forms.L2VPNTerminationForm
template_name = 'vpn/l2vpntermination_edit.html'
@register_model_view(L2VPNTermination, 'delete') @register_model_view(L2VPNTermination, 'delete')