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:
@ -7,7 +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.rendering import TabbedGroups
|
||||||
from utilities.forms.widgets import DatePicker, NumberWithOptions
|
from utilities.forms.widgets import DatePicker, NumberWithOptions
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -153,7 +153,7 @@ class CircuitTerminationForm(NetBoxModelForm):
|
|||||||
'term_side',
|
'term_side',
|
||||||
'description',
|
'description',
|
||||||
'tags',
|
'tags',
|
||||||
TabbedFieldGroups(
|
TabbedGroups(
|
||||||
(_('Site'), 'site'),
|
(_('Site'), 'site'),
|
||||||
(_('Provider Network'), 'provider_network'),
|
(_('Provider Network'), 'provider_network'),
|
||||||
),
|
),
|
||||||
|
@ -16,7 +16,7 @@ from utilities.forms.fields import (
|
|||||||
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField,
|
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField,
|
||||||
NumericArrayField, SlugField,
|
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 utilities.forms.widgets import APISelect, ClearableFileInput, HTMXSelect, NumberWithOptions, SelectWithPK
|
||||||
from virtualization.models import Cluster
|
from virtualization.models import Cluster
|
||||||
from wireless.models import WirelessLAN, WirelessLANGroup
|
from wireless.models import WirelessLAN, WirelessLANGroup
|
||||||
@ -237,8 +237,8 @@ class RackForm(TenancyForm, NetBoxModelForm):
|
|||||||
'width',
|
'width',
|
||||||
'starting_unit',
|
'starting_unit',
|
||||||
'u_height',
|
'u_height',
|
||||||
InlineFields(_('Outer Dimensions'), 'outer_width', 'outer_depth', 'outer_unit'),
|
InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')),
|
||||||
InlineFields(_('Weight'), 'weight', 'max_weight', 'weight_unit'),
|
InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')),
|
||||||
'mounting_depth',
|
'mounting_depth',
|
||||||
'desc_units',
|
'desc_units',
|
||||||
)),
|
)),
|
||||||
@ -1415,7 +1415,7 @@ class InventoryItemForm(DeviceComponentForm):
|
|||||||
(_('Inventory Item'), ('device', 'parent', 'name', 'label', 'role', 'description', 'tags')),
|
(_('Inventory Item'), ('device', 'parent', 'name', 'label', 'role', 'description', 'tags')),
|
||||||
(_('Hardware'), ('manufacturer', 'part_id', 'serial', 'asset_tag')),
|
(_('Hardware'), ('manufacturer', 'part_id', 'serial', 'asset_tag')),
|
||||||
(_('Component Assignment'), (
|
(_('Component Assignment'), (
|
||||||
TabbedFieldGroups(
|
TabbedGroups(
|
||||||
(_('Interface'), 'interface'),
|
(_('Interface'), 'interface'),
|
||||||
(_('Console Port'), 'consoleport'),
|
(_('Console Port'), 'consoleport'),
|
||||||
(_('Console Server Port'), 'consoleserverport'),
|
(_('Console Server Port'), 'consoleserverport'),
|
||||||
|
@ -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 InlineFields, ObjectAttribute, TabbedFieldGroups
|
from utilities.forms.rendering import InlineFields, ObjectAttribute, TabbedGroups
|
||||||
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
|
||||||
|
|
||||||
@ -312,7 +312,7 @@ class IPAddressForm(TenancyForm, NetBoxModelForm):
|
|||||||
(_('IP Address'), ('address', 'status', 'role', 'vrf', 'dns_name', 'description', 'tags')),
|
(_('IP Address'), ('address', 'status', 'role', 'vrf', 'dns_name', 'description', 'tags')),
|
||||||
(_('Tenancy'), ('tenant_group', 'tenant')),
|
(_('Tenancy'), ('tenant_group', 'tenant')),
|
||||||
(_('Assignment'), (
|
(_('Assignment'), (
|
||||||
TabbedFieldGroups(
|
TabbedGroups(
|
||||||
(_('Device'), 'interface'),
|
(_('Device'), 'interface'),
|
||||||
(_('Virtual Machine'), 'vminterface'),
|
(_('Virtual Machine'), 'vminterface'),
|
||||||
(_('FHRP Group'), 'fhrpgroup'),
|
(_('FHRP Group'), 'fhrpgroup'),
|
||||||
@ -725,12 +725,12 @@ class ServiceForm(NetBoxModelForm):
|
|||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(_('Service'), (
|
(_('Service'), (
|
||||||
TabbedFieldGroups(
|
TabbedGroups(
|
||||||
(_('Device'), 'device'),
|
(_('Device'), 'device'),
|
||||||
(_('Virtual Machine'), 'virtual_machine'),
|
(_('Virtual Machine'), 'virtual_machine'),
|
||||||
),
|
),
|
||||||
'name',
|
'name',
|
||||||
InlineFields(_('Port(s)'), 'protocol', 'ports'),
|
InlineFields('protocol', 'ports', label=_('Port(s)')),
|
||||||
'ipaddresses',
|
'ipaddresses',
|
||||||
'description',
|
'description',
|
||||||
'tags',
|
'tags',
|
||||||
@ -753,11 +753,11 @@ class ServiceCreateForm(ServiceForm):
|
|||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(_('Service'), (
|
(_('Service'), (
|
||||||
TabbedFieldGroups(
|
TabbedGroups(
|
||||||
(_('Device'), 'device'),
|
(_('Device'), 'device'),
|
||||||
(_('Virtual Machine'), 'virtual_machine'),
|
(_('Virtual Machine'), 'virtual_machine'),
|
||||||
),
|
),
|
||||||
TabbedFieldGroups(
|
TabbedGroups(
|
||||||
(_('From Template'), 'service_template'),
|
(_('From Template'), 'service_template'),
|
||||||
(_('Custom'), 'name', 'protocol', 'ports'),
|
(_('Custom'), 'name', 'protocol', 'ports'),
|
||||||
),
|
),
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{# Render grouped fields according to Form #}
|
{# Render grouped fields according to Form #}
|
||||||
{% for group, items in form.fieldsets %}
|
{% for fieldset in form.fieldsets %}
|
||||||
{% render_fieldset form items heading=group %}
|
{% render_fieldset form fieldset %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if form.custom_fields %}
|
{% if form.custom_fields %}
|
||||||
|
@ -3,28 +3,39 @@ import string
|
|||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
'FieldSet',
|
||||||
'InlineFields',
|
'InlineFields',
|
||||||
'ObjectAttribute',
|
'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
|
self.label = label
|
||||||
|
|
||||||
|
|
||||||
class InlineFields(FieldGroup):
|
class TabbedGroups:
|
||||||
pass
|
"""
|
||||||
|
Two or more groups of fields (FieldSets) arranged under tabs among which the user can navigate.
|
||||||
|
"""
|
||||||
class TabbedFieldGroups:
|
|
||||||
|
|
||||||
def __init__(self, *groups):
|
def __init__(self, *groups):
|
||||||
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)
|
# Initialize a random ID for the group (for tab selection)
|
||||||
@ -37,13 +48,15 @@ class TabbedFieldGroups:
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'id': f'{self.id}_{i}',
|
'id': f'{self.id}_{i}',
|
||||||
'title': group.label,
|
'title': group.name,
|
||||||
'fields': group.field_names,
|
'fields': group.fields,
|
||||||
} for i, group in enumerate(self.groups, start=1)
|
} for i, group in enumerate(self.groups, start=1)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ObjectAttribute:
|
class ObjectAttribute:
|
||||||
|
"""
|
||||||
|
Renders the value for a specific attribute on the form's instance.
|
||||||
|
"""
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django import template
|
from django import template
|
||||||
|
|
||||||
from utilities.forms.rendering import InlineFields, ObjectAttribute, TabbedFieldGroups
|
from utilities.forms.rendering import FieldSet, InlineFields, ObjectAttribute, TabbedGroups
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'getfield',
|
'getfield',
|
||||||
@ -48,24 +48,29 @@ def widget_type(field):
|
|||||||
#
|
#
|
||||||
|
|
||||||
@register.inclusion_tag('form_helpers/render_fieldset.html')
|
@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.
|
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 = []
|
rows = []
|
||||||
for item in fieldset:
|
for item in fieldset.fields:
|
||||||
|
|
||||||
# Multiple fields side-by-side
|
# Multiple fields side-by-side
|
||||||
if type(item) is InlineFields:
|
if type(item) is InlineFields:
|
||||||
fields = [
|
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(
|
rows.append(
|
||||||
('inline', item.label, fields)
|
('inline', item.label, fields)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Tabbed groups of fields
|
# Tabbed groups of fields
|
||||||
elif type(item) is TabbedFieldGroups:
|
elif type(item) is TabbedGroups:
|
||||||
tabs = [
|
tabs = [
|
||||||
{
|
{
|
||||||
'id': tab['id'],
|
'id': tab['id'],
|
||||||
@ -95,7 +100,7 @@ def render_fieldset(form, fieldset, heading=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'heading': heading,
|
'heading': fieldset.name,
|
||||||
'rows': rows,
|
'rows': rows,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +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.rendering import TabbedGroups
|
||||||
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
|
||||||
@ -448,7 +448,7 @@ class L2VPNTerminationForm(NetBoxModelForm):
|
|||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, (
|
(None, (
|
||||||
'l2vpn',
|
'l2vpn',
|
||||||
TabbedFieldGroups(
|
TabbedGroups(
|
||||||
(_('VLAN'), 'vlan'),
|
(_('VLAN'), 'vlan'),
|
||||||
(_('Device'), 'interface'),
|
(_('Device'), 'interface'),
|
||||||
(_('Virtual Machine'), 'vminterface'),
|
(_('Virtual Machine'), 'vminterface'),
|
||||||
|
Reference in New Issue
Block a user