nautobot 2.4.0b1__py3-none-any.whl → 2.4.2__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 (668) 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/tests/integration/test_circuits_bulk_operations.py +43 -0
  22. nautobot/circuits/tests/integration/test_relationships.py +1 -1
  23. nautobot/circuits/views.py +3 -3
  24. nautobot/cloud/api/views.py +6 -10
  25. nautobot/cloud/models.py +1 -1
  26. nautobot/cloud/views.py +0 -16
  27. nautobot/core/api/constants.py +11 -0
  28. nautobot/core/api/fields.py +5 -5
  29. nautobot/core/api/filter_backends.py +3 -9
  30. nautobot/core/api/schema.py +13 -2
  31. nautobot/core/api/serializers.py +40 -34
  32. nautobot/core/api/views.py +56 -4
  33. nautobot/core/apps/__init__.py +0 -5
  34. nautobot/core/celery/log.py +4 -4
  35. nautobot/core/celery/schedulers.py +2 -2
  36. nautobot/core/choices.py +2 -2
  37. nautobot/core/events/__init__.py +3 -3
  38. nautobot/core/filters.py +67 -35
  39. nautobot/core/forms/__init__.py +19 -19
  40. nautobot/core/forms/fields.py +14 -11
  41. nautobot/core/forms/forms.py +33 -2
  42. nautobot/core/graphql/types.py +1 -1
  43. nautobot/core/jobs/__init__.py +28 -7
  44. nautobot/core/jobs/bulk_actions.py +285 -0
  45. nautobot/core/jobs/cleanup.py +48 -12
  46. nautobot/core/jobs/groups.py +1 -1
  47. nautobot/core/management/commands/validate_models.py +1 -1
  48. nautobot/core/models/__init__.py +3 -1
  49. nautobot/core/models/query_functions.py +2 -2
  50. nautobot/core/models/tree_queries.py +6 -3
  51. nautobot/core/settings.py +29 -2
  52. nautobot/core/settings.yaml +21 -0
  53. nautobot/core/tables.py +79 -61
  54. nautobot/core/templates/about.html +67 -0
  55. nautobot/core/templates/generic/object_bulk_destroy.html +1 -1
  56. nautobot/core/templates/inc/media.html +3 -0
  57. nautobot/core/templates/inc/nav_menu.html +1 -0
  58. nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
  59. nautobot/core/templates/inc/tenant_table_row.html +11 -0
  60. nautobot/core/templates/nautobot_config.py.j2 +13 -0
  61. nautobot/core/templates/search.html +7 -0
  62. nautobot/core/templates/utilities/render_jinja2.html +1 -1
  63. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  64. nautobot/core/templates/utilities/theme_preview.html +7 -0
  65. nautobot/core/templatetags/helpers.py +11 -2
  66. nautobot/core/testing/__init__.py +8 -8
  67. nautobot/core/testing/api.py +170 -15
  68. nautobot/core/testing/filters.py +45 -10
  69. nautobot/core/testing/forms.py +2 -0
  70. nautobot/core/testing/integration.py +514 -5
  71. nautobot/core/testing/mixins.py +7 -2
  72. nautobot/core/testing/views.py +44 -29
  73. nautobot/core/tests/integration/test_app_home.py +0 -1
  74. nautobot/core/tests/integration/test_app_navbar.py +0 -1
  75. nautobot/core/tests/integration/test_filters.py +0 -2
  76. nautobot/core/tests/integration/test_home.py +0 -1
  77. nautobot/core/tests/integration/test_navbar.py +0 -1
  78. nautobot/core/tests/integration/test_view_authentication.py +1 -0
  79. nautobot/core/tests/runner.py +1 -1
  80. nautobot/core/tests/test_api.py +98 -1
  81. nautobot/core/tests/test_csv.py +25 -3
  82. nautobot/core/tests/test_filters.py +209 -246
  83. nautobot/core/tests/test_forms.py +1 -0
  84. nautobot/core/tests/test_jobs.py +492 -1
  85. nautobot/core/tests/test_models.py +9 -0
  86. nautobot/core/tests/test_settings_schema.py +7 -0
  87. nautobot/core/tests/test_tables.py +100 -0
  88. nautobot/core/tests/test_utils.py +63 -1
  89. nautobot/core/tests/test_views.py +30 -3
  90. nautobot/core/ui/nav.py +1 -0
  91. nautobot/core/ui/object_detail.py +15 -1
  92. nautobot/core/urls.py +11 -0
  93. nautobot/core/utils/git.py +7 -2
  94. nautobot/core/utils/lookup.py +11 -8
  95. nautobot/core/utils/querysets.py +64 -0
  96. nautobot/core/utils/requests.py +24 -9
  97. nautobot/core/views/__init__.py +42 -0
  98. nautobot/core/views/generic.py +131 -197
  99. nautobot/core/views/mixins.py +136 -41
  100. nautobot/core/views/renderers.py +6 -6
  101. nautobot/core/views/utils.py +2 -2
  102. nautobot/dcim/api/serializers.py +56 -64
  103. nautobot/dcim/api/views.py +47 -113
  104. nautobot/dcim/constants.py +6 -13
  105. nautobot/dcim/factory.py +6 -1
  106. nautobot/dcim/filters/__init__.py +31 -2
  107. nautobot/dcim/forms.py +48 -17
  108. nautobot/dcim/graphql/types.py +2 -2
  109. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
  110. nautobot/dcim/models/__init__.py +1 -1
  111. nautobot/dcim/models/device_component_templates.py +2 -2
  112. nautobot/dcim/models/device_components.py +22 -20
  113. nautobot/dcim/models/devices.py +10 -1
  114. nautobot/dcim/models/locations.py +3 -3
  115. nautobot/dcim/models/power.py +6 -5
  116. nautobot/dcim/models/racks.py +4 -4
  117. nautobot/dcim/tables/__init__.py +3 -3
  118. nautobot/dcim/tables/devices.py +9 -6
  119. nautobot/dcim/tables/devicetypes.py +2 -2
  120. nautobot/dcim/tables/racks.py +1 -1
  121. nautobot/dcim/templates/dcim/cable.html +1 -1
  122. nautobot/dcim/templates/dcim/controller_create.html +1 -7
  123. nautobot/dcim/templates/dcim/controller_retrieve.html +1 -9
  124. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +2 -0
  125. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +5 -0
  126. nautobot/dcim/templates/dcim/device/base.html +1 -1
  127. nautobot/dcim/templates/dcim/device.html +3 -11
  128. nautobot/dcim/templates/dcim/device_component.html +1 -1
  129. nautobot/dcim/templates/dcim/device_edit.html +36 -37
  130. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  131. nautobot/dcim/templates/dcim/location.html +2 -10
  132. nautobot/dcim/templates/dcim/location_edit.html +1 -7
  133. nautobot/dcim/templates/dcim/locationtype.html +1 -1
  134. nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
  135. nautobot/dcim/templates/dcim/manufacturer.html +1 -1
  136. nautobot/dcim/templates/dcim/platform.html +1 -1
  137. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  138. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  139. nautobot/dcim/templates/dcim/rack.html +2 -10
  140. nautobot/dcim/templates/dcim/rack_edit.html +1 -7
  141. nautobot/dcim/templates/dcim/rackgroup.html +1 -1
  142. nautobot/dcim/templates/dcim/rackreservation.html +3 -11
  143. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  144. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +1 -9
  145. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +1 -7
  146. nautobot/dcim/tests/integration/test_controller.py +62 -0
  147. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
  148. nautobot/dcim/tests/integration/test_device_bulk_operations.py +30 -0
  149. nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -0
  150. nautobot/dcim/tests/test_api.py +16 -5
  151. nautobot/dcim/tests/test_filters.py +33 -0
  152. nautobot/dcim/tests/test_forms.py +51 -2
  153. nautobot/dcim/tests/test_graphql.py +52 -0
  154. nautobot/dcim/tests/test_jobs.py +118 -0
  155. nautobot/dcim/tests/test_models.py +52 -9
  156. nautobot/dcim/tests/test_views.py +30 -84
  157. nautobot/dcim/views.py +13 -28
  158. nautobot/extras/api/customfields.py +2 -2
  159. nautobot/extras/api/serializers.py +123 -85
  160. nautobot/extras/api/views.py +33 -30
  161. nautobot/extras/constants.py +3 -0
  162. nautobot/extras/datasources/git.py +125 -0
  163. nautobot/extras/filters/__init__.py +8 -6
  164. nautobot/extras/forms/base.py +2 -2
  165. nautobot/extras/forms/forms.py +139 -31
  166. nautobot/extras/forms/mixins.py +14 -6
  167. nautobot/extras/group_sync.py +3 -3
  168. nautobot/extras/health_checks.py +1 -2
  169. nautobot/extras/jobs.py +85 -18
  170. nautobot/extras/managers.py +3 -1
  171. nautobot/extras/migrations/0018_joblog_data_migration.py +7 -9
  172. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
  173. nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
  174. nautobot/extras/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
  175. nautobot/extras/models/__init__.py +1 -1
  176. nautobot/extras/models/contacts.py +1 -1
  177. nautobot/extras/models/customfields.py +41 -23
  178. nautobot/extras/models/datasources.py +85 -0
  179. nautobot/extras/models/groups.py +11 -9
  180. nautobot/extras/models/jobs.py +23 -4
  181. nautobot/extras/models/models.py +17 -2
  182. nautobot/extras/models/relationships.py +17 -5
  183. nautobot/extras/plugins/__init__.py +13 -2
  184. nautobot/extras/plugins/marketplace_manifest.yml +84 -79
  185. nautobot/extras/plugins/tables.py +16 -14
  186. nautobot/extras/plugins/views.py +65 -69
  187. nautobot/extras/registry.py +1 -1
  188. nautobot/extras/secrets/__init__.py +2 -2
  189. nautobot/extras/signals.py +15 -1
  190. nautobot/extras/tables.py +7 -5
  191. nautobot/extras/templates/extras/computedfield.html +1 -1
  192. nautobot/extras/templates/extras/configcontext.html +1 -1
  193. nautobot/extras/templates/extras/configcontextschema.html +1 -1
  194. nautobot/extras/templates/extras/customfield.html +1 -1
  195. nautobot/extras/templates/extras/customlink.html +1 -1
  196. nautobot/extras/templates/extras/dynamicgroup.html +2 -10
  197. nautobot/extras/templates/extras/exporttemplate.html +1 -1
  198. nautobot/extras/templates/extras/gitrepository.html +1 -1
  199. nautobot/extras/templates/extras/graphqlquery.html +1 -1
  200. nautobot/extras/templates/extras/job_detail.html +17 -1
  201. nautobot/extras/templates/extras/job_edit.html +1 -0
  202. nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
  203. nautobot/extras/templates/extras/jobhook.html +1 -1
  204. nautobot/extras/templates/extras/jobqueue_retrieve.html +1 -9
  205. nautobot/extras/templates/extras/jobresult.html +1 -1
  206. nautobot/extras/templates/extras/marketplace.html +29 -11
  207. nautobot/extras/templates/extras/objectchange.html +1 -1
  208. nautobot/extras/templates/extras/plugin_detail.html +33 -16
  209. nautobot/extras/templates/extras/plugins_tiles.html +21 -10
  210. nautobot/extras/templates/extras/relationship.html +1 -63
  211. nautobot/extras/templates/extras/role_retrieve.html +1 -1
  212. nautobot/extras/templates/extras/scheduledjob.html +1 -1
  213. nautobot/extras/templates/extras/secret.html +1 -1
  214. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  215. nautobot/extras/templates/extras/status.html +1 -1
  216. nautobot/extras/templates/extras/tag.html +1 -1
  217. nautobot/extras/templates/extras/webhook.html +1 -1
  218. nautobot/extras/templatetags/job_buttons.py +4 -4
  219. nautobot/extras/test_jobs/api_test_job.py +1 -1
  220. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  221. nautobot/extras/test_jobs/dry_run.py +1 -1
  222. nautobot/extras/test_jobs/fail.py +5 -5
  223. nautobot/extras/test_jobs/file_output.py +1 -1
  224. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  225. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  226. nautobot/extras/test_jobs/ipaddress_vars.py +3 -1
  227. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  228. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  229. nautobot/extras/test_jobs/log_redaction.py +1 -1
  230. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  231. nautobot/extras/test_jobs/modify_db.py +1 -1
  232. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  233. nautobot/extras/test_jobs/object_var_required.py +1 -1
  234. nautobot/extras/test_jobs/object_vars.py +1 -1
  235. nautobot/extras/test_jobs/pass.py +3 -3
  236. nautobot/extras/test_jobs/profiling.py +1 -1
  237. nautobot/extras/test_jobs/relative_import.py +3 -3
  238. nautobot/extras/test_jobs/singleton.py +16 -0
  239. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  240. nautobot/extras/test_jobs/task_queues.py +1 -1
  241. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
  242. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
  243. nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
  244. nautobot/extras/tests/git_helper.py +9 -1
  245. nautobot/extras/tests/integration/__init__.py +29 -16
  246. nautobot/extras/tests/integration/test_plugin_banner.py +0 -2
  247. nautobot/extras/tests/test_api.py +19 -13
  248. nautobot/extras/tests/test_customfields.py +50 -52
  249. nautobot/extras/tests/test_datasources.py +29 -1
  250. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  251. nautobot/extras/tests/test_filters.py +6 -6
  252. nautobot/extras/tests/test_forms.py +33 -1
  253. nautobot/extras/tests/test_jobs.py +178 -32
  254. nautobot/extras/tests/test_models.py +299 -10
  255. nautobot/extras/tests/test_plugins.py +62 -9
  256. nautobot/extras/tests/test_relationships.py +120 -9
  257. nautobot/extras/tests/test_utils.py +22 -1
  258. nautobot/extras/tests/test_views.py +56 -194
  259. nautobot/extras/utils.py +20 -10
  260. nautobot/extras/views.py +85 -110
  261. nautobot/ipam/api/fields.py +3 -3
  262. nautobot/ipam/api/serializers.py +41 -33
  263. nautobot/ipam/api/views.py +68 -117
  264. nautobot/ipam/factory.py +1 -1
  265. nautobot/ipam/filters.py +3 -2
  266. nautobot/ipam/lookups.py +101 -62
  267. nautobot/ipam/models.py +74 -18
  268. nautobot/ipam/querysets.py +2 -2
  269. nautobot/ipam/tables.py +25 -9
  270. nautobot/ipam/templates/ipam/ipaddress.html +2 -10
  271. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
  272. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
  273. nautobot/ipam/templates/ipam/prefix.html +2 -10
  274. nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
  275. nautobot/ipam/templates/ipam/rir.html +1 -1
  276. nautobot/ipam/templates/ipam/routetarget.html +1 -1
  277. nautobot/ipam/templates/ipam/service.html +1 -1
  278. nautobot/ipam/templates/ipam/vlan.html +2 -10
  279. nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
  280. nautobot/ipam/templates/ipam/vlangroup.html +1 -1
  281. nautobot/ipam/templates/ipam/vrf.html +1 -1
  282. nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
  283. nautobot/ipam/tests/test_api.py +436 -3
  284. nautobot/ipam/tests/test_forms.py +49 -47
  285. nautobot/ipam/tests/test_migrations.py +30 -30
  286. nautobot/ipam/tests/test_models.py +119 -34
  287. nautobot/ipam/tests/test_querysets.py +63 -1
  288. nautobot/ipam/tests/test_utils.py +41 -2
  289. nautobot/ipam/tests/test_views.py +3 -0
  290. nautobot/ipam/utils/__init__.py +54 -17
  291. nautobot/ipam/views.py +61 -87
  292. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  293. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  294. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
  295. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  296. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  297. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  298. nautobot/project-static/docs/404.html +131 -14
  299. nautobot/project-static/docs/apps/index.html +131 -14
  300. nautobot/project-static/docs/apps/nautobot-apps.html +132 -16
  301. nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
  302. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.60a45f97.min.js} +2 -2
  303. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.60a45f97.min.js.map} +2 -2
  304. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
  305. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
  306. nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
  307. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +147 -20
  308. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +144 -17
  309. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +459 -132
  310. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +175 -28
  311. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +180 -31
  312. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +138 -16
  313. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +137 -15
  314. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +164 -27
  315. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +187 -38
  316. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +193 -31
  317. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +216 -48
  318. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +324 -75
  319. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +666 -175
  320. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +194 -46
  321. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +538 -177
  322. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +578 -221
  323. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +145 -18
  324. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +156 -25
  325. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +492 -65
  326. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +705 -215
  327. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +943 -422
  328. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +144 -17
  329. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +619 -200
  330. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +474 -159
  331. nautobot/project-static/docs/development/apps/api/configuration-view.html +131 -14
  332. nautobot/project-static/docs/development/apps/api/database-backend-config.html +131 -14
  333. nautobot/project-static/docs/development/apps/api/models/django-admin.html +131 -14
  334. nautobot/project-static/docs/development/apps/api/models/global-search.html +131 -14
  335. nautobot/project-static/docs/development/apps/api/models/graphql.html +131 -14
  336. nautobot/project-static/docs/development/apps/api/models/index.html +131 -14
  337. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +131 -14
  338. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +131 -14
  339. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +131 -14
  340. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +131 -14
  341. nautobot/project-static/docs/development/apps/api/platform-features/index.html +131 -14
  342. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +131 -14
  343. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +131 -14
  344. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +131 -14
  345. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +131 -14
  346. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +153 -17
  347. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +131 -14
  348. nautobot/project-static/docs/development/apps/api/prometheus.html +131 -14
  349. nautobot/project-static/docs/development/apps/api/setup.html +131 -14
  350. nautobot/project-static/docs/development/apps/api/testing.html +131 -14
  351. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +131 -14
  352. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +131 -14
  353. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +131 -14
  354. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +131 -14
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +131 -14
  356. nautobot/project-static/docs/development/apps/api/views/base-template.html +131 -14
  357. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +131 -14
  358. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +131 -14
  359. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +131 -14
  360. nautobot/project-static/docs/development/apps/api/views/index.html +131 -14
  361. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +131 -14
  362. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +131 -14
  363. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +131 -14
  364. nautobot/project-static/docs/development/apps/api/views/notes.html +131 -14
  365. nautobot/project-static/docs/development/apps/api/views/rest-api.html +137 -16
  366. nautobot/project-static/docs/development/apps/api/views/urls.html +131 -14
  367. nautobot/project-static/docs/development/apps/index.html +131 -14
  368. nautobot/project-static/docs/development/apps/migration/code-updates.html +131 -14
  369. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +131 -14
  370. nautobot/project-static/docs/development/apps/migration/from-v1.html +131 -14
  371. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +131 -14
  372. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +131 -14
  373. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +131 -14
  374. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +131 -14
  375. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +135 -18
  376. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +131 -14
  377. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +297 -25
  378. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +131 -14
  379. nautobot/project-static/docs/development/apps/porting-from-netbox.html +131 -14
  380. nautobot/project-static/docs/development/core/application-registry.html +131 -14
  381. nautobot/project-static/docs/development/core/best-practices.html +131 -14
  382. nautobot/project-static/docs/development/core/bootstrap-ui.html +131 -14
  383. nautobot/project-static/docs/development/core/caching.html +131 -14
  384. nautobot/project-static/docs/development/core/controllers.html +131 -14
  385. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +158 -84
  386. nautobot/project-static/docs/development/core/generic-views.html +131 -14
  387. nautobot/project-static/docs/development/core/getting-started.html +334 -234
  388. nautobot/project-static/docs/development/core/homepage.html +134 -17
  389. nautobot/project-static/docs/development/core/index.html +131 -14
  390. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9829 -0
  391. nautobot/project-static/docs/development/core/model-checklist.html +141 -22
  392. nautobot/project-static/docs/development/core/model-features.html +131 -14
  393. nautobot/project-static/docs/development/core/natural-keys.html +131 -14
  394. nautobot/project-static/docs/development/core/navigation-menu.html +131 -14
  395. nautobot/project-static/docs/development/core/release-checklist.html +134 -17
  396. nautobot/project-static/docs/development/core/role-internals.html +131 -14
  397. nautobot/project-static/docs/development/core/settings.html +131 -14
  398. nautobot/project-static/docs/development/core/style-guide.html +134 -17
  399. nautobot/project-static/docs/development/core/templates.html +132 -15
  400. nautobot/project-static/docs/development/core/testing.html +131 -14
  401. nautobot/project-static/docs/development/core/ui-component-framework.html +454 -283
  402. nautobot/project-static/docs/development/core/user-preferences.html +131 -14
  403. nautobot/project-static/docs/development/index.html +131 -14
  404. nautobot/project-static/docs/development/jobs/index.html +301 -132
  405. nautobot/project-static/docs/development/jobs/migration/from-v1.html +131 -14
  406. nautobot/project-static/docs/index.html +139 -33
  407. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  408. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  409. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  410. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  411. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  412. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  413. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  414. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  415. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  416. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  417. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  418. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  419. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  420. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  421. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  422. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  423. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  424. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  425. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  426. nautobot/project-static/docs/objects.inv +0 -0
  427. nautobot/project-static/docs/overview/application_stack.html +132 -17
  428. nautobot/project-static/docs/overview/design_philosophy.html +131 -14
  429. nautobot/project-static/docs/release-notes/index.html +137 -22
  430. nautobot/project-static/docs/release-notes/version-1.0.html +319 -203
  431. nautobot/project-static/docs/release-notes/version-1.1.html +316 -200
  432. nautobot/project-static/docs/release-notes/version-1.2.html +391 -275
  433. nautobot/project-static/docs/release-notes/version-1.3.html +417 -301
  434. nautobot/project-static/docs/release-notes/version-1.4.html +502 -387
  435. nautobot/project-static/docs/release-notes/version-1.5.html +690 -576
  436. nautobot/project-static/docs/release-notes/version-1.6.html +989 -457
  437. nautobot/project-static/docs/release-notes/version-2.0.html +613 -499
  438. nautobot/project-static/docs/release-notes/version-2.1.html +448 -334
  439. nautobot/project-static/docs/release-notes/version-2.2.html +441 -327
  440. nautobot/project-static/docs/release-notes/version-2.3.html +1171 -451
  441. nautobot/project-static/docs/release-notes/version-2.4.html +800 -111
  442. nautobot/project-static/docs/requirements.txt +2 -2
  443. nautobot/project-static/docs/search/search_index.json +1 -1
  444. nautobot/project-static/docs/sitemap.xml +303 -287
  445. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  446. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +131 -14
  447. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +131 -14
  448. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +133 -16
  449. nautobot/project-static/docs/user-guide/administration/configuration/index.html +131 -14
  450. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +131 -14
  451. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +195 -18
  452. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +131 -14
  453. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +134 -17
  454. nautobot/project-static/docs/user-guide/administration/guides/docker.html +131 -14
  455. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +131 -14
  456. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +131 -14
  457. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +131 -14
  458. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +131 -14
  459. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +131 -14
  460. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +133 -16
  461. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +131 -14
  462. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +131 -14
  463. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +131 -14
  464. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +151 -18
  465. nautobot/project-static/docs/user-guide/administration/installation/index.html +131 -14
  466. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +132 -15
  467. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +131 -14
  468. nautobot/project-static/docs/user-guide/administration/installation/services.html +131 -14
  469. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +131 -14
  470. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +131 -14
  471. nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
  472. nautobot/project-static/docs/user-guide/administration/security/notices.html +9843 -0
  473. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +131 -14
  474. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +131 -14
  475. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +131 -14
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +131 -14
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +131 -14
  478. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +131 -14
  479. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +131 -14
  480. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +134 -18
  481. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +131 -14
  482. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +131 -14
  483. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +131 -14
  484. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +131 -14
  485. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +131 -14
  486. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +131 -14
  487. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +131 -14
  488. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +131 -14
  489. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +131 -14
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +131 -14
  491. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +131 -14
  492. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +131 -14
  493. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +131 -14
  494. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +131 -14
  495. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +131 -14
  496. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +131 -14
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +131 -14
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +131 -14
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +131 -14
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +131 -14
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +131 -14
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +131 -14
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +131 -14
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +131 -14
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +131 -14
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +131 -14
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +131 -14
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +131 -14
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +131 -14
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +131 -14
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +131 -14
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +131 -14
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +131 -14
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +131 -14
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +131 -14
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +131 -14
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +131 -14
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +131 -14
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +131 -14
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +131 -14
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +131 -14
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +131 -14
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +131 -14
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +131 -14
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +131 -14
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +131 -14
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +131 -14
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +131 -14
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +131 -14
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +131 -14
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +131 -14
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +131 -14
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +131 -14
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +131 -14
  535. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +131 -14
  536. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +131 -14
  537. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +131 -14
  538. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +135 -22
  539. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +131 -14
  540. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +131 -14
  541. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +131 -14
  542. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +131 -14
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +131 -14
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +131 -14
  545. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +131 -14
  546. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +131 -14
  547. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +131 -14
  548. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +131 -14
  549. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +131 -14
  550. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +131 -14
  551. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +134 -17
  552. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +131 -14
  553. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +131 -14
  554. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +131 -14
  555. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +131 -14
  556. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +131 -14
  557. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +131 -14
  558. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +131 -14
  559. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +131 -14
  560. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +131 -14
  561. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +131 -14
  562. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +131 -14
  563. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +131 -14
  564. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +131 -14
  565. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +131 -14
  566. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +131 -14
  567. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +134 -17
  568. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +131 -14
  569. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +131 -14
  570. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +131 -14
  571. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +131 -14
  572. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +131 -14
  573. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +131 -14
  574. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +236 -34
  575. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +131 -14
  576. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  587. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  589. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +131 -14
  590. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +131 -14
  591. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +134 -17
  592. nautobot/project-static/docs/{development/core/local-k8s.html → user-guide/feature-guides/wireless-networks-and-controllers.html} +632 -566
  593. nautobot/project-static/docs/user-guide/index.html +131 -14
  594. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +131 -14
  595. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +135 -18
  596. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +131 -14
  597. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +131 -14
  598. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +131 -14
  599. nautobot/project-static/docs/user-guide/platform-functionality/events.html +131 -14
  600. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +131 -14
  601. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +131 -14
  602. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +451 -16
  603. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +131 -14
  604. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +131 -14
  605. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +131 -14
  606. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +135 -17
  607. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +131 -14
  608. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +134 -17
  609. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +131 -14
  610. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +131 -14
  611. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9797 -0
  612. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +131 -14
  613. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +131 -14
  614. nautobot/project-static/docs/user-guide/platform-functionality/note.html +131 -14
  615. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +131 -14
  616. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +132 -15
  617. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +131 -14
  618. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +131 -14
  619. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +179 -35
  620. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +159 -15
  621. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +131 -14
  622. nautobot/project-static/docs/user-guide/platform-functionality/role.html +131 -14
  623. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +131 -14
  624. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +131 -14
  625. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +131 -14
  626. nautobot/project-static/docs/user-guide/platform-functionality/status.html +131 -14
  627. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +131 -14
  628. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +131 -14
  629. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +131 -14
  630. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +131 -14
  631. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +131 -14
  632. nautobot/project-static/js/forms.js +1 -1
  633. nautobot/tenancy/api/views.py +9 -13
  634. nautobot/tenancy/templates/tenancy/tenant.html +1 -2
  635. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  636. nautobot/tenancy/views.py +4 -2
  637. nautobot/users/admin.py +1 -1
  638. nautobot/users/api/serializers.py +5 -4
  639. nautobot/users/api/views.py +3 -3
  640. nautobot/virtualization/api/serializers.py +4 -4
  641. nautobot/virtualization/api/views.py +5 -24
  642. nautobot/virtualization/filters.py +20 -3
  643. nautobot/virtualization/models.py +1 -1
  644. nautobot/virtualization/tables.py +2 -2
  645. nautobot/virtualization/templates/virtualization/cluster.html +1 -1
  646. nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
  647. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
  648. nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
  649. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -10
  650. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
  651. nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
  652. nautobot/virtualization/tests/test_filters.py +17 -0
  653. nautobot/wireless/filters.py +2 -2
  654. nautobot/wireless/forms.py +1 -1
  655. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +1 -9
  656. nautobot/wireless/tests/integration/__init__.py +0 -0
  657. nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
  658. nautobot/wireless/tests/test_filters.py +29 -1
  659. nautobot/wireless/tests/test_views.py +22 -1
  660. nautobot/wireless/views.py +0 -10
  661. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/METADATA +9 -9
  662. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/RECORD +667 -610
  663. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/WHEEL +1 -1
  664. nautobot/core/fixtures/user-data.json +0 -59
  665. /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
  666. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/LICENSE.txt +0 -0
  667. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/NOTICE +0 -0
  668. {nautobot-2.4.0b1.dist-info → nautobot-2.4.2.dist-info}/entry_points.txt +0 -0
