nautobot 2.4.0b1__py3-none-any.whl → 2.4.1__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.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (601) hide show
  1. nautobot/apps/__init__.py +1 -1
  2. nautobot/apps/api.py +8 -8
  3. nautobot/apps/change_logging.py +2 -2
  4. nautobot/apps/choices.py +4 -4
  5. nautobot/apps/events.py +3 -3
  6. nautobot/apps/factory.py +2 -2
  7. nautobot/apps/filters.py +1 -1
  8. nautobot/apps/forms.py +20 -20
  9. nautobot/apps/graphql.py +2 -2
  10. nautobot/apps/jobs.py +8 -8
  11. nautobot/apps/models.py +19 -19
  12. nautobot/apps/tables.py +1 -1
  13. nautobot/apps/testing.py +10 -10
  14. nautobot/apps/ui.py +2 -2
  15. nautobot/apps/utils.py +7 -7
  16. nautobot/apps/views.py +7 -7
  17. nautobot/circuits/api/serializers.py +1 -0
  18. nautobot/circuits/api/views.py +4 -8
  19. nautobot/circuits/tables.py +2 -1
  20. nautobot/circuits/templates/circuits/circuit_create.html +1 -7
  21. nautobot/circuits/views.py +3 -3
  22. nautobot/cloud/api/views.py +6 -10
  23. nautobot/cloud/models.py +1 -1
  24. nautobot/cloud/views.py +0 -16
  25. nautobot/core/api/constants.py +11 -0
  26. nautobot/core/api/fields.py +5 -5
  27. nautobot/core/api/filter_backends.py +3 -9
  28. nautobot/core/api/schema.py +13 -2
  29. nautobot/core/api/serializers.py +40 -34
  30. nautobot/core/api/views.py +56 -4
  31. nautobot/core/celery/log.py +4 -4
  32. nautobot/core/celery/schedulers.py +2 -2
  33. nautobot/core/choices.py +2 -2
  34. nautobot/core/events/__init__.py +3 -3
  35. nautobot/core/filters.py +67 -35
  36. nautobot/core/forms/__init__.py +19 -19
  37. nautobot/core/forms/fields.py +14 -11
  38. nautobot/core/forms/forms.py +33 -2
  39. nautobot/core/graphql/types.py +1 -1
  40. nautobot/core/jobs/__init__.py +28 -7
  41. nautobot/core/jobs/bulk_actions.py +285 -0
  42. nautobot/core/jobs/cleanup.py +48 -12
  43. nautobot/core/jobs/groups.py +1 -1
  44. nautobot/core/management/commands/validate_models.py +1 -1
  45. nautobot/core/models/__init__.py +3 -1
  46. nautobot/core/models/query_functions.py +2 -2
  47. nautobot/core/models/tree_queries.py +6 -3
  48. nautobot/core/settings.py +29 -2
  49. nautobot/core/settings.yaml +21 -0
  50. nautobot/core/tables.py +79 -61
  51. nautobot/core/templates/about.html +67 -0
  52. nautobot/core/templates/inc/media.html +3 -0
  53. nautobot/core/templates/inc/nav_menu.html +1 -0
  54. nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
  55. nautobot/core/templates/inc/tenant_table_row.html +11 -0
  56. nautobot/core/templates/nautobot_config.py.j2 +13 -0
  57. nautobot/core/templates/search.html +7 -0
  58. nautobot/core/templates/utilities/render_jinja2.html +1 -1
  59. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  60. nautobot/core/templates/utilities/theme_preview.html +7 -0
  61. nautobot/core/templatetags/helpers.py +11 -2
  62. nautobot/core/testing/__init__.py +8 -8
  63. nautobot/core/testing/api.py +170 -15
  64. nautobot/core/testing/filters.py +45 -10
  65. nautobot/core/testing/forms.py +2 -0
  66. nautobot/core/testing/integration.py +86 -4
  67. nautobot/core/testing/mixins.py +7 -2
  68. nautobot/core/testing/views.py +44 -29
  69. nautobot/core/tests/integration/test_app_home.py +0 -1
  70. nautobot/core/tests/integration/test_app_navbar.py +0 -1
  71. nautobot/core/tests/integration/test_filters.py +0 -2
  72. nautobot/core/tests/integration/test_home.py +0 -1
  73. nautobot/core/tests/integration/test_navbar.py +0 -1
  74. nautobot/core/tests/integration/test_view_authentication.py +1 -0
  75. nautobot/core/tests/runner.py +1 -1
  76. nautobot/core/tests/test_api.py +98 -1
  77. nautobot/core/tests/test_csv.py +25 -3
  78. nautobot/core/tests/test_filters.py +209 -246
  79. nautobot/core/tests/test_forms.py +1 -0
  80. nautobot/core/tests/test_jobs.py +460 -1
  81. nautobot/core/tests/test_models.py +9 -0
  82. nautobot/core/tests/test_settings_schema.py +7 -0
  83. nautobot/core/tests/test_tables.py +100 -0
  84. nautobot/core/tests/test_utils.py +63 -1
  85. nautobot/core/tests/test_views.py +30 -3
  86. nautobot/core/ui/nav.py +1 -0
  87. nautobot/core/ui/object_detail.py +15 -1
  88. nautobot/core/urls.py +11 -0
  89. nautobot/core/utils/lookup.py +11 -8
  90. nautobot/core/utils/querysets.py +64 -0
  91. nautobot/core/utils/requests.py +24 -9
  92. nautobot/core/views/__init__.py +42 -0
  93. nautobot/core/views/generic.py +131 -197
  94. nautobot/core/views/mixins.py +126 -38
  95. nautobot/core/views/renderers.py +6 -6
  96. nautobot/dcim/api/serializers.py +56 -64
  97. nautobot/dcim/api/views.py +47 -113
  98. nautobot/dcim/constants.py +6 -13
  99. nautobot/dcim/factory.py +6 -1
  100. nautobot/dcim/filters/__init__.py +31 -2
  101. nautobot/dcim/forms.py +36 -17
  102. nautobot/dcim/graphql/types.py +2 -2
  103. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
  104. nautobot/dcim/models/__init__.py +1 -1
  105. nautobot/dcim/models/device_component_templates.py +2 -2
  106. nautobot/dcim/models/device_components.py +22 -20
  107. nautobot/dcim/models/devices.py +10 -1
  108. nautobot/dcim/models/locations.py +3 -3
  109. nautobot/dcim/models/power.py +6 -5
  110. nautobot/dcim/models/racks.py +4 -4
  111. nautobot/dcim/tables/__init__.py +3 -3
  112. nautobot/dcim/tables/devices.py +7 -5
  113. nautobot/dcim/tables/devicetypes.py +2 -2
  114. nautobot/dcim/tables/racks.py +1 -1
  115. nautobot/dcim/templates/dcim/controller_create.html +1 -7
  116. nautobot/dcim/templates/dcim/controller_retrieve.html +1 -9
  117. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +2 -0
  118. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +5 -0
  119. nautobot/dcim/templates/dcim/device.html +1 -9
  120. nautobot/dcim/templates/dcim/device_edit.html +36 -37
  121. nautobot/dcim/templates/dcim/location.html +1 -9
  122. nautobot/dcim/templates/dcim/location_edit.html +1 -7
  123. nautobot/dcim/templates/dcim/rack.html +1 -9
  124. nautobot/dcim/templates/dcim/rack_edit.html +1 -7
  125. nautobot/dcim/templates/dcim/rackreservation.html +1 -9
  126. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +1 -9
  127. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +1 -7
  128. nautobot/dcim/tests/integration/test_controller.py +62 -0
  129. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
  130. nautobot/dcim/tests/integration/test_device_bulk_delete.py +189 -0
  131. nautobot/dcim/tests/integration/test_device_bulk_edit.py +181 -0
  132. nautobot/dcim/tests/test_api.py +16 -5
  133. nautobot/dcim/tests/test_filters.py +33 -0
  134. nautobot/dcim/tests/test_forms.py +51 -2
  135. nautobot/dcim/tests/test_graphql.py +52 -0
  136. nautobot/dcim/tests/test_jobs.py +118 -0
  137. nautobot/dcim/tests/test_models.py +52 -9
  138. nautobot/dcim/tests/test_views.py +21 -83
  139. nautobot/dcim/views.py +1 -13
  140. nautobot/extras/api/customfields.py +2 -2
  141. nautobot/extras/api/serializers.py +90 -85
  142. nautobot/extras/api/views.py +22 -27
  143. nautobot/extras/constants.py +2 -0
  144. nautobot/extras/filters/__init__.py +8 -6
  145. nautobot/extras/forms/base.py +2 -2
  146. nautobot/extras/forms/forms.py +139 -31
  147. nautobot/extras/forms/mixins.py +14 -6
  148. nautobot/extras/group_sync.py +3 -3
  149. nautobot/extras/health_checks.py +1 -2
  150. nautobot/extras/jobs.py +85 -18
  151. nautobot/extras/managers.py +3 -1
  152. nautobot/extras/migrations/0018_joblog_data_migration.py +7 -9
  153. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
  154. nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
  155. nautobot/extras/models/__init__.py +1 -1
  156. nautobot/extras/models/contacts.py +1 -1
  157. nautobot/extras/models/customfields.py +12 -11
  158. nautobot/extras/models/groups.py +11 -9
  159. nautobot/extras/models/jobs.py +23 -4
  160. nautobot/extras/models/models.py +2 -2
  161. nautobot/extras/plugins/__init__.py +13 -2
  162. nautobot/extras/plugins/marketplace_manifest.yml +84 -79
  163. nautobot/extras/plugins/tables.py +16 -14
  164. nautobot/extras/plugins/views.py +65 -69
  165. nautobot/extras/registry.py +1 -1
  166. nautobot/extras/secrets/__init__.py +2 -2
  167. nautobot/extras/tables.py +7 -5
  168. nautobot/extras/templates/extras/dynamicgroup.html +1 -9
  169. nautobot/extras/templates/extras/job_detail.html +16 -0
  170. nautobot/extras/templates/extras/job_edit.html +1 -0
  171. nautobot/extras/templates/extras/jobqueue_retrieve.html +1 -9
  172. nautobot/extras/templates/extras/marketplace.html +29 -11
  173. nautobot/extras/templates/extras/plugin_detail.html +32 -15
  174. nautobot/extras/templates/extras/plugins_tiles.html +21 -10
  175. nautobot/extras/templatetags/job_buttons.py +4 -4
  176. nautobot/extras/test_jobs/api_test_job.py +1 -1
  177. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  178. nautobot/extras/test_jobs/dry_run.py +1 -1
  179. nautobot/extras/test_jobs/fail.py +5 -5
  180. nautobot/extras/test_jobs/file_output.py +1 -1
  181. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  182. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  183. nautobot/extras/test_jobs/ipaddress_vars.py +3 -1
  184. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  185. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  186. nautobot/extras/test_jobs/log_redaction.py +1 -1
  187. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  188. nautobot/extras/test_jobs/modify_db.py +1 -1
  189. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  190. nautobot/extras/test_jobs/object_var_required.py +1 -1
  191. nautobot/extras/test_jobs/object_vars.py +1 -1
  192. nautobot/extras/test_jobs/pass.py +3 -3
  193. nautobot/extras/test_jobs/profiling.py +1 -1
  194. nautobot/extras/test_jobs/relative_import.py +3 -3
  195. nautobot/extras/test_jobs/singleton.py +16 -0
  196. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  197. nautobot/extras/test_jobs/task_queues.py +1 -1
  198. nautobot/extras/tests/integration/test_plugin_banner.py +0 -2
  199. nautobot/extras/tests/test_api.py +13 -13
  200. nautobot/extras/tests/test_customfields.py +1 -1
  201. nautobot/extras/tests/test_datasources.py +2 -1
  202. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  203. nautobot/extras/tests/test_filters.py +6 -6
  204. nautobot/extras/tests/test_forms.py +33 -1
  205. nautobot/extras/tests/test_jobs.py +178 -32
  206. nautobot/extras/tests/test_models.py +16 -10
  207. nautobot/extras/tests/test_plugins.py +62 -9
  208. nautobot/extras/tests/test_relationships.py +120 -9
  209. nautobot/extras/tests/test_views.py +56 -194
  210. nautobot/extras/utils.py +3 -2
  211. nautobot/extras/views.py +30 -98
  212. nautobot/ipam/api/fields.py +3 -3
  213. nautobot/ipam/api/serializers.py +41 -33
  214. nautobot/ipam/api/views.py +68 -117
  215. nautobot/ipam/factory.py +1 -1
  216. nautobot/ipam/filters.py +3 -2
  217. nautobot/ipam/lookups.py +101 -62
  218. nautobot/ipam/models.py +66 -16
  219. nautobot/ipam/querysets.py +2 -2
  220. nautobot/ipam/tables.py +23 -7
  221. nautobot/ipam/templates/ipam/ipaddress.html +1 -9
  222. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
  223. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
  224. nautobot/ipam/templates/ipam/prefix.html +1 -9
  225. nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
  226. nautobot/ipam/templates/ipam/vlan.html +1 -9
  227. nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
  228. nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
  229. nautobot/ipam/tests/test_api.py +436 -3
  230. nautobot/ipam/tests/test_forms.py +49 -47
  231. nautobot/ipam/tests/test_migrations.py +30 -30
  232. nautobot/ipam/tests/test_models.py +95 -34
  233. nautobot/ipam/tests/test_querysets.py +63 -1
  234. nautobot/ipam/tests/test_views.py +3 -0
  235. nautobot/ipam/utils/__init__.py +36 -6
  236. nautobot/ipam/views.py +61 -87
  237. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  238. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  239. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
  240. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  241. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  242. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  243. nautobot/project-static/docs/404.html +46 -4
  244. nautobot/project-static/docs/apps/index.html +46 -4
  245. nautobot/project-static/docs/apps/nautobot-apps.html +47 -6
  246. nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
  247. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.88dd0f4e.min.js} +2 -2
  248. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.88dd0f4e.min.js.map} +2 -2
  249. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +62 -10
  250. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +59 -7
  251. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +374 -122
  252. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +90 -18
  253. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +95 -21
  254. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +53 -6
  255. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +52 -5
  256. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +79 -17
  257. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +102 -28
  258. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +108 -21
  259. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +131 -38
  260. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +239 -65
  261. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +581 -165
  262. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +109 -36
  263. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +453 -167
  264. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +493 -211
  265. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +60 -8
  266. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +71 -15
  267. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +407 -55
  268. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +620 -205
  269. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +858 -412
  270. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +59 -7
  271. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +448 -186
  272. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +365 -147
  273. nautobot/project-static/docs/development/apps/api/configuration-view.html +46 -4
  274. nautobot/project-static/docs/development/apps/api/database-backend-config.html +46 -4
  275. nautobot/project-static/docs/development/apps/api/models/django-admin.html +46 -4
  276. nautobot/project-static/docs/development/apps/api/models/global-search.html +46 -4
  277. nautobot/project-static/docs/development/apps/api/models/graphql.html +46 -4
  278. nautobot/project-static/docs/development/apps/api/models/index.html +46 -4
  279. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +46 -4
  280. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +46 -4
  281. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +46 -4
  282. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +46 -4
  283. nautobot/project-static/docs/development/apps/api/platform-features/index.html +46 -4
  284. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +46 -4
  285. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +46 -4
  286. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +46 -4
  287. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +46 -4
  288. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +68 -7
  289. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +46 -4
  290. nautobot/project-static/docs/development/apps/api/prometheus.html +46 -4
  291. nautobot/project-static/docs/development/apps/api/setup.html +46 -4
  292. nautobot/project-static/docs/development/apps/api/testing.html +46 -4
  293. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +46 -4
  294. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +46 -4
  295. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +46 -4
  296. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +46 -4
  297. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +46 -4
  298. nautobot/project-static/docs/development/apps/api/views/base-template.html +46 -4
  299. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +46 -4
  300. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +46 -4
  301. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +46 -4
  302. nautobot/project-static/docs/development/apps/api/views/index.html +46 -4
  303. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +46 -4
  304. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +46 -4
  305. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +46 -4
  306. nautobot/project-static/docs/development/apps/api/views/notes.html +46 -4
  307. nautobot/project-static/docs/development/apps/api/views/rest-api.html +52 -6
  308. nautobot/project-static/docs/development/apps/api/views/urls.html +46 -4
  309. nautobot/project-static/docs/development/apps/index.html +46 -4
  310. nautobot/project-static/docs/development/apps/migration/code-updates.html +46 -4
  311. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +46 -4
  312. nautobot/project-static/docs/development/apps/migration/from-v1.html +46 -4
  313. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +46 -4
  314. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +46 -4
  315. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +46 -4
  316. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +46 -4
  317. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +50 -8
  318. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +46 -4
  319. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +211 -14
  320. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +46 -4
  321. nautobot/project-static/docs/development/apps/porting-from-netbox.html +46 -4
  322. nautobot/project-static/docs/development/core/application-registry.html +46 -4
  323. nautobot/project-static/docs/development/core/best-practices.html +46 -4
  324. nautobot/project-static/docs/development/core/bootstrap-ui.html +46 -4
  325. nautobot/project-static/docs/development/core/caching.html +46 -4
  326. nautobot/project-static/docs/development/core/controllers.html +46 -4
  327. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +73 -74
  328. nautobot/project-static/docs/development/core/generic-views.html +46 -4
  329. nautobot/project-static/docs/development/core/getting-started.html +249 -224
  330. nautobot/project-static/docs/development/core/homepage.html +49 -7
  331. nautobot/project-static/docs/development/core/index.html +46 -4
  332. nautobot/project-static/docs/development/core/{local-k8s.html → minikube-dev-environment-for-k8s-jobs.html} +469 -168
  333. nautobot/project-static/docs/development/core/model-checklist.html +56 -12
  334. nautobot/project-static/docs/development/core/model-features.html +46 -4
  335. nautobot/project-static/docs/development/core/natural-keys.html +46 -4
  336. nautobot/project-static/docs/development/core/navigation-menu.html +46 -4
  337. nautobot/project-static/docs/development/core/release-checklist.html +49 -7
  338. nautobot/project-static/docs/development/core/role-internals.html +46 -4
  339. nautobot/project-static/docs/development/core/settings.html +46 -4
  340. nautobot/project-static/docs/development/core/style-guide.html +49 -7
  341. nautobot/project-static/docs/development/core/templates.html +46 -4
  342. nautobot/project-static/docs/development/core/testing.html +46 -4
  343. nautobot/project-static/docs/development/core/ui-component-framework.html +369 -273
  344. nautobot/project-static/docs/development/core/user-preferences.html +46 -4
  345. nautobot/project-static/docs/development/index.html +46 -4
  346. nautobot/project-static/docs/development/jobs/index.html +216 -122
  347. nautobot/project-static/docs/development/jobs/migration/from-v1.html +46 -4
  348. nautobot/project-static/docs/index.html +54 -23
  349. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  350. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  351. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  352. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  353. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  354. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  355. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  356. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  357. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  358. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  359. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  360. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  361. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  362. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  363. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  364. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  365. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  366. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  367. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  368. nautobot/project-static/docs/objects.inv +0 -0
  369. nautobot/project-static/docs/overview/application_stack.html +47 -7
  370. nautobot/project-static/docs/overview/design_philosophy.html +46 -4
  371. nautobot/project-static/docs/release-notes/index.html +52 -12
  372. nautobot/project-static/docs/release-notes/version-1.0.html +234 -193
  373. nautobot/project-static/docs/release-notes/version-1.1.html +231 -190
  374. nautobot/project-static/docs/release-notes/version-1.2.html +306 -265
  375. nautobot/project-static/docs/release-notes/version-1.3.html +332 -291
  376. nautobot/project-static/docs/release-notes/version-1.4.html +417 -377
  377. nautobot/project-static/docs/release-notes/version-1.5.html +605 -566
  378. nautobot/project-static/docs/release-notes/version-1.6.html +904 -447
  379. nautobot/project-static/docs/release-notes/version-2.0.html +528 -489
  380. nautobot/project-static/docs/release-notes/version-2.1.html +363 -324
  381. nautobot/project-static/docs/release-notes/version-2.2.html +356 -317
  382. nautobot/project-static/docs/release-notes/version-2.3.html +997 -352
  383. nautobot/project-static/docs/release-notes/version-2.4.html +525 -101
  384. nautobot/project-static/docs/requirements.txt +2 -2
  385. nautobot/project-static/docs/search/search_index.json +1 -1
  386. nautobot/project-static/docs/sitemap.xml +295 -287
  387. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  388. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +46 -4
  389. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +46 -4
  390. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +48 -6
  391. nautobot/project-static/docs/user-guide/administration/configuration/index.html +46 -4
  392. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +46 -4
  393. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +110 -8
  394. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +46 -4
  395. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +46 -4
  396. nautobot/project-static/docs/user-guide/administration/guides/docker.html +46 -4
  397. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +46 -4
  398. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +46 -4
  399. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +46 -4
  400. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +46 -4
  401. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +46 -4
  402. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +48 -6
  403. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +46 -4
  404. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +46 -4
  405. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +46 -4
  406. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +66 -8
  407. nautobot/project-static/docs/user-guide/administration/installation/index.html +46 -4
  408. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +47 -5
  409. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +46 -4
  410. nautobot/project-static/docs/user-guide/administration/installation/services.html +46 -4
  411. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +46 -4
  412. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +46 -4
  413. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +46 -4
  414. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +46 -4
  415. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +46 -4
  416. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +46 -4
  417. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +46 -4
  418. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +46 -4
  419. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +46 -4
  420. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +49 -8
  421. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +46 -4
  422. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +46 -4
  423. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +46 -4
  424. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +46 -4
  425. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +46 -4
  426. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +46 -4
  427. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +46 -4
  428. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +46 -4
  429. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +46 -4
  430. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +46 -4
  431. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +46 -4
  432. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +46 -4
  433. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +46 -4
  434. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +46 -4
  435. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +46 -4
  436. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +46 -4
  437. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +46 -4
  438. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +46 -4
  439. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +46 -4
  440. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +46 -4
  441. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +46 -4
  442. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +46 -4
  443. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +46 -4
  444. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +46 -4
  445. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +46 -4
  446. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +46 -4
  447. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +46 -4
  448. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +46 -4
  449. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +46 -4
  450. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +46 -4
  451. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +46 -4
  452. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +46 -4
  453. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +46 -4
  454. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +46 -4
  455. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +46 -4
  456. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +46 -4
  457. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +46 -4
  458. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +46 -4
  459. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +46 -4
  460. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +46 -4
  461. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +46 -4
  462. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +46 -4
  463. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +46 -4
  464. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +46 -4
  465. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +46 -4
  466. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +46 -4
  467. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +46 -4
  468. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +46 -4
  469. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +46 -4
  470. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +46 -4
  471. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +46 -4
  472. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +46 -4
  473. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +46 -4
  474. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +46 -4
  475. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +46 -4
  476. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +46 -4
  477. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +46 -4
  478. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +50 -12
  479. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +46 -4
  480. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +46 -4
  481. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +46 -4
  482. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +46 -4
  483. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +46 -4
  484. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +46 -4
  485. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +46 -4
  486. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +46 -4
  487. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +46 -4
  488. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +46 -4
  489. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +46 -4
  490. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +46 -4
  491. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +49 -7
  492. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +46 -4
  493. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +46 -4
  494. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +46 -4
  495. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +46 -4
  496. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +46 -4
  497. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +46 -4
  498. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +46 -4
  499. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +46 -4
  500. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +46 -4
  501. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +46 -4
  502. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +46 -4
  503. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +46 -4
  504. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +46 -4
  505. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +46 -4
  506. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +46 -4
  507. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +46 -4
  508. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +46 -4
  509. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +46 -4
  510. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +46 -4
  511. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +46 -4
  512. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +46 -4
  513. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +46 -4
  514. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +51 -7
  515. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +46 -4
  516. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  517. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  518. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  519. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  520. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  521. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  522. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  523. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  524. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  525. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  526. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  527. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  528. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  529. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +46 -4
  530. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +46 -4
  531. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +49 -7
  532. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
  533. nautobot/project-static/docs/user-guide/index.html +46 -4
  534. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +46 -4
  535. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +50 -8
  536. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +46 -4
  537. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +46 -4
  538. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +46 -4
  539. nautobot/project-static/docs/user-guide/platform-functionality/events.html +46 -4
  540. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +46 -4
  541. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +46 -4
  542. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +46 -4
  543. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +46 -4
  544. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +46 -4
  545. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +46 -4
  546. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +50 -7
  547. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +46 -4
  548. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +49 -7
  549. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +46 -4
  550. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +46 -4
  551. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
  552. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +46 -4
  553. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +46 -4
  554. nautobot/project-static/docs/user-guide/platform-functionality/note.html +46 -4
  555. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +46 -4
  556. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +47 -5
  557. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +46 -4
  558. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +46 -4
  559. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +94 -25
  560. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +74 -5
  561. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +46 -4
  562. nautobot/project-static/docs/user-guide/platform-functionality/role.html +46 -4
  563. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +46 -4
  564. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +46 -4
  565. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +46 -4
  566. nautobot/project-static/docs/user-guide/platform-functionality/status.html +46 -4
  567. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +46 -4
  568. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +46 -4
  569. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +46 -4
  570. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +46 -4
  571. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +46 -4
  572. nautobot/project-static/js/forms.js +1 -1
  573. nautobot/tenancy/api/views.py +9 -13
  574. nautobot/tenancy/views.py +4 -2
  575. nautobot/users/admin.py +1 -1
  576. nautobot/users/api/serializers.py +5 -4
  577. nautobot/users/api/views.py +3 -3
  578. nautobot/virtualization/api/serializers.py +4 -4
  579. nautobot/virtualization/api/views.py +5 -24
  580. nautobot/virtualization/filters.py +20 -3
  581. nautobot/virtualization/models.py +1 -1
  582. nautobot/virtualization/tables.py +2 -2
  583. nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
  584. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
  585. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
  586. nautobot/virtualization/tests/test_filters.py +17 -0
  587. nautobot/wireless/filters.py +2 -2
  588. nautobot/wireless/forms.py +1 -1
  589. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +1 -9
  590. nautobot/wireless/tests/integration/__init__.py +0 -0
  591. nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
  592. nautobot/wireless/tests/test_filters.py +29 -1
  593. nautobot/wireless/tests/test_views.py +22 -1
  594. nautobot/wireless/views.py +0 -10
  595. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/METADATA +6 -6
  596. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/RECORD +600 -550
  597. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/WHEEL +1 -1
  598. nautobot/core/fixtures/user-data.json +0 -59
  599. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/LICENSE.txt +0 -0
  600. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/NOTICE +0 -0
  601. {nautobot-2.4.0b1.dist-info → nautobot-2.4.1.dist-info}/entry_points.txt +0 -0
