nautobot 3.0.0a2__py3-none-any.whl → 3.0.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (659) hide show
  1. nautobot/apps/choices.py +4 -2
  2. nautobot/apps/filters.py +7 -9
  3. nautobot/apps/models.py +2 -2
  4. nautobot/apps/ui.py +13 -1
  5. nautobot/apps/utils.py +8 -0
  6. nautobot/circuits/filters.py +3 -2
  7. nautobot/circuits/navigation.py +3 -2
  8. nautobot/circuits/templates/circuits/circuit_create.html +3 -3
  9. nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
  10. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
  11. nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
  12. nautobot/circuits/tests/integration/test_circuit.py +10 -13
  13. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
  14. nautobot/circuits/views.py +6 -2
  15. nautobot/cloud/filters.py +1 -1
  16. nautobot/cloud/navigation.py +3 -2
  17. nautobot/core/api/schema.py +1 -1
  18. nautobot/core/api/serializers.py +6 -1
  19. nautobot/core/api/urls.py +2 -0
  20. nautobot/core/api/views.py +12 -0
  21. nautobot/core/apps/__init__.py +11 -10
  22. nautobot/core/celery/__init__.py +3 -5
  23. nautobot/core/checks.py +46 -0
  24. nautobot/core/choices.py +1 -1
  25. nautobot/core/cli/bootstrap_v3_to_v5.py +105 -13
  26. nautobot/core/cli/migrate_deprecated_templates.py +227 -0
  27. nautobot/core/constants.py +3 -0
  28. nautobot/core/context_processors.py +9 -1
  29. nautobot/core/filters.py +4 -0
  30. nautobot/core/forms/__init__.py +2 -0
  31. nautobot/core/forms/forms.py +1 -1
  32. nautobot/core/forms/widgets.py +21 -2
  33. nautobot/core/jobs/__init__.py +62 -3
  34. nautobot/core/jobs/groups.py +31 -1
  35. nautobot/core/management/commands/generate_test_data.py +28 -9
  36. nautobot/core/models/__init__.py +11 -0
  37. nautobot/core/models/generics.py +9 -1
  38. nautobot/core/models/tree_queries.py +10 -5
  39. nautobot/core/models/utils.py +1 -1
  40. nautobot/core/settings.py +35 -19
  41. nautobot/core/settings.yaml +17 -33
  42. nautobot/core/signals.py +12 -1
  43. nautobot/core/tables.py +13 -6
  44. nautobot/core/templates/40x.html +1 -1
  45. nautobot/core/templates/500.html +2 -2
  46. nautobot/core/templates/admin/base.html +1 -2
  47. nautobot/core/templates/admin/change_list.html +9 -12
  48. nautobot/core/templates/admin/config/config.html +12 -12
  49. nautobot/core/templates/admin/index.html +3 -3
  50. nautobot/core/templates/base_django.html +1 -2
  51. nautobot/core/templates/buttons/export.html +1 -1
  52. nautobot/core/templates/components/button/dropdown.html +5 -3
  53. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
  54. nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
  55. nautobot/core/templates/components/panel/panel.html +3 -3
  56. nautobot/core/templates/components/tab/content_wrapper.html +6 -7
  57. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
  58. nautobot/core/templates/echarts/echarts.html +22 -9
  59. nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
  60. nautobot/core/templates/generic/object_bulk_create.html +6 -5
  61. nautobot/core/templates/generic/object_bulk_delete.html +1 -1
  62. nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
  63. nautobot/core/templates/generic/object_bulk_edit.html +1 -1
  64. nautobot/core/templates/generic/object_bulk_import.html +1 -1
  65. nautobot/core/templates/generic/object_bulk_remove.html +2 -2
  66. nautobot/core/templates/generic/object_bulk_update.html +5 -4
  67. nautobot/core/templates/generic/object_create.html +5 -4
  68. nautobot/core/templates/generic/object_delete.html +1 -1
  69. nautobot/core/templates/generic/object_detail.html +1 -1
  70. nautobot/core/templates/generic/object_edit.html +1 -1
  71. nautobot/core/templates/generic/object_import.html +2 -1
  72. nautobot/core/templates/generic/object_list.html +12 -4
  73. nautobot/core/templates/generic/object_notes.html +5 -3
  74. nautobot/core/templates/generic/object_retrieve.html +4 -5
  75. nautobot/core/templates/graphene/graphiql.html +7 -8
  76. nautobot/core/templates/home.html +1 -1
  77. nautobot/core/templates/import_success.html +2 -1
  78. nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
  79. nautobot/core/templates/inc/created_updated.html +7 -3
  80. nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
  81. nautobot/core/templates/inc/footer.html +3 -1
  82. nautobot/core/templates/inc/form_static_field.html +6 -0
  83. nautobot/core/templates/inc/header.html +11 -1
  84. nautobot/core/templates/inc/image_attachments.html +2 -1
  85. nautobot/core/templates/inc/media.html +14 -0
  86. nautobot/core/templates/inc/nav_menu.html +3 -9
  87. nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
  88. nautobot/core/templates/inc/search_panel.html +4 -4
  89. nautobot/core/templates/login.html +4 -2
  90. nautobot/core/templates/nautobot_config.py.j2 +6 -11
  91. nautobot/core/templates/redoc_ui.html +7 -0
  92. nautobot/core/templates/rest_framework/api.html +103 -2
  93. nautobot/core/templates/search.html +1 -1
  94. nautobot/core/templates/swagger_ui.html +17 -3
  95. nautobot/core/templates/system_jobs/import_objects.html +1 -2
  96. nautobot/core/templates/utilities/confirmation_form.html +2 -2
  97. nautobot/core/templates/utilities/obj_table.html +10 -2
  98. nautobot/core/templates/utilities/render_field.html +7 -7
  99. nautobot/core/templates/utilities/render_jinja2.html +2 -2
  100. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +37 -4
  101. nautobot/core/templates/utilities/theme_preview.html +19 -3
  102. nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
  103. nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
  104. nautobot/core/templatetags/helpers.py +76 -18
  105. nautobot/core/testing/api.py +68 -9
  106. nautobot/core/testing/filters.py +0 -23
  107. nautobot/core/testing/integration.py +41 -17
  108. nautobot/core/testing/mixins.py +2 -0
  109. nautobot/core/testing/utils.py +18 -4
  110. nautobot/core/testing/views.py +104 -13
  111. nautobot/core/tests/integration/test_app_home.py +34 -30
  112. nautobot/core/tests/integration/test_app_navbar.py +3 -0
  113. nautobot/core/tests/integration/test_filters.py +48 -11
  114. nautobot/core/tests/integration/test_theme.py +22 -21
  115. nautobot/core/tests/nautobot_config.py +3 -0
  116. nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
  117. nautobot/core/tests/runner.py +8 -1
  118. nautobot/core/tests/test_api.py +5 -3
  119. nautobot/core/tests/test_breadcrumbs.py +27 -28
  120. nautobot/core/tests/test_checks.py +28 -0
  121. nautobot/core/tests/test_cli.py +40 -0
  122. nautobot/core/tests/test_config.py +2 -1
  123. nautobot/core/tests/test_forms.py +55 -13
  124. nautobot/core/tests/test_jobs.py +144 -3
  125. nautobot/core/tests/test_nautobot_server.py +2 -0
  126. nautobot/core/tests/test_navigations.py +76 -1
  127. nautobot/core/tests/test_patch_social_django.py +42 -0
  128. nautobot/core/tests/test_renderers.py +59 -0
  129. nautobot/core/tests/test_settings_schema.py +1 -0
  130. nautobot/core/tests/test_tables.py +3 -1
  131. nautobot/core/tests/test_templatetags_helpers.py +62 -13
  132. nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
  133. nautobot/core/tests/test_titles.py +0 -16
  134. nautobot/core/tests/test_tree_queries.py +14 -1
  135. nautobot/core/tests/test_ui.py +123 -4
  136. nautobot/core/tests/test_utils.py +72 -5
  137. nautobot/core/tests/test_views.py +159 -31
  138. nautobot/core/ui/breadcrumbs.py +70 -29
  139. nautobot/core/ui/bulk_buttons.py +1 -1
  140. nautobot/core/ui/choices.py +143 -27
  141. nautobot/core/ui/constants.py +76 -12
  142. nautobot/core/ui/echarts.py +15 -20
  143. nautobot/core/ui/object_detail.py +143 -55
  144. nautobot/core/ui/titles.py +3 -6
  145. nautobot/core/urls.py +20 -9
  146. nautobot/core/utils/cache.py +2 -1
  147. nautobot/core/utils/filtering.py +28 -18
  148. nautobot/core/utils/lookup.py +49 -8
  149. nautobot/core/utils/module_loading.py +21 -0
  150. nautobot/core/utils/patch_social_django.py +128 -0
  151. nautobot/core/views/__init__.py +38 -1
  152. nautobot/core/views/generic.py +3 -3
  153. nautobot/core/views/mixins.py +45 -22
  154. nautobot/core/views/renderers.py +4 -3
  155. nautobot/core/views/viewsets.py +2 -1
  156. nautobot/data_validation/apps.py +1 -5
  157. nautobot/data_validation/custom_validators.py +4 -4
  158. nautobot/data_validation/filters.py +1 -1
  159. nautobot/data_validation/forms.py +40 -0
  160. nautobot/data_validation/migrations/0001_initial.py +0 -7
  161. nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -14
  162. nautobot/data_validation/models.py +16 -7
  163. nautobot/data_validation/navigation.py +8 -1
  164. nautobot/data_validation/tables.py +12 -5
  165. nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
  166. nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
  167. nautobot/data_validation/tests/__init__.py +2 -2
  168. nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
  169. nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
  170. nautobot/data_validation/tests/test_filters.py +8 -6
  171. nautobot/data_validation/tests/test_models.py +15 -0
  172. nautobot/data_validation/tests/test_views.py +190 -32
  173. nautobot/data_validation/urls.py +2 -5
  174. nautobot/data_validation/views.py +73 -40
  175. nautobot/dcim/api/serializers.py +3 -13
  176. nautobot/dcim/apps.py +4 -0
  177. nautobot/dcim/choices.py +65 -0
  178. nautobot/dcim/constants.py +7 -0
  179. nautobot/dcim/custom_validators.py +84 -0
  180. nautobot/dcim/factory.py +1 -1
  181. nautobot/dcim/filter_mixins.py +353 -4
  182. nautobot/dcim/{filters/__init__.py → filters.py} +15 -36
  183. nautobot/dcim/forms.py +90 -4
  184. nautobot/dcim/migrations/0075_interface_duplex_interface_speed_and_more.py +32 -0
  185. nautobot/dcim/migrations/{0075_add_deviceclusterassignment.py → 0076_add_deviceclusterassignment.py} +1 -1
  186. nautobot/dcim/migrations/{0076_device_cluster_to_clusters_data_migration.py → 0077_device_cluster_to_clusters_data_migration.py} +1 -1
  187. nautobot/dcim/migrations/{0077_remove_device_cluster.py → 0078_remove_device_cluster.py} +1 -1
  188. nautobot/dcim/migrations/0079_remove_device_location_tenant_name_uniqueness.py +16 -0
  189. nautobot/dcim/migrations/0080_device_name_data_migration.py +59 -0
  190. nautobot/dcim/migrations/0081_alter_device_device_redundancy_group_priority_and_more.py +25 -0
  191. nautobot/dcim/models/device_component_templates.py +33 -1
  192. nautobot/dcim/models/device_components.py +98 -64
  193. nautobot/dcim/models/devices.py +30 -20
  194. nautobot/dcim/navigation.py +7 -6
  195. nautobot/dcim/tables/devices.py +18 -0
  196. nautobot/dcim/tables/devicetypes.py +8 -1
  197. nautobot/dcim/tables/racks.py +0 -2
  198. nautobot/dcim/tables/template_code.py +15 -15
  199. nautobot/dcim/templates/dcim/cable_connect.html +28 -112
  200. nautobot/dcim/templates/dcim/cable_trace.html +0 -4
  201. nautobot/dcim/templates/dcim/{cable_edit.html → cable_update.html} +1 -1
  202. nautobot/dcim/templates/dcim/consoleport.html +7 -6
  203. nautobot/dcim/templates/dcim/consoleserverport.html +7 -6
  204. nautobot/dcim/templates/dcim/device/config.html +2 -2
  205. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
  206. nautobot/dcim/templates/dcim/device/status.html +8 -8
  207. nautobot/dcim/templates/dcim/device.html +1 -1
  208. nautobot/dcim/templates/dcim/device_component_add.html +2 -2
  209. nautobot/dcim/templates/dcim/device_create.html +5 -3
  210. nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
  211. nautobot/dcim/templates/dcim/device_list.html +73 -10
  212. nautobot/dcim/templates/dcim/devicebay.html +1 -1
  213. nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
  214. nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
  215. nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
  216. nautobot/dcim/templates/dcim/frontport.html +10 -9
  217. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
  218. nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
  219. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +1 -1
  220. nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
  221. nautobot/dcim/templates/dcim/interface.html +35 -7
  222. nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
  223. nautobot/dcim/templates/dcim/interface_edit.html +2 -0
  224. nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
  225. nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
  226. nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
  227. nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
  228. nautobot/dcim/templates/dcim/module/base.html +49 -9
  229. nautobot/dcim/templates/dcim/module_consoleports.html +1 -1
  230. nautobot/dcim/templates/dcim/module_consoleserverports.html +1 -1
  231. nautobot/dcim/templates/dcim/module_frontports.html +1 -1
  232. nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
  233. nautobot/dcim/templates/dcim/module_list.html +57 -8
  234. nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
  235. nautobot/dcim/templates/dcim/module_poweroutlets.html +1 -1
  236. nautobot/dcim/templates/dcim/module_powerports.html +1 -1
  237. nautobot/dcim/templates/dcim/module_rearports.html +1 -1
  238. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
  239. nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
  240. nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
  241. nautobot/dcim/templates/dcim/platform_create.html +1 -1
  242. nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
  243. nautobot/dcim/templates/dcim/powerport.html +6 -5
  244. nautobot/dcim/templates/dcim/rack_elevation_list.html +17 -5
  245. nautobot/dcim/templates/dcim/rack_retrieve.html +22 -15
  246. nautobot/dcim/templates/dcim/rearport.html +8 -7
  247. nautobot/dcim/templates/dcim/trace/cable.html +1 -1
  248. nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
  249. nautobot/dcim/templates/dcim/virtualchassis_update.html +15 -7
  250. nautobot/dcim/tests/integration/test_controller.py +4 -6
  251. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +1 -5
  252. nautobot/dcim/tests/integration/test_create_device.py +0 -2
  253. nautobot/dcim/tests/integration/test_device_bulk_operations.py +1 -3
  254. nautobot/dcim/tests/integration/test_fileinputpicker.py +6 -10
  255. nautobot/dcim/tests/integration/test_location_bulk_operations.py +0 -2
  256. nautobot/dcim/tests/integration/test_module_bay_position.py +3 -4
  257. nautobot/dcim/tests/test_api.py +194 -6
  258. nautobot/dcim/tests/test_custom_validators.py +229 -0
  259. nautobot/dcim/tests/test_filters.py +55 -7
  260. nautobot/dcim/tests/test_forms.py +110 -8
  261. nautobot/dcim/tests/test_graphql.py +44 -1
  262. nautobot/dcim/tests/test_models.py +328 -4
  263. nautobot/dcim/tests/test_tables.py +160 -0
  264. nautobot/dcim/tests/test_views.py +132 -29
  265. nautobot/dcim/urls.py +64 -21
  266. nautobot/dcim/utils.py +3 -3
  267. nautobot/dcim/views.py +777 -397
  268. nautobot/extras/api/views.py +60 -45
  269. nautobot/extras/choices.py +2 -13
  270. nautobot/extras/datasources/git.py +3 -1
  271. nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
  272. nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
  273. nautobot/extras/{filters/__init__.py → filters.py} +33 -48
  274. nautobot/extras/forms/forms.py +14 -15
  275. nautobot/extras/forms/mixins.py +0 -41
  276. nautobot/extras/jobs.py +2 -0
  277. nautobot/extras/jobs_ui.py +4 -3
  278. nautobot/extras/management/__init__.py +11 -0
  279. nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
  280. nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
  281. nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
  282. nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
  283. nautobot/extras/migrations/0131_configcontext_device_families.py +18 -0
  284. nautobot/extras/models/__init__.py +1 -2
  285. nautobot/extras/models/approvals.py +33 -14
  286. nautobot/extras/models/change_logging.py +4 -0
  287. nautobot/extras/models/contacts.py +2 -0
  288. nautobot/extras/models/groups.py +44 -5
  289. nautobot/extras/models/jobs.py +60 -4
  290. nautobot/extras/models/mixins.py +28 -0
  291. nautobot/extras/models/models.py +23 -2
  292. nautobot/extras/models/secrets.py +1 -0
  293. nautobot/extras/models/statuses.py +0 -15
  294. nautobot/extras/navigation.py +13 -9
  295. nautobot/extras/plugins/__init__.py +33 -55
  296. nautobot/extras/plugins/marketplace_manifest.yml +49 -1
  297. nautobot/extras/plugins/tables.py +3 -3
  298. nautobot/extras/plugins/urls.py +2 -21
  299. nautobot/extras/plugins/utils.py +1 -33
  300. nautobot/extras/plugins/views.py +0 -9
  301. nautobot/extras/querysets.py +8 -0
  302. nautobot/extras/signals.py +20 -19
  303. nautobot/extras/tables.py +64 -68
  304. nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
  305. nautobot/extras/templates/extras/approval_dashboard.html +7 -5
  306. nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
  307. nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
  308. nautobot/extras/templates/extras/configcontext_update.html +1 -0
  309. nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
  310. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
  311. nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
  312. nautobot/extras/templates/extras/gitrepository_result.html +0 -2
  313. nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
  314. nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
  315. nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
  316. nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
  317. nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
  318. nautobot/extras/templates/extras/inc/json_format.html +10 -3
  319. nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
  320. nautobot/extras/templates/extras/job.html +29 -12
  321. nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
  322. nautobot/extras/templates/extras/job_edit.html +52 -46
  323. nautobot/extras/templates/extras/job_list.html +29 -25
  324. nautobot/extras/templates/extras/marketplace.html +5 -9
  325. nautobot/extras/templates/extras/object_configcontext.html +1 -1
  326. nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
  327. nautobot/extras/templates/extras/objectchange_retrieve.html +19 -39
  328. nautobot/extras/templates/extras/plugin_detail.html +29 -24
  329. nautobot/extras/templates/extras/plugins_list.html +16 -26
  330. nautobot/extras/templates/extras/role_retrieve.html +64 -0
  331. nautobot/extras/templates/extras/scheduledjob.html +4 -2
  332. nautobot/extras/templates/extras/secret_create.html +1 -1
  333. nautobot/extras/templatetags/custom_links.py +12 -12
  334. nautobot/extras/templatetags/job_buttons.py +14 -12
  335. nautobot/extras/test_jobs/invalid_import.py +9 -0
  336. nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
  337. nautobot/extras/test_jobs/missing_import.py +11 -0
  338. nautobot/extras/tests/integration/test_computedfields.py +8 -9
  339. nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
  340. nautobot/extras/tests/integration/test_customfields.py +9 -10
  341. nautobot/extras/tests/integration/test_dynamicgroups.py +12 -9
  342. nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
  343. nautobot/extras/tests/integration/test_plugins.py +18 -6
  344. nautobot/extras/tests/integration/test_relationships.py +0 -2
  345. nautobot/extras/tests/test_api.py +90 -18
  346. nautobot/extras/tests/test_approvals.py +38 -38
  347. nautobot/extras/tests/test_changelog.py +59 -5
  348. nautobot/extras/tests/test_customfields.py +22 -13
  349. nautobot/extras/tests/test_customfields_filters.py +479 -0
  350. nautobot/extras/tests/test_dynamicgroups.py +39 -1
  351. nautobot/extras/tests/test_filters.py +57 -22
  352. nautobot/extras/tests/test_forms.py +18 -21
  353. nautobot/extras/tests/test_jobs.py +25 -4
  354. nautobot/extras/tests/test_migrations.py +1 -0
  355. nautobot/extras/tests/test_models.py +51 -33
  356. nautobot/extras/tests/test_plugins.py +36 -10
  357. nautobot/extras/tests/test_utils.py +3 -4
  358. nautobot/extras/tests/test_views.py +52 -112
  359. nautobot/extras/urls.py +0 -14
  360. nautobot/extras/views.py +164 -71
  361. nautobot/ipam/factory.py +7 -0
  362. nautobot/ipam/filter_mixins.py +38 -0
  363. nautobot/ipam/filters.py +53 -38
  364. nautobot/ipam/formfields.py +1 -1
  365. nautobot/ipam/forms.py +6 -3
  366. nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
  367. nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
  368. nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
  369. nautobot/ipam/models.py +29 -2
  370. nautobot/ipam/navigation.py +3 -2
  371. nautobot/ipam/signals.py +71 -0
  372. nautobot/ipam/tables.py +19 -6
  373. nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
  374. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
  375. nautobot/ipam/templates/ipam/ipaddress.html +14 -0
  376. nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
  377. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
  378. nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
  379. nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
  380. nautobot/ipam/templates/ipam/namespace_update.html +15 -0
  381. nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
  382. nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
  383. nautobot/ipam/templates/ipam/prefix_list.html +14 -13
  384. nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
  385. nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
  386. nautobot/ipam/tests/migration/test_migrations.py +89 -0
  387. nautobot/ipam/tests/test_api.py +13 -6
  388. nautobot/ipam/tests/test_filters.py +36 -1
  389. nautobot/ipam/tests/test_forms.py +1 -1
  390. nautobot/ipam/tests/test_models.py +44 -2
  391. nautobot/ipam/tests/test_tables.py +1 -2
  392. nautobot/ipam/tests/test_utils.py +1 -1
  393. nautobot/ipam/tests/test_views.py +13 -14
  394. nautobot/ipam/ui.py +0 -17
  395. nautobot/ipam/utils/migrations.py +16 -2
  396. nautobot/ipam/utils/testing.py +9 -3
  397. nautobot/ipam/views.py +53 -11
  398. nautobot/load_balancers/__init__.py +0 -0
  399. nautobot/load_balancers/api/__init__.py +1 -0
  400. nautobot/load_balancers/api/serializers.py +75 -0
  401. nautobot/load_balancers/api/urls.py +23 -0
  402. nautobot/load_balancers/api/views.py +61 -0
  403. nautobot/load_balancers/apps.py +17 -0
  404. nautobot/load_balancers/choices.py +167 -0
  405. nautobot/load_balancers/filters.py +225 -0
  406. nautobot/load_balancers/forms.py +532 -0
  407. nautobot/load_balancers/management/commands/__init__.py +0 -0
  408. nautobot/load_balancers/management/commands/generate_load_balancer_models_test_data.py +38 -0
  409. nautobot/load_balancers/migrations/0001_initial.py +465 -0
  410. nautobot/load_balancers/migrations/0002_create_default_statuses_pool_members.py +31 -0
  411. nautobot/load_balancers/migrations/__init__.py +0 -0
  412. nautobot/load_balancers/models.py +423 -0
  413. nautobot/load_balancers/navigation.py +80 -0
  414. nautobot/load_balancers/tables.py +255 -0
  415. nautobot/load_balancers/tests/__init__.py +474 -0
  416. nautobot/load_balancers/tests/test_api.py +353 -0
  417. nautobot/load_balancers/tests/test_filters.py +134 -0
  418. nautobot/load_balancers/tests/test_forms.py +266 -0
  419. nautobot/load_balancers/tests/test_models.py +195 -0
  420. nautobot/load_balancers/tests/test_views.py +229 -0
  421. nautobot/load_balancers/urls.py +17 -0
  422. nautobot/load_balancers/views.py +248 -0
  423. nautobot/project-static/dist/css/github-dark.min.css +10 -0
  424. nautobot/project-static/dist/css/github.min.css +10 -0
  425. nautobot/project-static/dist/css/nautobot.css +1 -11
  426. nautobot/project-static/dist/css/nautobot.css.map +1 -1
  427. nautobot/project-static/dist/js/libraries.js +1 -1
  428. nautobot/project-static/dist/js/libraries.js.map +1 -1
  429. nautobot/project-static/dist/js/nautobot.js +1 -1
  430. nautobot/project-static/dist/js/nautobot.js.map +1 -1
  431. nautobot/project-static/js/cabletrace.js +1 -1
  432. nautobot/project-static/js/forms.js +13 -0
  433. nautobot/project-static/js/interface_filtering.js +20 -16
  434. nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
  435. nautobot/project-static/nautobot-icons/bus-globe.svg +3 -0
  436. nautobot/project-static/nautobot-icons/bus-shield-check.svg +3 -0
  437. nautobot/project-static/nautobot-icons/bus-shield.svg +3 -0
  438. nautobot/project-static/nautobot-icons/cloud.svg +1 -1
  439. nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
  440. nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
  441. nautobot/project-static/nautobot-icons/elements.svg +1 -1
  442. nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
  443. nautobot/project-static/nautobot-icons/hammer.svg +1 -1
  444. nautobot/project-static/nautobot-icons/organization.svg +3 -0
  445. nautobot/project-static/nautobot-icons/secrets.svg +1 -1
  446. nautobot/project-static/nautobot-icons/security.svg +3 -0
  447. nautobot/project-static/nautobot-icons/server.svg +1 -1
  448. nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
  449. nautobot/project-static/nautobot-icons/star.svg +1 -1
  450. nautobot/tenancy/api/serializers.py +1 -0
  451. nautobot/tenancy/api/views.py +2 -1
  452. nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
  453. nautobot/tenancy/navigation.py +3 -1
  454. nautobot/tenancy/tests/test_filters.py +0 -2
  455. nautobot/tenancy/views.py +2 -1
  456. nautobot/ui/package-lock.json +87 -4
  457. nautobot/ui/package.json +2 -1
  458. nautobot/ui/src/js/collapse.js +3 -3
  459. nautobot/ui/src/js/nautobot.js +16 -1
  460. nautobot/ui/src/js/select2.js +53 -2
  461. nautobot/ui/src/scss/colors.scss +1 -1
  462. nautobot/ui/src/scss/nautobot.scss +112 -30
  463. nautobot/ui/webpack.config.js +13 -0
  464. nautobot/users/templates/users/preferences.html +11 -2
  465. nautobot/users/templates/users/profile.html +45 -12
  466. nautobot/users/templates/users/sessionkey_delete.html +1 -1
  467. nautobot/users/tests/test_api.py +4 -0
  468. nautobot/users/views.py +4 -2
  469. nautobot/virtualization/filters.py +6 -1
  470. nautobot/virtualization/models.py +1 -68
  471. nautobot/virtualization/navigation.py +3 -2
  472. nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
  473. nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
  474. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
  475. nautobot/virtualization/tests/test_api.py +3 -0
  476. nautobot/virtualization/tests/test_filters.py +10 -1
  477. nautobot/virtualization/tests/test_models.py +45 -4
  478. nautobot/virtualization/views.py +4 -1
  479. nautobot/vpn/__init__.py +0 -0
  480. nautobot/vpn/api/serializers.py +113 -0
  481. nautobot/vpn/api/urls.py +19 -0
  482. nautobot/vpn/api/views.py +70 -0
  483. nautobot/vpn/apps.py +8 -0
  484. nautobot/vpn/choices.py +171 -0
  485. nautobot/vpn/factory.py +219 -0
  486. nautobot/vpn/filters.py +234 -0
  487. nautobot/vpn/forms.py +487 -0
  488. nautobot/vpn/homepage.py +19 -0
  489. nautobot/vpn/migrations/0001_initial.py +541 -0
  490. nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
  491. nautobot/vpn/migrations/__init__.py +0 -0
  492. nautobot/vpn/models.py +535 -0
  493. nautobot/vpn/navigation.py +98 -0
  494. nautobot/vpn/tables.py +383 -0
  495. nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
  496. nautobot/vpn/tests/__init__.py +0 -0
  497. nautobot/vpn/tests/test_api.py +336 -0
  498. nautobot/vpn/tests/test_filters.py +139 -0
  499. nautobot/vpn/tests/test_forms.py +293 -0
  500. nautobot/vpn/tests/test_models.py +147 -0
  501. nautobot/vpn/tests/test_views.py +300 -0
  502. nautobot/vpn/urls.py +16 -0
  503. nautobot/vpn/views.py +495 -0
  504. nautobot/wireless/navigation.py +3 -2
  505. nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
  506. nautobot/wireless/tests/test_api.py +1 -1
  507. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +15 -15
  508. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +514 -572
  509. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +1 -0
  510. nautobot/circuits/templates/circuits/circuit.html +0 -2
  511. nautobot/circuits/templates/circuits/circuit_edit.html +0 -2
  512. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -2
  513. nautobot/circuits/templates/circuits/circuit_update.html +0 -1
  514. nautobot/circuits/templates/circuits/circuittermination.html +0 -2
  515. nautobot/circuits/templates/circuits/circuittermination_edit.html +0 -2
  516. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +0 -2
  517. nautobot/circuits/templates/circuits/circuittermination_update.html +0 -1
  518. nautobot/circuits/templates/circuits/circuittype.html +0 -2
  519. nautobot/circuits/templates/circuits/circuittype_retrieve.html +0 -2
  520. nautobot/circuits/templates/circuits/inc/circuit_termination.html +0 -85
  521. nautobot/circuits/templates/circuits/provider.html +0 -2
  522. nautobot/circuits/templates/circuits/provider_edit.html +0 -2
  523. nautobot/circuits/templates/circuits/provider_retrieve.html +0 -1
  524. nautobot/circuits/templates/circuits/provider_update.html +0 -1
  525. nautobot/circuits/templates/circuits/providernetwork.html +0 -2
  526. nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
  527. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +0 -2
  528. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +0 -2
  529. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -2
  530. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -2
  531. nautobot/core/templates/buttons/import.html +0 -9
  532. nautobot/data_validation/template_content.py +0 -42
  533. nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +0 -1
  534. nautobot/dcim/filters/mixins.py +0 -354
  535. nautobot/dcim/templates/dcim/controller/base.html +0 -2
  536. nautobot/dcim/templates/dcim/controller_retrieve.html +0 -2
  537. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +0 -2
  538. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -2
  539. nautobot/dcim/templates/dcim/device/base.html +0 -2
  540. nautobot/dcim/templates/dcim/device/consoleports.html +0 -2
  541. nautobot/dcim/templates/dcim/device/consoleserverports.html +0 -2
  542. nautobot/dcim/templates/dcim/device/devicebays.html +0 -2
  543. nautobot/dcim/templates/dcim/device/frontports.html +0 -2
  544. nautobot/dcim/templates/dcim/device/interfaces.html +0 -2
  545. nautobot/dcim/templates/dcim/device/inventory.html +0 -2
  546. nautobot/dcim/templates/dcim/device/modulebays.html +0 -2
  547. nautobot/dcim/templates/dcim/device/poweroutlets.html +0 -2
  548. nautobot/dcim/templates/dcim/device/powerports.html +0 -2
  549. nautobot/dcim/templates/dcim/device/rearports.html +0 -2
  550. nautobot/dcim/templates/dcim/device/wireless.html +0 -2
  551. nautobot/dcim/templates/dcim/device_component.html +0 -2
  552. nautobot/dcim/templates/dcim/device_edit.html +0 -2
  553. nautobot/dcim/templates/dcim/devicefamily_retrieve.html +0 -2
  554. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -2
  555. nautobot/dcim/templates/dcim/devicetype.html +0 -2
  556. nautobot/dcim/templates/dcim/devicetype_edit.html +0 -2
  557. nautobot/dcim/templates/dcim/devicetype_retrieve.html +0 -2
  558. nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +0 -1
  559. nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +0 -2
  560. nautobot/dcim/templates/dcim/location.html +0 -2
  561. nautobot/dcim/templates/dcim/location_edit.html +0 -2
  562. nautobot/dcim/templates/dcim/location_retrieve.html +0 -243
  563. nautobot/dcim/templates/dcim/locationtype.html +0 -2
  564. nautobot/dcim/templates/dcim/locationtype_retrieve.html +0 -2
  565. nautobot/dcim/templates/dcim/manufacturer.html +0 -2
  566. nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -1
  567. nautobot/dcim/templates/dcim/platform.html +0 -2
  568. nautobot/dcim/templates/dcim/powerfeed.html +0 -2
  569. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +0 -2
  570. nautobot/dcim/templates/dcim/powerpanel.html +0 -2
  571. nautobot/dcim/templates/dcim/powerpanel_edit.html +0 -2
  572. nautobot/dcim/templates/dcim/powerpanel_retrieve.html +0 -2
  573. nautobot/dcim/templates/dcim/rack.html +0 -2
  574. nautobot/dcim/templates/dcim/rack_edit.html +0 -2
  575. nautobot/dcim/templates/dcim/rackgroup.html +0 -2
  576. nautobot/dcim/templates/dcim/rackreservation.html +0 -2
  577. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +0 -2
  578. nautobot/dcim/templates/dcim/softwareversion_retrieve.html +0 -2
  579. nautobot/dcim/templates/dcim/virtualchassis.html +0 -2
  580. nautobot/dcim/templates/dcim/virtualchassis_add.html +0 -2
  581. nautobot/dcim/templates/dcim/virtualchassis_edit.html +0 -2
  582. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +0 -2
  583. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -2
  584. nautobot/dcim/ui.py +0 -29
  585. nautobot/extras/templates/extras/computedfield.html +0 -2
  586. nautobot/extras/templates/extras/computedfield_retrieve.html +0 -2
  587. nautobot/extras/templates/extras/configcontext.html +0 -2
  588. nautobot/extras/templates/extras/configcontext_edit.html +0 -2
  589. nautobot/extras/templates/extras/configcontext_retrieve.html +0 -2
  590. nautobot/extras/templates/extras/configcontextschema.html +0 -2
  591. nautobot/extras/templates/extras/configcontextschema_edit.html +0 -2
  592. nautobot/extras/templates/extras/contact_retrieve.html +0 -2
  593. nautobot/extras/templates/extras/customfield.html +0 -2
  594. nautobot/extras/templates/extras/customfield_edit.html +0 -2
  595. nautobot/extras/templates/extras/customfield_retrieve.html +0 -2
  596. nautobot/extras/templates/extras/customlink.html +0 -2
  597. nautobot/extras/templates/extras/dynamicgroup.html +0 -2
  598. nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -2
  599. nautobot/extras/templates/extras/exporttemplate.html +0 -2
  600. nautobot/extras/templates/extras/gitrepository.html +0 -2
  601. nautobot/extras/templates/extras/gitrepository_object_edit.html +0 -2
  602. nautobot/extras/templates/extras/graphqlquery.html +0 -2
  603. nautobot/extras/templates/extras/graphqlquery_list.html +0 -1
  604. nautobot/extras/templates/extras/graphqlquery_retrieve.html +0 -97
  605. nautobot/extras/templates/extras/job_detail.html +0 -2
  606. nautobot/extras/templates/extras/jobbutton_retrieve.html +0 -2
  607. nautobot/extras/templates/extras/jobhook.html +0 -2
  608. nautobot/extras/templates/extras/jobqueue_retrieve.html +0 -2
  609. nautobot/extras/templates/extras/jobresult.html +0 -2
  610. nautobot/extras/templates/extras/metadatatype_retrieve.html +0 -2
  611. nautobot/extras/templates/extras/note.html +0 -2
  612. nautobot/extras/templates/extras/note_retrieve.html +0 -1
  613. nautobot/extras/templates/extras/object_changelog.html +0 -2
  614. nautobot/extras/templates/extras/object_notes.html +0 -2
  615. nautobot/extras/templates/extras/objectchange.html +0 -2
  616. nautobot/extras/templates/extras/objectchange_list.html +0 -3
  617. nautobot/extras/templates/extras/relationship.html +0 -1
  618. nautobot/extras/templates/extras/secret.html +0 -1
  619. nautobot/extras/templates/extras/secret_edit.html +0 -1
  620. nautobot/extras/templates/extras/secretsgroup.html +0 -2
  621. nautobot/extras/templates/extras/secretsgroup_edit.html +0 -2
  622. nautobot/extras/templates/extras/secretsgroup_retrieve.html +0 -2
  623. nautobot/extras/templates/extras/status.html +0 -2
  624. nautobot/extras/templates/extras/tag.html +0 -2
  625. nautobot/extras/templates/extras/tag_edit.html +0 -2
  626. nautobot/extras/templates/extras/tag_retrieve.html +0 -2
  627. nautobot/extras/templates/extras/team_retrieve.html +0 -2
  628. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
  629. nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -1
  630. nautobot/ipam/templates/ipam/prefix.html +0 -2
  631. nautobot/ipam/templates/ipam/prefix_edit.html +0 -1
  632. nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -2
  633. nautobot/ipam/templates/ipam/rir.html +0 -2
  634. nautobot/ipam/templates/ipam/routetarget.html +0 -1
  635. nautobot/ipam/templates/ipam/service.html +0 -2
  636. nautobot/ipam/templates/ipam/service_edit.html +0 -2
  637. nautobot/ipam/templates/ipam/service_retrieve.html +0 -2
  638. nautobot/ipam/templates/ipam/vlan.html +0 -2
  639. nautobot/ipam/templates/ipam/vlan_edit.html +0 -2
  640. nautobot/ipam/templates/ipam/vlan_retrieve.html +0 -2
  641. nautobot/ipam/templates/ipam/vlangroup.html +0 -2
  642. nautobot/ipam/templates/ipam/vrf.html +0 -1
  643. nautobot/tenancy/templates/tenancy/tenant.html +0 -2
  644. nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -2
  645. nautobot/tenancy/templates/tenancy/tenantgroup.html +0 -2
  646. nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +0 -1
  647. nautobot/virtualization/templates/virtualization/clustergroup.html +0 -2
  648. nautobot/virtualization/templates/virtualization/clustertype.html +0 -2
  649. nautobot/virtualization/templates/virtualization/virtualmachine.html +0 -2
  650. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +0 -2
  651. nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +0 -2
  652. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +0 -2
  653. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +0 -2
  654. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +0 -2
  655. /nautobot/dcim/templates/dcim/{cable.html → cable_retrieve.html} +0 -0
  656. /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
  657. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
  658. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
  659. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,84 @@