nautobot/extras/utils.py CHANGED
@@ -6,6 +6,7 @@ import hmac
6
6
  import logging
7
7
  import re
8
8
  import sys
9
+ from typing import Optional
9
10
 
10
11
  from django.apps import apps
11
12
  from django.conf import settings
@@ -13,7 +14,7 @@ from django.contrib.contenttypes.models import ContentType
13
14
  from django.core.cache import cache
14
15
  from django.core.validators import ValidationError
15
16
  from django.db import transaction
16
- from django.db.models import Q
17
+ from django.db.models import Model, Q
17
18
  from django.template.loader import get_template, TemplateDoesNotExist
18
19
  from django.utils.deconstruct import deconstructible
19
20
  import kubernetes.client
@@ -36,16 +37,24 @@ from nautobot.extras.registry import registry
36
37
  logger = logging.getLogger(__name__)
37
38
 
38
39
 
39
- def get_base_template(base_template, model):
40
+ def get_base_template(base_template: Optional[str], model: type[Model]) -> str:
40
41
  """
41
- Returns the name of the base template, if the base_template is not None
42
- Otherwise, default to using "<app>/<model>.html" as the base template, if it exists.
43
- Otherwise, check if "<app>/<model>_retrieve.html" used in `NautobotUIViewSet` exists.
44
- If both templates do not exist, fall back to "base.html".
42
+ Attempt to locate the correct base template for an object detail view and related views, if one was not specified.
43
+
44
+ Args:
45
+ base_template (str, optional): If not None, this explicitly specified template will be preferred.
46
+ model (Model): The model to identify a base template for, if base_template is None.
47
+
48
+ Returns the specified `base_template`, if not `None`.
49
+ Otherwise, if `"<app>/<model_name>.html"` exists (legacy ObjectView pattern), returns that string.
50
+ Otherwise, if `"<app>/<model_name>_retrieve.html"` exists (as used in `NautobotUIViewSet`), returns that string.
51
+ If all else fails, returns `"generic/object_retrieve.html"`.
52
+
53
+ Note: before Nautobot 2.4.2, this API would default to "base.html" rather than "generic/object_retrieve.html".
54
+ This behavior was changed to the current behavior to address issue #6550 and similar incorrect behavior.
45
55
  """
