nautobot 2.4.17__py3-none-any.whl → 2.4.19__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 (627) hide show
  1. nautobot/apps/ui.py +6 -0
  2. nautobot/apps/views.py +2 -0
  3. nautobot/circuits/tables.py +1 -1
  4. nautobot/circuits/templates/circuits/circuit_create.html +7 -7
  5. nautobot/circuits/templates/circuits/circuit_retrieve.html +1 -5
  6. nautobot/circuits/templates/circuits/circuittermination_create.html +26 -26
  7. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -8
  8. nautobot/circuits/templates/circuits/inc/circuit_termination.html +20 -20
  9. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +3 -3
  10. nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +9 -0
  11. nautobot/circuits/templates/circuits/providernetwork_retrieve.html +1 -3
  12. nautobot/circuits/tests/integration/test_circuit.py +2 -2
  13. nautobot/circuits/views.py +49 -15
  14. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +1 -4
  15. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +1 -7
  16. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +1 -3
  17. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +1 -5
  18. nautobot/cloud/views.py +45 -0
  19. nautobot/core/filters.py +2 -2
  20. nautobot/core/graphql/generators.py +5 -2
  21. nautobot/core/jobs/bulk_actions.py +48 -85
  22. nautobot/core/models/querysets.py +2 -1
  23. nautobot/core/settings.py +1 -0
  24. nautobot/core/settings.yaml +9 -0
  25. nautobot/core/tables.py +21 -23
  26. nautobot/core/templates/40x.html +15 -15
  27. nautobot/core/templates/500.html +21 -21
  28. nautobot/core/templates/admin/app_index.html +8 -8
  29. nautobot/core/templates/admin/base.html +104 -104
  30. nautobot/core/templates/admin/change_form.html +65 -65
  31. nautobot/core/templates/admin/change_list.html +60 -60
  32. nautobot/core/templates/admin/change_list_results.html +39 -39
  33. nautobot/core/templates/admin/config/config.html +47 -47
  34. nautobot/core/templates/admin/delete_confirmation.html +47 -47
  35. nautobot/core/templates/admin/edit_inline/stacked.html +124 -124
  36. nautobot/core/templates/admin/edit_inline/tabular.html +60 -60
  37. nautobot/core/templates/admin/includes/fieldset.html +4 -4
  38. nautobot/core/templates/admin/index.html +60 -60
  39. nautobot/core/templates/admin/prepopulated_fields_js.html +18 -18
  40. nautobot/core/templates/admin/submit_line.html +4 -4
  41. nautobot/core/templates/base_django.html +46 -46
  42. nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +8 -8
  43. nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +8 -8
  44. nautobot/core/templates/buttons/export.html +3 -3
  45. nautobot/core/templates/components/breadcrumbs.html +19 -0
  46. nautobot/core/templates/components/button/default.html +3 -3
  47. nautobot/core/templates/components/button/dropdown.html +7 -7
  48. nautobot/core/templates/components/button/formbutton.html +4 -4
  49. nautobot/core/templates/components/panel/body_content_data_table.html +1 -1
  50. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +3 -0
  51. nautobot/core/templates/components/panel/footer_content_table.html +3 -1
  52. nautobot/core/templates/components/panel/header_extra_content_table.html +10 -1
  53. nautobot/core/templates/components/tab/content_wrapper.html +1 -1
  54. nautobot/core/templates/components/tab/label_wrapper.html +1 -1
  55. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +10 -3
  56. nautobot/core/templates/generic/object_bulk_add_component.html +40 -40
  57. nautobot/core/templates/generic/object_bulk_create.html +3 -3
  58. nautobot/core/templates/generic/object_bulk_destroy.html +6 -6
  59. nautobot/core/templates/generic/object_bulk_update.html +52 -52
  60. nautobot/core/templates/generic/object_changelog.html +0 -2
  61. nautobot/core/templates/generic/object_import.html +33 -33
  62. nautobot/core/templates/generic/object_list.html +271 -268
  63. nautobot/core/templates/generic/object_notes.html +0 -2
  64. nautobot/core/templates/generic/object_retrieve.html +264 -257
  65. nautobot/core/templates/graphene/graphiql.html +127 -127
  66. nautobot/core/templates/home.html +62 -62
  67. nautobot/core/templates/inc/computed_fields/panel_data.html +13 -13
  68. nautobot/core/templates/inc/created_updated.html +8 -8
  69. nautobot/core/templates/inc/custom_fields/panel_data.html +13 -13
  70. nautobot/core/templates/inc/dynamic_groups_panel.html +11 -11
  71. nautobot/core/templates/inc/footer.html +19 -19
  72. nautobot/core/templates/inc/javascript.html +1 -1
  73. nautobot/core/templates/inc/media.html +46 -46
  74. nautobot/core/templates/inc/nav_menu.html +1 -1
  75. nautobot/core/templates/inc/relationships_table_rows.html +22 -22
  76. nautobot/core/templates/inc/tenant_table_row.html +1 -1
  77. nautobot/core/templates/login.html +77 -77
  78. nautobot/core/templates/media_failure.html +38 -38
  79. nautobot/core/templates/panel_table.html +1 -1
  80. nautobot/core/templates/rest_framework/api.html +3 -3
  81. nautobot/core/templates/search.html +1 -1
  82. nautobot/core/templates/swagger_ui.html +9 -9
  83. nautobot/core/templates/utilities/confirmation_form.html +18 -18
  84. nautobot/core/templates/utilities/render_field.html +1 -1
  85. nautobot/core/templates/utilities/render_jinja2.html +43 -43
  86. nautobot/core/templates/utilities/templatetags/filter_form_modal.html +56 -56
  87. nautobot/core/templates/utilities/templatetags/utilization_graph.html +1 -1
  88. nautobot/core/templates/utilities/theme_preview.html +799 -799
  89. nautobot/core/templates/utilities/worker_status.html +122 -122
  90. nautobot/core/templates/widgets/clearable_file.html +3 -3
  91. nautobot/core/templates/widgets/sluginput.html +1 -1
  92. nautobot/core/templatetags/buttons.py +8 -2
  93. nautobot/core/templatetags/helpers.py +24 -0
  94. nautobot/core/templatetags/ui_framework.py +40 -5
  95. nautobot/core/testing/filters.py +37 -21
  96. nautobot/core/testing/integration.py +7 -4
  97. nautobot/core/testing/views.py +49 -5
  98. nautobot/core/tests/test_breadcrumbs.py +78 -4
  99. nautobot/core/tests/test_commands.py +7 -4
  100. nautobot/core/tests/test_graphql.py +20 -5
  101. nautobot/core/tests/test_jobs.py +34 -21
  102. nautobot/core/tests/test_tables.py +43 -6
  103. nautobot/core/tests/test_templatetags_ui_framework.py +146 -0
  104. nautobot/core/tests/test_titles.py +2 -2
  105. nautobot/core/tests/test_ui.py +188 -1
  106. nautobot/core/tests/test_utils.py +35 -0
  107. nautobot/core/tests/test_views.py +45 -0
  108. nautobot/core/tests/test_views_generic.py +43 -0
  109. nautobot/core/tests/test_views_utils.py +239 -5
  110. nautobot/core/ui/breadcrumbs.py +220 -28
  111. nautobot/core/ui/bulk_buttons.py +8 -0
  112. nautobot/core/ui/object_detail.py +181 -60
  113. nautobot/core/ui/titles.py +10 -5
  114. nautobot/core/utils/requests.py +27 -2
  115. nautobot/core/views/__init__.py +24 -3
  116. nautobot/core/views/generic.py +70 -35
  117. nautobot/core/views/mixins.py +226 -122
  118. nautobot/core/views/utils.py +270 -1
  119. nautobot/dcim/api/serializers.py +8 -2
  120. nautobot/dcim/constants.py +1 -0
  121. nautobot/dcim/factory.py +4 -3
  122. nautobot/dcim/filters/mixins.py +1 -2
  123. nautobot/dcim/forms.py +5 -1
  124. nautobot/dcim/migrations/0074_alter_rack_u_height.py +21 -0
  125. nautobot/dcim/models/devices.py +30 -1
  126. nautobot/dcim/models/racks.py +2 -2
  127. nautobot/dcim/tables/__init__.py +2 -0
  128. nautobot/dcim/tables/devices.py +24 -0
  129. nautobot/dcim/tables/power.py +2 -2
  130. nautobot/dcim/templates/dcim/cable.html +53 -53
  131. nautobot/dcim/templates/dcim/cable_connect.html +182 -182
  132. nautobot/dcim/templates/dcim/cable_trace.html +1 -1
  133. nautobot/dcim/templates/dcim/console_port_connection_list.html +5 -5
  134. nautobot/dcim/templates/dcim/consoleport.html +86 -86
  135. nautobot/dcim/templates/dcim/consoleserverport.html +86 -86
  136. nautobot/dcim/templates/dcim/controller_create.html +34 -34
  137. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +68 -68
  138. nautobot/dcim/templates/dcim/device/base.html +1 -114
  139. nautobot/dcim/templates/dcim/device/config.html +17 -17
  140. nautobot/dcim/templates/dcim/device/consoleports.html +1 -52
  141. nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -52
  142. nautobot/dcim/templates/dcim/device/devicebays.html +1 -48
  143. nautobot/dcim/templates/dcim/device/frontports.html +1 -52
  144. nautobot/dcim/templates/dcim/device/interfaces.html +1 -56
  145. nautobot/dcim/templates/dcim/device/inventory.html +1 -48
  146. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +64 -64
  147. nautobot/dcim/templates/dcim/device/modulebays.html +1 -48
  148. nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -52
  149. nautobot/dcim/templates/dcim/device/powerports.html +1 -52
  150. nautobot/dcim/templates/dcim/device/rearports.html +1 -52
  151. nautobot/dcim/templates/dcim/device/status.html +66 -66
  152. nautobot/dcim/templates/dcim/device/wireless.html +1 -72
  153. nautobot/dcim/templates/dcim/device.html +4 -422
  154. nautobot/dcim/templates/dcim/device_component.html +0 -19
  155. nautobot/dcim/templates/dcim/device_component_add.html +25 -25
  156. nautobot/dcim/templates/dcim/device_create.html +229 -0
  157. nautobot/dcim/templates/dcim/device_edit.html +2 -227
  158. nautobot/dcim/templates/dcim/devicebay.html +41 -41
  159. nautobot/dcim/templates/dcim/devicebay_populate.html +32 -32
  160. nautobot/dcim/templates/dcim/devicetype_component_add.html +28 -28
  161. nautobot/dcim/templates/dcim/devicetype_retrieve.html +1 -3
  162. nautobot/dcim/templates/dcim/frontport.html +84 -84
  163. nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +1 -1
  164. nautobot/dcim/templates/dcim/inc/device_interface_filter.html +8 -0
  165. nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +1 -15
  166. nautobot/dcim/templates/dcim/inc/location_hierarchy.html +22 -22
  167. nautobot/dcim/templates/dcim/interface.html +206 -206
  168. nautobot/dcim/templates/dcim/interface_connection_list.html +5 -5
  169. nautobot/dcim/templates/dcim/interfaceredundancygroupassociation_create.html +6 -6
  170. nautobot/dcim/templates/dcim/inventoryitem.html +44 -44
  171. nautobot/dcim/templates/dcim/inventoryitem_add.html +32 -32
  172. nautobot/dcim/templates/dcim/inventoryitem_edit.html +22 -22
  173. nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +46 -46
  174. nautobot/dcim/templates/dcim/location_retrieve.html +1 -7
  175. nautobot/dcim/templates/dcim/locationtype.html +1 -6
  176. nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -7
  177. nautobot/dcim/templates/dcim/module/base.html +85 -85
  178. nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
  179. nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
  180. nautobot/dcim/templates/dcim/module_retrieve.html +52 -52
  181. nautobot/dcim/templates/dcim/module_update.html +61 -61
  182. nautobot/dcim/templates/dcim/modulebay_destroy.html +1 -1
  183. nautobot/dcim/templates/dcim/modulebay_retrieve.html +83 -99
  184. nautobot/dcim/templates/dcim/modulebay_update.html +33 -33
  185. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
  186. nautobot/dcim/templates/dcim/moduletype_retrieve.html +140 -144
  187. nautobot/dcim/templates/dcim/platform_create.html +38 -38
  188. nautobot/dcim/templates/dcim/power_port_connection_list.html +5 -5
  189. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -8
  190. nautobot/dcim/templates/dcim/poweroutlet.html +85 -85
  191. nautobot/dcim/templates/dcim/powerpanel_retrieve.html +1 -8
  192. nautobot/dcim/templates/dcim/powerport.html +91 -91
  193. nautobot/dcim/templates/dcim/rack_elevation_list.html +18 -18
  194. nautobot/dcim/templates/dcim/rack_retrieve.html +264 -274
  195. nautobot/dcim/templates/dcim/rackreservation_retrieve.html +0 -3
  196. nautobot/dcim/templates/dcim/rearport.html +78 -78
  197. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -50
  198. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +1 -5
  199. nautobot/dcim/tests/integration/test_device_bulk_operations.py +3 -2
  200. nautobot/dcim/tests/integration/test_location_bulk_operations.py +6 -2
  201. nautobot/dcim/tests/test_api.py +33 -1
  202. nautobot/dcim/tests/test_views.py +189 -4
  203. nautobot/dcim/ui.py +29 -0
  204. nautobot/dcim/urls.py +1 -109
  205. nautobot/dcim/utils.py +30 -0
  206. nautobot/dcim/views.py +1149 -550
  207. nautobot/extras/filters/mixins.py +1 -1
  208. nautobot/extras/forms/forms.py +15 -0
  209. nautobot/extras/models/groups.py +10 -1
  210. nautobot/extras/models/jobs.py +2 -2
  211. nautobot/extras/plugins/views.py +18 -5
  212. nautobot/extras/tables.py +24 -2
  213. nautobot/extras/templates/extras/computedfield_edit.html +4 -4
  214. nautobot/extras/templates/extras/configcontext_update.html +1 -1
  215. nautobot/extras/templates/extras/configcontextschema_retrieve.html +32 -32
  216. nautobot/extras/templates/extras/customfield_retrieve.html +1 -128
  217. nautobot/extras/templates/extras/customfield_update.html +23 -23
  218. nautobot/extras/templates/extras/dynamicgroup.html +2 -99
  219. nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -199
  220. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +99 -0
  221. nautobot/extras/templates/extras/dynamicgroup_update.html +199 -0
  222. nautobot/extras/templates/extras/gitrepository.html +2 -82
  223. nautobot/extras/templates/extras/gitrepository_list.html +10 -10
  224. nautobot/extras/templates/extras/gitrepository_object_edit.html +2 -13
  225. nautobot/extras/templates/extras/gitrepository_retrieve.html +82 -0
  226. nautobot/extras/templates/extras/gitrepository_update.html +13 -0
  227. nautobot/extras/templates/extras/graphqlquery_retrieve.html +73 -73
  228. nautobot/extras/templates/extras/inc/configcontext_format.html +2 -2
  229. nautobot/extras/templates/extras/inc/job_table.html +10 -10
  230. nautobot/extras/templates/extras/inc/jobresult.html +21 -21
  231. nautobot/extras/templates/extras/inc/jobresult_js.html +6 -6
  232. nautobot/extras/templates/extras/inc/tags_panel.html +10 -10
  233. nautobot/extras/templates/extras/job.html +64 -64
  234. nautobot/extras/templates/extras/job_approval_request.html +9 -9
  235. nautobot/extras/templates/extras/job_bulk_edit.html +13 -13
  236. nautobot/extras/templates/extras/job_edit.html +45 -45
  237. nautobot/extras/templates/extras/job_list.html +4 -4
  238. nautobot/extras/templates/extras/jobresult_retrieve.html +0 -25
  239. nautobot/extras/templates/extras/marketplace.html +101 -101
  240. nautobot/extras/templates/extras/metadatatype_create.html +20 -20
  241. nautobot/extras/templates/extras/note_retrieve.html +0 -52
  242. nautobot/extras/templates/extras/object_assign_contact_or_team.html +18 -18
  243. nautobot/extras/templates/extras/object_configcontext.html +1 -3
  244. nautobot/extras/templates/extras/objectchange.html +2 -165
  245. nautobot/extras/templates/extras/objectchange_retrieve.html +165 -0
  246. nautobot/extras/templates/extras/plugin_detail.html +44 -48
  247. nautobot/extras/templates/extras/plugins_list.html +9 -11
  248. nautobot/extras/templates/extras/plugins_tiles.html +26 -26
  249. nautobot/extras/templates/extras/relationship_edit.html +4 -4
  250. nautobot/extras/templates/extras/role_retrieve.html +13 -13
  251. nautobot/extras/templates/extras/scheduled_jobs_approval_queue_list.html +21 -21
  252. nautobot/extras/templates/extras/scheduledjob.html +128 -128
  253. nautobot/extras/templates/extras/secret_create.html +53 -53
  254. nautobot/extras/templates/extras/secretsgroup_update.html +13 -13
  255. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +3 -3
  256. nautobot/extras/templates/extras/webhook.html +79 -79
  257. nautobot/extras/tests/integration/test_relationships.py +6 -6
  258. nautobot/extras/tests/test_dynamicgroups.py +73 -18
  259. nautobot/extras/tests/test_filters.py +1 -1
  260. nautobot/extras/tests/test_jobs.py +2 -0
  261. nautobot/extras/tests/test_views.py +8 -3
  262. nautobot/extras/urls.py +3 -97
  263. nautobot/extras/views.py +524 -456
  264. nautobot/ipam/filters.py +2 -2
  265. nautobot/ipam/migrations/0053_alter_vrfdeviceassignment_options_and_more.py +20 -0
  266. nautobot/ipam/models.py +34 -0
  267. nautobot/ipam/querysets.py +3 -3
  268. nautobot/ipam/signals.py +6 -1
  269. nautobot/ipam/tables.py +3 -1
  270. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +4 -0
  271. nautobot/ipam/templates/ipam/inc/toggle_available.html +8 -8
  272. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +4 -4
  273. nautobot/ipam/templates/ipam/ipaddress.html +119 -123
  274. nautobot/ipam/templates/ipam/ipaddress_assign.html +10 -10
  275. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -1
  276. nautobot/ipam/templates/ipam/ipaddress_merge.html +180 -180
  277. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +48 -48
  278. nautobot/ipam/templates/ipam/prefix.html +2 -115
  279. nautobot/ipam/templates/ipam/prefix_create.html +34 -0
  280. nautobot/ipam/templates/ipam/prefix_edit.html +1 -34
  281. nautobot/ipam/templates/ipam/prefix_retrieve.html +3 -0
  282. nautobot/ipam/templates/ipam/service_retrieve.html +1 -6
  283. nautobot/ipam/templates/ipam/vlan_retrieve.html +1 -7
  284. nautobot/ipam/templates/ipam/vrf_edit.html +1 -1
  285. nautobot/ipam/tests/test_api.py +5 -0
  286. nautobot/ipam/tests/test_models.py +387 -0
  287. nautobot/ipam/tests/test_querysets.py +46 -0
  288. nautobot/ipam/tests/test_views.py +34 -0
  289. nautobot/ipam/ui.py +145 -0
  290. nautobot/ipam/urls.py +1 -46
  291. nautobot/ipam/utils/__init__.py +26 -0
  292. nautobot/ipam/utils/migrations.py +1 -1
  293. nautobot/ipam/views.py +234 -112
  294. nautobot/project-static/docs/404.html +11 -11
  295. nautobot/project-static/docs/apps/index.html +11 -11
  296. nautobot/project-static/docs/apps/nautobot-apps.html +11 -11
  297. nautobot/project-static/docs/assets/javascripts/{bundle.92b07e13.min.js → bundle.f55a23d4.min.js} +2 -2
  298. nautobot/project-static/docs/assets/javascripts/{bundle.92b07e13.min.js.map → bundle.f55a23d4.min.js.map} +2 -2
  299. nautobot/project-static/docs/assets/stylesheets/{main.7e37652d.min.css → main.e53b48f4.min.css} +1 -1
  300. nautobot/project-static/docs/assets/stylesheets/{main.7e37652d.min.css.map → main.e53b48f4.min.css.map} +1 -1
  301. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +11 -11
  302. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +11 -11
  303. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +11 -11
  304. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +11 -11
  305. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +11 -11
  306. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +11 -11
  307. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +11 -11
  308. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +11 -11
  309. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +11 -11
  310. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +11 -11
  311. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +11 -11
  312. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +11 -11
  313. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +11 -11
  314. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +11 -11
  315. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +11 -11
  316. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +11 -11
  317. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +11 -11
  318. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +11 -11
  319. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +11 -11
  320. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +83 -11
  321. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1265 -281
  322. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +11 -11
  323. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +12 -12
  324. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +452 -29
  325. nautobot/project-static/docs/development/apps/api/configuration-view.html +11 -11
  326. nautobot/project-static/docs/development/apps/api/database-backend-config.html +11 -11
  327. nautobot/project-static/docs/development/apps/api/models/django-admin.html +11 -11
  328. nautobot/project-static/docs/development/apps/api/models/global-search.html +11 -11
  329. nautobot/project-static/docs/development/apps/api/models/graphql.html +11 -11
  330. nautobot/project-static/docs/development/apps/api/models/index.html +11 -11
  331. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +12 -12
  332. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +11 -11
  333. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +11 -11
  334. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +11 -11
  335. nautobot/project-static/docs/development/apps/api/platform-features/index.html +11 -11
  336. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +11 -11
  337. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +11 -11
  338. nautobot/project-static/docs/development/apps/api/platform-features/prepopulating-data.html +11 -11
  339. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +11 -11
  340. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +11 -11
  341. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +11 -11
  342. nautobot/project-static/docs/development/apps/api/prometheus.html +11 -11
  343. nautobot/project-static/docs/development/apps/api/setup.html +11 -11
  344. nautobot/project-static/docs/development/apps/api/testing.html +11 -11
  345. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +11 -11
  346. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +11 -11
  347. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +11 -11
  348. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +11 -11
  349. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +11 -11
  350. nautobot/project-static/docs/development/apps/api/views/base-template.html +11 -11
  351. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +11 -11
  352. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +11 -11
  353. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +11 -11
  354. nautobot/project-static/docs/development/apps/api/views/index.html +11 -11
  355. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +11 -11
  356. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +11 -11
  357. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +11 -11
  358. nautobot/project-static/docs/development/apps/api/views/notes.html +11 -11
  359. nautobot/project-static/docs/development/apps/api/views/rest-api.html +11 -11
  360. nautobot/project-static/docs/development/apps/api/views/urls.html +11 -11
  361. nautobot/project-static/docs/development/apps/index.html +11 -11
  362. nautobot/project-static/docs/development/apps/migration/code-updates.html +11 -11
  363. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +11 -11
  364. nautobot/project-static/docs/development/apps/migration/from-v1.html +11 -11
  365. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +11 -11
  366. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +11 -11
  367. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +11 -11
  368. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +11 -11
  369. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +11 -11
  370. nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +11 -11
  371. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +11 -11
  372. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +11 -11
  373. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +11 -11
  374. nautobot/project-static/docs/development/apps/porting-from-netbox.html +11 -11
  375. nautobot/project-static/docs/development/core/application-registry.html +11 -11
  376. nautobot/project-static/docs/development/core/best-practices.html +11 -11
  377. nautobot/project-static/docs/development/core/bootstrap-ui.html +11 -11
  378. nautobot/project-static/docs/development/core/caching.html +11 -11
  379. nautobot/project-static/docs/development/core/controllers.html +11 -11
  380. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +11 -11
  381. nautobot/project-static/docs/development/core/generic-views.html +11 -11
  382. nautobot/project-static/docs/development/core/getting-started.html +50 -63
  383. nautobot/project-static/docs/development/core/homepage.html +11 -11
  384. nautobot/project-static/docs/development/core/index.html +11 -11
  385. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +11 -11
  386. nautobot/project-static/docs/development/core/model-checklist.html +11 -11
  387. nautobot/project-static/docs/development/core/model-features.html +11 -11
  388. nautobot/project-static/docs/development/core/natural-keys.html +11 -11
  389. nautobot/project-static/docs/development/core/navigation-menu.html +11 -11
  390. nautobot/project-static/docs/development/core/release-checklist.html +11 -11
  391. nautobot/project-static/docs/development/core/role-internals.html +11 -11
  392. nautobot/project-static/docs/development/core/settings.html +11 -11
  393. nautobot/project-static/docs/development/core/style-guide.html +15 -11
  394. nautobot/project-static/docs/development/core/templates.html +11 -11
  395. nautobot/project-static/docs/development/core/testing.html +11 -11
  396. nautobot/project-static/docs/development/core/ui-component-framework.html +17 -22
  397. nautobot/project-static/docs/development/core/user-preferences.html +11 -11
  398. nautobot/project-static/docs/development/index.html +11 -11
  399. nautobot/project-static/docs/development/jobs/getting-started.html +11 -11
  400. nautobot/project-static/docs/development/jobs/index.html +11 -11
  401. nautobot/project-static/docs/development/jobs/installation.html +11 -11
  402. nautobot/project-static/docs/development/jobs/job-extensions.html +11 -11
  403. nautobot/project-static/docs/development/jobs/job-logging.html +11 -11
  404. nautobot/project-static/docs/development/jobs/job-patterns.html +11 -11
  405. nautobot/project-static/docs/development/jobs/job-structure.html +11 -11
  406. nautobot/project-static/docs/development/jobs/migration/from-v1.html +11 -11
  407. nautobot/project-static/docs/development/jobs/testing.html +11 -11
  408. nautobot/project-static/docs/index.html +11 -11
  409. nautobot/project-static/docs/objects.inv +0 -0
  410. nautobot/project-static/docs/overview/application_stack.html +11 -11
  411. nautobot/project-static/docs/overview/design_philosophy.html +11 -11
  412. nautobot/project-static/docs/release-notes/index.html +11 -11
  413. nautobot/project-static/docs/release-notes/version-1.0.html +11 -11
  414. nautobot/project-static/docs/release-notes/version-1.1.html +11 -11
  415. nautobot/project-static/docs/release-notes/version-1.2.html +11 -11
  416. nautobot/project-static/docs/release-notes/version-1.3.html +11 -11
  417. nautobot/project-static/docs/release-notes/version-1.4.html +11 -11
  418. nautobot/project-static/docs/release-notes/version-1.5.html +11 -11
  419. nautobot/project-static/docs/release-notes/version-1.6.html +11 -11
  420. nautobot/project-static/docs/release-notes/version-2.0.html +11 -11
  421. nautobot/project-static/docs/release-notes/version-2.1.html +11 -11
  422. nautobot/project-static/docs/release-notes/version-2.2.html +11 -11
  423. nautobot/project-static/docs/release-notes/version-2.3.html +11 -11
  424. nautobot/project-static/docs/release-notes/version-2.4.html +418 -11
  425. nautobot/project-static/docs/search/search_index.json +1 -1
  426. nautobot/project-static/docs/sitemap.xml +300 -300
  427. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  428. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +11 -11
  429. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +11 -11
  430. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +11 -11
  431. nautobot/project-static/docs/user-guide/administration/configuration/index.html +11 -11
  432. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +11 -11
  433. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +38 -11
  434. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +11 -11
  435. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +89 -14
  436. nautobot/project-static/docs/user-guide/administration/guides/docker.html +11 -11
  437. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +11 -11
  438. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +11 -11
  439. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +11 -11
  440. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +11 -11
  441. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +11 -11
  442. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +11 -11
  443. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +11 -11
  444. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +11 -11
  445. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +11 -11
  446. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +11 -11
  447. nautobot/project-static/docs/user-guide/administration/installation/index.html +11 -11
  448. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +11 -11
  449. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +11 -11
  450. nautobot/project-static/docs/user-guide/administration/installation/services.html +11 -11
  451. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +11 -11
  452. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +11 -11
  453. nautobot/project-static/docs/user-guide/administration/security/index.html +11 -11
  454. nautobot/project-static/docs/user-guide/administration/security/notices.html +11 -11
  455. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +11 -11
  456. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +11 -11
  457. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +11 -11
  458. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +11 -11
  459. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +11 -11
  460. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +11 -11
  461. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +11 -11
  462. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +11 -11
  463. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +11 -11
  464. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +11 -11
  465. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +11 -11
  466. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +11 -11
  467. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +11 -11
  468. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +11 -11
  469. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +11 -11
  470. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +11 -11
  471. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +11 -11
  472. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +11 -11
  473. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +11 -11
  474. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +11 -11
  475. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +11 -11
  476. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +11 -11
  477. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +11 -11
  478. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +11 -11
  479. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +11 -11
  480. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +11 -11
  481. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +11 -11
  482. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +11 -11
  483. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +11 -11
  484. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +11 -11
  485. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +11 -11
  486. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +11 -11
  487. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +11 -11
  488. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +11 -11
  489. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +11 -11
  490. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +11 -11
  491. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +11 -11
  492. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +11 -11
  493. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +11 -11
  494. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +11 -11
  495. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +11 -11
  496. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +11 -11
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +11 -11
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +11 -11
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +11 -11
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +11 -11
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +11 -11
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +11 -11
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +11 -11
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +11 -11
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +11 -11
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +11 -11
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +11 -11
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +11 -11
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +11 -11
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +11 -11
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +11 -11
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +11 -11
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +11 -11
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +11 -11
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +11 -11
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +11 -11
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +11 -11
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +11 -11
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +11 -11
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +11 -11
  521. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +11 -11
  522. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +11 -11
  523. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +11 -11
  524. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +11 -11
  525. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +11 -11
  526. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +11 -11
  527. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +11 -11
  528. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +11 -11
  529. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +11 -11
  530. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +11 -11
  531. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +11 -11
  532. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +11 -11
  533. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +11 -11
  534. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +11 -11
  535. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +11 -11
  536. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +11 -11
  537. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +11 -11
  538. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +11 -11
  539. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +11 -11
  540. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +11 -11
  541. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +11 -11
  542. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +11 -11
  543. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +11 -11
  544. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +11 -11
  545. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +11 -11
  546. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -11
  547. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +11 -11
  548. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +11 -11
  549. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +11 -11
  550. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +11 -11
  551. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +11 -11
  552. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +11 -11
  553. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +11 -11
  554. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +11 -11
  555. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +11 -11
  556. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +11 -11
  557. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +11 -11
  558. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +11 -11
  559. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +11 -11
  560. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +11 -11
  561. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +11 -11
  562. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +11 -11
  563. nautobot/project-static/docs/user-guide/index.html +11 -11
  564. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +11 -11
  565. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +11 -11
  566. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +11 -11
  567. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +11 -11
  568. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +11 -11
  569. nautobot/project-static/docs/user-guide/platform-functionality/events.html +11 -11
  570. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +11 -11
  571. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +11 -11
  572. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +11 -11
  573. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +11 -11
  574. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +11 -11
  575. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +11 -11
  576. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +11 -11
  577. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +11 -11
  578. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +11 -11
  579. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +11 -11
  580. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +11 -11
  581. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +11 -11
  582. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +11 -11
  583. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +11 -11
  584. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +11 -11
  585. nautobot/project-static/docs/user-guide/platform-functionality/note.html +11 -11
  586. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +11 -11
  587. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +11 -11
  588. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +11 -11
  589. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +11 -11
  590. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +11 -11
  591. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +11 -11
  592. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +11 -11
  593. nautobot/project-static/docs/user-guide/platform-functionality/role.html +11 -11
  594. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +11 -11
  595. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +11 -11
  596. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +11 -11
  597. nautobot/project-static/docs/user-guide/platform-functionality/status.html +11 -11
  598. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +11 -11
  599. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +11 -11
  600. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +11 -11
  601. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +11 -11
  602. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +11 -11
  603. nautobot/project-static/img/nautobot_icon.svg +32 -34
  604. nautobot/project-static/js/table_sorting_indicator.js +0 -2
  605. nautobot/tenancy/templates/tenancy/tenant.html +1 -7
  606. nautobot/tenancy/views.py +13 -0
  607. nautobot/users/templates/users/api_tokens.html +4 -4
  608. nautobot/users/templates/users/base.html +28 -28
  609. nautobot/virtualization/templates/virtualization/cluster.html +64 -64
  610. nautobot/virtualization/templates/virtualization/inc/virtualmachine_vminterface_filter.html +8 -0
  611. nautobot/virtualization/templates/virtualization/virtualmachine_component_add.html +25 -25
  612. nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +1 -251
  613. nautobot/virtualization/templates/virtualization/vminterface.html +70 -70
  614. nautobot/virtualization/urls.py +0 -12
  615. nautobot/virtualization/views.py +158 -54
  616. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +13 -13
  617. nautobot/wireless/tests/integration/test_radio_profile.py +1 -1
  618. {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/METADATA +4 -4
  619. {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/RECORD +623 -607
  620. nautobot/core/templates/inc/breadcrumbs.html +0 -14
  621. nautobot/ipam/templates/ipam/prefix_ipaddresses.html +0 -11
  622. nautobot/ipam/templates/ipam/prefix_prefixes.html +0 -11
  623. nautobot/project-static/docs/requirements.txt +0 -14
  624. {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/LICENSE.txt +0 -0
  625. {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/NOTICE +0 -0
  626. {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/WHEEL +0 -0
  627. {nautobot-2.4.17.dist-info → nautobot-2.4.19.dist-info}/entry_points.txt +0 -0
@@ -45,6 +45,14 @@ class BulkEditButton(BaseBulkButton):
45
45
  weight = 300
46
46
 
47
47
 
48
+ class BulkDisconnectButton(BaseBulkButton):
49
+ action = "disconnect"
50
+ color = ButtonActionColorChoices.DISCONNECT
51
+ icon = "mdi-ethernet-cable-off"
52
+ label = "Disconnect"
53
+ weight = 350
54
+
55
+
48
56
  class BulkDeleteButton(BaseBulkButton):
49
57
  action = "delete"
50
58
  color = ButtonActionColorChoices.DELETE
@@ -9,6 +9,7 @@ from django.contrib.contenttypes.models import ContentType
9
9
  from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
10
10
  from django.db import models
11
11
  from django.db.models import CharField, JSONField, Q, URLField
12
+ from django.db.models.constants import LOOKUP_SEP
12
13
  from django.db.models.fields.related import ManyToManyField
13
14
  from django.template import Context
14
15
  from django.template.defaultfilters import truncatechars
@@ -42,9 +43,11 @@ from nautobot.core.utils.lookup import get_filterset_for_model, get_route_for_mo
42
43
  from nautobot.core.utils.permissions import get_permission_for_model
43
44
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
44
45
  from nautobot.core.views.utils import get_obj_from_context
46
+ from nautobot.dcim.models import Rack
45
47
  from nautobot.extras.choices import CustomFieldTypeChoices
46
48
  from nautobot.extras.tables import AssociatedContactsTable, DynamicGroupTable, ObjectMetadataTable
47
49
  from nautobot.tenancy.models import Tenant
50
+ from nautobot.virtualization.models import Cluster
48
51
 
49
52
  logger = logging.getLogger(__name__)
50
53
 
@@ -122,16 +125,24 @@ class ObjectDetailContent:
122
125
  class Component:
123
126
  """Common base class for renderable components (tabs, panels, etc.)."""
124
127
 
125
- def __init__(self, *, weight):
128
+ def __init__(
129
+ self,
130
+ *,
131
+ weight,
132
+ required_permissions=None,
133
+ ):
126
134
  """Initialize common Component properties.
127
135
 
128
136
  Args:
129
137
  weight (int): A relative weighting of this Component relative to its peers. Typically lower weights will be
130
138
  rendered "first", usually towards the top left of the page.
139
+ required_permissions (list, optional): Permissions such as `["dcim.add_consoleport"]`.
140
+ The component will only be rendered if the user has these permissions.
131
141
  """
132
142
  self.weight = weight
143
+ self.required_permissions = required_permissions or []
133
144
 
134
- def should_render(self, context: dict):
145
+ def should_render(self, context: Context):
135
146
  """
136
147
  Check whether this component should be rendered at all.
137
148
 
@@ -139,9 +150,13 @@ class Component:
139
150
  In general most Components may also return an empty string when actually rendered, which is typically also a
140
151
  means to specify that they do not need to be rendered, but may be more expensive to derive.
141
152
 
153
+ The default implementation here checks for `self.required_permissions` if any.
154
+
142
155
  Returns:
143
156
  (bool): `True` (default) if this component should be rendered.
144
157
  """
158
+ if self.required_permissions:
159
+ return context["request"].user.has_perms(self.required_permissions)
145
160
  return True
146
161
 
147
162
  def render(self, context: Context):
@@ -177,12 +192,12 @@ class Button(Component):
177
192
  link_name=None,
178
193
  icon=None,
179
194
  template_path="components/button/default.html",
180
- required_permissions=None,
181
195
  javascript_template_path=None,
182
196
  attributes=None,
183
197
  size=None,
184
198
  link_includes_pk=True,
185
199
  context_object_key=None,
200
+ render_on_tab_id="main",
186
201
  **kwargs,
187
202
  ):
188
203
  """
@@ -198,30 +213,25 @@ class Button(Component):
198
213
  context_object_key (str, optional): The key in the render context that will contain the linked object.
199
214
  icon (str, optional): Material Design Icons icon, to include on the button, for example `"mdi-plus-bold"`.
200
215
  template_path (str): Template to render for this button.
201
- required_permissions (list, optional): Permissions such as `["dcim.add_consoleport"]`.
202
- The button will only be rendered if the user has these permissions.
203
216
  javascript_template_path (str, optional): JavaScript template to render and include with this button.
204
217
  Does not need to include the wrapping `<script>...</script>` tags as those will be added automatically.
205
218
  attributes (dict, optional): Additional HTML attributes and their values to attach to the button.
206
219
  size (str, optional): The size of the button (e.g. `xs` or `sm`), used to apply a Bootstrap-style sizing.
220
+ render_on_tab_id (str, optional): The (only) tab that this button should appear on.
207
221
  """
208
222
  self.label = label
209
223
  self.color = color
210
224
  self.link_name = link_name
211
225
  self.icon = icon
212
226
  self.template_path = template_path
213
- self.required_permissions = required_permissions or []
214
227
  self.javascript_template_path = javascript_template_path
215
228
  self.attributes = attributes
216
229
  self.size = size
217
230
  self.link_includes_pk = link_includes_pk
218
231
  self.context_object_key = context_object_key
232
+ self.render_on_tab_id = render_on_tab_id
219
233
  super().__init__(**kwargs)
220
234
 
221
- def should_render(self, context: Context):
222
- """Render if and only if the requesting user has appropriate permissions (if any)."""
223
- return context["request"].user.has_perms(self.required_permissions)
224
-
225
235
  def get_link(self, context: Context):
226
236
  """
227
237
  Get the hyperlink URL (if any) for this button.
@@ -247,6 +257,11 @@ class Button(Component):
247
257
  "size": self.size,
248
258
  }
249
259
 
260
+ def should_render(self, context: Context):
261
+ if not super().should_render(context):
262
+ return False
263
+ return context.get("active_tab", "main") == self.render_on_tab_id
264
+
250
265
  def render(self, context: Context):
251
266
  """Render this button to HTML, possibly including any associated JavaScript."""
252
267
  if not self.should_render(context):
@@ -398,9 +413,18 @@ class Tab(Component):
398
413
  """
399
414
  return self.label
400
415
 
416
+ def should_render_content(self, context: Context):
417
+ """
418
+ Only render a main-view Tab if the active request is for the main object view rather than a separate action.
419
+ """
420
+ request = context["request"]
421
+ obj = get_obj_from_context(context)
422
+ return request.path == obj.get_absolute_url()
423
+
401
424
  def render(self, context: Context):
402
425
  """Render the tab's contents (layout and panels) to HTML."""
403
- if not self.should_render(context):
426
+ # Check should_render_content first as it's generally a cheaper calculation than should_render checking perms
427
+ if not self.should_render_content(context) or not self.should_render(context):
404
428
  return ""
405
429
 
406
430
  with context.update(
@@ -426,6 +450,7 @@ class DistinctViewTab(Tab):
426
450
  url_name (str): The name of the URL pattern to link to, which will be reversed to generate the URL.
427
451
  label_wrapper_template_path (str, optional): Template path to render the tab label to HTML.
428
452
  related_object_attribute (str, optional): The name of the related object attribute to count for the tab label.
453
+ hide_if_empty (bool, optional): Do not render this tab if the related_object_attribute is an empty queryset.
429
454
  """
430
455
 
431
456
  def __init__(
@@ -434,39 +459,62 @@ class DistinctViewTab(Tab):
434
459
  url_name,
435
460
  label_wrapper_template_path="components/tab/label_wrapper_distinct_view.html",
436
461
  related_object_attribute="",
462
+ hide_if_empty=False,
437
463
  **kwargs,
438
464
  ):
439
465
  self.url_name = url_name
440
466
  self.related_object_attribute = related_object_attribute
467
+ self.hide_if_empty = hide_if_empty
441
468
  super().__init__(label_wrapper_template_path=label_wrapper_template_path, **kwargs)
442
469
 
443
470
  def get_extra_context(self, context: Context):
444
471
  return {"url": reverse(self.url_name, kwargs={"pk": get_obj_from_context(context).pk})}
445
472
 
446
- def render_label(self, context: Context):
473
+ def should_render(self, context: Context):
474
+ if not super().should_render(context):
475
+ return False
476
+
477
+ self.related_object_count = None
447
478
  if not self.related_object_attribute:
448
- return super().render_label(context)
479
+ return True
449
480
 
450
481
  obj = get_obj_from_context(context)
451
482
  if not hasattr(obj, self.related_object_attribute):
452
483
  logger.warning(
453
484
  f"{obj} does not have a related attribute {self.related_object_attribute} to count for tab label."
454
485
  )
455
- return super().render_label(context)
486
+ return True
456
487
 
457
488
  try:
458
- related_obj_count = getattr(obj, self.related_object_attribute).count()
459
- return format_html(
460
- "{} {}",
461
- self.label,
462
- render_to_string("utilities/templatetags/badge.html", badge(related_obj_count, True)),
463
- )
489
+ self.related_object_count = getattr(obj, self.related_object_attribute).count()
464
490
  except AttributeError:
465
- logger.warning(
491
+ # Not a warning log, as there are cases where this is expected.
492
+ logger.debug(
466
493
  f"{obj}'s attribute {self.related_object_attribute} is not a related manager to count for tab label."
467
494
  )
495
+
496
+ if self.hide_if_empty and not self.related_object_count:
497
+ return False
498
+ return True
499
+
500
+ def should_render_content(self, context: Context):
501
+ """
502
+ A DistinctViewTab should only render its content if the view in question is active.
503
+ """
504
+ with context.update(self.get_extra_context(context)):
505
+ request = context["request"]
506
+ return request.path == context["url"]
507
+
508
+ def render_label(self, context: Context):
509
+ if not self.related_object_attribute or not self.related_object_count:
468
510
  return super().render_label(context)
469
511
 
512
+ return format_html(
513
+ "{} {}",
514
+ self.label,
515
+ render_to_string("utilities/templatetags/badge.html", badge(self.related_object_count, True)),
516
+ )
517
+
470
518
 
471
519
  class Panel(Component):
472
520
  """Base class for defining an individual display panel within a Layout within a Tab."""
@@ -606,7 +654,7 @@ class DataTablePanel(Panel):
606
654
  **kwargs,
607
655
  ):
608
656
  """
609
- Instantiate a DataDictTablePanel.
657
+ Instantiate a DataTablePanel.
610
658
 
611
659
  Args:
612
660
  context_data_key (str): The key in the render context that stores the data used to populate the table.
@@ -683,6 +731,7 @@ class ObjectsTablePanel(Panel):
683
731
  table_class=None,
684
732
  table_filter=None,
685
733
  table_attribute=None,
734
+ distinct=False,
686
735
  select_related_fields=None,
687
736
  prefetch_related_fields=None,
688
737
  order_by_fields=None,
@@ -725,6 +774,7 @@ class ObjectsTablePanel(Panel):
725
774
  table_attribute (str, optional): The attribute of the detail view instance that contains the queryset to
726
775
  initialize the table class. e.g. `dynamic_groups`.
727
776
  Mutually exclusive with `table_filter`.
777
+ distinct (bool, optional): If True, apply `.distinct()` to the table queryset.
728
778
  select_related_fields (list, optional): list of fields to pass to table queryset's `select_related` method.
729
779
  prefetch_related_fields (list, optional): list of fields to pass to table queryset's `prefetch_related`
730
780
  method.
@@ -760,6 +810,7 @@ class ObjectsTablePanel(Panel):
760
810
  table_class,
761
811
  table_filter,
762
812
  table_attribute,
813
+ distinct,
763
814
  select_related_fields,
764
815
  prefetch_related_fields,
765
816
  order_by_fields,
@@ -768,8 +819,8 @@ class ObjectsTablePanel(Panel):
768
819
  ):
769
820
  raise ValueError(
770
821
  "context_table_key cannot be combined with any of the args that are used to dynamically construct the "
771
- "table (table_class, table_filter, table_attribute, select_related_fields, prefetch_related_fields, "
772
- "order_by_fields, hide_hierarchy_ui)."
822
+ "table (table_class, table_filter, table_attribute, distinct, select_related_fields, "
823
+ "prefetch_related_fields, order_by_fields, hide_hierarchy_ui)."
773
824
  )
774
825
  self.context_table_key = context_table_key
775
826
  self.table_class = table_class
@@ -781,6 +832,7 @@ class ObjectsTablePanel(Panel):
781
832
  raise ValueError("You must provide a `related_field_name` when specifying `table_attribute`")
782
833
  self.table_filter = table_filter
783
834
  self.table_attribute = table_attribute
835
+ self.distinct = distinct
784
836
  self.select_related_fields = select_related_fields
785
837
  self.prefetch_related_fields = prefetch_related_fields
786
838
  self.order_by_fields = order_by_fields
@@ -791,7 +843,7 @@ class ObjectsTablePanel(Panel):
791
843
  self.include_columns = include_columns
792
844
  self.exclude_columns = exclude_columns
793
845
  self.add_button_route = add_button_route
794
- self.add_permissions = add_permissions
846
+ self.add_permissions = add_permissions or []
795
847
  self.hide_hierarchy_ui = hide_hierarchy_ui
796
848
  self.related_field_name = related_field_name
797
849
  self.enable_bulk_actions = enable_bulk_actions
@@ -822,21 +874,26 @@ class ObjectsTablePanel(Panel):
822
874
  if self.tab_id:
823
875
  return_url += f"?tab={self.tab_id}"
824
876
 
825
- if self.add_button_route == "default":
826
- body_content_table_class = self.table_class or context[self.context_table_key].__class__
827
- body_content_table_model = body_content_table_class.Meta.model
828
- permission_name = get_permission_for_model(body_content_table_model, "add")
829
- if request.user.has_perms([permission_name]):
830
- try:
831
- add_route = reverse(get_route_for_model(body_content_table_model, "add"))
877
+ if self.add_button_route is not None:
878
+ add_permissions = self.add_permissions
879
+ if not add_permissions:
880
+ body_content_table_class = self.table_class or context[self.context_table_key].__class__
881
+ body_content_table_model = body_content_table_class.Meta.model
882
+ add_permissions = [get_permission_for_model(body_content_table_model, "add")]
883
+
884
+ if self.add_button_route == "default":
885
+ if request.user.has_perms(add_permissions):
886
+ try:
887
+ add_route = reverse(get_route_for_model(body_content_table_model, "add"))
888
+ body_content_table_add_url = (
889
+ f"{add_route}?{related_field_name}={obj.pk}&return_url={return_url}"
890
+ )
891
+ except NoReverseMatch:
892
+ logger.warning("add route for `body_content_table_model` not found")
893
+ else:
894
+ if request.user.has_perms(add_permissions):
895
+ add_route = reverse(self.add_button_route)
832
896
  body_content_table_add_url = f"{add_route}?{related_field_name}={obj.pk}&return_url={return_url}"
833
- except NoReverseMatch:
834
- logger.warning("add route for `body_content_table_model` not found")
835
-
836
- elif self.add_button_route is not None:
837
- if request.user.has_perms(self.add_permissions or []):
838
- add_route = reverse(self.add_button_route)
839
- body_content_table_add_url = f"{add_route}?{related_field_name}={obj.pk}&return_url={return_url}"
840
897
 
841
898
  return body_content_table_add_url
842
899
 
@@ -879,7 +936,8 @@ class ObjectsTablePanel(Panel):
879
936
  )
880
937
  if self.order_by_fields:
881
938
  body_content_table_queryset = body_content_table_queryset.order_by(*self.order_by_fields)
882
- body_content_table_queryset = body_content_table_queryset.distinct()
939
+ if self.distinct:
940
+ body_content_table_queryset = body_content_table_queryset.distinct()
883
941
  body_content_table = body_content_table_class(
884
942
  body_content_table_queryset, hide_hierarchy_ui=self.hide_hierarchy_ui, user=request.user
885
943
  )
@@ -971,6 +1029,7 @@ class KeyValueTablePanel(Panel):
971
1029
  context_data_key=None,
972
1030
  hide_if_unset=(),
973
1031
  value_transforms=None,
1032
+ key_transforms=None,
974
1033
  body_wrapper_template_path="components/panel/body_wrapper_key_value_table.html",
975
1034
  **kwargs,
976
1035
  ):
@@ -989,6 +1048,8 @@ class KeyValueTablePanel(Panel):
989
1048
 
990
1049
  - `[render_markdown, placeholder]` - render the given text as Markdown, or render a placeholder if blank
991
1050
  - `[humanize_speed, placeholder]` - convert the given kbps value to Mbps or Gbps for display
1051
+ key_transforms (dict, optional): A mapping of original field names to custom display names to be used when rendering keys
1052
+ For example: {'content_types': 'Content Type'}.
992
1053
  """
993
1054
  if data and context_data_key:
994
1055
  raise ValueError("The data and context_data_key parameters are mutually exclusive")
@@ -996,9 +1057,12 @@ class KeyValueTablePanel(Panel):
996
1057
  self.context_data_key = context_data_key or "data"
997
1058
  self.hide_if_unset = hide_if_unset
998
1059
  self.value_transforms = value_transforms or {}
1060
+ self.key_transforms = key_transforms or {}
999
1061
  super().__init__(body_wrapper_template_path=body_wrapper_template_path, **kwargs)
1000
1062
 
1001
1063
  def should_render(self, context: Context):
1064
+ if not super().should_render(context):
1065
+ return False
1002
1066
  return bool(self.get_data(context))
1003
1067
 
1004
1068
  def get_data(self, context: Context):
@@ -1088,6 +1152,10 @@ class KeyValueTablePanel(Panel):
1088
1152
  elif isinstance(value, models.Model):
1089
1153
  if hasattr(value, "color"):
1090
1154
  display = hyperlinked_object_with_color(value)
1155
+ elif isinstance(value, Cluster) and value.cluster_group is not None:
1156
+ display = format_html("{} / {}", hyperlinked_object(value.cluster_group), hyperlinked_object(value))
1157
+ elif isinstance(value, Rack) and value.rack_group is not None:
1158
+ display = format_html("{} / {}", hyperlinked_object(value.rack_group), hyperlinked_object(value))
1091
1159
  elif isinstance(value, Tenant) and value.tenant_group is not None:
1092
1160
  display = format_html("{} / {}", hyperlinked_object(value.tenant_group), hyperlinked_object(value))
1093
1161
  # TODO: render location hierarchy for Location objects
@@ -1176,6 +1244,7 @@ class ObjectFieldsPanel(KeyValueTablePanel):
1176
1244
  self,
1177
1245
  *,
1178
1246
  fields="__all__",
1247
+ additional_fields=(),
1179
1248
  exclude_fields=(),
1180
1249
  context_object_key=None,
1181
1250
  ignore_nonexistent_fields=False,
@@ -1189,6 +1258,11 @@ class ObjectFieldsPanel(KeyValueTablePanel):
1189
1258
  fields (str, list): The ordered list of fields to display, or `"__all__"` to display fields automatically.
1190
1259
  Note that ManyToMany fields and reverse relations are **not** included in `"__all__"` at this time, nor
1191
1260
  are any hidden fields, nor the specially handled `id`, `created`, `last_updated` fields on most models.
1261
+ When a list is specified, it may include model `@property` attributes and nested lookups (if desired) in
1262
+ addition to concrete model fields; when using `fields="__all__"`, such additional attributes and lookups
1263
+ may be specified with the `additional_fields` parameter.
1264
+ additional_fields (list): Only relevant if `fields == "__all__"`, in which case it can specify additional
1265
+ non-default fields to include such as reverse relations, `@property` attributes, nested lookups, etc.
1192
1266
  exclude_fields (list): Only relevant if `fields == "__all__"`, in which case it excludes the given fields.
1193
1267
  context_object_key (str): The key in the render context that will contain the object to derive fields from.
1194
1268
  ignore_nonexistent_fields (bool): If True, `fields` is permitted to include field names that don't actually
@@ -1197,6 +1271,11 @@ class ObjectFieldsPanel(KeyValueTablePanel):
1197
1271
  (see `render_label()`).
1198
1272
  """
1199
1273
  self.fields = fields
1274
+ if additional_fields and fields != "__all__":
1275
+ raise ValueError("additional_fields may only be used in combination with fields='__all__'")
1276
+ self.additional_fields = additional_fields
1277
+ if exclude_fields and fields != "__all__":
1278
+ raise ValueError("exclude_fields may only be used in combination with fields='__all__'")
1200
1279
  self.exclude_fields = exclude_fields
1201
1280
  self.context_object_key = context_object_key
1202
1281
  self.ignore_nonexistent_fields = ignore_nonexistent_fields
@@ -1210,6 +1289,7 @@ class ObjectFieldsPanel(KeyValueTablePanel):
1210
1289
 
1211
1290
  def render_value(self, key, value, context: Context):
1212
1291
  obj = get_obj_from_context(context, self.context_object_key)
1292
+ # TODO: handle nested keys, e.g. device_type__device_family
1213
1293
  try:
1214
1294
  field_instance = obj._meta.get_field(key)
1215
1295
  except FieldDoesNotExist:
@@ -1230,8 +1310,18 @@ class ObjectFieldsPanel(KeyValueTablePanel):
1230
1310
  if isinstance(field_instance, JSONField):
1231
1311
  return format_html("<pre>{}</pre>", render_json(value))
1232
1312
 
1233
- if isinstance(field_instance, ManyToManyField) and field_instance.related_model == ContentType:
1234
- return render_content_types(value)
1313
+ if isinstance(field_instance, ManyToManyField):
1314
+ if field_instance.related_model == ContentType:
1315
+ return render_content_types(value)
1316
+ # TODO: this would be nice but it's probably too error-prone in general:
1317
+ # return render_m2m(
1318
+ # value.all(),
1319
+ # (
1320
+ # reverse(get_route_for_model(field_instance.related_model, "list")) + "?" +
1321
+ # obj._meta.verbose_name_plural.lower().replace(" ", "_") + "=" + str(obj.pk)
1322
+ # ),
1323
+ # key,
1324
+ # )
1235
1325
 
1236
1326
  if isinstance(field_instance, CharField) and hasattr(obj, f"get_{key}_display"):
1237
1327
  # For example, Secret.provider -> Secret.get_provider_display()
@@ -1273,6 +1363,8 @@ class ObjectFieldsPanel(KeyValueTablePanel):
1273
1363
  # TODO: apply a default ordering "smarter" than declaration order? Alphabetical? By field type?
1274
1364
  # TODO: allow model to specify an alternative field ordering?
1275
1365
 
1366
+ fields += self.additional_fields
1367
+
1276
1368
  data = {}
1277
1369
 
1278
1370
  if isinstance(instance, TreeModel) and (self.fields == "__all__" or "_hierarchy" in self.fields):
@@ -1283,9 +1375,16 @@ class ObjectFieldsPanel(KeyValueTablePanel):
1283
1375
  if field_name in self.exclude_fields:
1284
1376
  continue
1285
1377
  try:
1286
- field_value = getattr(instance, field_name)
1287
- except ObjectDoesNotExist:
1288
- field_value = None
1378
+ field_value = instance
1379
+ # Handle nested lookups, e.g. device_type__device_family
1380
+ for token in field_name.split(LOOKUP_SEP):
1381
+ try:
1382
+ field_value = getattr(field_value, token)
1383
+ if field_value is None:
1384
+ break
1385
+ except ObjectDoesNotExist:
1386
+ field_value = None
1387
+ break
1289
1388
  except AttributeError:
1290
1389
  if self.ignore_nonexistent_fields:
1291
1390
  continue
@@ -1301,6 +1400,10 @@ class ObjectFieldsPanel(KeyValueTablePanel):
1301
1400
 
1302
1401
  def render_key(self, key, value, context: Context):
1303
1402
  """Render the `verbose_name` of the model field whose name corresponds to the given key, if applicable."""
1403
+
1404
+ if key in self.key_transforms:
1405
+ return self.key_transforms[key]
1406
+
1304
1407
  instance = get_obj_from_context(context, self.context_object_key)
1305
1408
 
1306
1409
  if instance is not None:
@@ -1572,7 +1675,7 @@ class _ObjectCustomFieldsPanel(GroupedKeyValueTablePanel):
1572
1675
  if not hasattr(obj, "get_custom_field_groupings"):
1573
1676
  return False
1574
1677
  self.custom_field_data = obj.get_custom_field_groupings(advanced_ui=self.advanced_ui)
1575
- return bool(self.custom_field_data)
1678
+ return bool(self.custom_field_data) and super().should_render(context)
1576
1679
 
1577
1680
  def get_data(self, context: Context):
1578
1681
  """Remap the response from `get_custom_field_groupings()` to a nested dict as expected by the parent class."""
@@ -1648,7 +1751,7 @@ class _ObjectComputedFieldsPanel(GroupedKeyValueTablePanel):
1648
1751
  if not hasattr(obj, "get_computed_fields_grouping"):
1649
1752
  return False
1650
1753
  self.computed_fields_data = obj.get_computed_fields_grouping(advanced_ui=self.advanced_ui)
1651
- return bool(self.computed_fields_data)
1754
+ return bool(self.computed_fields_data) and super().should_render(context)
1652
1755
 
1653
1756
  def get_data(self, context: Context):
1654
1757
  """Remap `get_computed_fields_grouping()` to the nested dict format expected by the base class."""
@@ -1694,7 +1797,7 @@ class _ObjectRelationshipsPanel(KeyValueTablePanel):
1694
1797
  self.relationships_data["source"]
1695
1798
  or self.relationships_data["destination"]
1696
1799
  or self.relationships_data["peer"]
1697
- )
1800
+ ) and super().should_render(context)
1698
1801
 