@@ -8,7 +8,7 @@ from django.utils.html import format_html
8
8
  from django.utils.safestring import mark_safe
9
9
 
10
10
  from nautobot.core.utils.data import render_jinja2
11
- from nautobot.extras.models import Job, JobButton
11
+ from nautobot.extras.models import Job, JobButton, JobQueue
12
12
 
13
13
  register = template.Library()
14
14
 
@@ -108,17 +108,17 @@ def _render_job_button_for_obj(job_button, obj, context, content_type):
108
108
  # Disable buttons if the user doesn't have permission to run the underlying Job.
109
109
  has_run_perm = Job.objects.check_perms(context["user"], instance=job_button.job, action="run")
110
110
  try:
111
- job_queues = list(job_button.job.job_queues.all().values_list("name", flat=True))
111
+ job_queues = job_button.job.job_queues.all()
112
112
  _job_queue = job_queues[0]
113
113
  except IndexError:
114
- _job_queue = settings.CELERY_TASK_DEFAULT_QUEUE
114
+ _job_queue = JobQueue.objects.get(name=settings.CELERY_TASK_DEFAULT_QUEUE)
115
115
  hidden_inputs = format_html(
116
116
  HIDDEN_INPUTS,
117
117
  csrf_token=context["csrf_token"],
118
118
  object_pk=obj.pk,
119
119
  object_model_name=f"{content_type.app_label}.{content_type.model}",
120
120
  redirect_path=context["request"].path,
121
- job_queue=_job_queue,
121
+ job_queue=_job_queue.pk,
122
122
  )