46
56
  if base_template is None:
47
57
  base_template = f"{model._meta.app_label}/{model._meta.model_name}.html"
48
- # 2.0 TODO(Hanlin): This can be removed once an object view has been established for every model.
49
58
  try:
50
59
  get_template(base_template)
51
60
  except TemplateDoesNotExist:
@@ -53,7 +62,7 @@ def get_base_template(base_template, model):
53
62
  try:
54
63
  get_template(base_template)
55
64
  except TemplateDoesNotExist:
56
- base_template = "base.html"
65
+ base_template = "generic/object_retrieve.html"
57
66
  return base_template
58
67
 
59
68
 
@@ -579,6 +588,7 @@ def refresh_job_model_from_job_class(job_model_class, job_class, job_queue_class
579
588
  "installed": True,
580
589
  "enabled": False,
581
590
  "default_job_queue": default_job_queue,
591
+ "is_singleton": job_class.is_singleton,
582
592
  },
583
593
  )
584
594
 
@@ -665,9 +675,9 @@ def run_kubernetes_job_and_return_job_result(job_queue, job_result, job_kwargs):
665
675
  "runjob_with_job_result",
666
676
  f"{job_result.pk}",
667
677
  ]
668
- logger.info("Creating job pod %s in namespace %s", pod_name, pod_namespace)
678
+ job_result.log(f"Creating job pod {pod_name} in namespace {pod_namespace}")
669
679
  api_instance.create_namespaced_job(body=pod_manifest, namespace=pod_namespace)