1
+ from django.contrib.contenttypes.models import ContentType
2
+
3
+ from nautobot.apps.models import CustomValidator
4
+ from nautobot.core.utils.config import get_settings_or_config
5
+ from nautobot.data_validation.models import RequiredValidationRule
6
+ from nautobot.dcim.choices import DeviceUniquenessChoices
7
+ from nautobot.dcim.models import Device
8
+
9
+
10
+ class DeviceUniquenessValidator(CustomValidator):
11
+ """Custom validator enforcing device uniqueness based on DEVICE_UNIQUENESS setting."""
12
+
13
+ model = "dcim.device"
14
+
15
+ def clean(self):
16
+ obj = self.context["object"]
17
+ try:
18
+ uniqueness_mode = get_settings_or_config("DEVICE_UNIQUENESS", fallback=DeviceUniquenessChoices.DEFAULT)
19
+ except AttributeError:
20
+ uniqueness_mode = DeviceUniquenessChoices.DEFAULT
21
+ device_name_required = RequiredValidationRule.objects.filter(
22
+ content_type=ContentType.objects.get_for_model(Device), field="name"
23
+ ).exists()
24
+
25
+ # Rule 1: If we don't set DEVICE_NAME_REQUIRED then it's acceptable for any number of devices to be "unnamed",
26
+ # regardless of the DEVICE_UNIQUENESS setting. Note that we consider both `None` and `""` to be "unnamed".
27
+ if not obj.name and not device_name_required:
28
+ return
29
+
30
+ # If not obj.name and device_name_required, this will be detected by RequiredValidationRule.
31
+
32
+ if uniqueness_mode == DeviceUniquenessChoices.LOCATION_TENANT_NAME:
33
+ # Rule 2: name is not None, tenant is None, given location --> no duplicates
34
+ # Rule 3: tenant is None, name is duplicated --> error
35
+ if obj.tenant is None:
36
+ duplicates = Device.objects.filter(
37
+ name=obj.name,
38
+ tenant__isnull=True,
39
+ location=obj.location,
40
+ ).exclude(pk=obj.pk)
41
+ if duplicates.exists():
42
+ self.validation_error(
43
+ {
44
+ "__all__": (
45
+ f"A device named '{obj.name}' with no tenant already exists in this location: {obj.location}. "
46
+ "Device names must be unique when tenant is None and DEVICE_UNIQUENESS='location_tenant_name'."
47
+ )
48
+ }
49
+ )
50
+ else:
51
+ # When tenant is set, enforce uniqueness per (location, tenant, name)
52
+ duplicates = Device.objects.filter(
53
+ name=obj.name,
54
+ tenant=obj.tenant,
55
+ location=obj.location,
56
+ ).exclude(pk=obj.pk)
57
+ if duplicates.exists():
58
+ self.validation_error(
59
+ {
60
+ "__all__": (
61
+ f"A device named '{obj.name}' already exists in this location: {obj.location} and tenant: {obj.tenant}. "
62
+ "Device names must be unique per (Location, Tenant) when DEVICE_UNIQUENESS='location_tenant_name'."
63
+ )
64
+ }
65
+ )
66
+
67
+ elif uniqueness_mode == DeviceUniquenessChoices.NAME:
68
+ duplicates = Device.objects.filter(name=obj.name).exclude(pk=obj.pk)
69
+ if duplicates.exists():
70
+ self.validation_error(
71
+ {
72
+ "name": (
73
+ f"At least one other device named '{obj.name}' already exists. "
74
+ "Device names must be globally unique when DEVICE_UNIQUENESS='name'."
75
+ )
76
+ }
77
+ )
78
+
79
+ elif uniqueness_mode == "none":
80
+ # Explicitly no uniqueness enforcement
81
+ return
82
+
83
+
84
+ custom_validators = [DeviceUniquenessValidator]
nautobot/dcim/factory.py CHANGED
@@ -186,7 +186,7 @@ class DeviceFactory(PrimaryModelFactory):
186
186
  )