123
123
  template_args = {
124
124
  "button_id": job_button.pk,
@@ -18,7 +18,7 @@ class APITestJob(Job):
18
18
  var3 = BooleanVar()
19
19
  var4 = ObjectVar(model=Role)
20
20
 
21
- def run(self, var1, var2, var3, var4):
21
+ def run(self, var1, var2, var3, var4): # pylint:disable=arguments-differ
22
22
  logger.debug(var1)
23
23
  logger.info(var2)
24
24
  logger.warning(var3)
@@ -19,7 +19,7 @@ class TestAtomicDecorator(Job):
19
19
  fail = BooleanVar()
20
20
 
21
21
  @transaction.atomic
22
- def run(self, fail=False):
22
+ def run(self, fail=False): # pylint:disable=arguments-differ
23
23
  try:
24
24
  Status.objects.create(name="Test database atomic rollback 1")
25
25
  if fail:
@@ -37,7 +37,7 @@ class TestAtomicContextManager(Job):
37
37
 
38
38
  fail = BooleanVar()
39
39
 
40
- def run(self, fail=False):
40
+ def run(self, fail=False): # pylint:disable=arguments-differ
41
41
  try:
42
42
  with transaction.atomic():
43
43
  Status.objects.create(name="Test database atomic rollback 2")
@@ -13,7 +13,7 @@ class TestDryRun(Job):
13
13
  dryrun = DryRunVar()
14
14
  value = IntegerVar(required=False)
15
15
 
16
- def run(self, dryrun, value=None):
16
+ def run(self, dryrun, value=None): # pylint:disable=arguments-differ
17
17
  """
18
18
  Job function.
19
19
  """
@@ -7,7 +7,7 @@ from nautobot.extras.jobs import get_task_logger, Job, RunJobTaskFailed
7
7
  logger = get_task_logger(__name__)
8
8
 
9
9
 
10
- class TestFail(Job):
10
+ class TestFailJob(Job):
11
11
  """
12
12
  Job with fail result.
13
13
  """
@@ -15,7 +15,7 @@ class TestFail(Job):
15
15
  description = "Validate job import"
16
16
 
17
17
  def before_start(self, task_id, args, kwargs):
18
- if task_id != self.request.id:
18
+ if task_id != self.request.id: # pylint: disable=no-member
19
19
  raise RuntimeError(f"Expected task_id {task_id} to equal self.request.id {self.request.id}")
20
20
  if args:
21
21
  raise RuntimeError(f"Expected args to be empty, but it was {args!r}")
@@ -23,7 +23,7 @@ class TestFail(Job):
23
23
  raise RuntimeError(f"Expected kwargs to be empty, but it was {kwargs!r}")
24
24
  logger.info("before_start() was called as expected")
25
25
 
26
- def run(self):
26
+ def run(self): # pylint:disable=arguments-differ
27
27
  """
28
28
  Job function.
29
29
  """
@@ -71,7 +71,7 @@ class TestFailWithSanitization(Job):
71
71
 
72
72
  description = "Validate job failure sanitization"
73
73
 
74
- def run(self):
74
+ def run(self): # pylint:disable=arguments-differ
75
75
  logger.info("I'm a test job that fails and sanitizes the exception!")
76
76
  exc = RunJobTaskFailed(
77
77
  "fatal: could not read Password for 'https://abc123@github.com': terminal prompts disabled"
@@ -91,4 +91,4 @@ class TestFailWithSanitization(Job):
91
91
  raise exc
92
92
 
93
93
 
94
- register_jobs(TestFail, TestFailWithSanitization)
94
+ register_jobs(TestFailJob, TestFailWithSanitization)
@@ -9,7 +9,7 @@ class FileOutputJob(Job):
9
9
  name = "File Output job"
10
10
  description = "Creates a text file as output."
11
11
 
12
- def run(self, lines):
12
+ def run(self, lines): # pylint:disable=arguments-differ
13
13
  self.create_file("output.txt", "Hello World!\n" * lines)
14
14
 
15
15
 
@@ -21,7 +21,7 @@ class TestFileUploadFail(Job):
21
21
  description="File to upload",
22
22
  )
23
23
 
24
- def run(self, file):
24
+ def run(self, file): # pylint:disable=arguments-differ
25
25
  contents = str(file.read())
26
26
  logger.warning("File contents: %s", contents)
27
27
 
@@ -13,7 +13,7 @@ class TestFileUploadPass(Job):
13
13
  description="File to upload",
14
14
  )