670
- logger.info("Reading job pod %s in namespace %s", pod_name, pod_namespace)
680
+ job_result.log(f"Reading job pod {pod_name} in namespace {pod_namespace}")
671
681
  api_instance.read_namespaced_job(name="nautobot-job-" + str(job_result.pk), namespace=pod_namespace)
672
682
  return job_result
673
683
 
nautobot/extras/views.py CHANGED
@@ -1,8 +1,7 @@
1
1
  import logging
2
+ from typing import Optional
2
3
  from urllib.parse import parse_qs
3
4
 
4
- from celery import chain
5
- from django.conf import settings
6
5
  from django.contrib import messages
7
6
  from django.contrib.contenttypes.models import ContentType
8
7
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
@@ -33,6 +32,7 @@ from nautobot.core.models.utils import pretty_print_query, serialize_object_v2
33
32
  from nautobot.core.tables import ButtonsColumn
34
33
  from nautobot.core.ui import object_detail
35
34
  from nautobot.core.ui.choices import SectionChoices
35
+ from nautobot.core.ui.object_detail import ObjectDetailContent, ObjectFieldsPanel
36
36
  from nautobot.core.utils.config import get_settings_or_config
37
37
  from nautobot.core.utils.lookup import (
38
38
  get_filterset_for_model,
@@ -68,10 +68,7 @@ from nautobot.dcim.tables import (
68
68
  RackTable,
69
69
  VirtualDeviceContextTable,
70
70
  )
71
- from nautobot.extras.constants import JOB_OVERRIDABLE_FIELDS
72
71
  from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
73
- from nautobot.extras.signals import change_context_state
74
- from nautobot.extras.tasks import delete_custom_field_data
75
72
  from nautobot.extras.utils import get_base_template, get_job_queue, get_worker_count
76
73
  from nautobot.ipam.models import IPAddress, Prefix, VLAN
77
74
  from nautobot.ipam.tables import IPAddressTable, PrefixTable, VLANTable
@@ -659,37 +656,7 @@ class CustomFieldBulkDeleteView(generic.BulkDeleteView):
659
656
  queryset = CustomField.objects.all()
660
657
  table = tables.CustomFieldTable
661
658
  filterset = filters.CustomFieldFilterSet
662
-
663
- def construct_custom_field_delete_tasks(self, queryset):
664
- """
665
- Helper method to construct a list of celery tasks to execute when bulk deleting custom fields.
666
- """
667
- change_context = change_context_state.get()
668
- if change_context is None:
669
- context = None
670
- else:
671
- context = change_context.as_dict(queryset)
672
- context["context_detail"] = "bulk delete custom field data"
673
- tasks = [
674
- delete_custom_field_data.si(obj.key, set(obj.content_types.values_list("pk", flat=True)), context)
675
- for obj in queryset
676
- ]
677
- return tasks
678
-
679
- def perform_pre_delete(self, request, queryset):
680
- """
681
- Remove all Custom Field Keys/Values from _custom_field_data of the related ContentType in the background.
682
- """
683
- if not get_worker_count():
684
- messages.error(
685
- request, "Celery worker process not running. Object custom fields may fail to reflect this deletion."
686
- )
687
- return
688
- tasks = self.construct_custom_field_delete_tasks(queryset)
689
- # Executing the tasks in the background sequentially using chain() aligns with how a single
690
- # CustomField object is deleted. We decided to not check the result because it needs at least one worker
691
- # to be active and comes with extra performance penalty.
692
- chain(*tasks).apply_async()
659
+ form = forms.CustomFieldBulkDeleteForm
693
660
 
694
661
 
695
662
  #
@@ -935,10 +902,13 @@ class DynamicGroupBulkDeleteView(generic.BulkDeleteView):
935
902
  class ObjectDynamicGroupsView(generic.GenericView):
936
903
  """
937
904
  Present a list of dynamic groups associated to a particular object.
938
- base_template: The name of the template to extend. If not provided, "<app>/<model>.html" will be used.
905
+
906
+ base_template: Specify to explicitly identify the base object detail template to render.
907
+ If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
908
+ will be used, as per `get_base_template()`.
939
909
  """
940
910
 
941
- base_template = None
911
+ base_template: Optional[str] = None
942
912
 
943
913
  def get(self, request, model, **kwargs):
944
914
  # Handle QuerySet restriction of parent object if needed
@@ -961,7 +931,7 @@ class ObjectDynamicGroupsView(generic.GenericView):
961
931
  }