187
187
  device_redundancy_group_priority = factory.Maybe(
188
188
  "has_device_redundancy_group",
189
- factory.Faker("pyint", min_value=1, max_value=500),
189
+ factory.Faker("pyint", min_value=1, max_value=65535),
190
190
  )
191
191
 
192
192
  controller_managed_device_group = random_instance(ControllerManagedDeviceGroup)
@@ -1,5 +1,354 @@
1
- # TODO: remove this entire file in 2.2
2
- # for backwards compatibility with Nautobot 1.4 - avoid importing this file if at all possible!
3
- from nautobot.dcim.filters.mixins import LocatableModelFilterSetMixin
1
+ from django.db.models import Q
2
+ import django_filters
4
3
 
5
- __all__ = ("LocatableModelFilterSetMixin",)
4
+ from nautobot.core.filters import (
5
+ MultiValueCharFilter,
6
+ MultiValueMACAddressFilter,
7
+ MultiValueUUIDFilter,
8
+ NameSearchFilterSet,
9
+ NaturalKeyOrPKMultipleChoiceFilter,
10
+ RelatedMembershipBooleanFilter,
11
+ SearchFilter,
12
+ TreeNodeMultipleChoiceFilter,
13
+ )
14
+ from nautobot.core.utils.data import is_uuid
15
+ from nautobot.dcim.constants import MODULE_RECURSION_DEPTH_LIMIT
16
+ from nautobot.dcim.models import (
17
+ Cable,
18
+ ConsolePort,
19
+ ConsolePortTemplate,
20
+ ConsoleServerPort,
21
+ ConsoleServerPortTemplate,
22
+ Device,
23
+ DeviceType,
24
+ FrontPort,
25
+ FrontPortTemplate,
26
+ Interface,
27
+ InterfaceTemplate,
28
+ Location,
29
+ Manufacturer,
30
+ Module,
31
+ ModuleBay,
32
+ ModuleBayTemplate,
33
+ ModuleType,
34
+ PowerOutlet,
35
+ PowerOutletTemplate,
36
+ PowerPort,
37
+ PowerPortTemplate,
38
+ RearPort,
39
+ RearPortTemplate,
40
+ )
41
+ from nautobot.extras.filter_mixins import CustomFieldModelFilterSetMixin, RelationshipModelFilterSetMixin
42
+
43
+
44
+ class CableTerminationModelFilterSetMixin(django_filters.FilterSet):
45
+ has_cable = RelatedMembershipBooleanFilter(
46
+ field_name="cable",
47
+ label="Has cable",
48
+ )
49
+ cable = django_filters.ModelMultipleChoiceFilter(
50
+ queryset=Cable.objects.all(),
51
+ label="Cable",
52
+ )
53
+
54
+
55
+ class DeviceComponentTemplateModelFilterSetMixin(NameSearchFilterSet, CustomFieldModelFilterSetMixin):
56
+ device_type = NaturalKeyOrPKMultipleChoiceFilter(
57
+ queryset=DeviceType.objects.all(),
58
+ to_field_name="model",
59
+ label="Device type (model or ID)",
60
+ )
61
+ label = MultiValueCharFilter(label="Label")
62
+ description = MultiValueCharFilter(label="Description")
63
+ id = MultiValueUUIDFilter(label="ID")
64
+ name = MultiValueCharFilter(label="Name")
65
+
66
+
67
+ class ModularDeviceComponentTemplateModelFilterSetMixin(DeviceComponentTemplateModelFilterSetMixin):
68
+ has_device_type = RelatedMembershipBooleanFilter(
69
+ field_name="device_type",
70
+ label="Has device type",
71
+ )
72
+ module_type = NaturalKeyOrPKMultipleChoiceFilter(
73
+ queryset=ModuleType.objects.all(),
74
+ to_field_name="model",
75
+ label="Module type (model or ID)",
76
+ )
77
+ has_module_type = RelatedMembershipBooleanFilter(
78
+ field_name="module_type",
79
+ label="Has module type",
80
+ )
81
+
82
+
83
+ class DeviceComponentModelFilterSetMixin(CustomFieldModelFilterSetMixin):
84
+ q = SearchFilter(
85
+ filter_predicates={
86
+ "name": "icontains",
87
+ "label": "icontains",
88
+ "description": "icontains",
89
+ },
90
+ )
91
+ location = NaturalKeyOrPKMultipleChoiceFilter(
92
+ field_name="device__location",
93
+ queryset=Location.objects.all(),
94
+ to_field_name="name",
95
+ label="Location (name or ID)",
96
+ )
97
+ device = NaturalKeyOrPKMultipleChoiceFilter(
98
+ queryset=Device.objects.all(),
99
+ to_field_name="name",
100
+ label="Device (name or ID)",
101
+ )
102
+
103
+
104
+ class ModularDeviceComponentModelFilterSetMixin(DeviceComponentModelFilterSetMixin, RelationshipModelFilterSetMixin):
105
+ module = NaturalKeyOrPKMultipleChoiceFilter(
106
+ queryset=Module.objects.all(),
107
+ to_field_name="module_type__model",
108
+ label="Module (model or ID)",
109
+ )
110
+ device = NaturalKeyOrPKMultipleChoiceFilter(
111
+ queryset=Device.objects.all(),
112
+ to_field_name="name",
113
+ label="Device (name or ID)",
114
+ method="filter_device",
115
+ )
116
+
117
+ def _construct_device_filter_recursively(self, field_name, value):
118
+ recursion_depth = MODULE_RECURSION_DEPTH_LIMIT - 1
119
+ query = Q(**{f"device__{field_name}__in": value})
120
+ for level in range(recursion_depth):
121
+ recursive_query = "module__parent_module_bay__" + "parent_module__parent_module_bay__" * level
122
+ query = query | Q(**{f"{recursive_query}parent_device__{field_name}__in": value})
123
+ return query
124
+
125
+ def generate_query_filter_device(self, value):
126
+ if not hasattr(value, "__iter__") or isinstance(value, str):
127
+ value = [value]
128
+
129
+ device_ids = set(str(item) for item in value if is_uuid(item))
130
+ device_names = set(str(item) for item in value if not is_uuid(item))
131
+ query = self._construct_device_filter_recursively("name", device_names)
132
+ query |= self._construct_device_filter_recursively("id", device_ids)
133
+ return query
134
+
135
+ def filter_device(self, queryset, name, value):
136
+ if not value:
137
+ return queryset
138
+ params = self.generate_query_filter_device(value)
139
+ return queryset.filter(params)
140
+
141
+
142
+ class LocatableModelFilterSetMixin(django_filters.FilterSet):
143
+ """Mixin to add `location` filter fields to a FilterSet.
144
+
145
+ The expectation is that the linked model has `location` FK fields.
146
+ """
147
+
148
+ location = TreeNodeMultipleChoiceFilter(
149
+ prefers_id=True,
150
+ queryset=Location.objects.all(),
151
+ to_field_name="name",
152
+ label="Location (name or ID)",
153
+ )
154
+
155
+
156
+ class PathEndpointModelFilterSetMixin(django_filters.FilterSet):
157
+ connected = django_filters.BooleanFilter(method="filter_connected", label="Connected status (bool)")
158
+
159
+ def filter_connected(self, queryset, name, value):
160
+ if value:
161
+ return queryset.filter(_path__is_active=True)
162
+ else:
163
+ return queryset.filter(Q(_path__isnull=True) | Q(_path__is_active=False))
164
+
165
+
166
+ class DeviceModuleCommonFiltersMixin(django_filters.FilterSet):
167
+ mac_address = MultiValueMACAddressFilter(
168
+ field_name="interfaces__mac_address",
169
+ label="MAC address",
170
+ )
171
+ has_console_ports = RelatedMembershipBooleanFilter(
172
+ field_name="console_ports",
173
+ label="Has console ports",
174
+ )
175
+ console_ports = NaturalKeyOrPKMultipleChoiceFilter(
176
+ queryset=ConsolePort.objects.all(),
177
+ to_field_name="name",
178
+ label="Console Ports (name or ID)",
179
+ )
180
+ has_console_server_ports = RelatedMembershipBooleanFilter(
181
+ field_name="console_server_ports",
182
+ label="Has console server ports",
183
+ )
184
+ console_server_ports = NaturalKeyOrPKMultipleChoiceFilter(
185
+ queryset=ConsoleServerPort.objects.all(),
186
+ to_field_name="name",
187
+ label="Console Server Ports (name or ID)",
188
+ )
189
+ has_power_ports = RelatedMembershipBooleanFilter(
190
+ field_name="power_ports",
191
+ label="Has power ports",
192
+ )
193
+ power_ports = NaturalKeyOrPKMultipleChoiceFilter(
194
+ queryset=PowerPort.objects.all(),
195
+ to_field_name="name",
196
+ label="Power Ports (name or ID)",
197
+ )
198
+ has_power_outlets = RelatedMembershipBooleanFilter(
199
+ field_name="power_outlets",
200
+ label="Has power outlets",
201
+ )
202
+ power_outlets = NaturalKeyOrPKMultipleChoiceFilter(
203
+ queryset=PowerOutlet.objects.all(),
204
+ to_field_name="name",
205
+ label="Power Outlets (name or ID)",
206
+ )
207
+ has_interfaces = RelatedMembershipBooleanFilter(
208
+ field_name="interfaces",
209
+ label="Has interfaces",
210
+ )
211
+ interfaces = NaturalKeyOrPKMultipleChoiceFilter(
212
+ queryset=Interface.objects.all(),
213
+ to_field_name="name",
214
+ label="Interfaces (name or ID)",
215
+ )
216
+ has_front_ports = RelatedMembershipBooleanFilter(
217
+ field_name="front_ports",
218
+ label="Has front ports",
219
+ )
220
+ front_ports = NaturalKeyOrPKMultipleChoiceFilter(
221
+ queryset=FrontPort.objects.all(),
222
+ to_field_name="name",
223
+ label="Front Ports (name or ID)",
224
+ )
225
+ has_rear_ports = RelatedMembershipBooleanFilter(
226
+ field_name="rear_ports",
227
+ label="Has rear ports",
228
+ )
229
+ rear_ports = NaturalKeyOrPKMultipleChoiceFilter(
230
+ queryset=RearPort.objects.all(),
231
+ to_field_name="name",
232
+ label="Rear Ports (name or ID)",
233
+ )
234
+ has_module_bays = RelatedMembershipBooleanFilter(
235
+ field_name="module_bays",
236
+ label="Has module bays",
237
+ )
238
+ has_empty_module_bays = django_filters.BooleanFilter(
239
+ method="filter_has_empty_module_bays",
240
+ label="Has empty module bays",
241
+ )
242
+ module_bays = django_filters.ModelMultipleChoiceFilter(
243
+ queryset=ModuleBay.objects.all(),
244
+ label="Module Bays",
245
+ )
246
+ has_modules = RelatedMembershipBooleanFilter(
247
+ field_name="module_bays__installed_module",
248
+ label="Has modules",
249
+ )
250
+
251
+ def generate_query_filter_has_empty_module_bays(self, value):
252
+ if value is True:
253
+ query = Q(module_bays__isnull=False, module_bays__installed_module__isnull=True)
254
+ else:
255
+ query = Q(module_bays__isnull=True) | ~Q(module_bays__installed_module__isnull=True)
256
+ return query
257
+
258
+ def filter_has_empty_module_bays(self, queryset, name, value):
259
+ if value is None:
260
+ return queryset.none
261
+ params = self.generate_query_filter_has_empty_module_bays(value)
262
+ return queryset.filter(params)
263
+
264
+
265
+ class DeviceTypeModuleTypeCommonFiltersMixin(django_filters.FilterSet):
266
+ manufacturer = NaturalKeyOrPKMultipleChoiceFilter(
267
+ queryset=Manufacturer.objects.all(),
268
+ to_field_name="name",
269
+ label="Manufacturer (name or ID)",
270
+ )
271
+ # TODO: solve https://github.com/nautobot/nautobot/issues/2875 to use this filter correctly
272
+ console_port_templates = NaturalKeyOrPKMultipleChoiceFilter(
273
+ prefers_id=True,
274
+ to_field_name="name",
275
+ queryset=ConsolePortTemplate.objects.all(),
276
+ label="Console port templates (name or ID)",
277
+ )
278
+ has_console_port_templates = RelatedMembershipBooleanFilter(
279
+ field_name="console_port_templates",
280
+ label="Has console port templates",
281
+ )
282
+ # TODO: solve https://github.com/nautobot/nautobot/issues/2875 to use this filter correctly
283
+ console_server_port_templates = NaturalKeyOrPKMultipleChoiceFilter(
284
+ prefers_id=True,
285
+ to_field_name="name",
286
+ queryset=ConsoleServerPortTemplate.objects.all(),
287
+ label="Console server port templates (name or ID)",
288
+ )
289
+ has_console_server_port_templates = RelatedMembershipBooleanFilter(
290
+ field_name="console_server_port_templates",
291
+ label="Has console server port templates",
292
+ )
293
+ # TODO: solve https://github.com/nautobot/nautobot/issues/2875 to use this filter correctly
294
+ power_port_templates = NaturalKeyOrPKMultipleChoiceFilter(
295
+ prefers_id=True,
296
+ to_field_name="name",
297
+ queryset=PowerPortTemplate.objects.all(),
298
+ label="Power port templates (name or ID)",
299
+ )
300
+ has_power_port_templates = RelatedMembershipBooleanFilter(
301
+ field_name="power_port_templates",
302
+ label="Has power port templates",
303
+ )
304
+ # TODO: solve https://github.com/nautobot/nautobot/issues/2875 to use this filter correctly
305
+ power_outlet_templates = NaturalKeyOrPKMultipleChoiceFilter(
306
+ prefers_id=True,
307
+ to_field_name="name",
308
+ queryset=PowerOutletTemplate.objects.all(),
309
+ label="Power outlet templates (name or ID)",
310
+ )
311
+ has_power_outlet_templates = RelatedMembershipBooleanFilter(
312
+ field_name="power_outlet_templates",
313
+ label="Has power outlet templates",
314
+ )
315
+ # TODO: solve https://github.com/nautobot/nautobot/issues/2875 to use this filter correctly
316
+ interface_templates = NaturalKeyOrPKMultipleChoiceFilter(
317
+ prefers_id=True,
318
+ to_field_name="name",
319
+ queryset=InterfaceTemplate.objects.all(),
320
+ label="Interface templates (name or ID)",
321
+ )
322
+ has_interface_templates = RelatedMembershipBooleanFilter(
323
+ field_name="interface_templates",
324
+ label="Has interface templates",
325
+ )
326
+ # TODO: solve https://github.com/nautobot/nautobot/issues/2875 to use this filter correctly
327
+ front_port_templates = NaturalKeyOrPKMultipleChoiceFilter(
328
+ prefers_id=True,
329
+ to_field_name="name",
330
+ queryset=FrontPortTemplate.objects.all(),
331
+ label="Front port templates (name or ID)",
332
+ )
333
+ has_front_port_templates = RelatedMembershipBooleanFilter(
334
+ field_name="front_port_templates",
335
+ label="Has front port templates",
336
+ )
337
+ # TODO: solve https://github.com/nautobot/nautobot/issues/2875 to use this filter correctly
338
+ rear_port_templates = NaturalKeyOrPKMultipleChoiceFilter(
339
+ prefers_id=True,
340
+ to_field_name="name",
341
+ queryset=RearPortTemplate.objects.all(),
342
+ label="Rear port templates (name or ID)",
343
+ )
344
+ has_rear_port_templates = RelatedMembershipBooleanFilter(
345
+ field_name="rear_port_templates",
346
+ label="Has rear port templates",
347
+ )
348
+ module_bay_templates = django_filters.ModelMultipleChoiceFilter(
349
+ queryset=ModuleBayTemplate.objects.all(),
350
+ )
351
+ has_module_bay_templates = RelatedMembershipBooleanFilter(
352
+ field_name="module_bay_templates",
353
+ label="Has module bay templates",
354
+ )
@@ -10,6 +10,7 @@ from nautobot.core.filters import (
10
10
  ModelMultipleChoiceFilter,
11
11
  MultiValueCharFilter,
12
12
  MultiValueMACAddressFilter,
13
+ MultiValueNumberFilter,
13
14
  MultiValueUUIDFilter,
14
15
  NameSearchFilterSet,
15
16
  NaturalKeyOrPKMultipleChoiceFilter,
@@ -18,11 +19,12 @@ from nautobot.core.filters import (
18
19
  TreeNodeMultipleChoiceFilter,
19
20
  )
20
21
  from nautobot.core.utils.data import is_uuid
21
- from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
22
22
  from nautobot.dcim.choices import (
23
23
  CableTypeChoices,
24
24
  ConsolePortTypeChoices,
25
25
  ControllerCapabilitiesChoices,
26
+ InterfaceDuplexChoices,
27
+ InterfaceSpeedChoices,
26
28
  InterfaceTypeChoices,
27
29
  PowerOutletTypeChoices,
28
30
  PowerPortTypeChoices,
@@ -35,7 +37,7 @@ from nautobot.dcim.constants import (
35
37
  VIRTUAL_IFACE_TYPES,
36
38
  WIRELESS_IFACE_TYPES,
37
39
  )
38
- from nautobot.dcim.filters.mixins import (
40
+ from nautobot.dcim.filter_mixins import (
39
41
  CableTerminationModelFilterSetMixin,
40
42
  DeviceComponentModelFilterSetMixin,
41
43
  DeviceComponentTemplateModelFilterSetMixin,
@@ -103,15 +105,14 @@ from nautobot.extras.filters import (
103
105
  )
104
106
  from nautobot.extras.models import ExternalIntegration, SecretsGroup
105
107
  from nautobot.extras.utils import FeatureQuery
106
- from nautobot.ipam.models import IPAddress, VLAN, VLANGroup
107
- from nautobot.tenancy.filters.mixins import TenancyModelFilterSetMixin
108
+ from nautobot.ipam.models import IPAddress, VLAN, VLANGroup, VRF
109
+ from nautobot.tenancy.filter_mixins import TenancyModelFilterSetMixin
108
110
  from nautobot.tenancy.models import Tenant
109
111
  from nautobot.virtualization.models import Cluster, VirtualMachine
110
112
  from nautobot.wireless.models import RadioProfile, WirelessNetwork
111
113
 
112
114
  __all__ = (
113
115
  "CableFilterSet",
114
- "CableTerminationFilterSet",
115
116
  "CableTerminationModelFilterSetMixin",
116
117
  "ConsoleConnectionFilterSet",
117
118
  "ConsolePortFilterSet",
@@ -145,7 +146,6 @@ __all__ = (
145
146
  "ModuleFamilyFilterSet",
146
147
  "ModuleFilterSet",
147
148
  "ModuleTypeFilterSet",
148
- "PathEndpointFilterSet",
149
149
  "PathEndpointModelFilterSetMixin",
150
150
  "PlatformFilterSet",
151
151
  "PowerConnectionFilterSet",
@@ -667,12 +667,6 @@ class DeviceTypeFilterSet(DeviceTypeModuleTypeCommonFiltersMixin, NautobotFilter
667
667
  return queryset.exclude(device_bay_templates__isnull=value)
668
668
 
669
669
 
670
- # TODO: remove in 2.2
671
- @class_deprecated_in_favor_of(DeviceComponentTemplateModelFilterSetMixin)
672
- class DeviceTypeComponentFilterSet(DeviceComponentTemplateModelFilterSetMixin):
673
- pass
674
-
675
-
676
670
  class ConsolePortTemplateFilterSet(ModularDeviceComponentTemplateModelFilterSetMixin, BaseFilterSet):
677
671
  class Meta:
678
672
  model = ConsolePortTemplate
@@ -939,6 +933,11 @@ class DeviceFilterSet(
939
933
  to_field_name="name",
940
934
  label="Controller (name or ID)",
941
935
  )
936
+ vrfs = NaturalKeyOrPKMultipleChoiceFilter(
937
+ queryset=VRF.objects.all(),
938
+ to_field_name="rd",
939
+ label="VRFs (ID or RD)",
940
+ )
942
941
 
943
942
  def filter_ip_addresses(self, queryset, name, value):
944
943
  pk_values = set(item for item in value if is_uuid(item))
@@ -978,24 +977,6 @@ class DeviceFilterSet(
978
977
  return queryset.filter(params)
979
978
 
980
979
 
981
- # TODO: remove in 2.2
982
- @class_deprecated_in_favor_of(DeviceComponentModelFilterSetMixin)
983
- class DeviceComponentFilterSet(DeviceComponentModelFilterSetMixin):
984
- pass
985
-
986
-
987
- # TODO: remove in 2.2
988
- @class_deprecated_in_favor_of(CableTerminationModelFilterSetMixin)
989
- class CableTerminationFilterSet(CableTerminationModelFilterSetMixin):
990
- pass
991
-
992
-
993
- # TODO: remove in 2.2
994
- @class_deprecated_in_favor_of(PathEndpointModelFilterSetMixin)
995
- class PathEndpointFilterSet(PathEndpointModelFilterSetMixin):
996
- pass
997
-
998
-
999
980
  class ConsolePortFilterSet(
1000
981
  ModularDeviceComponentModelFilterSetMixin,
1001
982
  CableTerminationModelFilterSetMixin,
@@ -1156,6 +1137,8 @@ class InterfaceFilterSet(
1156
1137
  vlan_id = django_filters.CharFilter(method="filter_vlan_id", label="Assigned VLAN")
1157
1138
  vlan = django_filters.NumberFilter(method="filter_vlan", label="Assigned VID")
1158
1139
  type = django_filters.MultipleChoiceFilter(choices=InterfaceTypeChoices, null_value=None)
1140
+ duplex = django_filters.MultipleChoiceFilter(choices=InterfaceDuplexChoices, null_value=None)
1141
+ speed = MultiValueNumberFilter(lookup_expr="exact", choices=InterfaceSpeedChoices)
1159
1142
  interface_redundancy_groups = NaturalKeyOrPKMultipleChoiceFilter(
1160
1143
  queryset=InterfaceRedundancyGroup.objects.all(),
1161
1144
  to_field_name="name",
@@ -1188,6 +1171,8 @@ class InterfaceFilterSet(
1188
1171
  "id",
1189
1172
  "name",
1190
1173
  "type",
1174
+ "duplex",
1175
+ "speed",
1191
1176
  "enabled",
1192
1177
  "mtu",
1193
1178
  "mgmt_only",
@@ -1524,12 +1509,6 @@ class ConnectionFilterSetMixin:
1524
1509
  return queryset.filter(**{f"{name}__in": value})
1525
1510
 
1526
1511
 
1527
- # TODO: remove in 2.2
1528
- @class_deprecated_in_favor_of(ConnectionFilterSetMixin)
1529
- class ConnectionFilterSet(ConnectionFilterSetMixin):
1530
- pass
1531
-
1532
-
1533
1512
  class ConsoleConnectionFilterSet(ConnectionFilterSetMixin, BaseFilterSet):
1534
1513
  location = django_filters.CharFilter(
1535
1514
  method="filter_location",