15
15
 
16
- def run(self, file):
16
+ def run(self, file): # pylint:disable=arguments-differ
17
17
  contents = str(file.read())
18
18
  logger.warning("File contents: %s", contents)
19
19
  logger.info("Job didn't crash!")
@@ -39,7 +39,9 @@ class TestIPAddresses(Job):
39
39
  if kwargs[expected_kwarg] is None:
40
40
  raise RuntimeError(f"kwargs[{expected_kwarg}] is unexpectedly None!")
41
41
 
42
- def run(self, *, ipv4_address, ipv4_with_mask, ipv4_network, ipv6_address, ipv6_with_mask, ipv6_network):
42
+ def run( # pylint:disable=arguments-differ
43
+ self, *, ipv4_address, ipv4_with_mask, ipv4_network, ipv6_address, ipv6_with_mask, ipv6_network
44
+ ):
43
45
  if not isinstance(ipv4_address, netaddr.IPAddress):
44
46
  raise RuntimeError(f"Expected ipv4_address to be a netaddr.IPAddress, but it was {ipv4_address!r}")
45
47
  if not isinstance(ipv4_with_mask, netaddr.IPNetwork):
@@ -2,5 +2,5 @@ from nautobot.apps.jobs import Job
2
2
 
3
3
 
4
4
  class ChildJob(Job):