962
932
  RequestConfig(request, paginate).configure(dynamicgroups_table)
963
933
 
964
- self.base_template = get_base_template(self.base_template, model)
934
+ base_template = get_base_template(self.base_template, model)
965
935
 
966
936
  return render(
967
937
  request,
@@ -971,7 +941,7 @@ class ObjectDynamicGroupsView(generic.GenericView):
971
941
  "verbose_name": obj._meta.verbose_name,
972
942
  "verbose_name_plural": obj._meta.verbose_name_plural,
973
943
  "table": dynamicgroups_table,
974
- "base_template": self.base_template,
944
+ "base_template": base_template,
975
945
  "active_tab": "dynamic-groups",
976
946
  },
977
947
  )
@@ -1097,10 +1067,10 @@ class GitRepositoryEditView(generic.ObjectEditView):
1097
1067
  obj.user = request.user
1098
1068
  return super().alter_obj(obj, request, url_args, url_kwargs)
1099
1069
 
1100
- def get_return_url(self, request, obj):
1070
+ def get_return_url(self, request, obj=None, default_return_url=None):
1101
1071
  if request.method == "POST":
1102
1072
  return reverse("extras:gitrepository_result", kwargs={"pk": obj.pk})
1103
- return super().get_return_url(request, obj)
1073
+ return super().get_return_url(request, obj=obj, default_return_url=default_return_url)
1104
1074
 
