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

Refactor form rendering components & add docstrings

This commit is contained in:
Jeremy Stretch
2024-03-15 12:59:42 -04:00
parent 2aaa552067
commit 3b28e8e615
7 changed files with 54 additions and 36 deletions

View File

@ -7,7 +7,7 @@ from ipam.models import ASN
from netbox.forms import NetBoxModelForm
from tenancy.forms import TenancyForm
from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
from utilities.forms.rendering import TabbedFieldGroups
from utilities.forms.rendering import TabbedGroups
from utilities.forms.widgets import DatePicker, NumberWithOptions
__all__ = (
@ -153,7 +153,7 @@ class CircuitTerminationForm(NetBoxModelForm):
'term_side',
'description',
'tags',
TabbedFieldGroups(
TabbedGroups(
(_('Site'), 'site'),
(_('Provider Network'), 'provider_network'),
),

View File

@ -16,7 +16,7 @@ from utilities.forms.fields import (
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField,
NumericArrayField, SlugField,
)
from utilities.forms.rendering import InlineFields, TabbedFieldGroups
from utilities.forms.rendering import InlineFields, TabbedGroups
from utilities.forms.widgets import APISelect, ClearableFileInput, HTMXSelect, NumberWithOptions, SelectWithPK
from virtualization.models import Cluster
from wireless.models import WirelessLAN, WirelessLANGroup
@ -237,8 +237,8 @@ class RackForm(TenancyForm, NetBoxModelForm):
'width',
'starting_unit',
'u_height',
InlineFields(_('Outer Dimensions'), 'outer_width', 'outer_depth', 'outer_unit'),
InlineFields(_('Weight'), 'weight', 'max_weight', 'weight_unit'),
InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')),
InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')),
'mounting_depth',
'desc_units',
)),
@ -1415,7 +1415,7 @@ class InventoryItemForm(DeviceComponentForm):
(_('Inventory Item'), ('device', 'parent', 'name', 'label', 'role', 'description', 'tags')),
(_('Hardware'), ('manufacturer', 'part_id', 'serial', 'asset_tag')),
(_('Component Assignment'), (
TabbedFieldGroups(
TabbedGroups(
(_('Interface'), 'interface'),
(_('Console Port'), 'consoleport'),
(_('Console Server Port'), 'consoleserverport'),

View File

@ -16,7 +16,7 @@ from utilities.forms.fields import (
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
SlugField,
)
from utilities.forms.rendering import InlineFields, ObjectAttribute, TabbedFieldGroups
from utilities.forms.rendering import InlineFields, ObjectAttribute, TabbedGroups
from utilities.forms.widgets import DatePicker
from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
@ -312,7 +312,7 @@ class IPAddressForm(TenancyForm, NetBoxModelForm):
(_('IP Address'), ('address', 'status', 'role', 'vrf', 'dns_name', 'description', 'tags')),
(_('Tenancy'), ('tenant_group', 'tenant')),
(_('Assignment'), (
TabbedFieldGroups(
TabbedGroups(
(_('Device'), 'interface'),
(_('Virtual Machine'), 'vminterface'),
(_('FHRP Group'), 'fhrpgroup'),
@ -725,12 +725,12 @@ class ServiceForm(NetBoxModelForm):
fieldsets = (
(_('Service'), (
TabbedFieldGroups(
TabbedGroups(
(_('Device'), 'device'),
(_('Virtual Machine'), 'virtual_machine'),
),
'name',
InlineFields(_('Port(s)'), 'protocol', 'ports'),
InlineFields('protocol', 'ports', label=_('Port(s)')),
'ipaddresses',
'description',
'tags',
@ -753,11 +753,11 @@ class ServiceCreateForm(ServiceForm):
fieldsets = (
(_('Service'), (
TabbedFieldGroups(
TabbedGroups(
(_('Device'), 'device'),
(_('Virtual Machine'), 'virtual_machine'),
),
TabbedFieldGroups(
TabbedGroups(
(_('From Template'), 'service_template'),
(_('Custom'), 'name', 'protocol', 'ports'),
),

View File

@ -9,8 +9,8 @@
{% endfor %}
{# Render grouped fields according to Form #}
{% for group, items in form.fieldsets %}
{% render_fieldset form items heading=group %}
{% for fieldset in form.fieldsets %}
{% render_fieldset form fieldset %}
{% endfor %}
{% if form.custom_fields %}

View File

@ -3,28 +3,39 @@ import string
from functools import cached_property
__all__ = (
'FieldSet',
'InlineFields',
'ObjectAttribute',
'TabbedFieldGroups',
'TabbedGroups',
)
class FieldGroup:
class FieldSet:
"""
A generic grouping of fields, with an optional name. Each field will be rendered
on its own row under the heading (name).
"""
def __init__(self, *fields, name=None):
self.fields = fields
self.name = name
def __init__(self, label, *field_names):
self.field_names = field_names
class InlineFields:
"""
A set of fields rendered inline (side-by-side) with a shared label; typically nested within a FieldSet.
"""
def __init__(self, *fields, label=None):
self.fields = fields
self.label = label
class InlineFields(FieldGroup):
pass
class TabbedFieldGroups:
class TabbedGroups:
"""
Two or more groups of fields (FieldSets) arranged under tabs among which the user can navigate.
"""
def __init__(self, *groups):
self.groups = [
FieldGroup(*group) for group in groups
FieldSet(*group, name=name) for name, *group in groups
]
# Initialize a random ID for the group (for tab selection)
@ -37,13 +48,15 @@ class TabbedFieldGroups:
return [
{
'id': f'{self.id}_{i}',
'title': group.label,
'fields': group.field_names,
'title': group.name,
'fields': group.fields,
} for i, group in enumerate(self.groups, start=1)
]
class ObjectAttribute:
"""
Renders the value for a specific attribute on the form's instance.
"""
def __init__(self, name):
self.name = name

View File

@ -1,6 +1,6 @@
from django import template
from utilities.forms.rendering import InlineFields, ObjectAttribute, TabbedFieldGroups
from utilities.forms.rendering import FieldSet, InlineFields, ObjectAttribute, TabbedGroups
__all__ = (
'getfield',
@ -48,24 +48,29 @@ def widget_type(field):
#
@register.inclusion_tag('form_helpers/render_fieldset.html')
def render_fieldset(form, fieldset, heading=None):
def render_fieldset(form, fieldset):
"""
Render a group set of fields.
"""
# Handle legacy tuple-based fieldset definitions, e.g. (_('Label'), ('field1, 'field2', 'field3'))
if type(fieldset) is not FieldSet:
name, fields = fieldset
fieldset = FieldSet(*fields, name=name)
rows = []
for item in fieldset:
for item in fieldset.fields:
# Multiple fields side-by-side
if type(item) is InlineFields:
fields = [
form[name] for name in item.field_names if name in form.fields
form[name] for name in item.fields if name in form.fields
]
rows.append(
('inline', item.label, fields)
)
# Tabbed groups of fields
elif type(item) is TabbedFieldGroups:
elif type(item) is TabbedGroups:
tabs = [
{
'id': tab['id'],
@ -95,7 +100,7 @@ def render_fieldset(form, fieldset, heading=None):
)
return {
'heading': heading,
'heading': fieldset.name,
'rows': rows,
}

View File

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