5
- def run(self):
5
+ def run(self): # pylint:disable=arguments-differ
6
6
  pass
@@ -15,7 +15,7 @@ class TestCreateLocationWithCustomField(Job):
15
15
  name = "Location and Custom Field Creation"
16
16
  description = "Location with a custom field"
17
17
 
18
- def run(self):
18
+ def run(self): # pylint: disable=arguments-differ
19
19
  with transaction.atomic():
20
20
  obj_type = ContentType.objects.get_for_model(Location)
21
21
  cf = CustomField(label="cf1", type=CustomFieldTypeChoices.TYPE_TEXT, default="-")
@@ -8,7 +8,7 @@ class TestLogRedaction(Job):
8
8
  class Meta:
9
9
  description = "Test redaction of logs"
10
10
 
11
- def run(self):
11
+ def run(self): # pylint: disable=arguments-differ
12
12
  logger.debug("The secret is supersecret123")
13
13
  logger.info("The secret is supersecret123")
14
14
  logger.warning("The secret is supersecret123")
@@ -8,7 +8,7 @@ class TestLogSkipDBLogging(Job):
8
8
  class Meta:
9
9
  description = "Test logs not being saved to the database"
10
10
 
11
- def run(self):
11
+ def run(self): # pylint: disable=arguments-differ
12
12
  logger.debug("I should NOT be logged to the database", extra={"skip_db_logging": True})