1105
1075
 
1106
1076
  class GitRepositoryDeleteView(generic.ObjectDeleteView):
@@ -1248,27 +1218,27 @@ class ImageAttachmentEditView(generic.ObjectEditView):
1248
1218
  return get_object_or_404(self.queryset, pk=kwargs["pk"])
1249
1219
  return self.queryset.model()
1250
1220
 
1251
- def alter_obj(self, imageattachment, request, args, kwargs):
1252
- if not imageattachment.present_in_database:
1221
+ def alter_obj(self, obj, request, url_args, url_kwargs):
1222
+ if not obj.present_in_database:
1253
1223
  # Assign the parent object based on URL kwargs
1254
- model = kwargs.get("model")
1255
- if "object_id" in kwargs:
1256
- imageattachment.parent = get_object_or_404(model, pk=kwargs["object_id"])
1257
- elif "slug" in kwargs:
1258
- imageattachment.parent = get_object_or_404(model, slug=kwargs["slug"])
1224
+ model = url_kwargs.get("model")
1225
+ if "object_id" in url_kwargs:
1226
+ obj.parent = get_object_or_404(model, pk=url_kwargs["object_id"])
1227
+ elif "slug" in url_kwargs:
1228
+ obj.parent = get_object_or_404(model, slug=url_kwargs["slug"])
1259
1229
  else:
1260
1230
  raise RuntimeError("Neither object_id nor slug were provided?")
1261
- return imageattachment
1231
+ return obj
1262
1232
 
1263
- def get_return_url(self, request, imageattachment):
1264
- return imageattachment.parent.get_absolute_url()
1233
+ def get_return_url(self, request, obj=None, default_return_url=None):
1234
+ return obj.parent.get_absolute_url()
1265
1235
 
1266
1236
 
1267
1237
  class ImageAttachmentDeleteView(generic.ObjectDeleteView):
1268
1238
  queryset = ImageAttachment.objects.all()
1269
1239
 
1270
- def get_return_url(self, request, imageattachment):
1271
- return imageattachment.parent.get_absolute_url()
1240
+ def get_return_url(self, request, obj=None, default_return_url=None):
1241
+ return obj.parent.get_absolute_url()
1272
1242
 
1273
1243
 
1274
1244
  #
@@ -1365,6 +1335,9 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1365
1335
  pass
1366
1336
  initial["_job_queue"] = jq
1367
1337
  initial["_profile"] = job_result.celery_kwargs.get("nautobot_job_profile", False)
1338
+ initial["_ignore_singleton_lock"] = job_result.celery_kwargs.get(
1339
+ "nautobot_job_ignore_singleton_lock", False
1340
+ )
1368
1341
  initial.update(explicit_initial)
1369
1342
  except JobResult.DoesNotExist:
1370
1343
  messages.warning(
@@ -1379,7 +1352,9 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1379
1352
  get_template(job_class.template_name)
1380
1353
  template_name = job_class.template_name
1381
1354
  except TemplateDoesNotExist as err:
1382
- messages.error(request, f'Unable to render requested custom job template "{template_name}": {err}')
1355
+ messages.error(
1356
+ request, f'Unable to render requested custom job template "{job_class.template_name}": {err}'
1357
+ )
1383
1358
  except RuntimeError as err:
1384
1359
  messages.error(request, f"Unable to run or schedule '{job_model}': {err}")
1385
1360
  return redirect("extras:job_list")
@@ -1447,6 +1422,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1447
1422
  dryrun = job_form.cleaned_data.get("dryrun", False)
1448
1423
  # Run the job. A new JobResult is created.
1449
1424
  profile = job_form.cleaned_data.pop("_profile")
1425
+ ignore_singleton_lock = job_form.cleaned_data.pop("_ignore_singleton_lock", False)
1450
1426
  schedule_type = schedule_form.cleaned_data["_schedule_type"]
1451
1427
 
1452
1428
  if (not dryrun and job_model.approval_required) or schedule_type in JobExecutionType.SCHEDULE_CHOICES:
@@ -1460,6 +1436,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1460
1436
  approval_required=job_model.approval_required,
1461
1437
  task_queue=jq.name if jq else None,
1462
1438
  profile=profile,
1439
+ ignore_singleton_lock=ignore_singleton_lock,
1463
1440
  **job_class.serialize_data(job_form.cleaned_data),
1464
1441
  )
1465
1442
 
@@ -1477,6 +1454,7 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1477
1454
  job_model,
1478
1455
  request.user,
1479
1456
  profile=profile,
1457
+ ignore_singleton_lock=ignore_singleton_lock,
1480
1458
  task_queue=jq.name if jq else None,
1481
1459
  **job_class.serialize_data(job_kwargs),
1482
1460
  )
@@ -1502,7 +1480,9 @@ class JobRunView(ObjectPermissionRequiredMixin, View):
1502
1480
  get_template(job_class.template_name)
1503
1481
  template_name = job_class.template_name
1504
1482
  except TemplateDoesNotExist as err:
1505
- messages.error(request, f'Unable to render requested custom job template "{template_name}": {err}')
1483
+ messages.error(
1484
+ request, f'Unable to render requested custom job template "{job_class.template_name}": {err}'
1485
+ )
1506
1486
 
