diff --git a/netbox/core/forms/model_forms.py b/netbox/core/forms/model_forms.py index e9cc962cd..464c3eb47 100644 --- a/netbox/core/forms/model_forms.py +++ b/netbox/core/forms/model_forms.py @@ -5,7 +5,7 @@ from django import forms from core.models import * from netbox.forms import NetBoxModelForm from netbox.registry import registry -from utilities.forms import CommentField +from utilities.forms import CommentField, get_field_value __all__ = ( 'DataSourceForm', @@ -44,7 +44,7 @@ class DataSourceForm(NetBoxModelForm): ] if self.backend_fields: fieldsets.append( - ('Backend', self.backend_fields) + ('Backend Parameters', self.backend_fields) ) return fieldsets @@ -52,16 +52,11 @@ class DataSourceForm(NetBoxModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - backend_classes = registry['data_backends'] - - if self.is_bound and self.data.get('type') in backend_classes: - type_ = self.data['type'] - elif self.initial and self.initial.get('type') in backend_classes: - type_ = self.initial['type'] - else: - type_ = self.fields['type'].initial - backend = backend_classes.get(type_) + # Determine the selected backend type + backend_type = get_field_value(self, 'type') + backend = registry['data_backends'].get(backend_type) + # Add backend-specific form fields self.backend_fields = [] for name, form_field in backend.parameters.items(): field_name = f'backend_{name}' diff --git a/netbox/dcim/forms/common.py b/netbox/dcim/forms/common.py index a2243ce2d..f047d621b 100644 --- a/netbox/dcim/forms/common.py +++ b/netbox/dcim/forms/common.py @@ -3,6 +3,7 @@ from django.utils.translation import gettext as _ from dcim.choices import * from dcim.constants import * +from utilities.forms.utils import get_field_value __all__ = ( 'InterfaceCommonForm', @@ -23,6 +24,20 @@ class InterfaceCommonForm(forms.Form): label=_('MTU') ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Determine the selected 802.1Q mode + interface_mode = get_field_value(self, 'mode') + + # Delete VLAN tagging fields which are not relevant for the selected mode + if interface_mode in (InterfaceModeChoices.MODE_ACCESS, InterfaceModeChoices.MODE_TAGGED_ALL): + del self.fields['tagged_vlans'] + elif not interface_mode: + del self.fields['vlan_group'] + del self.fields['untagged_vlan'] + del self.fields['tagged_vlans'] + def clean(self): super().clean() diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 2e7ca0d4b..34f91bbe8 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1367,6 +1367,13 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): ] widgets = { 'speed': SelectSpeedWidget(), + 'mode': forms.Select( + attrs={ + 'hx-get': '.', + 'hx-include': '#form_fields input', + 'hx-target': '#form_fields', + } + ), } labels = { 'mode': '802.1Q Mode', diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 2dff8b274..1ba789cf1 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -431,6 +431,12 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView): form = self.initialize_form(request) instance = self.alter_object(self.queryset.model(), request) + # If this is an HTMX request, return only the rendered form HTML + if is_htmx(request): + return render(request, 'htmx/form.html', { + 'form': form, + }) + return render(request, self.template_name, { 'object': instance, 'form': form, diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index e5793c128..d0058eae9 100644 --- a/netbox/project-static/dist/netbox.js +++ b/netbox/project-static/dist/netbox.js @@ -1,8 +1,8 @@ -(()=>{var C_=Object.create;var Ms=Object.defineProperty,L_=Object.defineProperties,D_=Object.getOwnPropertyDescriptor,M_=Object.getOwnPropertyDescriptors,I_=Object.getOwnPropertyNames,yd=Object.getOwnPropertySymbols,N_=Object.getPrototypeOf,Ed=Object.prototype.hasOwnProperty,k_=Object.prototype.propertyIsEnumerable;var Oc=(tn,nn,en)=>nn in tn?Ms(tn,nn,{enumerable:!0,configurable:!0,writable:!0,value:en}):tn[nn]=en,Un=(tn,nn)=>{for(var en in nn||(nn={}))Ed.call(nn,en)&&Oc(tn,en,nn[en]);if(yd)for(var en of yd(nn))k_.call(nn,en)&&Oc(tn,en,nn[en]);return tn},ka=(tn,nn)=>L_(tn,M_(nn)),_d=tn=>Ms(tn,"__esModule",{value:!0});var Tn=(tn,nn)=>()=>(nn||tn((nn={exports:{}}).exports,nn),nn.exports),P_=(tn,nn)=>{_d(tn);for(var en in nn)Ms(tn,en,{get:nn[en],enumerable:!0})},R_=(tn,nn,en)=>{if(nn&&typeof nn=="object"||typeof nn=="function")for(let rn of I_(nn))!Ed.call(tn,rn)&&rn!=="default"&&Ms(tn,rn,{get:()=>nn[rn],enumerable:!(en=D_(nn,rn))||en.enumerable});return tn},yi=tn=>R_(_d(Ms(tn!=null?C_(N_(tn)):{},"default",tn&&tn.__esModule&&"default"in tn?{get:()=>tn.default,enumerable:!0}:{value:tn,enumerable:!0})),tn);var Kn=(tn,nn,en)=>(Oc(tn,typeof nn!="symbol"?nn+"":nn,en),en);var Kr=(tn,nn,en)=>new Promise((rn,on)=>{var sn=cn=>{try{ln(en.next(cn))}catch(un){on(un)}},an=cn=>{try{ln(en.throw(cn))}catch(un){on(un)}},ln=cn=>cn.done?rn(cn.value):Promise.resolve(cn.value).then(sn,an);ln((en=en.apply(tn,nn)).next())});var rp=Tn((exports,module)=>{(function(tn,nn){typeof define=="function"&&define.amd?define([],nn):tn.htmx=tn.htmx||nn()})(typeof self!="undefined"?self:exports,function(){return function(){"use strict";var U={onLoad:t,process:vt,on:X,off:F,trigger:$,ajax:nr,find:R,findAll:O,closest:N,values:function(tn,nn){var en=Pt(tn,nn||"post");return en.values},remove:q,addClass:L,removeClass:T,toggleClass:A,takeClass:H,defineExtension:fr,removeExtension:cr,logAll:C,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,inlineScriptNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:!1,scrollBehavior:"smooth",defaultFocusScroll:!1},parseInterval:v,_:e,createEventSource:function(tn){return new EventSource(tn,{withCredentials:!0})},createWebSocket:function(tn){return new WebSocket(tn,[])},version:"1.8.0"},r={addTriggerHandler:st,bodyContains:K,canAccessLocalStorage:E,filterValues:Ut,hasAttribute:o,getAttributeValue:V,getClosestMatch:h,getExpressionVars:Qt,getHeaders:Bt,getInputValues:Pt,getInternalData:W,getSwapSpecification:_t,getTriggerSpecs:Me,getTarget:re,makeFragment:g,mergeObjects:Y,makeSettleInfo:Gt,oobSwap:ae,selectAndSwap:Ee,settleImmediately:Lt,shouldCancel:je,triggerEvent:$,triggerErrorEvent:J,withExtensions:xt},n=["get","post","put","delete","patch"],i=n.map(function(tn){return"[hx-"+tn+"], [data-hx-"+tn+"]"}).join(", ");function v(tn){if(tn!=null)return tn.slice(-2)=="ms"?parseFloat(tn.slice(0,-2))||void 0:tn.slice(-1)=="s"?parseFloat(tn.slice(0,-1))*1e3||void 0:tn.slice(-1)=="m"?parseFloat(tn.slice(0,-1))*1e3*60||void 0:parseFloat(tn)||void 0}function f(tn,nn){return tn.getAttribute&&tn.getAttribute(nn)}function o(tn,nn){return tn.hasAttribute&&(tn.hasAttribute(nn)||tn.hasAttribute("data-"+nn))}function V(tn,nn){return f(tn,nn)||f(tn,"data-"+nn)}function u(tn){return tn.parentElement}function _(){return document}function h(tn,nn){for(;tn&&!nn(tn);)tn=u(tn);return tn||null}function a(tn,nn,en){var rn=V(nn,en),on=V(nn,"hx-disinherit");return tn!==nn&&on&&(on==="*"||on.split(" ").indexOf(en)>=0)?"unset":rn}function z(tn,nn){var en=null;if(h(tn,function(rn){return en=a(tn,rn,nn)}),en!=="unset")return en}function d(tn,nn){var en=tn.matches||tn.matchesSelector||tn.msMatchesSelector||tn.mozMatchesSelector||tn.webkitMatchesSelector||tn.oMatchesSelector;return en&&en.call(tn,nn)}function s(tn){var nn=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,en=nn.exec(tn);return en?en[1].toLowerCase():""}function l(tn,nn){for(var en=new DOMParser,rn=en.parseFromString(tn,"text/html"),on=rn.body;nn>0;)nn--,on=on.firstChild;return on==null&&(on=_().createDocumentFragment()),on}function g(tn){if(U.config.useTemplateFragments){var nn=l("
"+tn+"",0);return nn.querySelector("template").content}else{var en=s(tn);switch(en){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l("