Coverage for extras/forms/filtersets.py: 100%
153 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1from django import forms
2from django.utils.translation import gettext_lazy as _
4from core.models import DataFile, DataSource, ObjectType
5from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
6from extras.choices import *
7from extras.models import *
8from netbox.event_rules import get_event_rule_action_choices
9from netbox.events import get_event_type_choices
10from netbox.forms import NetBoxModelFilterSetForm, PrimaryModelFilterSetForm
11from netbox.forms.mixins import OwnerFilterMixin, SavedFiltersMixin
12from tenancy.models import Tenant, TenantGroup
13from users.models import Group, User
14from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
15from utilities.forms.fields import (
16 ContentTypeChoiceField,
17 ContentTypeMultipleChoiceField,
18 DynamicModelMultipleChoiceField,
19 TagFilterField,
20)
21from utilities.forms.rendering import FieldSet
22from utilities.forms.widgets import DateTimePicker
23from virtualization.models import Cluster, ClusterGroup, ClusterType
25__all__ = (
26 'ConfigContextFilterForm',
27 'ConfigContextProfileFilterForm',
28 'ConfigTemplateFilterForm',
29 'CustomFieldChoiceSetFilterForm',
30 'CustomFieldFilterForm',
31 'CustomLinkFilterForm',
32 'EventRuleFilterForm',
33 'ExportTemplateFilterForm',
34 'ImageAttachmentFilterForm',
35 'JournalEntryFilterForm',
36 'LocalConfigContextFilterForm',
37 'NotificationGroupFilterForm',
38 'SavedFilterFilterForm',
39 'TableConfigFilterForm',
40 'TagFilterForm',
41 'WebhookFilterForm',
42)
45class CustomFieldFilterForm(OwnerFilterMixin, SavedFiltersMixin, FilterForm):
46 model = CustomField
47 fieldsets = (
48 FieldSet('q', 'filter_id'),
49 FieldSet(
50 'object_type_id', 'type', 'group_name', 'weight', 'required', 'unique', 'status', name=_('Attributes')
51 ),
52 FieldSet('choice_set_id', 'related_object_type_id', name=_('Type Options')),
53 FieldSet('ui_visible', 'ui_editable', 'is_cloneable', 'nulls_first', name=_('Behavior')),
54 FieldSet('validation_minimum', 'validation_maximum', 'validation_regex', name=_('Validation')),
55 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
56 )
57 object_type_id = ContentTypeMultipleChoiceField(
58 queryset=ObjectType.objects.with_feature('custom_fields'),
59 required=False,
60 label=_('Object types'),
61 )
62 related_object_type_id = ContentTypeMultipleChoiceField(
63 queryset=ObjectType.objects.public(),
64 required=False,
65 label=_('Related object type'),
66 )
67 type = forms.MultipleChoiceField(
68 choices=CustomFieldTypeChoices,
69 required=False,
70 label=_('Field type')
71 )
72 status = forms.ChoiceField(
73 choices=add_blank_choice(CustomFieldStatusChoices),
74 required=False,
75 label=_('Status')
76 )
77 group_name = forms.CharField(
78 label=_('Group name'),
79 required=False
80 )
81 weight = forms.IntegerField(
82 label=_('Weight'),
83 required=False
84 )
85 required = forms.NullBooleanField(
86 label=_('Required'),
87 required=False,
88 widget=forms.Select(
89 choices=BOOLEAN_WITH_BLANK_CHOICES
90 )
91 )
92 unique = forms.NullBooleanField(
93 label=_('Must be unique'),
94 required=False,
95 widget=forms.Select(
96 choices=BOOLEAN_WITH_BLANK_CHOICES
97 )
98 )
99 choice_set_id = DynamicModelMultipleChoiceField(
100 queryset=CustomFieldChoiceSet.objects.all(),
101 required=False,
102 label=_('Choice set')
103 )
104 ui_visible = forms.ChoiceField(
105 choices=add_blank_choice(CustomFieldUIVisibleChoices),
106 required=False,
107 label=_('UI visible')
108 )
109 ui_editable = forms.ChoiceField(
110 choices=add_blank_choice(CustomFieldUIEditableChoices),
111 required=False,
112 label=_('UI editable')
113 )
114 is_cloneable = forms.NullBooleanField(
115 label=_('Is cloneable'),
116 required=False,
117 widget=forms.Select(
118 choices=BOOLEAN_WITH_BLANK_CHOICES
119 )
120 )
121 nulls_first = forms.NullBooleanField(
122 label=_('Nulls first'),
123 required=False,
124 widget=forms.Select(
125 choices=BOOLEAN_WITH_BLANK_CHOICES
126 )
127 )
128 validation_minimum = forms.DecimalField(
129 label=_('Minimum value'),
130 required=False
131 )
132 validation_maximum = forms.DecimalField(
133 label=_('Maximum value'),
134 required=False
135 )
136 validation_regex = forms.CharField(
137 label=_('Validation regex'),
138 required=False
139 )
142class CustomFieldChoiceSetFilterForm(OwnerFilterMixin, SavedFiltersMixin, FilterForm):
143 model = CustomFieldChoiceSet
144 fieldsets = (
145 FieldSet('q', 'filter_id'),
146 FieldSet('base_choices', 'choice', 'choice_colors', name=_('Choices')),
147 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
148 )
149 base_choices = forms.MultipleChoiceField(
150 choices=CustomFieldChoiceSetBaseChoices,
151 required=False
152 )
153 choice = forms.CharField(
154 required=False
155 )
156 choice_colors = forms.MultipleChoiceField(
157 choices=CustomFieldChoiceColorChoices,
158 required=False,
159 label=_('Choice colors'),
160 )
163class CustomLinkFilterForm(OwnerFilterMixin, SavedFiltersMixin, FilterForm):
164 model = CustomLink
165 fieldsets = (
166 FieldSet('q', 'filter_id'),
167 FieldSet('object_type_id', 'enabled', 'new_window', 'weight', name=_('Attributes')),
168 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
169 )
170 object_type_id = ContentTypeMultipleChoiceField(
171 label=_('Object types'),
172 queryset=ObjectType.objects.with_feature('custom_links'),
173 required=False,
174 )
175 enabled = forms.NullBooleanField(
176 label=_('Enabled'),
177 required=False,
178 widget=forms.Select(
179 choices=BOOLEAN_WITH_BLANK_CHOICES
180 )
181 )
182 new_window = forms.NullBooleanField(
183 label=_('New window'),
184 required=False,
185 widget=forms.Select(
186 choices=BOOLEAN_WITH_BLANK_CHOICES
187 )
188 )
189 weight = forms.IntegerField(
190 label=_('Weight'),
191 required=False
192 )
195class ExportTemplateFilterForm(OwnerFilterMixin, SavedFiltersMixin, FilterForm):
196 model = ExportTemplate
197 fieldsets = (
198 FieldSet('q', 'filter_id', 'object_type_id'),
199 FieldSet('data_source_id', 'data_file_id', name=_('Data')),
200 FieldSet('mime_type', 'file_name', 'file_extension', 'as_attachment', name=_('Rendering')),
201 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
202 )
203 data_source_id = DynamicModelMultipleChoiceField(
204 queryset=DataSource.objects.all(),
205 required=False,
206 label=_('Data source')
207 )
208 data_file_id = DynamicModelMultipleChoiceField(
209 queryset=DataFile.objects.all(),
210 required=False,
211 label=_('Data file'),
212 query_params={
213 'source_id': '$data_source_id'
214 }
215 )
216 object_type_id = ContentTypeMultipleChoiceField(
217 queryset=ObjectType.objects.with_feature('export_templates'),
218 required=False,
219 label=_('Content types')
220 )
221 mime_type = forms.CharField(
222 required=False,
223 label=_('MIME type')
224 )
225 file_name = forms.CharField(
226 label=_('File name'),
227 required=False
228 )
229 file_extension = forms.CharField(
230 label=_('File extension'),
231 required=False
232 )
233 as_attachment = forms.NullBooleanField(
234 label=_('As attachment'),
235 required=False,
236 widget=forms.Select(
237 choices=BOOLEAN_WITH_BLANK_CHOICES
238 )
239 )
242class ImageAttachmentFilterForm(SavedFiltersMixin, FilterForm):
243 model = ImageAttachment
244 fieldsets = (
245 FieldSet('q', 'filter_id'),
246 FieldSet('object_type_id', 'name', name=_('Attributes')),
247 )
248 object_type_id = ContentTypeChoiceField(
249 label=_('Object type'),
250 queryset=ObjectType.objects.with_feature('image_attachments'),
251 required=False
252 )
253 name = forms.CharField(
254 label=_('Name'),
255 required=False
256 )
259class SavedFilterFilterForm(OwnerFilterMixin, SavedFiltersMixin, FilterForm):
260 model = SavedFilter
261 fieldsets = (
262 FieldSet('q', 'filter_id'),
263 FieldSet('object_type_id', 'enabled', 'shared', 'weight', name=_('Attributes')),
264 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
265 )
266 object_type_id = ContentTypeMultipleChoiceField(
267 label=_('Object types'),
268 queryset=ObjectType.objects.public(),
269 required=False,
270 )
271 enabled = forms.NullBooleanField(
272 label=_('Enabled'),
273 required=False,
274 widget=forms.Select(
275 choices=BOOLEAN_WITH_BLANK_CHOICES
276 )
277 )
278 shared = forms.NullBooleanField(
279 label=_('Shared'),
280 required=False,
281 widget=forms.Select(
282 choices=BOOLEAN_WITH_BLANK_CHOICES
283 )
284 )
285 weight = forms.IntegerField(
286 label=_('Weight'),
287 required=False
288 )
291class TableConfigFilterForm(SavedFiltersMixin, FilterForm):
292 model = TableConfig
293 fieldsets = (
294 FieldSet('q', 'filter_id'),
295 FieldSet('object_type_id', 'enabled', 'shared', 'weight', name=_('Attributes')),
296 )
297 object_type_id = ContentTypeMultipleChoiceField(
298 label=_('Object types'),
299 queryset=ObjectType.objects.public(),
300 required=False
301 )
302 enabled = forms.NullBooleanField(
303 label=_('Enabled'),
304 required=False,
305 widget=forms.Select(
306 choices=BOOLEAN_WITH_BLANK_CHOICES
307 )
308 )
309 shared = forms.NullBooleanField(
310 label=_('Shared'),
311 required=False,
312 widget=forms.Select(
313 choices=BOOLEAN_WITH_BLANK_CHOICES
314 )
315 )
316 weight = forms.IntegerField(
317 label=_('Weight'),
318 required=False
319 )
322class WebhookFilterForm(OwnerFilterMixin, NetBoxModelFilterSetForm):
323 model = Webhook
324 fieldsets = (
325 FieldSet('q', 'filter_id', 'tag'),
326 FieldSet(
327 'payload_url', 'http_method', 'http_content_type', 'timeout__gte', 'timeout__lte', name=_('Attributes')
328 ),
329 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
330 )
331 http_content_type = forms.CharField(
332 label=_('HTTP content type'),
333 required=False
334 )
335 payload_url = forms.CharField(
336 label=_('Payload URL'),
337 required=False
338 )
339 http_method = forms.MultipleChoiceField(
340 choices=WebhookHttpMethodChoices,
341 required=False,
342 label=_('HTTP method')
343 )
344 timeout__gte = forms.IntegerField(
345 required=False,
346 min_value=1,
347 label=_('Minimum timeout (seconds)')
348 )
349 timeout__lte = forms.IntegerField(
350 required=False,
351 min_value=1,
352 label=_('Maximum timeout (seconds)')
353 )
354 tag = TagFilterField(model)
357class EventRuleFilterForm(OwnerFilterMixin, NetBoxModelFilterSetForm):
358 model = EventRule
359 fieldsets = (
360 FieldSet('q', 'filter_id', 'tag'),
361 FieldSet(
362 'object_type_id', 'event_type', 'action_type', 'action_is_available', 'enabled', name=_('Attributes')
363 ),
364 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
365 )
366 object_type_id = ContentTypeMultipleChoiceField(
367 queryset=ObjectType.objects.with_feature('event_rules'),
368 required=False,
369 label=_('Object type')
370 )
371 event_type = forms.MultipleChoiceField(
372 choices=get_event_type_choices,
373 required=False,
374 label=_('Event type')
375 )
376 action_type = forms.ChoiceField(
377 # Wrapped in a callable so the registry is read on each access, rather than frozen at the
378 # time this module is first imported (see EventRule.action_type).
379 choices=lambda: add_blank_choice(get_event_rule_action_choices()),
380 required=False,
381 label=_('Action type')
382 )
383 action_is_available = forms.NullBooleanField(
384 label=_('Action available'),
385 required=False,
386 widget=forms.Select(
387 choices=BOOLEAN_WITH_BLANK_CHOICES
388 )
389 )
390 enabled = forms.NullBooleanField(
391 label=_('Enabled'),
392 required=False,
393 widget=forms.Select(
394 choices=BOOLEAN_WITH_BLANK_CHOICES
395 )
396 )
397 tag = TagFilterField(model)
400class TagFilterForm(OwnerFilterMixin, SavedFiltersMixin, FilterForm):
401 model = Tag
402 fieldsets = (
403 FieldSet('q', 'filter_id'),
404 FieldSet('content_type_id', 'for_object_type_id', name=_('Attributes')),
405 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
406 )
407 content_type_id = ContentTypeMultipleChoiceField(
408 queryset=ObjectType.objects.with_feature('tags'),
409 required=False,
410 label=_('Tagged object type')
411 )
412 for_object_type_id = ContentTypeChoiceField(
413 queryset=ObjectType.objects.with_feature('tags'),
414 required=False,
415 label=_('Allowed object type')
416 )
419class ConfigContextProfileFilterForm(PrimaryModelFilterSetForm):
420 model = ConfigContextProfile
421 fieldsets = (
422 FieldSet('q', 'filter_id'),
423 FieldSet('data_source_id', 'data_file_id', name=_('Data')),
424 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
425 )
426 data_source_id = DynamicModelMultipleChoiceField(
427 queryset=DataSource.objects.all(),
428 required=False,
429 label=_('Data source')
430 )
431 data_file_id = DynamicModelMultipleChoiceField(
432 queryset=DataFile.objects.all(),
433 required=False,
434 label=_('Data file'),
435 query_params={
436 'source_id': '$data_source_id'
437 }
438 )
441class ConfigContextFilterForm(OwnerFilterMixin, SavedFiltersMixin, FilterForm):
442 model = ConfigContext
443 fieldsets = (
444 FieldSet('q', 'filter_id', 'tag_id'),
445 FieldSet('profile_id', name=_('Config Context')),
446 FieldSet('data_source_id', 'data_file_id', name=_('Data')),
447 FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')),
448 FieldSet('device_type_id', 'platform_id', 'device_role_id', name=_('Device')),
449 FieldSet('cluster_type_id', 'cluster_group_id', 'cluster_id', name=_('Cluster')),
450 FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
451 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
452 )
453 profile_id = DynamicModelMultipleChoiceField(
454 queryset=ConfigContextProfile.objects.all(),
455 required=False,
456 label=_('Profile')
457 )
458 data_source_id = DynamicModelMultipleChoiceField(
459 queryset=DataSource.objects.all(),
460 required=False,
461 label=_('Data source')
462 )
463 data_file_id = DynamicModelMultipleChoiceField(
464 queryset=DataFile.objects.all(),
465 required=False,
466 label=_('Data file'),
467 query_params={
468 'source_id': '$data_source_id'
469 }
470 )
471 region_id = DynamicModelMultipleChoiceField(
472 queryset=Region.objects.all(),
473 required=False,
474 label=_('Regions')
475 )
476 site_group_id = DynamicModelMultipleChoiceField(
477 queryset=SiteGroup.objects.all(),
478 required=False,
479 label=_('Site groups')
480 )
481 site_id = DynamicModelMultipleChoiceField(
482 queryset=Site.objects.all(),
483 required=False,
484 label=_('Sites')
485 )
486 location_id = DynamicModelMultipleChoiceField(
487 queryset=Location.objects.all(),
488 required=False,
489 label=_('Locations')
490 )
491 device_type_id = DynamicModelMultipleChoiceField(
492 queryset=DeviceType.objects.all(),
493 required=False,
494 label=_('Device types')
495 )
496 device_role_id = DynamicModelMultipleChoiceField(
497 queryset=DeviceRole.objects.all(),
498 required=False,
499 label=_('Roles')
500 )
501 platform_id = DynamicModelMultipleChoiceField(
502 queryset=Platform.objects.all(),
503 required=False,
504 label=_('Platforms')
505 )
506 cluster_type_id = DynamicModelMultipleChoiceField(
507 queryset=ClusterType.objects.all(),
508 required=False,
509 label=_('Cluster types')
510 )
511 cluster_group_id = DynamicModelMultipleChoiceField(
512 queryset=ClusterGroup.objects.all(),
513 required=False,
514 label=_('Cluster groups')
515 )
516 cluster_id = DynamicModelMultipleChoiceField(
517 queryset=Cluster.objects.all(),
518 required=False,
519 label=_('Clusters')
520 )
521 tenant_group_id = DynamicModelMultipleChoiceField(
522 queryset=TenantGroup.objects.all(),
523 required=False,
524 label=_('Tenant groups')
525 )
526 tenant_id = DynamicModelMultipleChoiceField(
527 queryset=Tenant.objects.all(),
528 required=False,
529 label=_('Tenant')
530 )
531 tag_id = DynamicModelMultipleChoiceField(
532 queryset=Tag.objects.all(),
533 required=False,
534 label=_('Tags')
535 )
538class ConfigTemplateFilterForm(OwnerFilterMixin, SavedFiltersMixin, FilterForm):
539 model = ConfigTemplate
540 fieldsets = (
541 FieldSet('q', 'filter_id', 'tag'),
542 FieldSet('data_source_id', 'data_file_id', 'auto_sync_enabled', name=_('Data')),
543 FieldSet('mime_type', 'file_name', 'file_extension', 'as_attachment', 'debug', name=_('Rendering')),
544 FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
545 )
546 data_source_id = DynamicModelMultipleChoiceField(
547 queryset=DataSource.objects.all(),
548 required=False,
549 label=_('Data source')
550 )
551 data_file_id = DynamicModelMultipleChoiceField(
552 queryset=DataFile.objects.all(),
553 required=False,
554 label=_('Data file'),
555 query_params={
556 'source_id': '$data_source_id'
557 }
558 )
559 auto_sync_enabled = forms.NullBooleanField(
560 label=_('Auto sync enabled'),
561 required=False,
562 widget=forms.Select(
563 choices=BOOLEAN_WITH_BLANK_CHOICES
564 )
565 )
566 tag = TagFilterField(ConfigTemplate)
567 mime_type = forms.CharField(
568 required=False,
569 label=_('MIME type')
570 )
571 file_name = forms.CharField(
572 label=_('File name'),
573 required=False
574 )
575 file_extension = forms.CharField(
576 label=_('File extension'),
577 required=False
578 )
579 as_attachment = forms.NullBooleanField(
580 label=_('As attachment'),
581 required=False,
582 widget=forms.Select(
583 choices=BOOLEAN_WITH_BLANK_CHOICES
584 )
585 )
586 debug = forms.NullBooleanField(
587 label=_('Debug'),
588 required=False,
589 widget=forms.Select(
590 choices=BOOLEAN_WITH_BLANK_CHOICES
591 )
592 )
595class LocalConfigContextFilterForm(forms.Form):
596 local_context_data = forms.NullBooleanField(
597 required=False,
598 label=_('Has local config context data'),
599 widget=forms.Select(
600 choices=BOOLEAN_WITH_BLANK_CHOICES
601 )
602 )
605class JournalEntryFilterForm(NetBoxModelFilterSetForm):
606 model = JournalEntry
607 fieldsets = (
608 FieldSet('q', 'filter_id', 'tag'),
609 FieldSet('created_before', 'created_after', 'created_by_id', name=_('Creation')),
610 FieldSet('assigned_object_type_id', 'kind', name=_('Attributes')),
611 )
612 created_after = forms.DateTimeField(
613 required=False,
614 label=_('After'),
615 widget=DateTimePicker()
616 )
617 created_before = forms.DateTimeField(
618 required=False,
619 label=_('Before'),
620 widget=DateTimePicker()
621 )
622 created_by_id = DynamicModelMultipleChoiceField(
623 queryset=User.objects.all(),
624 required=False,
625 label=_('User')
626 )
627 assigned_object_type_id = ContentTypeMultipleChoiceField(
628 queryset=ObjectType.objects.with_feature('journaling'),
629 required=False,
630 label=_('Object Type'),
631 )
632 kind = forms.ChoiceField(
633 label=_('Kind'),
634 choices=add_blank_choice(JournalEntryKindChoices),
635 required=False
636 )
637 tag = TagFilterField(model)
640class NotificationGroupFilterForm(SavedFiltersMixin, FilterForm):
641 model = NotificationGroup
642 user_id = DynamicModelMultipleChoiceField(
643 queryset=User.objects.all(),
644 required=False,
645 label=_('User')
646 )
647 group_id = DynamicModelMultipleChoiceField(
648 queryset=Group.objects.all(),
649 required=False,
650 label=_('Group')
651 )