diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 1c72a12dd..c1a8355db 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, + SlugField, SelectSpeedWidget, APISelectWithSelector ) from virtualization.models import Cluster, ClusterGroup from wireless.models import WirelessLAN, WirelessLANGroup @@ -441,26 +441,27 @@ 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' - } - ) + # 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 ) location = DynamicModelChoiceField( queryset=Location.objects.all(), @@ -556,7 +557,7 @@ class DeviceForm(TenancyForm, NetBoxModelForm): class Meta: model = Device fields = [ - 'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'rack', + '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' 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..bde710fe3 --- /dev/null +++ b/netbox/netbox/views/htmx.py @@ -0,0 +1,20 @@ +from django.shortcuts import render +from django.views.generic import View + +from dcim.forms import SiteFilterForm + + +class ObjectSelectorView(View): + template_name = 'htmx/object_selector.html' + + def get(self, request): + form_class = self._get_form_class() + form = form_class(request.GET) + + return render(request, self.template_name, { + 'form': form, + }) + + def _get_form_class(self): + # TODO: Determine form class from request parameters + return SiteFilterForm diff --git a/netbox/templates/dcim/device_edit.html b/netbox/templates/dcim/device_edit.html index 07e3bbdc9..15725f559 100644 --- a/netbox/templates/dcim/device_edit.html +++ b/netbox/templates/dcim/device_edit.html @@ -29,8 +29,8 @@
Location
- {% render_field form.region %} - {% render_field form.site_group %} +{# {% render_field form.region %}#} +{# {% render_field form.site_group %}#} {% render_field form.site %} {% render_field form.location %} {% render_field form.rack %} @@ -117,3 +117,7 @@ {% endblock %} + +{% block modals %} + {% include 'inc/htmx_modal.html' %} +{% endblock %} diff --git a/netbox/templates/htmx/object_selector.html b/netbox/templates/htmx/object_selector.html new file mode 100644 index 000000000..b5858ab89 --- /dev/null +++ b/netbox/templates/htmx/object_selector.html @@ -0,0 +1,11 @@ +{% load form_helpers %} + +
+ + +
diff --git a/netbox/utilities/forms/widgets.py b/netbox/utilities/forms/widgets.py index c7e1cfb81..a1613ebf7 100644 --- a/netbox/utilities/forms/widgets.py +++ b/netbox/utilities/forms/widgets.py @@ -11,6 +11,7 @@ from .utils import add_blank_choice, parse_numeric_range __all__ = ( 'APISelect', 'APISelectMultiple', + 'APISelectWithSelector', 'BulkEditNullBooleanSelect', 'ClearableFileInput', 'ColorSelect', @@ -259,6 +260,10 @@ class APISelectMultiple(APISelect, forms.SelectMultiple): self.attrs['data-multiple'] = 1 +class APISelectWithSelector(APISelect): + template_name = 'widgets/apiselect_with_selector.html' + + class DatePicker(forms.TextInput): """ Date picker using Flatpickr. diff --git a/netbox/utilities/templates/widgets/apiselect_with_selector.html b/netbox/utilities/templates/widgets/apiselect_with_selector.html new file mode 100644 index 000000000..aa9b0f62a --- /dev/null +++ b/netbox/utilities/templates/widgets/apiselect_with_selector.html @@ -0,0 +1,14 @@ +
+ {% include 'django/forms/widgets/select.html' %} + +