Coverage for extras/graphql/filters.py: 85%

223 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 18:35 +0000

1from typing import TYPE_CHECKING, Annotated 

2 

3import strawberry 

4import strawberry_django 

5from django.db.models import Q, QuerySet 

6from strawberry.scalars import ID 

7from strawberry_django import BaseFilterLookup, DatetimeFilterLookup, FilterLookup, StrFilterLookup 

8 

9from extras import models 

10from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin 

11from netbox.graphql.filter_mixins import SyncedDataFilterMixin 

12from netbox.graphql.filters import BaseModelFilter, ChangeLoggedModelFilter, PrimaryModelFilter, register_filter 

13 

14if TYPE_CHECKING: 14 ↛ 15line 14 didn't jump to line 15 because the condition on line 14 was never true

15 from core.graphql.filters import ContentTypeFilter 

16 from dcim.graphql.filters import ( 

17 DeviceRoleFilter, 

18 DeviceTypeFilter, 

19 LocationFilter, 

20 PlatformFilter, 

21 RegionFilter, 

22 SiteFilter, 

23 SiteGroupFilter, 

24 ) 

25 from extras.graphql.filter_lookups import ExtraChoicesLookup 

26 from netbox.graphql.enums import ColorEnum 

27 from netbox.graphql.filter_lookups import ( 

28 BigIntegerLookup, 

29 FloatLookup, 

30 IntegerLookup, 

31 JSONFilter, 

32 StringArrayLookup, 

33 TreeNodeFilter, 

34 ) 

35 from tenancy.graphql.filters import TenantFilter, TenantGroupFilter 

36 from users.graphql.filters import GroupFilter, UserFilter 

37 from virtualization.graphql.filters import ClusterFilter, ClusterGroupFilter, ClusterTypeFilter 

38 

39 from .enums import * 

40 

41__all__ = ( 

42 'ConfigContextFilter', 

43 'ConfigContextProfileFilter', 

44 'ConfigTemplateFilter', 

45 'CustomFieldChoiceSetFilter', 

46 'CustomFieldFilter', 

47 'CustomLinkFilter', 

48 'EventRuleFilter', 

49 'ExportTemplateFilter', 

50 'ImageAttachmentFilter', 

51 'JournalEntryFilter', 

52 'NotificationFilter', 

53 'NotificationGroupFilter', 

54 'SavedFilterFilter', 

55 'SubscriptionFilter', 

56 'TableConfigFilter', 

57 'TagFilter', 

58 'WebhookFilter', 

59) 

60 

61 

62@register_filter(models.ConfigContext, lookups=True) 

63class ConfigContextFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter): 

64 name: StrFilterLookup | None = strawberry_django.filter_field() 

65 weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

66 strawberry_django.filter_field() 

67 ) 

68 description: StrFilterLookup | None = strawberry_django.filter_field() 

69 is_active: FilterLookup[bool] | None = strawberry_django.filter_field() 

70 regions: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( 

71 strawberry_django.filter_field() 

72 ) 

73 region_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

74 strawberry_django.filter_field() 

75 ) 

76 site_groups: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( 

77 strawberry_django.filter_field() 

78 ) 

79 site_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

80 strawberry_django.filter_field() 

81 ) 

82 sites: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() 

83 locations: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( 

84 strawberry_django.filter_field() 

85 ) 

86 device_types: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( 

87 strawberry_django.filter_field() 

88 ) 

89 roles: Annotated['DeviceRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( 

90 strawberry_django.filter_field() 

91 ) 

92 platforms: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( 

93 strawberry_django.filter_field() 

94 ) 

95 cluster_types: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( 

96 strawberry_django.filter_field() 

97 ) 

98 cluster_groups: Annotated['ClusterGroupFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( 

99 strawberry_django.filter_field() 

100 ) 

101 clusters: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( 

102 strawberry_django.filter_field() 

103 ) 

104 tenant_groups: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( 

105 strawberry_django.filter_field() 

106 ) 

107 tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

108 strawberry_django.filter_field() 

109 ) 

110 tenants: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( 

111 strawberry_django.filter_field() 

112 ) 

113 tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field() 

114 data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

115 strawberry_django.filter_field() 

116 ) 

117 

118 

119@register_filter(models.ConfigContextProfile, lookups=True) 

120class ConfigContextProfileFilter(SyncedDataFilterMixin, PrimaryModelFilter): 

121 name: StrFilterLookup | None = strawberry_django.filter_field() 

122 description: StrFilterLookup | None = strawberry_django.filter_field() 

123 tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field() 

124 

125 