13
13
  logger.info("I should be logged to the database")
14
14
 
@@ -10,7 +10,7 @@ class TestModifyDB(Job):
10
10
  Job that modifies the database.
11
11
  """
12
12
 
13
- def run(self):
13
+ def run(self): # pylint: disable=arguments-differ
14
14
  """
15
15
  Job function.
16
16
  """
@@ -12,7 +12,7 @@ class TestOptionalObjectVar(Job):
12
12
  required=False,
13
13
  )
14
14
 
15
- def run(self, location=None):
15
+ def run(self, location=None): # pylint: disable=arguments-differ
16
16
  logger.info("The Location if any that the user provided.", extra={"object": location})
17
17
  return "Nice Location (or not)!"
18
18
 
@@ -12,7 +12,7 @@ class TestRequiredObjectVar(Job):
12
12
  required=True,
13
13
  )
14
14
 
15
- def run(self, location):
15
+ def run(self, location): # pylint: disable=arguments-differ
16
16
  logger.info("The Location that the user provided.", extra={"object": location})
17
17
  return "Nice Location!"
18
18
 
@@ -13,7 +13,7 @@ class TestObjectVars(Job):
13
13
  role = ObjectVar(model=Role)
14
14
  roles = MultiObjectVar(model=Role)
15
15
 
16
- def run(self, role, roles):
16
+ def run(self, role, roles): # pylint: disable=arguments-differ
17
17
  logger.info("Role: %s", role)
18
18
  logger.warning("Roles: %s", roles)
19
19
 
@@ -5,7 +5,7 @@ from nautobot.extras.jobs import get_task_logger, Job
5
5
  logger = get_task_logger(__name__)
6
6
 
7
7
 
8
- class TestPass(Job):
8
+ class TestPassJob(Job):
9
9
  """
10
10
  Job with pass result.
11
11
  """
@@ -24,7 +24,7 @@ class TestPass(Job):
24
24
  raise RuntimeError(f"Expected kwargs to be empty, but it was {kwargs!r}")
25
25
  logger.info("before_start() was called as expected")
26
26
 
27
- def run(self):
27
+ def run(self): # pylint: disable=arguments-differ
28
28
  """
29
29
  Job function.
30
30
  """
@@ -61,4 +61,4 @@ class TestPass(Job):
61
61
  logger.info("after_return() was called as expected")
62
62
 
63
63
 
64
- register_jobs(TestPass)
64
+ register_jobs(TestPassJob)
@@ -14,7 +14,7 @@ class TestProfilingJob(Job):
14
14
 
15
15
  description = "Test profiling"
16
16
 
17
- def run(self):
17
+ def run(self): # pylint: disable=arguments-differ
18
18
  """
19
19
  Job function.
20
20
  """
@@ -1,10 +1,10 @@
1
- from fail import TestFail # pylint: disable=import-error
1
+ from fail import TestFailJob # pylint: disable=import-error
2
2
 
3
3
  from nautobot.apps.jobs import register_jobs
4
4
 
5
5
 
6
- class TestReallyPass(TestFail):
7
- def run(self):
6
+ class TestReallyPass(TestFailJob):
7
+ def run(self): # pylint: disable=arguments-differ
8
8
  pass
9
9
 
10
10
 
@@ -0,0 +1,16 @@
1
+ import time
2
+
3
+ from nautobot.core.celery import register_jobs
4
+ from nautobot.extras.jobs import Job
5
+
6
+
7
+ class TestSingletonJob(Job):
8
+ class Meta:
9
+ name = "Example Singleton Job"
10
+ is_singleton = True
11
+
12
+ def run(self, *args, **kwargs):
13
+ time.sleep(1)
14
+
15
+
16
+ register_jobs(TestSingletonJob)
@@ -8,7 +8,7 @@ class TestSoftTimeLimitGreaterThanHardTimeLimit(Job):
8
8
  soft_time_limit = 10
9
9
  time_limit = 5
10
10
 
11
- def run(self):
11
+ def run(self): # pylint: disable=arguments-differ
12
12
  pass
13
13
 
14
14
 
@@ -16,7 +16,7 @@ class TestWorkerQueues(Job):
16
16
  "nonexistent", # This queue doesn't exist and should have zero workers
17
17
  ]
18
18
 
19
- def run(self):
19
+ def run(self): # pylint: disable=arguments-differ
20
20
  pass
21
21
 
22
22
 
@@ -4,8 +4,6 @@ from nautobot.core.testing.integration import SeleniumTestCase
4
4
  class PluginBannerTestCase(SeleniumTestCase):
5
5
  """Integration test for rendering of plugin-injected banner content."""
6
6
 
7
- fixtures = ("user-data",)
8
-
9
7
  def test_banner_not_rendered(self):
10
8
  """As implemented, plugin banner does not render if the user is not logged in.