1699
1802
  def get_data(self, context: Context):
1700
1803
  """Remap `get_relationships_with_related_objects()` to the flat dict format expected by the base class."""
@@ -1745,6 +1848,8 @@ class _ObjectTagsPanel(Panel):
1745
1848
  )
1746
1849
 
1747
1850
  def should_render(self, context: Context):
1851
+ if not super().should_render(context):
1852
+ return False
1748
1853
  return hasattr(get_obj_from_context(context), "tags")
1749
1854
 
1750
1855
  def get_extra_context(self, context: Context):
@@ -1776,6 +1881,8 @@ class _ObjectCommentPanel(ObjectTextPanel):
1776
1881
  )
1777
1882
 
1778
1883
  def should_render(self, context: Context):
1884
+ if not super().should_render(context):
1885
+ return False
1779
1886
  return hasattr(get_obj_from_context(context), "comments")
1780
1887
 
1781
1888
 
@@ -1909,6 +2016,8 @@ class _ObjectDetailContactsTab(Tab):
1909
2016
  super().__init__(tab_id=tab_id, label=label, weight=weight, panels=panels, **kwargs)
1910
2017
 
1911
2018
  def should_render(self, context: Context):
2019
+ if not super().should_render(context):
2020
+ return False
1912
2021
  return getattr(get_obj_from_context(context), "is_contact_associable_model", False)
