mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-26 09:16:10 -06:00
Leverage HTMX to simplify rendering of VLAN Form
Associated with issue #11891
This commit is contained in:
parent
dee4aec62d
commit
5c4e2fe04c
@ -155,6 +155,16 @@ class VLANStatusChoices(ChoiceSet):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class VLANAssignmentTypeChoices(ChoiceSet):
|
||||||
|
VLAN_GROUP = 'vlan_group'
|
||||||
|
SITE = 'site'
|
||||||
|
|
||||||
|
CHOICES = [
|
||||||
|
(VLAN_GROUP, 'VLAN Group'),
|
||||||
|
(SITE, 'Site'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Services
|
# Services
|
||||||
#
|
#
|
||||||
|
@ -16,7 +16,8 @@ from utilities.forms.fields import (
|
|||||||
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
|
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
|
||||||
SlugField,
|
SlugField,
|
||||||
)
|
)
|
||||||
from utilities.forms.widgets import DatePicker
|
from utilities.forms.utils import get_field_value
|
||||||
|
from utilities.forms.widgets import DatePicker, HTMXSelect
|
||||||
from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
|
from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -633,12 +634,42 @@ class VLANForm(TenancyForm, NetBoxModelForm):
|
|||||||
)
|
)
|
||||||
comments = CommentField()
|
comments = CommentField()
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('VLAN', ('vid', 'name', 'status', 'role', 'description', 'tags')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
('Assignment', ('assignment_type', 'site', 'group')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VLAN
|
model = VLAN
|
||||||
fields = [
|
fields = [
|
||||||
'site', 'group', 'vid', 'name', 'status', 'role', 'tenant_group', 'tenant', 'description', 'comments',
|
'assignment_type', 'site', 'group', 'vid', 'name', 'status', 'role', 'tenant_group', 'tenant',
|
||||||
'tags',
|
'description', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
|
widgets = {
|
||||||
|
'assignment_type': HTMXSelect(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
assignment_type = get_field_value(self, 'assignment_type')
|
||||||
|
|
||||||
|
if assignment_type != VLANAssignmentTypeChoices.VLAN_GROUP:
|
||||||
|
del self.fields['group']
|
||||||
|
if assignment_type != VLANAssignmentTypeChoices.SITE:
|
||||||
|
del self.fields['site']
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
super().clean()
|
||||||
|
|
||||||
|
assignment_type = self.cleaned_data.get('assignment_type')
|
||||||
|
|
||||||
|
if assignment_type != VLANAssignmentTypeChoices.VLAN_GROUP:
|
||||||
|
self.cleaned_data['group'] = None
|
||||||
|
|
||||||
|
if assignment_type != VLANAssignmentTypeChoices.SITE:
|
||||||
|
self.cleaned_data['site'] = None
|
||||||
|
|
||||||
|
|
||||||
class ServiceTemplateForm(NetBoxModelForm):
|
class ServiceTemplateForm(NetBoxModelForm):
|
||||||
|
37
netbox/ipam/migrations/0067_vlan_assignment_type.py
Normal file
37
netbox/ipam/migrations/0067_vlan_assignment_type.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import sys
|
||||||
|
from django.db import migrations, models
|
||||||
|
from ipam.choices import VLANAssignmentTypeChoices
|
||||||
|
|
||||||
|
|
||||||
|
def populate_assignment_type_field(apps, schema_editor):
|
||||||
|
VLAN = apps.get_model('ipam', 'VLAN')
|
||||||
|
|
||||||
|
total_count = VLAN.objects.count()
|
||||||
|
if 'test' not in sys.argv:
|
||||||
|
print(f'\nUpdating {total_count} VLANs...')
|
||||||
|
|
||||||
|
for row in VLAN.objects.all():
|
||||||
|
if row.group:
|
||||||
|
row.assignment_type = VLANAssignmentTypeChoices.VLAN_GROUP
|
||||||
|
elif row.site:
|
||||||
|
row.assignment_type = VLANAssignmentTypeChoices.SITE
|
||||||
|
else:
|
||||||
|
# Assign the default value if nothing else matches
|
||||||
|
row.assignment_type = VLANAssignmentTypeChoices.VLAN_GROUP
|
||||||
|
row.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('ipam', '0066_iprange_mark_utilized'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='vlan',
|
||||||
|
name='assignment_type',
|
||||||
|
field=models.CharField(default='vlan_group', max_length=50),
|
||||||
|
),
|
||||||
|
migrations.RunPython(populate_assignment_type_field),
|
||||||
|
]
|
@ -124,6 +124,12 @@ class VLAN(PrimaryModel):
|
|||||||
Like Prefixes, each VLAN is assigned an operational status and optionally a user-defined Role. A VLAN can have zero
|
Like Prefixes, each VLAN is assigned an operational status and optionally a user-defined Role. A VLAN can have zero
|
||||||
or more Prefixes assigned to it.
|
or more Prefixes assigned to it.
|
||||||
"""
|
"""
|
||||||
|
assignment_type = models.CharField(
|
||||||
|
verbose_name='Assignment Type',
|
||||||
|
max_length=50,
|
||||||
|
choices=VLANAssignmentTypeChoices,
|
||||||
|
default=VLANAssignmentTypeChoices.VLAN_GROUP
|
||||||
|
)
|
||||||
site = models.ForeignKey(
|
site = models.ForeignKey(
|
||||||
to='dcim.Site',
|
to='dcim.Site',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
@ -183,7 +189,7 @@ class VLAN(PrimaryModel):
|
|||||||
objects = VLANQuerySet.as_manager()
|
objects = VLANQuerySet.as_manager()
|
||||||
|
|
||||||
clone_fields = [
|
clone_fields = [
|
||||||
'site', 'group', 'tenant', 'status', 'role', 'description',
|
'assignment_type', 'site', 'group', 'tenant', 'status', 'role', 'description',
|
||||||
]
|
]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -129,8 +129,8 @@ class VLANTable(TenancyColumnsMixin, NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = VLAN
|
model = VLAN
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'tenant_group', 'status', 'role',
|
'pk', 'id', 'vid', 'name', 'assignment_type', 'site', 'group', 'prefixes', 'tenant', 'tenant_group',
|
||||||
'description', 'comments', 'tags', 'l2vpn', 'created', 'last_updated',
|
'status', 'role', 'description', 'comments', 'tags', 'l2vpn', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description')
|
default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description')
|
||||||
row_attrs = {
|
row_attrs = {
|
||||||
|
@ -763,6 +763,7 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||||
|
|
||||||
cls.form_data = {
|
cls.form_data = {
|
||||||
|
'assignment_type': VLANAssignmentTypeChoices.VLAN_GROUP,
|
||||||
'site': sites[1].pk,
|
'site': sites[1].pk,
|
||||||
'group': vlangroups[1].pk,
|
'group': vlangroups[1].pk,
|
||||||
'vid': 999,
|
'vid': 999,
|
||||||
|
@ -1120,7 +1120,6 @@ class VLANVMInterfacesView(generic.ObjectChildrenView):
|
|||||||
class VLANEditView(generic.ObjectEditView):
|
class VLANEditView(generic.ObjectEditView):
|
||||||
queryset = VLAN.objects.all()
|
queryset = VLAN.objects.all()
|
||||||
form = forms.VLANForm
|
form = forms.VLANForm
|
||||||
template_name = 'ipam/vlan_edit.html'
|
|
||||||
|
|
||||||
|
|
||||||
@register_model_view(VLAN, 'delete')
|
@register_model_view(VLAN, 'delete')
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
{% extends 'generic/object_edit.html' %}
|
|
||||||
{% load static %}
|
|
||||||
{% load form_helpers %}
|
|
||||||
{% load helpers %}
|
|
||||||
|
|
||||||
{% block form %}
|
|
||||||
<div class="field-group my-5">
|
|
||||||
<div class="row mb-2">
|
|
||||||
<h5 class="offset-sm-3">VLAN</h5>
|
|
||||||
</div>
|
|
||||||
{% render_field form.vid %}
|
|
||||||
{% render_field form.name %}
|
|
||||||
{% render_field form.status %}
|
|
||||||
{% render_field form.role %}
|
|
||||||
{% render_field form.description %}
|
|
||||||
{% render_field form.tags %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-group my-5">
|
|
||||||
<div class="row mb-2">
|
|
||||||
<h5 class="offset-sm-3">Tenancy</h5>
|
|
||||||
</div>
|
|
||||||
{% render_field form.tenant_group %}
|
|
||||||
{% render_field form.tenant %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-group my-5">
|
|
||||||
<div class="row mb-2">
|
|
||||||
<h5 class="offset-sm-3">Assignment</h5>
|
|
||||||
</div>
|
|
||||||
{% with site_tab_active=form.initial.site %}
|
|
||||||
<div class="row mb-2">
|
|
||||||
<div class="offset-sm-3">
|
|
||||||
<ul class="nav nav-pills" role="tablist">
|
|
||||||
<li class="nav-item" role="presentation">
|
|
||||||
<a class="nav-link{% if not site_tab_active %} active{% endif %}" href="#group" role="tab" data-bs-toggle="tab">VLAN Group</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation">
|
|
||||||
<a class="nav-link{% if site_tab_active %} active{% endif %}" href="#site" role="tab" data-bs-toggle="tab">Site</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tab-content p-0 border-0">
|
|
||||||
<div class="tab-pane{% if not site_tab_active %} active{% endif %}" id="group">
|
|
||||||
{% render_field form.group %}
|
|
||||||
</div>
|
|
||||||
<div class="tab-pane{% if site_tab_active %} active{% endif %}" id="site">
|
|
||||||
{% render_field form.site %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endwith %}
|
|
||||||
</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 mb-2">
|
|
||||||
<h5 class="offset-sm-3">Custom Fields</h5>
|
|
||||||
</div>
|
|
||||||
{% render_custom_fields form %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
Loading…
Reference in New Issue
Block a user