126@register_filter(models.ConfigTemplate, lookups=True) 

127class ConfigTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter): 

128 name: StrFilterLookup | None = strawberry_django.filter_field() 

129 description: StrFilterLookup | None = strawberry_django.filter_field() 

130 template_code: StrFilterLookup | None = strawberry_django.filter_field() 

131 environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

132 strawberry_django.filter_field() 

133 ) 

134 mime_type: StrFilterLookup | None = strawberry_django.filter_field() 

135 file_name: StrFilterLookup | None = strawberry_django.filter_field() 

136 file_extension: StrFilterLookup | None = strawberry_django.filter_field() 

137 as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field() 

138 

139 

140@register_filter(models.CustomField, lookups=True) 

141class CustomFieldFilter(ChangeLoggedModelFilter): 

142 type: BaseFilterLookup[Annotated['CustomFieldTypeEnum', strawberry.lazy('extras.graphql.enums')]] | None = ( 

143 strawberry_django.filter_field() 

144 ) 

145 object_types: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( 

146 strawberry_django.filter_field() 

147 ) 

148 related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( 

149 strawberry_django.filter_field() 

150 ) 

151 name: StrFilterLookup | None = strawberry_django.filter_field() 

152 status: BaseFilterLookup[Annotated['CustomFieldStatusEnum', strawberry.lazy('extras.graphql.enums')]] | None = ( 

153 strawberry_django.filter_field() 

154 ) 

155 label: StrFilterLookup | None = strawberry_django.filter_field() 

156 group_name: StrFilterLookup | None = strawberry_django.filter_field() 

157 description: StrFilterLookup | None = strawberry_django.filter_field() 

158 required: FilterLookup[bool] | None = strawberry_django.filter_field() 

159 unique: FilterLookup[bool] | None = strawberry_django.filter_field() 

160 search_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

161 strawberry_django.filter_field() 

162 ) 

163 filter_logic: ( 

164 BaseFilterLookup[Annotated['CustomFieldFilterLogicEnum', strawberry.lazy('extras.graphql.enums')]] | None 

165 ) = ( 

166 strawberry_django.filter_field() 

167 ) 

168 default: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

169 strawberry_django.filter_field() 

170 ) 

171 related_object_filter: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

172 strawberry_django.filter_field() 

173 ) 

174 weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

175 strawberry_django.filter_field() 

176 ) 

177 validation_minimum: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

178 strawberry_django.filter_field() 

179 ) 

180 validation_maximum: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

181 strawberry_django.filter_field() 

182 ) 

183 validation_regex: StrFilterLookup | None = strawberry_django.filter_field() 

184 choice_set: Annotated['CustomFieldChoiceSetFilter', strawberry.lazy('extras.graphql.filters')] | None = ( 

185 strawberry_django.filter_field() 

186 ) 

187 choice_set_id: ID | None = strawberry_django.filter_field() 

188 ui_visible: ( 

189 BaseFilterLookup[Annotated['CustomFieldUIVisibleEnum', strawberry.lazy('extras.graphql.enums')]] | None 

190 ) = ( 

191 strawberry_django.filter_field() 

192 ) 

193 ui_editable: ( 

194 BaseFilterLookup[Annotated['CustomFieldUIEditableEnum', strawberry.lazy('extras.graphql.enums')]] | None 

195 ) = ( 

196 strawberry_django.filter_field() 

197 ) 

198 is_cloneable: FilterLookup[bool] | None = strawberry_django.filter_field() 

199 nulls_first: FilterLookup[bool] | None = strawberry_django.filter_field() 

200 comments: StrFilterLookup | None = strawberry_django.filter_field() 

201 

202 

203@register_filter(models.CustomFieldChoiceSet, lookups=True) 

204class CustomFieldChoiceSetFilter(ChangeLoggedModelFilter): 

205 name: StrFilterLookup | None = strawberry_django.filter_field() 

206 description: StrFilterLookup | None = strawberry_django.filter_field() 