11
9
 
@@ -1536,10 +1536,10 @@ class JobTest(
1536
1536
  self.assertHttpStatus(response, status.HTTP_404_NOT_FOUND)
1537
1537
 
1538
1538
  # Try post to permitted job
1539
- job_model = Job.objects.get_for_class_path("pass.TestPass")
1539
+ job_model = Job.objects.get_for_class_path("pass.TestPassJob")
1540
1540
  job_model.enabled = True
1541
1541
  job_model.validated_save()
1542
- url = self.get_run_url("pass.TestPass")
1542
+ url = self.get_run_url("pass.TestPassJob")
1543
1543
  response = self.client.post(url, **self.header)
1544
1544
  self.assertHttpStatus(response, self.run_success_response_status)
1545
1545
 
@@ -2129,7 +2129,7 @@ class JobTest(
2129
2129
  @mock.patch("nautobot.extras.api.views.get_worker_count", return_value=1)
2130
2130
  def test_run_job_with_default_queue_with_empty_job_model_job_queues(self, _):
2131
2131
  self.add_permissions("extras.run_job")
2132
- job_model = Job.objects.get_for_class_path("pass.TestPass")
2132
+ job_model = Job.objects.get_for_class_path("pass.TestPassJob")
2133
2133
  data = {
2134
2134
  "task_queue": job_model.default_job_queue.name,
2135
2135
  }
@@ -2137,7 +2137,7 @@ class JobTest(
2137
2137
  job_model.job_queues.set([])
2138
2138
  job_model.enabled = True
2139
2139
  job_model.validated_save()
2140
- url = self.get_run_url("pass.TestPass")
2140
+ url = self.get_run_url("pass.TestPassJob")
2141
2141
  response = self.client.post(url, data, format="json", **self.header)
2142
2142
  self.assertHttpStatus(response, self.run_success_response_status)
2143
2143
 
@@ -2564,10 +2564,10 @@ class ScheduledJobTest(
2564
2564
  @classmethod
2565
2565
  def setUpTestData(cls):
2566
2566
  user = User.objects.create(username="user1", is_active=True)
2567
- job_model = Job.objects.get_for_class_path("pass.TestPass")
2567
+ job_model = Job.objects.get_for_class_path("pass.TestPassJob")
2568
2568
  ScheduledJob.objects.create(
2569
2569
  name="test1",
2570
- task="pass.TestPass",
2570
+ task="pass.TestPassJob",
2571
2571
  job_model=job_model,
2572
2572
  interval=JobExecutionType.TYPE_IMMEDIATELY,
2573
2573
  user=user,
@@ -2576,7 +2576,7 @@ class ScheduledJobTest(
2576
2576
  )
2577
2577
  ScheduledJob.objects.create(
2578
2578
  name="test2",
2579
- task="pass.TestPass",
2579
+ task="pass.TestPassJob",
2580
2580
  job_model=job_model,
2581
2581
  interval=JobExecutionType.TYPE_DAILY,
2582
2582
  user=user,
@@ -2586,7 +2586,7 @@ class ScheduledJobTest(
2586
2586
  )
2587
2587
  ScheduledJob.objects.create(
2588
2588
  name="test3",
2589
- task="pass.TestPass",
2589
+ task="pass.TestPassJob",
2590
2590
  job_model=job_model,
2591
2591
  interval=JobExecutionType.TYPE_CUSTOM,
2592
2592
  crontab="34 12 * * *",
@@ -2601,12 +2601,12 @@ class JobApprovalTest(APITestCase):
2601
2601
  @classmethod
2602
2602
  def setUpTestData(cls):
2603
2603
  cls.additional_user = User.objects.create(username="user1", is_active=True)
2604
- cls.job_model = Job.objects.get_for_class_path("pass.TestPass")
2604
+ cls.job_model = Job.objects.get_for_class_path("pass.TestPassJob")
2605
2605
  cls.job_model.enabled = True
2606
2606
  cls.job_model.save()
2607
2607
  cls.scheduled_job = ScheduledJob.objects.create(
2608
2608
  name="test pass",
2609
- task="pass.TestPass",
2609
+ task="pass.TestPassJob",
2610
2610
  job_model=cls.job_model,
2611
2611
  interval=JobExecutionType.TYPE_IMMEDIATELY,
2612
2612
  user=cls.additional_user,
@@ -2659,7 +2659,7 @@ class JobApprovalTest(APITestCase):
2659
2659
  self.add_permissions("extras.approve_job", "extras.view_scheduledjob", "extras.change_scheduledjob")
2660
2660
  scheduled_job = ScheduledJob.objects.create(
2661
2661
  name="test",
2662
- task="pass.TestPass",
2662
+ task="pass.TestPassJob",
2663
2663
  job_model=self.job_model,
2664
2664
  interval=JobExecutionType.TYPE_IMMEDIATELY,
2665
2665
  user=self.user,
@@ -2682,7 +2682,7 @@ class JobApprovalTest(APITestCase):
2682
2682
  self.add_permissions("extras.approve_job", "extras.view_scheduledjob", "extras.change_scheduledjob")
2683
2683
  scheduled_job = ScheduledJob.objects.create(
2684
2684
  name="test",
2685
- task="pass.TestPass",
2685
+ task="pass.TestPassJob",
2686
2686
  job_model=self.job_model,
2687
2687
  interval=JobExecutionType.TYPE_FUTURE,
2688
2688
  one_off=True,
@@ -2699,7 +2699,7 @@ class JobApprovalTest(APITestCase):
2699
2699
  self.add_permissions("extras.approve_job", "extras.view_scheduledjob", "extras.change_scheduledjob")
2700
2700
  scheduled_job = ScheduledJob.objects.create(
2701
2701
  name="test",
2702
- task="pass.TestPass",
2702
+ task="pass.TestPassJob",
2703
2703
  job_model=self.job_model,
2704
2704
  interval=JobExecutionType.TYPE_FUTURE,
2705
2705
  one_off=True,
@@ -2240,7 +2240,7 @@ class CustomFieldTableTest(TestCase):
2240
2240
  self.assertIsNotNone(custom_column, internal_col_name)
2241
2241
  self.assertIsInstance(custom_column, CustomFieldColumn)
2242
2242
 
2243
- rendered_value = bound_row.get_cell(internal_col_name)
2243
+ rendered_value = bound_row.get_cell(internal_col_name) # pylint: disable=no-member
2244
2244
  self.assertEqual(rendered_value, col_expected_value)
2245
2245
 
2246
2246
 
@@ -285,7 +285,8 @@ class GitTest(TransactionTestCase):
285
285
 
286
286
  self.repo.secrets_group = secrets_group
287
287
  self.repo.remote_url = "http://localhost/git.git"
288
- self.repo.provided_contents.remove("extras.job") # avoid failing due to lack of jobs module
288
+ # avoid failing due to lack of jobs module
289
+ self.repo.provided_contents.remove("extras.job") # pylint: disable=no-member
289
290
  self.repo.save()
290
291
 
291
292
  self.mock_request.id = uuid.uuid4()
@@ -587,7 +587,7 @@ class DynamicGroupModelTest(DynamicGroupTestBase): # TODO: BaseModelTestCase mi
587
587
  """
588
588
  pfx_content_type = ContentType.objects.get_for_model(Prefix)
589
589
  group = DynamicGroup(name="pfx", content_type=pfx_content_type)
590
- filterset = group.filterset_class()
590
+ filterset = group.filterset_class() # pylint: disable=not-callable # should not be None here!
591
591
  fields = group._map_filter_fields
592
592
 
593
593
  # We know that `within_include` does not have a `generate_query_{filter_method}` method.
@@ -890,11 +890,11 @@ class JobFilterSetTestCase(FilterTestCases.FilterTestCase):
890
890
  Job.objects.last().tags.set(Tag.objects.get_for_model(Job)[:3])
891
891
 
892
892
  def test_installed(self):
893
- params = {"job_class_name": "TestPass", "installed": True}
893
+ params = {"job_class_name": "TestPassJob", "installed": True}
894
894
  self.assertTrue(self.filterset(params, self.queryset).qs.exists())
895
895
 
896
896
  def test_enabled(self):
897
- params = {"job_class_name": "TestPass", "enabled": False}
897
+ params = {"job_class_name": "TestPassJob", "enabled": False}
898
898
  self.assertTrue(self.filterset(params, self.queryset).qs.exists())
899
899
 
900
900
  def test_dryrun_default(self):
@@ -992,11 +992,11 @@ class JobResultFilterSetTestCase(FilterTestCases.FilterTestCase):
992
992
  jobs = Job.objects.all()[:3]
993
993
  cls.jobs = jobs
994
994
  user = User.objects.create(username="user1", is_active=True)
995
- job_model = Job.objects.get_for_class_path("pass.TestPass")
995
+ job_model = Job.objects.get_for_class_path("pass.TestPassJob")
996
996
  scheduled_jobs = [
997
997
  ScheduledJob.objects.create(
998
998
  name="test1",
999
- task="pass.TestPass",
999
+ task="pass.TestPassJob",
1000
1000
  job_model=job_model,
1001
1001
  interval=JobExecutionType.TYPE_IMMEDIATELY,
1002
1002
  user=user,
@@ -1005,7 +1005,7 @@ class JobResultFilterSetTestCase(FilterTestCases.FilterTestCase):
1005
1005
  ),
1006
1006
  ScheduledJob.objects.create(
1007
1007
  name="test2",
1008
- task="pass.TestPass",
1008
+ task="pass.TestPassJob",
1009
1009
  job_model=job_model,
1010
1010
  interval=JobExecutionType.TYPE_DAILY,
1011
1011
  user=user,
@@ -1015,7 +1015,7 @@ class JobResultFilterSetTestCase(FilterTestCases.FilterTestCase):
1015
1015
  ),
1016
1016
  ScheduledJob.objects.create(
1017
1017
  name="test3",
1018
- task="pass.TestPass",
1018
+ task="pass.TestPassJob",
1019
1019
  job_model=job_model,
1020
1020
  interval=JobExecutionType.TYPE_CUSTOM,
1021
1021
  crontab="34 12 * * *",
@@ -4,12 +4,13 @@ import warnings
4
4
  from django.contrib.auth import get_user_model
5
5
  from django.contrib.contenttypes.models import ContentType
6
6
  from django.db.models import Q
7
+ from django.forms import ChoiceField, MultipleChoiceField
7
8
  from django.test import override_settings, TestCase
8
9
 
9
10
  from nautobot.dcim.forms import DeviceForm, LocationBulkEditForm, LocationForm
10
11
  import nautobot.dcim.models as dcim_models
11
12
  from nautobot.dcim.models import Device, Location, LocationType
12
- from nautobot.extras.choices import RelationshipTypeChoices
13
+ from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipTypeChoices
13
14
  from nautobot.extras.forms import (
14
15
  ConfigContextFilterForm,
15
16
  ConfigContextForm,
@@ -25,6 +26,7 @@ from nautobot.extras.forms import (
25
26
  WebhookForm,
26
27
  )
27
28
  from nautobot.extras.models import (
29
+ CustomField,
28
30
  Job,
29
31
  JobButton,
30
32
  JobHook,
@@ -908,6 +910,19 @@ class RelationshipModelBulkEditFormMixinTestCase(TestCase):
908
910
  ras = RelationshipAssociation.objects.filter(relationship=self.rel_mtom_s)
909
911
  self.assertEqual(1, ras.count())
910
912
 
913
+ def test_form_save_relationship_with_nullified_fields_is_none(self):
914
+ """Test save_relationships with nullified_fields=None."""
915
+ form = LocationBulkEditForm(
916
+ model=dcim_models.Location,
917
+ data={
918
+ "pks": [self.locations[0].pk],
919
+ "add_cr_multiplexing__destination": [ipaddress.pk for ipaddress in self.ipaddresses],
920
+ "add_cr_peer_locations__peer": [self.locations[1].pk],
921
+ },
922
+ )
923
+ form.is_valid()
924
+ form.save_relationships(instance=self.locations[0], nullified_fields=None)
925
+
911
926
  def test_location_form_remove_mtom(self):
912
927
  """Test removal of relationship-associations for many-to-many relationships."""
913
928
  RelationshipAssociation.objects.create(
@@ -1217,3 +1232,20 @@ class CustomFieldModelFormMixinTestCase(TestCase):
1217
1232
 
1218
1233
  custom_field_form = TestForm()
1219
1234
  self.assertIn("_custom_field_data", custom_field_form.fields)
1235
+
1236
+
1237
+ class CustomFieldTestCase(TestCase):
1238
+ def test_to_form_field_type_select(self):
1239
+ """Verify that `to_form_field` and `to_filter_form_field` return the correct field types for a select-type CustomField."""
1240
+ custom_field = CustomField.objects.create(
1241
+ type=CustomFieldTypeChoices.TYPE_SELECT,
1242
+ label="Custom Field Select",
1243
+ )
1244
+ form = custom_field.to_filter_form_field()
1245
+ self.assertIsInstance(form, MultipleChoiceField)
1246
+
1247
+ form = custom_field.to_form_field(for_filter_form=True)
1248
+ self.assertIsInstance(form, MultipleChoiceField)
1249
+
1250
+ form = custom_field.to_form_field(for_filter_form=False)
1251
+ self.assertIsInstance(form, ChoiceField)