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

1from django import forms 

2from django.utils.translation import gettext_lazy as _ 

3 

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 

24 

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) 

43 

44 

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 ) 

140 

141 

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 ) 

161 

162 

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 ) 

193 

194 

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 ) 

240 

241 

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 ) 

257 

258 

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 ) 

289 

290 

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 ) 

320 

321 

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) 

355 

356 

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) 

398 

399 

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 ) 

417 

418 

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 ) 

439 

440 

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 ) 

536 

537 

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 ) 

593 

594 

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 ) 

603 

604 

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) 

638 

639 

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 )