Leverage HTMX to simplify rendering of VLAN Form

Associated with issue #11891
This commit is contained in:
Dillon Henschen 2023-06-02 00:36:21 -04:00
parent dee4aec62d
commit 5c4e2fe04c
8 changed files with 91 additions and 74 deletions

View File

@ -155,6 +155,16 @@ class VLANStatusChoices(ChoiceSet):
]
class VLANAssignmentTypeChoices(ChoiceSet):
VLAN_GROUP = 'vlan_group'
SITE = 'site'
CHOICES = [
(VLAN_GROUP, 'VLAN Group'),
(SITE, 'Site'),
]
#
# Services
#

View File

@ -16,7 +16,8 @@ from utilities.forms.fields import (
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
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
__all__ = (
@ -633,12 +634,42 @@ class VLANForm(TenancyForm, NetBoxModelForm):
)
comments = CommentField()
fieldsets = (
('VLAN', ('vid', 'name', 'status', 'role', 'description', 'tags')),
('Tenancy', ('tenant_group', 'tenant')),
('Assignment', ('assignment_type', 'site', 'group')),
)
class Meta:
model = VLAN
fields = [
'site', 'group', 'vid', 'name', 'status', 'role', 'tenant_group', 'tenant', 'description', 'comments',
'tags',
'assignment_type', 'site', 'group', 'vid', 'name', 'status', 'role', 'tenant_group', 'tenant',
'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):

View 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),
]

View File

@ -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
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(
to='dcim.Site',
on_delete=models.PROTECT,
@ -183,7 +189,7 @@ class VLAN(PrimaryModel):
objects = VLANQuerySet.as_manager()
clone_fields = [
'site', 'group', 'tenant', 'status', 'role', 'description',
'assignment_type', 'site', 'group', 'tenant', 'status', 'role', 'description',
]
class Meta:

View File

@ -129,8 +129,8 @@ class VLANTable(TenancyColumnsMixin, NetBoxTable):
class Meta(NetBoxTable.Meta):
model = VLAN
fields = (
'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'tenant_group', 'status', 'role',
'description', 'comments', 'tags', 'l2vpn', 'created', 'last_updated',
'pk', 'id', 'vid', 'name', 'assignment_type', 'site', 'group', 'prefixes', 'tenant', 'tenant_group',
'status', 'role', 'description', 'comments', 'tags', 'l2vpn', 'created', 'last_updated',
)
default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description')
row_attrs = {

View File

@ -763,6 +763,7 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'assignment_type': VLANAssignmentTypeChoices.VLAN_GROUP,
'site': sites[1].pk,
'group': vlangroups[1].pk,
'vid': 999,

View File

@ -1120,7 +1120,6 @@ class VLANVMInterfacesView(generic.ObjectChildrenView):
class VLANEditView(generic.ObjectEditView):
queryset = VLAN.objects.all()
form = forms.VLANForm
template_name = 'ipam/vlan_edit.html'
@register_model_view(VLAN, 'delete')

View File

@ -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 %}