1913
2022
 
1914
2023
  def render_label(self, context: Context):
@@ -1933,6 +2042,7 @@ class _ObjectDetailGroupsTab(Tab):
1933
2042
  label="Dynamic Groups",
1934
2043
  weight=Tab.WEIGHT_GROUPS_TAB,
1935
2044
  panels=None,
2045
+ required_permissions=("extras.view_dynamic_group",),
1936
2046
  **kwargs,
1937
2047
  ):
1938
2048
  if panels is None:
@@ -1946,15 +2056,20 @@ class _ObjectDetailGroupsTab(Tab):
1946
2056
  related_field_name="member_id",
1947
2057
  ),
1948
2058
  )
1949
- super().__init__(tab_id=tab_id, label=label, weight=weight, panels=panels, **kwargs)
2059
+ super().__init__(
2060
+ tab_id=tab_id,
2061
+ label=label,
2062
+ weight=weight,
2063
+ panels=panels,
2064
+ required_permissions=required_permissions,
2065
+ **kwargs,
2066
+ )
1950
2067
 
1951
2068
  def should_render(self, context: Context):
2069
+ if not super().should_render(context):
2070
+ return False
1952
2071
  obj = get_obj_from_context(context)
1953
- return (
1954
- getattr(obj, "is_dynamic_group_associable_model", False)
1955
- and context["request"].user.has_perm("extras.view_dynamicgroup")
1956
- and obj.dynamic_groups.exists()
1957
- )
2072
+ return getattr(obj, "is_dynamic_group_associable_model", False) and obj.dynamic_groups.exists()
1958
2073
 