1507
1487
  return render(
1508
1488
  request,
@@ -1533,52 +1513,6 @@ class JobBulkEditView(generic.BulkEditView):
1533
1513
  form = forms.JobBulkEditForm
1534
1514
  template_name = "extras/job_bulk_edit.html"
1535
1515
 
1536
- def extra_post_save_action(self, obj, form):
1537
- cleaned_data = form.cleaned_data
1538
-
1539
- # Handle text related fields
1540
- for overridable_field in JOB_OVERRIDABLE_FIELDS:
1541
- override_field = overridable_field + "_override"
1542
- clear_override_field = "clear_" + overridable_field + "_override"
1543
- reset_override = cleaned_data.get(clear_override_field, False)
1544
- override_value = cleaned_data.get(overridable_field)
1545
- if reset_override:
1546
- setattr(obj, override_field, False)
1547
- elif not reset_override and override_value not in [None, ""]:
1548
- setattr(obj, override_field, True)
1549
- setattr(obj, overridable_field, override_value)
1550
-
1551
- # Handle job queues
1552
- clear_override_field = "clear_job_queues_override"
1553
- reset_override = cleaned_data.get(clear_override_field, False)
1554
- if reset_override:
1555
- meta_task_queues = obj.job_class.task_queues
1556
- job_queues = []
1557
- for queue_name in meta_task_queues:
1558
- try:
1559
- job_queues.append(JobQueue.objects.get(name=queue_name))
1560
- except JobQueue.DoesNotExist:
1561
- # Do we want to create the Job Queue for the users here if we do not have it in the database?
1562
- pass
1563
- obj.job_queues_override = False
1564
- obj.job_queues.set(job_queues)
1565
- elif cleaned_data["job_queues"]:
1566
- obj.job_queues_override = True
1567
-
1568
- # Handle default job queue
1569
- clear_override_field = "clear_default_job_queue_override"
1570
- reset_override = cleaned_data.get(clear_override_field, False)
1571
- if reset_override:
1572
- meta_task_queues = obj.job_class.task_queues
1573
- obj.default_job_queue_override = False
1574
- obj.default_job_queue, _ = JobQueue.objects.get_or_create(
1575
- name=meta_task_queues[0] if meta_task_queues else settings.CELERY_TASK_DEFAULT_QUEUE
1576
- )
1577
- elif cleaned_data["default_job_queue"]:
1578
- obj.default_job_queue_override = True
1579
-
1580
- obj.validated_save()
1581
-
1582
1516
 
1583
1517
  class JobDeleteView(generic.ObjectDeleteView):
1584
1518
  queryset = JobModel.objects.all()
@@ -2098,6 +2032,9 @@ def get_annotated_jobresult_queryset():
2098
2032
  debug_log_count=count_related(
2099
2033
  JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_DEBUG}
2100
2034
  ),
2035
+ success_log_count=count_related(
2036
+ JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_SUCCESS}
2037
+ ),
2101
2038
  info_log_count=count_related(
2102
2039
  JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_INFO}
2103
2040
  ),
@@ -2253,10 +2190,13 @@ class ObjectChangeView(generic.ObjectView):
2253
2190
  class ObjectChangeLogView(generic.GenericView):
2254
2191
  """
2255
2192
  Present a history of changes made to a particular object.
2256
- base_template: The name of the template to extend. If not provided, "<app>/<model>.html" will be used.
2193
+
2194
+ base_template: Specify to explicitly identify the base object detail template to render.
2195
+ If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
2196
+ will be used, as per `get_base_template()`.
2257
2197
  """
2258
2198
 
2259
- base_template = None
2199
+ base_template: Optional[str] = None
2260
2200
 
2261
2201
  def get(self, request, model, **kwargs):
2262
2202
  # Handle QuerySet restriction of parent object if needed
@@ -2284,7 +2224,7 @@ class ObjectChangeLogView(generic.GenericView):
2284
2224
  }
2285
2225
  RequestConfig(request, paginate).configure(objectchanges_table)
2286
2226
 
2287
- self.base_template = get_base_template(self.base_template, model)
2227
+ base_template = get_base_template(self.base_template, model)
2288
2228
 
2289
2229
  return render(
2290
2230
  request,
@@ -2294,7 +2234,7 @@ class ObjectChangeLogView(generic.GenericView):
2294
2234
  "verbose_name": obj._meta.verbose_name,
2295
2235
  "verbose_name_plural": obj._meta.verbose_name_plural,
2296
2236
  "table": objectchanges_table,
2297
- "base_template": self.base_template,
2237
+ "base_template": base_template,
2298
2238
  "active_tab": "changelog",
2299
2239
  },
2300
2240
  )
@@ -2387,10 +2327,13 @@ class NoteDeleteView(generic.ObjectDeleteView):
2387
2327
  class ObjectNotesView(generic.GenericView):
2388
2328
  """
2389
2329
  Present a list of notes associated to a particular object.
2390
- base_template: The name of the template to extend. If not provided, "<app>/<model>.html" will be used.
2330
+
2331
+ base_template: Specify to explicitly identify the base object detail template to render.
2332
+ If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
2333
+ will be used, as per `get_base_template()`.
2391
2334
  """
2392
2335
 
2393
- base_template = None
2336
+ base_template: Optional[str] = None
2394
2337
 
2395
2338
  def get(self, request, model, **kwargs):
2396
2339
  # Handle QuerySet restriction of parent object if needed
@@ -2414,7 +2357,7 @@ class ObjectNotesView(generic.GenericView):
2414
2357
  }
2415
2358
  RequestConfig(request, paginate).configure(notes_table)
2416
2359
 
2417
- self.base_template = get_base_template(self.base_template, model)
2360
+ base_template = get_base_template(self.base_template, model)
2418
2361
 
2419
2362
  return render(
2420
2363
  request,
@@ -2424,7 +2367,7 @@ class ObjectNotesView(generic.GenericView):
2424
2367
  "verbose_name": obj._meta.verbose_name,
2425
2368
  "verbose_name_plural": obj._meta.verbose_name_plural,
2426
2369
  "table": notes_table,
2427
- "base_template": self.base_template,
2370
+ "base_template": base_template,
2428
2371
  "active_tab": "notes",
2429
2372
  "form": notes_form,
2430
2373
  },
@@ -2446,6 +2389,38 @@ class RelationshipListView(generic.ObjectListView):
2446
2389
 
2447
2390
  class RelationshipView(generic.ObjectView):
2448
2391
  queryset = Relationship.objects.all()
2392
+ object_detail_content = ObjectDetailContent(
2393
+ panels=(
2394
+ ObjectFieldsPanel(
2395
+ label="Relationship",
2396
+ section=SectionChoices.LEFT_HALF,
2397
+ weight=100,
2398
+ fields="__all__",
2399
+ exclude_fields=[
2400
+ "source_type",
2401
+ "source_label",
2402
+ "source_hidden",
2403
+ "source_filter",
2404
+ "destination_type",
2405
+ "destination_label",
2406
+ "destination_hidden",
2407
+ "destination_filter",
2408
+ ],
2409
+ ),
2410
+ ObjectFieldsPanel(
2411
+ label="Source Attributes",
2412
+ section=SectionChoices.RIGHT_HALF,
2413
+ weight=100,
2414
+ fields=["source_type", "source_label", "source_hidden", "source_filter"],
2415
+ ),
2416
+ ObjectFieldsPanel(
2417
+ label="Destination Attributes",
2418
+ section=SectionChoices.RIGHT_HALF,
2419
+ weight=200,
2420
+ fields=["destination_type", "destination_label", "destination_hidden", "destination_filter"],
2421
+ ),
2422
+ )
2423
+ )
2449
2424
 
2450
2425
 
2451
2426
  class RelationshipEditView(generic.ObjectEditView):
@@ -7,11 +7,11 @@ class IPFieldSerializer(serializers.CharField):
7
7
  """Convert internal (IPNetwork) representation to API (string) representation."""