207 base_choices: ( 

208 BaseFilterLookup[Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')]] | None 

209 ) = ( 

210 strawberry_django.filter_field() 

211 ) 

212 extra_choices: Annotated['ExtraChoicesLookup', strawberry.lazy('extras.graphql.filter_lookups')] | None = ( 

213 strawberry_django.filter_field() 

214 ) 

215 order_alphabetically: FilterLookup[bool] | None = strawberry_django.filter_field() 

216 

217 @strawberry_django.filter_field(resolve_value=True) 

218 def choice_colors( 

219 self, 

220 queryset: QuerySet, 

221 value: list[Annotated['CustomFieldChoiceColorEnum', strawberry.lazy('extras.graphql.enums')]], 

222 prefix: str, 

223 ) -> tuple[QuerySet, Q]: 

224 if not value: 

225 return queryset, Q() 

226 

227 field_name = f'{prefix}choice_colors' 

228 choice_values = set() 

229 

230 for mapping in queryset.exclude(**{f'{field_name}__isnull': True}).values_list(field_name, flat=True): 

231 if isinstance(mapping, dict): 

232 choice_values.update(mapping.keys()) 

233 

234 if not choice_values: 

235 return queryset, Q(pk__in=[]) 

236 

237 params = Q() 

238 for color in value: 

239 for choice_value in choice_values: 

240 params |= Q(**{f'{field_name}__contains': {choice_value: color}}) 

241 

242 return queryset, params 

243 

244 

245@register_filter(models.CustomLink, lookups=True) 

246class CustomLinkFilter(ChangeLoggedModelFilter): 

247 name: StrFilterLookup | None = strawberry_django.filter_field() 

248 enabled: FilterLookup[bool] | None = strawberry_django.filter_field() 

249 link_text: StrFilterLookup | None = strawberry_django.filter_field() 

250 link_url: StrFilterLookup | None = strawberry_django.filter_field() 

251 weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

252 strawberry_django.filter_field() 

253 ) 

254 group_name: StrFilterLookup | None = strawberry_django.filter_field() 

255 button_class: ( 

256 BaseFilterLookup[Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')]] | None 

257 ) = ( 

258 strawberry_django.filter_field() 

259 ) 

260 new_window: FilterLookup[bool] | None = strawberry_django.filter_field() 

261 

262 

263@register_filter(models.ExportTemplate, lookups=True) 

264class ExportTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter): 

265 name: StrFilterLookup | None = strawberry_django.filter_field() 

266 description: StrFilterLookup | None = strawberry_django.filter_field() 

267 template_code: StrFilterLookup | None = strawberry_django.filter_field() 

268 environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

269 strawberry_django.filter_field() 

270 ) 

271 mime_type: StrFilterLookup | None = strawberry_django.filter_field() 

272 file_name: StrFilterLookup | None = strawberry_django.filter_field() 

273 file_extension: StrFilterLookup | None = strawberry_django.filter_field() 

274 as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field() 

275 

276 

277@register_filter(models.ImageAttachment, lookups=True) 

278class ImageAttachmentFilter(ChangeLoggedModelFilter): 

279 object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( 

280 strawberry_django.filter_field() 

281 ) 

282 object_id: ID | None = strawberry_django.filter_field() 

283 image_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

284 strawberry_django.filter_field() 

285 ) 

286 image_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

287 strawberry_django.filter_field() 

288 ) 

289 image_size: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

290 strawberry_django.filter_field() 

291 ) 

292 name: StrFilterLookup | None = strawberry_django.filter_field() 

293 

294 

295@register_filter(models.JournalEntry, lookups=True) 

296class JournalEntryFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter): 

297 assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( 

298 strawberry_django.filter_field() 

299 ) 

300 assigned_object_type_id: ID | None = strawberry_django.filter_field() 

301 assigned_object_id: ID | None = strawberry_django.filter_field() 

302 created_by: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = ( 

303 strawberry_django.filter_field() 

304 ) 

305 kind: BaseFilterLookup[Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')]] | None = ( 

306 strawberry_django.filter_field() 

307 ) 

308 comments: StrFilterLookup | None = strawberry_django.filter_field() 

309 

310 

311@register_filter(models.Notification, lookups=True) 

312class NotificationFilter(BaseModelFilter): 

313 created: DatetimeFilterLookup | None = strawberry_django.filter_field() 

314 read: DatetimeFilterLookup | None = strawberry_django.filter_field() 

315 user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() 

316 user_id: ID | None = strawberry_django.filter_field() 

317 object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( 

318 strawberry_django.filter_field() 

319 ) 

320 object_type_id: ID | None = strawberry_django.filter_field() 

321 object_id: ID | None = strawberry_django.filter_field() 

322 object_repr: StrFilterLookup | None = strawberry_django.filter_field() 

323 event_type: StrFilterLookup | None = strawberry_django.filter_field() 

324 

325 

326@register_filter(models.NotificationGroup, lookups=True) 

327class NotificationGroupFilter(ChangeLoggedModelFilter): 

328 name: StrFilterLookup | None = strawberry_django.filter_field() 

329 description: StrFilterLookup | None = strawberry_django.filter_field() 

330 groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() 

