diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index c1a8355db..38c4c2cfd 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -14,7 +14,7 @@ from tenancy.forms import TenancyForm from utilities.forms import ( APISelect, add_blank_choice, BootstrapMixin, ClearableFileInput, CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SelectWithPK, - SlugField, SelectSpeedWidget, APISelectWithSelector + SlugField, SelectSpeedWidget ) from virtualization.models import Cluster, ClusterGroup from wireless.models import WirelessLAN, WirelessLANGroup @@ -441,27 +441,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', - }, - widget=APISelectWithSelector + with_selector=True ) location = DynamicModelChoiceField( queryset=Location.objects.all(), @@ -492,43 +474,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' - } + with_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' - } + with_selector=True ) comments = CommentField() local_context_data = JSONField( @@ -537,7 +497,8 @@ class DeviceForm(TenancyForm, NetBoxModelForm): ) virtual_chassis = DynamicModelChoiceField( queryset=VirtualChassis.objects.all(), - required=False + required=False, + with_selector=True ) vc_position = forms.IntegerField( required=False, @@ -557,10 +518,10 @@ class DeviceForm(TenancyForm, NetBoxModelForm): class Meta: model = Device fields = [ - 'name', 'device_role', 'device_type', 'serial', 'asset_tag', '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): diff --git a/netbox/netbox/views/htmx.py b/netbox/netbox/views/htmx.py index f0f54658c..2c38402ff 100644 --- a/netbox/netbox/views/htmx.py +++ b/netbox/netbox/views/htmx.py @@ -1,22 +1,23 @@ +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 -from dcim.filtersets import SiteFilterSet -from dcim.forms import SiteFilterForm -from dcim.models import Site - class ObjectSelectorView(View): template_name = 'htmx/object_selector.html' def get(self, request): - form_class = self._get_form_class() + 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 - model = self._get_model() - filterset = self._get_filterset_class() + filterset = self._get_filterset_class(model) queryset = model.objects.restrict(request.user) if filterset: @@ -28,16 +29,27 @@ class ObjectSelectorView(View): return render(request, self.template_name, { 'form': form, + 'model': model, }) - def _get_model(self): - # TODO: Determine model from request parameters - return Site + 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): - # TODO: Determine form class from model - return SiteFilterForm + 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): - # TODO: Determine filterset class from model - return SiteFilterSet + 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/templates/dcim/device_edit.html b/netbox/templates/dcim/device_edit.html index ba81dfa13..4475dac9b 100644 --- a/netbox/templates/dcim/device_edit.html +++ b/netbox/templates/dcim/device_edit.html @@ -18,7 +18,6 @@