8
8
  return str(value)
9
9
 
10
- def to_internal_value(self, value):
10
+ def to_internal_value(self, data):
11
11
  """Convert API (string) representation to internal (IPNetwork) representation."""
12
12
  try:
13
- return netaddr.IPNetwork(value)
13
+ return netaddr.IPNetwork(data)
14
14
  except netaddr.AddrFormatError:
15
- raise serializers.ValidationError(f"Invalid IP address format: {value}")
15
+ raise serializers.ValidationError(f"Invalid IP address format: {data}")
16
16
  except (TypeError, ValueError) as e:
17
17
  raise serializers.ValidationError(e)
@@ -62,16 +62,16 @@ class VRFDeviceAssignmentSerializer(ValidatedModelSerializer):
62
62
  fields = "__all__"
63
63
  validators = []
64
64
 
65
- def validate(self, data):
66
- if data.get("device"):
65
+ def validate(self, attrs):
66
+ if attrs.get("device"):
67
67
  validator = UniqueTogetherValidator(queryset=VRFDeviceAssignment.objects.all(), fields=("device", "vrf"))
68
- validator(data, self)
69
- if data.get("virtual_machine"):
68
+ validator(attrs, self)
69
+ if attrs.get("virtual_machine"):
70
70
  validator = UniqueTogetherValidator(
71
71
  queryset=VRFDeviceAssignment.objects.all(), fields=("virtual_machine", "vrf")
72
72
  )
73
- validator(data, self)
74
- return super().validate(data)
73
+ validator(attrs, self)
74
+ return super().validate(attrs)
75
75
 
76
76
 
77
77
  class VRFPrefixAssignmentSerializer(ValidatedModelSerializer):
@@ -133,17 +133,17 @@ class VLANSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
133
133
  validators = []
134
134
  extra_kwargs = {"locations": {"read_only": True}}
135
135
 
136
- def validate(self, data):
136
+ def validate(self, attrs):
137
137
  # Validate uniqueness of vid and name if a group has been assigned.
138
- if data.get("vlan_group", None):
138
+ if attrs.get("vlan_group", None):
139
139
  for field in ["vid", "name"]:
140
140
  validator = UniqueTogetherValidator(queryset=VLAN.objects.all(), fields=("vlan_group", field))
141
- validator(data, self)
141
+ validator(attrs, self)
142
142
 
143
143
  # Enforce model validation
144
- super().validate(data)
144
+ super().validate(attrs)
145
145
 
146
- return data
146
+ return attrs
147
147
 
148
148
 
149
149
  class VLANLegacySerializer(VLANSerializer):
@@ -205,6 +205,12 @@ class PrefixSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
205
205
  write_only=True,
206
206
  )
207
207
 
208
+ def get_field_names(self, declared_fields, info):
209
+ """Add reverse M2M for VRF's to the fields for this serializer."""
210
+ field_names = list(super().get_field_names(declared_fields, info))
211
+ self.extend_field_names(field_names, "vrfs")
212
+ return field_names
213
+
208
214
  class Meta:
209
215
  model = Prefix
210
216
  fields = "__all__"
@@ -213,6 +219,7 @@ class PrefixSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
213
219
  "namespace": {"default": get_default_namespace},
214
220
  "prefix_length": {"read_only": True},
215
221
  "locations": {"read_only": True},
222
+ "vrfs": {"read_only": True},
216
223
  }
217
224
 
218
225
 
@@ -320,16 +327,16 @@ class IPAddressSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
320
327
  "parent": {"required": False},
321
328
  }
322
329
 
323
- def validate(self, data):
324
- namespace = data.get("namespace", None)
325
- parent = data.get("parent", None)
330
+ def validate(self, attrs):
331
+ namespace = attrs.get("namespace", None)
332
+ parent = attrs.get("parent", None)
326
333
 
327
334
  # Only assert namespace/parent on create.
328
335
  if self.instance is None and not any([namespace, parent]):
329
336
  raise ValidationError({"__all__": "One of parent or namespace must be provided"})
330
337
 
331
- super().validate(data)
332
- return data
338
+ super().validate(attrs)
339
+ return attrs
333
340
 
334
341
  def get_field_names(self, declared_fields, info):
335
342
  """Add reverse relations to the automatically discovered fields."""
@@ -379,9 +386,9 @@ class IPAllocationSerializer(NautobotModelSerializer, TaggedModelSerializerMixin
379
386
  "custom_fields",
380
387
  )
381
388
 
382
- def validate(self, data):
383
- data["mask_length"] = self.context["prefix"].prefix_length
384
- return super().validate(data)
389
+ def validate(self, attrs):
390
+ attrs["mask_length"] = self.context["prefix"].prefix_length
391
+ return super().validate(attrs)
385
392
 
386
393
 
387
394
  class VLANAllocationSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
@@ -391,12 +398,13 @@ class VLANAllocationSerializer(NautobotModelSerializer, TaggedModelSerializerMix
391
398
 
392
399
  vid = serializers.IntegerField(required=False, min_value=constants.VLAN_VID_MIN, max_value=constants.VLAN_VID_MAX)
393
400
 
394
- def validate(self, data):
401
+ def validate(self, attrs):
395
402
  """
396
403
  Skip `ValidatedModel` validation.
404
+
397
405
  This allows to skip `vid` attribute of `VLAN` model, while validate name and status.
398
406
  """
399
- return data
407
+ return attrs
400
408
 
401
409
  class Meta(VLANSerializer.Meta):
402
410
  model = VLAN
@@ -425,19 +433,19 @@ class IPAddressToInterfaceSerializer(ValidatedModelSerializer):
425
433
  fields = "__all__"
426
434
  validators = []
427
435
 
428
- def validate(self, data):
436
+ def validate(self, attrs):
429
437
  # Validate uniqueness of (parent, name) since we omitted the automatically created validator from Meta.
430
- if data.get("interface"):
438
+ if attrs.get("interface"):
431
439
  validator = UniqueTogetherValidator(
432
440
  queryset=IPAddressToInterface.objects.all(), fields=("interface", "ip_address")
433
441
  )
434
- validator(data, self)
435
- if data.get("vm_interface"):
442
+ validator(attrs, self)
443
+ if attrs.get("vm_interface"):
436
444
  validator = UniqueTogetherValidator(
437
445
  queryset=IPAddressToInterface.objects.all(), fields=("vm_interface", "ip_address")
438
446
  )
439
- validator(data, self)
440
- return super().validate(data)
447
+ validator(attrs, self)
448
+ return super().validate(attrs)
441
449
 
442
450
 
443
451
  #
@@ -464,11 +472,11 @@ class ServiceSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
464
472
  "virtual_machine": {"help_text": "Required if no device is specified"},
465
473
  }
466
474
 
467
- def validate(self, data):
468
- if data.get("device"):
475
+ def validate(self, attrs):
476
+ if attrs.get("device"):
469
477
  validator = UniqueTogetherValidator(queryset=Service.objects.all(), fields=("name", "device"))
470
- validator(data, self)
471
- if data.get("virtual_machine"):
478
+ validator(attrs, self)
479
+ if attrs.get("virtual_machine"):
472
480
  validator = UniqueTogetherValidator(queryset=Service.objects.all(), fields=("name", "virtual_machine"))
473
- validator(data, self)
474
- return super().validate(data)
481
+ validator(attrs, self)
482
+ return super().validate(attrs)