331 users: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() 

332 

333 

334@register_filter(models.SavedFilter, lookups=True) 

335class SavedFilterFilter(ChangeLoggedModelFilter): 

336 name: StrFilterLookup | None = strawberry_django.filter_field() 

337 slug: StrFilterLookup | None = strawberry_django.filter_field() 

338 description: StrFilterLookup | None = strawberry_django.filter_field() 

339 user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() 

340 user_id: ID | None = strawberry_django.filter_field() 

341 weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

342 strawberry_django.filter_field() 

343 ) 

344 enabled: FilterLookup[bool] | None = strawberry_django.filter_field() 

345 shared: FilterLookup[bool] | None = strawberry_django.filter_field() 

346 parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

347 strawberry_django.filter_field() 

348 ) 

349 

350 

351@register_filter(models.Subscription, lookups=True) 

352class SubscriptionFilter(BaseModelFilter): 

353 created: DatetimeFilterLookup | None = strawberry_django.filter_field() 

354 user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() 

355 user_id: ID | None = strawberry_django.filter_field() 

356 object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( 

357 strawberry_django.filter_field() 

358 ) 

359 object_type_id: ID | None = strawberry_django.filter_field() 

360 object_id: ID | None = strawberry_django.filter_field() 

361 

362 

363@register_filter(models.TableConfig, lookups=True) 

364class TableConfigFilter(ChangeLoggedModelFilter): 

365 name: StrFilterLookup | None = strawberry_django.filter_field() 

366 description: StrFilterLookup | None = strawberry_django.filter_field() 

367 user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() 

368 user_id: ID | None = strawberry_django.filter_field() 

369 weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

370 strawberry_django.filter_field() 

371 ) 

372 enabled: FilterLookup[bool] | None = strawberry_django.filter_field() 

373 shared: FilterLookup[bool] | None = strawberry_django.filter_field() 

374 

375 

376@register_filter(models.Tag, lookups=True) 

377class TagFilter(ChangeLoggedModelFilter): 

378 name: StrFilterLookup | None = strawberry_django.filter_field() 

379 slug: StrFilterLookup | None = strawberry_django.filter_field() 

380 color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = ( 

381 strawberry_django.filter_field() 

382 ) 

383 description: StrFilterLookup | None = strawberry_django.filter_field() 

384 

385 

386@register_filter(models.Webhook, lookups=True) 

387class WebhookFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter): 

388 name: StrFilterLookup | None = strawberry_django.filter_field() 

389 description: StrFilterLookup | None = strawberry_django.filter_field() 

390 payload_url: StrFilterLookup | None = strawberry_django.filter_field() 

391 http_method: ( 

392 BaseFilterLookup[Annotated['WebhookHttpMethodEnum', strawberry.lazy('extras.graphql.enums')]] | None 

393 ) = ( 

394 strawberry_django.filter_field() 

395 ) 

396 http_content_type: StrFilterLookup | None = strawberry_django.filter_field() 

397 additional_headers: StrFilterLookup | None = strawberry_django.filter_field() 

398 body_template: StrFilterLookup | None = strawberry_django.filter_field() 

399 secret: StrFilterLookup | None = strawberry_django.filter_field() 

400 ssl_verification: FilterLookup[bool] | None = strawberry_django.filter_field() 

401 ca_file_path: StrFilterLookup | None = strawberry_django.filter_field() 

402 timeout: FilterLookup[int] | None = strawberry_django.filter_field() 

403 events: Annotated['EventRuleFilter', strawberry.lazy('extras.graphql.filters')] | None = ( 

404 strawberry_django.filter_field() 

405 ) 

406 

407 

408@register_filter(models.EventRule, lookups=True) 

409class EventRuleFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter): 

410 name: StrFilterLookup | None = strawberry_django.filter_field() 

411 description: StrFilterLookup | None = strawberry_django.filter_field() 

412 event_types: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

413 strawberry_django.filter_field() 

414 ) 

415 enabled: FilterLookup[bool] | None = strawberry_django.filter_field() 

416 conditions: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

417 strawberry_django.filter_field() 

418 ) 

419 action_type: BaseFilterLookup[Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')]] | None = ( 

420 strawberry_django.filter_field() 

421 ) 

422 action_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( 

423 strawberry_django.filter_field() 

424 ) 

425 action_object_type_id: ID | None = strawberry_django.filter_field() 

426 action_object_id: ID | None = strawberry_django.filter_field() 

427 action_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( 

428 strawberry_django.filter_field() 

429 ) 

430 comments: StrFilterLookup | None = strawberry_django.filter_field()