1959
2074
  def render_label(self, context: Context):
1960
2075
  return format_html(
@@ -1977,6 +2092,7 @@ class _ObjectDetailMetadataTab(Tab):
1977
2092
  label="Object Metadata",
1978
2093
  weight=Tab.WEIGHT_METADATA_TAB,
1979
2094
  panels=None,
2095
+ required_permissions=("extras.view_objectmetadata",),
1980
2096
  **kwargs,
1981
2097
  ):
1982
2098
  if panels is None:
@@ -1992,15 +2108,20 @@ class _ObjectDetailMetadataTab(Tab):
1992
2108
  header_extra_content_template_path=None,
1993
2109
  ),
1994
2110
  )
1995
- super().__init__(tab_id=tab_id, label=label, weight=weight, panels=panels, **kwargs)
2111
+ super().__init__(
2112
+ tab_id=tab_id,
2113
+ label=label,
2114
+ weight=weight,
2115
+ panels=panels,
2116
+ required_permissions=required_permissions,
2117
+ **kwargs,
2118
+ )
1996
2119
 
1997
2120
  def should_render(self, context: Context):
2121
+ if not super().should_render(context):
2122
+ return False
1998
2123
  obj = get_obj_from_context(context)
1999
- return (
2000
- getattr(obj, "is_metadata_associable_model", False)
2001
- and context["request"].user.has_perm("extras.view_objectmetadata")
2002
- and obj.associated_object_metadata.exists()
2003
- )
2124
+ return getattr(obj, "is_metadata_associable_model", False) and obj.associated_object_metadata.exists()
2004
2125
 
