11from django import forms
2- from django . contrib . auth .models import User
2+ from users .models import User
33from django .utils .translation import gettext as _
44from extras .choices import DurationChoices
5- from extras .forms .mixins import SavedFiltersMixin
5+ from netbox .forms .mixins import SavedFiltersMixin
66from netbox .forms import NetBoxModelFilterSetForm , NetBoxModelForm
77from tenancy .models import Tenant
8- from utilities .forms import BootstrapMixin , FilterForm
8+ from utilities .forms import FilterForm
99from utilities .forms .fields import DynamicModelChoiceField , DynamicModelMultipleChoiceField , TagFilterField
1010from utilities .forms .widgets import APISelectMultiple , DateTimePicker , NumberWithOptions
11- from utilities .utils import local_now
11+ from utilities .datetime import local_now
12+ from utilities .forms .rendering import FieldSet
1213
1314from .choices import ScriptExecutionStatusChoices
1415from .models import ScriptExecution , ScriptInstance
@@ -57,20 +58,18 @@ class ScriptInstanceFilterForm(NetBoxModelFilterSetForm):
5758
5859class ScriptExecutionFilterForm (SavedFiltersMixin , FilterForm ):
5960 fieldsets = (
60- (None , ("q" , "filter_id" )),
61- (
62- "Creation" ,
63- (
64- "created__before" ,
65- "created__after" ,
66- "scheduled__before" ,
67- "scheduled__after" ,
68- "started__before" ,
69- "started__after" ,
70- "completed__before" ,
71- "completed__after" ,
72- "user" ,
73- ),
61+ FieldSet ("q" , "filter_id" , name = "Query Filters" ),
62+ FieldSet (
63+ "created__before" ,
64+ "created__after" ,
65+ "scheduled__before" ,
66+ "scheduled__after" ,
67+ "started__before" ,
68+ "started__after" ,
69+ "completed__before" ,
70+ "completed__after" ,
71+ "user" ,
72+ name = "Creation and Timing" ,
7473 ),
7574 )
7675 model = ScriptExecution
@@ -94,7 +93,7 @@ class ScriptExecutionFilterForm(SavedFiltersMixin, FilterForm):
9493 )
9594
9695
97- class ScriptForm (BootstrapMixin , forms .Form ):
96+ class ScriptForm (forms .Form ):
9897 default_renderer = forms .renderers .DjangoTemplates ()
9998
10099 _commit = forms .BooleanField (
@@ -126,6 +125,20 @@ def __init__(self, *args, scheduling_enabled=True, **kwargs):
126125 now = local_now ().strftime ("%Y-%m-%d %H:%M:%S" )
127126 self .fields ["_schedule_at" ].help_text += f" (current time: <strong>{ now } </strong>)"
128127
128+ # Stupid workaround for the insanely hacky netbox core code mentioned in #16293
129+ # We can't use the default form renderer because it messes up checkboxes,
130+ # so we have to manually inject the form-control class into all input fields.
131+ for field in self .fields .values ():
132+ widget_type = type (field .widget )
133+ if issubclass (widget_type , forms .widgets .Input ) or issubclass (widget_type , forms .widgets .Textarea ):
134+ if hasattr (field .widget , "input_type" ) and field .widget .input_type == "checkbox" :
135+ continue
136+
137+ if "class" in field .widget .attrs :
138+ field .widget .attrs ["class" ] += " form-control"
139+ else :
140+ field .widget .attrs ["class" ] = "form-control"
141+
129142 # Remove scheduling fields if scheduling is disabled
130143 if not scheduling_enabled :
131144 self .fields .pop ("_schedule_at" )
0 commit comments