diff --git a/netbox/circuits/forms/model_forms.py b/netbox/circuits/forms/model_forms.py index d06f0bd9d..537db50df 100644 --- a/netbox/circuits/forms/model_forms.py +++ b/netbox/circuits/forms/model_forms.py @@ -1,7 +1,7 @@ from django.utils.translation import gettext as _ from circuits.models import * -from dcim.models import Region, Site, SiteGroup +from dcim.models import Site from ipam.models import ASN from netbox.forms import NetBoxModelForm from tenancy.forms import TenancyForm @@ -114,50 +114,22 @@ class CircuitTerminationForm(NetBoxModelForm): 'provider_id': '$provider', }, ) - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) site = DynamicModelChoiceField( queryset=Site.objects.all(), - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - }, - required=False - ) - provider_network_provider = DynamicModelChoiceField( - queryset=Provider.objects.all(), required=False, - label='Provider', - initial_params={ - 'networks': 'provider_network' - } + selector=True ) provider_network = DynamicModelChoiceField( queryset=ProviderNetwork.objects.all(), - query_params={ - 'provider_id': '$provider_network_provider', - }, - required=False + required=False, + selector=True ) class Meta: model = CircuitTermination fields = [ - 'provider', 'circuit', 'term_side', 'region', 'site_group', 'site', 'provider_network_provider', - 'provider_network', 'mark_connected', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', - 'description', 'tags', + 'provider', 'circuit', 'term_side', 'site', 'provider_network', 'mark_connected', 'port_speed', + 'upstream_speed', 'xconnect_id', 'pp_info', 'description', 'tags', ] widgets = { 'port_speed': SelectSpeedWidget(), diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index f1f392c99..13c58d02f 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -14,9 +14,9 @@ from tenancy.forms import TenancyForm from utilities.forms import ( APISelect, add_blank_choice, BootstrapMixin, ClearableFileInput, CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SelectWithPK, - SlugField, SelectSpeedWidget, + SlugField, SelectSpeedWidget ) -from virtualization.models import Cluster, ClusterGroup +from virtualization.models import Cluster from wireless.models import WirelessLAN, WirelessLANGroup from .common import InterfaceCommonForm, ModuleCommonForm @@ -157,26 +157,9 @@ class SiteForm(TenancyForm, NetBoxModelForm): class LocationForm(TenancyForm, NetBoxModelForm): - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) site = DynamicModelChoiceField( queryset=Site.objects.all(), - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } + selector=True ) parent = DynamicModelChoiceField( queryset=Location.objects.all(), @@ -188,17 +171,14 @@ class LocationForm(TenancyForm, NetBoxModelForm): slug = SlugField() fieldsets = ( - ('Location', ( - 'region', 'site_group', 'site', 'parent', 'name', 'slug', 'status', 'description', 'tags', - )), + ('Location', ('site', 'parent', 'name', 'slug', 'status', 'description', 'tags')), ('Tenancy', ('tenant_group', 'tenant')), ) class Meta: model = Location fields = ( - 'region', 'site_group', 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', - 'tags', + 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', 'tags', ) @@ -219,26 +199,9 @@ class RackRoleForm(NetBoxModelForm): class RackForm(TenancyForm, NetBoxModelForm): - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) site = DynamicModelChoiceField( queryset=Site.objects.all(), - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } + selector=True ) location = DynamicModelChoiceField( queryset=Location.objects.all(), @@ -256,48 +219,16 @@ class RackForm(TenancyForm, NetBoxModelForm): class Meta: model = Rack fields = [ - 'region', 'site_group', 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', - 'role', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', - 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', 'tags', + 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial', + 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', + 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', 'tags', ] class RackReservationForm(TenancyForm, NetBoxModelForm): - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site = DynamicModelChoiceField( - queryset=Site.objects.all(), - required=False, - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } - ) - location = DynamicModelChoiceField( - queryset=Location.objects.all(), - required=False, - query_params={ - 'site_id': '$site' - } - ) rack = DynamicModelChoiceField( queryset=Rack.objects.all(), - query_params={ - 'site_id': '$site', - 'location_id': '$location', - } + selector=True ) units = NumericArrayField( base_field=forms.IntegerField(), @@ -311,15 +242,14 @@ class RackReservationForm(TenancyForm, NetBoxModelForm): comments = CommentField() fieldsets = ( - ('Reservation', ('region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'description', 'tags')), + ('Reservation', ('rack', 'units', 'user', 'description', 'tags')), ('Tenancy', ('tenant_group', 'tenant')), ) class Meta: model = RackReservation fields = [ - 'region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'tenant_group', 'tenant', - 'description', 'comments', 'tags', + 'rack', 'units', 'user', 'tenant_group', 'tenant', 'description', 'comments', 'tags', ] @@ -441,26 +371,9 @@ class PlatformForm(NetBoxModelForm): class DeviceForm(TenancyForm, NetBoxModelForm): - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) site = DynamicModelChoiceField( queryset=Site.objects.all(), - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } + selector=True ) location = DynamicModelChoiceField( queryset=Location.objects.all(), @@ -491,43 +404,21 @@ class DeviceForm(TenancyForm, NetBoxModelForm): } ) ) - manufacturer = DynamicModelChoiceField( - queryset=Manufacturer.objects.all(), - required=False, - initial_params={ - 'device_types': '$device_type' - } - ) device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), - query_params={ - 'manufacturer_id': '$manufacturer' - } + selector=True ) device_role = DynamicModelChoiceField( queryset=DeviceRole.objects.all() ) platform = DynamicModelChoiceField( queryset=Platform.objects.all(), - required=False, - query_params={ - 'manufacturer_id': ['$manufacturer', 'null'] - } - ) - cluster_group = DynamicModelChoiceField( - queryset=ClusterGroup.objects.all(), - required=False, - null_option='None', - initial_params={ - 'clusters': '$cluster' - } + required=False ) cluster = DynamicModelChoiceField( queryset=Cluster.objects.all(), required=False, - query_params={ - 'group_id': '$cluster_group' - } + selector=True ) comments = CommentField() local_context_data = JSONField( @@ -536,7 +427,8 @@ class DeviceForm(TenancyForm, NetBoxModelForm): ) virtual_chassis = DynamicModelChoiceField( queryset=VirtualChassis.objects.all(), - required=False + required=False, + selector=True ) vc_position = forms.IntegerField( required=False, @@ -556,10 +448,10 @@ class DeviceForm(TenancyForm, NetBoxModelForm): class Meta: model = Device fields = [ - 'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'rack', - 'location', 'position', 'face', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6', - 'cluster_group', 'cluster', 'tenant_group', 'tenant', 'virtual_chassis', 'vc_position', 'vc_priority', - 'description', 'config_template', 'comments', 'tags', 'local_context_data' + 'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'location', 'position', 'face', + 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6', 'cluster', 'tenant_group', 'tenant', + 'virtual_chassis', 'vc_position', 'vc_priority', 'description', 'config_template', 'comments', 'tags', + 'local_context_data' ] def __init__(self, *args, **kwargs): @@ -632,18 +524,9 @@ class ModuleForm(ModuleCommonForm, NetBoxModelForm): 'device_id': '$device' } ) - manufacturer = DynamicModelChoiceField( - queryset=Manufacturer.objects.all(), - required=False, - initial_params={ - 'module_types': '$module_type' - } - ) module_type = DynamicModelChoiceField( queryset=ModuleType.objects.all(), - query_params={ - 'manufacturer_id': '$manufacturer' - } + selector=True ) comments = CommentField() replicate_components = forms.BooleanField( @@ -651,7 +534,6 @@ class ModuleForm(ModuleCommonForm, NetBoxModelForm): initial=True, help_text=_("Automatically populate components associated with this module type") ) - adopt_components = forms.BooleanField( required=False, initial=False, @@ -659,9 +541,7 @@ class ModuleForm(ModuleCommonForm, NetBoxModelForm): ) fieldsets = ( - ('Module', ( - 'device', 'module_bay', 'manufacturer', 'module_type', 'status', 'description', 'tags', - )), + ('Module', ('device', 'module_bay', 'module_type', 'status', 'description', 'tags')), ('Hardware', ( 'serial', 'asset_tag', 'replicate_components', 'adopt_components', )), @@ -670,8 +550,8 @@ class ModuleForm(ModuleCommonForm, NetBoxModelForm): class Meta: model = Module fields = [ - 'device', 'module_bay', 'manufacturer', 'module_type', 'status', 'serial', 'asset_tag', 'tags', - 'replicate_components', 'adopt_components', 'description', 'comments', + 'device', 'module_bay', 'module_type', 'status', 'serial', 'asset_tag', 'tags', 'replicate_components', + 'adopt_components', 'description', 'comments', ] def __init__(self, *args, **kwargs): @@ -702,26 +582,9 @@ class CableForm(TenancyForm, NetBoxModelForm): class PowerPanelForm(NetBoxModelForm): - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) site = DynamicModelChoiceField( queryset=Site.objects.all(), - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } + selector=True ) location = DynamicModelChoiceField( queryset=Location.objects.all(), @@ -733,80 +596,38 @@ class PowerPanelForm(NetBoxModelForm): comments = CommentField() fieldsets = ( - ('Power Panel', ('region', 'site_group', 'site', 'location', 'name', 'description', 'tags')), + ('Power Panel', ('site', 'location', 'name', 'description', 'tags')), ) class Meta: model = PowerPanel fields = [ - 'region', 'site_group', 'site', 'location', 'name', 'description', 'comments', 'tags', + 'site', 'location', 'name', 'description', 'comments', 'tags', ] class PowerFeedForm(NetBoxModelForm): - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites__powerpanel': '$power_panel' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site = DynamicModelChoiceField( - queryset=Site.objects.all(), - required=False, - initial_params={ - 'powerpanel': '$power_panel' - }, - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } - ) power_panel = DynamicModelChoiceField( queryset=PowerPanel.objects.all(), - query_params={ - 'site_id': '$site' - } - ) - location = DynamicModelChoiceField( - queryset=Location.objects.all(), - required=False, - query_params={ - 'site_id': '$site' - }, - initial_params={ - 'racks': '$rack' - } + selector=True ) rack = DynamicModelChoiceField( queryset=Rack.objects.all(), required=False, - query_params={ - 'location_id': '$location', - 'site_id': '$site' - } + selector=True ) comments = CommentField() fieldsets = ( - ('Power Panel', ('region', 'site', 'power_panel')), - ('Power Feed', ('location', 'rack', 'name', 'status', 'type', 'description', 'mark_connected', 'tags')), + ('Power Feed', ('power_panel', 'rack', 'name', 'status', 'type', 'description', 'mark_connected', 'tags')), ('Characteristics', ('supply', 'voltage', 'amperage', 'phase', 'max_utilization')), ) class Meta: model = PowerFeed fields = [ - 'region', 'site_group', 'site', 'power_panel', 'location', 'rack', 'name', 'status', 'type', - 'mark_connected', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization', 'description', 'comments', - 'tags', + 'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage', + 'max_utilization', 'description', 'comments', 'tags', ] @@ -878,43 +699,12 @@ class DeviceVCMembershipForm(forms.ModelForm): class VCMemberSelectForm(BootstrapMixin, forms.Form): - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site = DynamicModelChoiceField( - queryset=Site.objects.all(), - required=False, - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } - ) - rack = DynamicModelChoiceField( - queryset=Rack.objects.all(), - required=False, - null_option='None', - query_params={ - 'site_id': '$site' - } - ) device = DynamicModelChoiceField( queryset=Device.objects.all(), query_params={ - 'site_id': '$site', - 'rack_id': '$rack', 'virtual_chassis_id': 'null', - } + }, + selector=True ) def clean_device(self): @@ -1150,7 +940,8 @@ class InventoryItemTemplateForm(ComponentTemplateForm): class DeviceComponentForm(NetBoxModelForm): device = DynamicModelChoiceField( - queryset=Device.objects.all() + queryset=Device.objects.all(), + selector=True ) def __init__(self, *args, **kwargs): @@ -1592,53 +1383,9 @@ class InventoryItemRoleForm(NetBoxModelForm): class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm): - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site = DynamicModelChoiceField( - queryset=Site.objects.all(), - required=False, - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } - ) - location = DynamicModelChoiceField( - queryset=Location.objects.all(), - required=False, - query_params={ - 'site_id': '$site' - }, - initial_params={ - 'racks': '$rack' - } - ) - rack = DynamicModelChoiceField( - queryset=Rack.objects.all(), - required=False, - query_params={ - 'site_id': '$site', - 'location_id': '$location', - } - ) device = DynamicModelChoiceField( queryset=Device.objects.all(), - query_params={ - 'site_id': '$site', - 'location_id': '$location', - 'rack_id': '$rack', - } + selector=True ) primary_ip4 = DynamicModelChoiceField( queryset=IPAddress.objects.all(), @@ -1660,14 +1407,13 @@ class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm): ) fieldsets = ( - ('Assigned Device', ('region', 'site_group', 'site', 'location', 'rack', 'device')), - ('Virtual Device Context', ('name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tags')), + ('Virtual Device Context', ('device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tags')), ('Tenancy', ('tenant_group', 'tenant')) ) class Meta: model = VirtualDeviceContext fields = [ - 'region', 'site_group', 'site', 'location', 'rack', 'device', 'name', 'status', 'identifier', - 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'comments', 'tags' + 'device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', + 'comments', 'tags' ] diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index a29aabebd..3904281a8 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -200,40 +200,11 @@ class PrefixForm(TenancyForm, NetBoxModelForm): required=False, label=_('VRF') ) - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) - site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - } - ) site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, - null_option='None', - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } - ) - vlan_group = DynamicModelChoiceField( - queryset=VLANGroup.objects.all(), - required=False, - label=_('VLAN group'), - null_option='None', - query_params={ - 'site': '$site' - }, - initial_params={ - 'vlans': '$vlan' - } + selector=True, + null_option='None' ) vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), @@ -241,7 +212,6 @@ class PrefixForm(TenancyForm, NetBoxModelForm): label=_('VLAN'), query_params={ 'site_id': '$site', - 'group_id': '$vlan_group', } ) role = DynamicModelChoiceField( @@ -252,7 +222,7 @@ class PrefixForm(TenancyForm, NetBoxModelForm): fieldsets = ( ('Prefix', ('prefix', 'status', 'vrf', 'role', 'is_pool', 'mark_utilized', 'description', 'tags')), - ('Site/VLAN Assignment', ('region', 'site_group', 'site', 'vlan_group', 'vlan')), + ('Site/VLAN Assignment', ('site', 'vlan')), ('Tenancy', ('tenant_group', 'tenant')), ) @@ -329,65 +299,22 @@ class IPAddressForm(TenancyForm, NetBoxModelForm): required=False, label=_('VRF') ) - nat_region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - label=_('Region'), - initial_params={ - 'sites': '$nat_site' - } - ) - nat_site_group = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - label=_('Site group'), - initial_params={ - 'sites': '$nat_site' - } - ) - nat_site = DynamicModelChoiceField( - queryset=Site.objects.all(), - required=False, - label=_('Site'), - query_params={ - 'region_id': '$nat_region', - 'group_id': '$nat_site_group', - } - ) - nat_rack = DynamicModelChoiceField( - queryset=Rack.objects.all(), - required=False, - label=_('Rack'), - null_option='None', - query_params={ - 'site_id': '$site' - } - ) nat_device = DynamicModelChoiceField( queryset=Device.objects.all(), required=False, - label=_('Device'), - query_params={ - 'site_id': '$site', - 'rack_id': '$nat_rack', - } - ) - nat_cluster = DynamicModelChoiceField( - queryset=Cluster.objects.all(), - required=False, - label=_('Cluster') + selector=True, + label=_('Device') ) nat_virtual_machine = DynamicModelChoiceField( queryset=VirtualMachine.objects.all(), required=False, - label=_('Virtual Machine'), - query_params={ - 'cluster_id': '$nat_cluster', - } + selector=True, + label=_('Virtual Machine') ) nat_vrf = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, + selector=True, label=_('VRF') ) nat_inside = DynamicModelChoiceField( @@ -409,9 +336,8 @@ class IPAddressForm(TenancyForm, NetBoxModelForm): class Meta: model = IPAddress fields = [ - 'address', 'vrf', 'status', 'role', 'dns_name', 'primary_for_parent', 'nat_site', 'nat_rack', 'nat_device', - 'nat_cluster', 'nat_virtual_machine', 'nat_vrf', 'nat_inside', 'tenant_group', 'tenant', 'description', - 'comments', 'tags', + 'address', 'vrf', 'status', 'role', 'dns_name', 'primary_for_parent', 'nat_device', 'nat_virtual_machine', + 'nat_vrf', 'nat_inside', 'tenant_group', 'tenant', 'description', 'comments', 'tags', ] def __init__(self, *args, **kwargs): @@ -714,58 +640,18 @@ class VLANGroupForm(NetBoxModelForm): class VLANForm(TenancyForm, NetBoxModelForm): - # VLANGroup assignment fields - scope_type = forms.ChoiceField( - choices=( - ('', ''), - ('dcim.region', 'Region'), - ('dcim.sitegroup', 'Site group'), - ('dcim.site', 'Site'), - ('dcim.location', 'Location'), - ('dcim.rack', 'Rack'), - ('virtualization.clustergroup', 'Cluster group'), - ('virtualization.cluster', 'Cluster'), - ), - required=False, - label=_('Group scope') - ) group = DynamicModelChoiceField( queryset=VLANGroup.objects.all(), required=False, - query_params={ - 'scope_type': '$scope_type', - }, + selector=True, label=_('VLAN Group') ) - - # Site assignment fields - region = DynamicModelChoiceField( - queryset=Region.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - }, - label=_('Region') - ) - sitegroup = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), - required=False, - initial_params={ - 'sites': '$site' - }, - label=_('Site group') - ) site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, null_option='None', - query_params={ - 'region_id': '$region', - 'group_id': '$sitegroup', - } + selector=True ) - - # Other fields role = DynamicModelChoiceField( queryset=Role.objects.all(), required=False @@ -804,11 +690,13 @@ class ServiceTemplateForm(NetBoxModelForm): class ServiceForm(NetBoxModelForm): device = DynamicModelChoiceField( queryset=Device.objects.all(), - required=False + required=False, + selector=True ) virtual_machine = DynamicModelChoiceField( queryset=VirtualMachine.objects.all(), - required=False + required=False, + selector=True ) ports = NumericArrayField( base_field=forms.IntegerField( @@ -908,43 +796,21 @@ class L2VPNTerminationForm(NetBoxModelForm): label=_('L2VPN'), fetch_trigger='open' ) - device_vlan = DynamicModelChoiceField( - queryset=Device.objects.all(), - label=_("Available on Device"), - required=False, - query_params={} - ) vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, - query_params={ - 'available_on_device': '$device_vlan' - }, + selector=True, label=_('VLAN') ) - device = DynamicModelChoiceField( - queryset=Device.objects.all(), - required=False, - query_params={} - ) interface = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, - query_params={ - 'device_id': '$device' - } - ) - virtual_machine = DynamicModelChoiceField( - queryset=VirtualMachine.objects.all(), - required=False, - query_params={} + selector=True ) vminterface = DynamicModelChoiceField( queryset=VMInterface.objects.all(), required=False, - query_params={ - 'virtual_machine_id': '$virtual_machine' - }, + selector=True, label=_('Interface') ) @@ -958,7 +824,6 @@ class L2VPNTerminationForm(NetBoxModelForm): if instance: if type(instance.assigned_object) is Interface: - initial['device'] = instance.assigned_object.parent initial['interface'] = instance.assigned_object elif type(instance.assigned_object) is VLAN: initial['vlan'] = instance.assigned_object diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py index 22c47f7bb..8c859528c 100644 --- a/netbox/netbox/urls.py +++ b/netbox/netbox/urls.py @@ -10,7 +10,7 @@ from extras.plugins.urls import plugin_admin_patterns, plugin_patterns, plugin_a from netbox.api.views import APIRootView, StatusView from netbox.graphql.schema import schema from netbox.graphql.views import GraphQLView -from netbox.views import HomeView, StaticMediaFailureView, SearchView +from netbox.views import HomeView, StaticMediaFailureView, SearchView, htmx from users.views import LoginView, LogoutView from .admin import admin_site @@ -51,6 +51,9 @@ _patterns = [ path('virtualization/', include('virtualization.urls')), path('wireless/', include('wireless.urls')), + # HTMX views + path('htmx/object-selector/', htmx.ObjectSelectorView.as_view(), name='htmx_object_selector'), + # API path('api/', APIRootView.as_view(), name='api-root'), path('api/circuits/', include('circuits.api.urls')), diff --git a/netbox/netbox/views/htmx.py b/netbox/netbox/views/htmx.py new file mode 100644 index 000000000..04ddcb06b --- /dev/null +++ b/netbox/netbox/views/htmx.py @@ -0,0 +1,56 @@ +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist +from django.http import Http404 +from django.shortcuts import render +from django.utils.module_loading import import_string +from django.views.generic import View + + +class ObjectSelectorView(View): + template_name = 'htmx/object_selector.html' + + def get(self, request): + model = self._get_model(request.GET.get('_model', '')) + + form_class = self._get_form_class(model) + form = form_class(request.GET) + + if '_search' in request.GET: + # Return only search results + filterset = self._get_filterset_class(model) + + queryset = model.objects.restrict(request.user) + if filterset: + queryset = filterset(request.GET, queryset, request=request).qs + + return render(request, 'htmx/object_selector_results.html', { + 'results': queryset[:100], + }) + + return render(request, self.template_name, { + 'form': form, + 'model': model, + 'target_id': request.GET.get('target'), + }) + + def _get_model(self, label): + try: + app_label, model_name = label.split('.') + content_type = ContentType.objects.get_by_natural_key(app_label, model_name) + except (ValueError, ObjectDoesNotExist): + raise Http404 + return content_type.model_class() + + def _get_form_class(self, model): + if hasattr(self, 'form_class'): + return self.form_class + app_label = model._meta.app_label + class_name = f'{model.__name__}FilterForm' + return import_string(f'{app_label}.forms.{class_name}') + + def _get_filterset_class(self, model): + if hasattr(self, 'filterset_class'): + return self.filterset_class + app_label = model._meta.app_label + class_name = f'{model.__name__}FilterSet' + return import_string(f'{app_label}.filtersets.{class_name}') diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index a64ac5df7..cbb0d10e3 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 05e29a02b..d58fb8a5d 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/src/htmx.ts b/netbox/project-static/src/htmx.ts index 5b2c65d80..09d423cbd 100644 --- a/netbox/project-static/src/htmx.ts +++ b/netbox/project-static/src/htmx.ts @@ -1,9 +1,10 @@ import { getElements, isTruthy } from './util'; import { initButtons } from './buttons'; import { initSelect } from './select'; +import { initObjectSelector } from './objectSelector'; function initDepedencies(): void { - for (const init of [initButtons, initSelect]) { + for (const init of [initButtons, initSelect, initObjectSelector]) { init(); } } diff --git a/netbox/project-static/src/objectSelector.ts b/netbox/project-static/src/objectSelector.ts new file mode 100644 index 000000000..1a6c2dc4b --- /dev/null +++ b/netbox/project-static/src/objectSelector.ts @@ -0,0 +1,32 @@ +import { getElements } from './util'; + +function handleSelection(link: HTMLAnchorElement): void { + const selector_results = document.getElementById('selector_results'); + if (selector_results == null) { + return + } + const target_id = selector_results.getAttribute('data-selector-target'); + if (target_id == null) { + return + } + const target = document.getElementById(target_id); + if (target == null) { + return + } + + const label = link.getAttribute('data-label'); + const value = link.getAttribute('data-value'); + + //@ts-ignore + target.slim.setData([ + {text: label, value: value} + ]); + +} + + +export function initObjectSelector(): void { + for (const element of getElements('#selector_results a')) { + element.addEventListener('click', () => handleSelection(element)); + } +} diff --git a/netbox/templates/circuits/circuittermination_edit.html b/netbox/templates/circuits/circuittermination_edit.html index f171ecc1b..acfb8d0ca 100644 --- a/netbox/templates/circuits/circuittermination_edit.html +++ b/netbox/templates/circuits/circuittermination_edit.html @@ -27,12 +27,9 @@
- {% render_field form.region %} - {% render_field form.site_group %} {% render_field form.site %}
- {% render_field form.provider_network_provider %} {% render_field form.provider_network %}
diff --git a/netbox/templates/dcim/device_edit.html b/netbox/templates/dcim/device_edit.html index 07e3bbdc9..d0677ad20 100644 --- a/netbox/templates/dcim/device_edit.html +++ b/netbox/templates/dcim/device_edit.html @@ -18,7 +18,6 @@
Hardware
- {% render_field form.manufacturer %} {% render_field form.device_type %} {% render_field form.airflow %} {% render_field form.serial %} @@ -29,8 +28,6 @@
Location
- {% render_field form.region %} - {% render_field form.site_group %} {% render_field form.site %} {% render_field form.location %} {% render_field form.rack %} @@ -76,7 +73,6 @@
Virtualization
- {% render_field form.cluster_group %} {% render_field form.cluster %} diff --git a/netbox/templates/dcim/rack_edit.html b/netbox/templates/dcim/rack_edit.html index cd9ed637a..413feff31 100644 --- a/netbox/templates/dcim/rack_edit.html +++ b/netbox/templates/dcim/rack_edit.html @@ -6,8 +6,6 @@
Rack
- {% render_field form.region %} - {% render_field form.site_group %} {% render_field form.site %} {% render_field form.location %} {% render_field form.name %} diff --git a/netbox/templates/generic/object_edit.html b/netbox/templates/generic/object_edit.html index 8531ad6df..26ceb7987 100644 --- a/netbox/templates/generic/object_edit.html +++ b/netbox/templates/generic/object_edit.html @@ -74,3 +74,7 @@ Context: {% endblock content-wrapper %} + +{% block modals %} + {% include 'inc/htmx_modal.html' with size='lg' %} +{% endblock %} diff --git a/netbox/templates/htmx/object_selector.html b/netbox/templates/htmx/object_selector.html new file mode 100644 index 000000000..f0b6da404 --- /dev/null +++ b/netbox/templates/htmx/object_selector.html @@ -0,0 +1,32 @@ +{% load form_helpers %} + + + diff --git a/netbox/templates/htmx/object_selector_results.html b/netbox/templates/htmx/object_selector_results.html new file mode 100644 index 000000000..67529967e --- /dev/null +++ b/netbox/templates/htmx/object_selector_results.html @@ -0,0 +1,13 @@ +
+ {% for object in results %} + +
+ {{ object }} + {% if object.status %}{% badge object.get_status_display bg_color=object.get_status_color %}{% endif %} +
+ {% if object.description %} + {{ object.description }} + {% endif %} +
+ {% endfor %} +
diff --git a/netbox/templates/inc/htmx_modal.html b/netbox/templates/inc/htmx_modal.html index 771f5d595..5361fc5f7 100644 --- a/netbox/templates/inc/htmx_modal.html +++ b/netbox/templates/inc/htmx_modal.html @@ -1,5 +1,5 @@