2005
2126
  def render_label(self, context: Context):
2006
2127
  return format_html(
@@ -1,9 +1,10 @@
1
- from typing import Literal, Optional
1
+ from typing import Literal, Optional, Union
2
2
 
3
3
  from django.template import Context, Template
4
4
  from django.utils.html import strip_tags
5
5
 
6
6
  DEFAULT_TITLES: dict[str, str] = {
7
+ "*": "{{ verbose_name_plural|bettertitle }}",
7
8
  "list": "{{ verbose_name_plural|bettertitle }}",
8
9
  "detail": "{{ object.display|default:object }}",
9
10
  "retrieve": "{{ object.display|default:object }}",
@@ -14,6 +15,7 @@ DEFAULT_TITLES: dict[str, str] = {
14
15
  "bulk_rename": "Renaming {{ selected_objects|length }} {{ verbose_name_plural|bettertitle }} on {{ parent_name }}",
15
16
  "bulk_update": "Editing {{ objs_count }} {{ verbose_name_plural|bettertitle }}",
16
17
  "changelog": "{{ object.display|default:object }} - Change Log",
18
+ "config_context": "{{ object.display|default:object }} - Config Context",
17
19
  "notes": "{{ object.display|default:object }} - Notes",
18
20
  "approve": "Approve {{ verbose_name|bettertitle }}?",
19
21
  "deny": "Deny {{ verbose_name|bettertitle }}?",
@@ -59,7 +61,7 @@ class Titles:
59
61
  if template_plugins:
60
62
  self.template_plugins.extend(template_plugins)
61
63
 
62
- def render(self, context: Context, mode: ModeType = "html") -> str:
64
+ def render(self, context: Union[dict, Context], mode: ModeType = "html") -> str:
63
65
  """
64
66
  Renders the title based on given context and current action.
65
67
 
@@ -68,12 +70,15 @@ class Titles:
68
70
  Make sure that needed context variables are in context and needed plugins are loaded.
69
71
 
70
72
  Args:
71
- context (Context): Render context.
73
+ context (Union[dict, Context]): Render context.
72
74
  mode (ModeType): Rendering mode: "html" or "plain".
73
75
 
74
76
  Returns:
75
77
  (str): HTML fragment or plain text, depending on `mode`.
76
78
  """
79
+ if isinstance(context, dict):
80
+ context = Context(context)
81
+
77
82
  with context.update(self.get_extra_context(context)):
78
83
  template_str = self.get_template_str(context)
79
84
  template = Template(self.template_plugins_str + template_str)
@@ -92,7 +97,7 @@ class Titles:
92
97
  Returns:
93
98
  str: The template string for the current action, or an empty string if not found.
94
99
  """
95
- action = context.get("view_action", "list")
100
+ action = context.get("view_action", "")
96
101
 
97
102
  template_str = self.titles.get(action)
98
103
  if template_str:
@@ -102,7 +107,7 @@ class Titles:
102
107
  if detail:
103
108
  return self.titles.get("detail", "")
104
109
 
105
- return ""
110
+ return self.titles.get("*", "")
106
111
 
107
112
  @property
108
113
  def template_plugins_str(self) -> str: