nautobot 2.4.14__py3-none-any.whl → 2.4.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. nautobot/apps/choices.py +8 -0
  2. nautobot/apps/ui.py +14 -0
  3. nautobot/core/api/views.py +2 -0
  4. nautobot/core/choices.py +4 -0
  5. nautobot/core/filters.py +21 -41
  6. nautobot/core/management/commands/check_job_approval_status.py +47 -0
  7. nautobot/core/management/commands/generate_test_data.py +1 -1
  8. nautobot/core/management/commands/migrate.py +1 -1
  9. nautobot/core/models/tree_queries.py +17 -0
  10. nautobot/core/settings.py +2 -2
  11. nautobot/core/tables.py +25 -2
  12. nautobot/core/templates/base_django.html +1 -1
  13. nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
  14. nautobot/core/templates/generic/object_list.html +17 -20
  15. nautobot/core/templates/inc/breadcrumbs.html +14 -0
  16. nautobot/core/templatetags/buttons.py +2 -4
  17. nautobot/core/templatetags/helpers.py +29 -6
  18. nautobot/core/templatetags/ui_framework.py +21 -0
  19. nautobot/core/testing/filters.py +20 -3
  20. nautobot/core/testing/forms.py +1 -1
  21. nautobot/core/tests/integration/test_filters.py +2 -2
  22. nautobot/core/tests/test_breadcrumbs.py +366 -0
  23. nautobot/core/tests/test_commands.py +40 -0
  24. nautobot/core/tests/test_filters.py +51 -1
  25. nautobot/core/tests/test_forms.py +1 -1
  26. nautobot/core/tests/test_graphql.py +4 -4
  27. nautobot/core/tests/test_titles.py +183 -0
  28. nautobot/core/tests/test_tree_queries.py +30 -0
  29. nautobot/core/tests/test_views.py +2 -2
  30. nautobot/core/tests/test_views_utils.py +1 -1
  31. nautobot/core/ui/breadcrumbs.py +538 -0
  32. nautobot/core/ui/bulk_buttons.py +53 -0
  33. nautobot/core/ui/object_detail.py +31 -8
  34. nautobot/core/ui/titles.py +127 -0
  35. nautobot/core/ui/utils.py +25 -0
  36. nautobot/core/utils/migrations.py +1 -1
  37. nautobot/core/views/__init__.py +1 -1
  38. nautobot/core/views/mixins.py +26 -1
  39. nautobot/core/views/renderers.py +20 -2
  40. nautobot/core/views/utils.py +13 -12
  41. nautobot/dcim/api/serializers.py +9 -0
  42. nautobot/dcim/choices.py +53 -0
  43. nautobot/dcim/filters/__init__.py +15 -3
  44. nautobot/dcim/forms.py +120 -7
  45. nautobot/dcim/management/commands/trace_paths.py +1 -1
  46. nautobot/dcim/migrations/0072_alter_powerfeed_options_and_more.py +97 -0
  47. nautobot/dcim/models/device_component_templates.py +8 -0
  48. nautobot/dcim/models/device_components.py +31 -12
  49. nautobot/dcim/models/devices.py +1 -1
  50. nautobot/dcim/models/power.py +171 -10
  51. nautobot/dcim/models/racks.py +7 -4
  52. nautobot/dcim/tables/devices.py +2 -0
  53. nautobot/dcim/tables/devicetypes.py +1 -0
  54. nautobot/dcim/tables/power.py +30 -2
  55. nautobot/dcim/templates/dcim/device.html +2 -2
  56. nautobot/dcim/templates/dcim/devicetype_retrieve.html +1 -214
  57. nautobot/dcim/templates/dcim/location_retrieve.html +2 -2
  58. nautobot/dcim/templates/dcim/powerfeed_edit.html +8 -0
  59. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -1
  60. nautobot/dcim/tests/integration/test_device_bulk_operations.py +61 -0
  61. nautobot/dcim/tests/test_api.py +24 -4
  62. nautobot/dcim/tests/test_filters.py +91 -13
  63. nautobot/dcim/tests/test_models.py +262 -0
  64. nautobot/dcim/tests/test_views.py +20 -12
  65. nautobot/dcim/utils.py +9 -0
  66. nautobot/dcim/views.py +390 -77
  67. nautobot/extras/factory.py +19 -20
  68. nautobot/extras/filters/__init__.py +3 -2
  69. nautobot/extras/filters/mixins.py +15 -1
  70. nautobot/extras/forms/__init__.py +2 -1
  71. nautobot/extras/forms/forms.py +62 -0
  72. nautobot/extras/managers.py +4 -1
  73. nautobot/extras/migrations/0125_jobresult_date_started.py +18 -0
  74. nautobot/extras/models/customfields.py +1 -2
  75. nautobot/extras/models/datasources.py +1 -2
  76. nautobot/extras/models/jobs.py +7 -3
  77. nautobot/extras/plugins/views.py +24 -1
  78. nautobot/extras/secrets/__init__.py +1 -1
  79. nautobot/extras/tables.py +21 -0
  80. nautobot/extras/templates/extras/customfield.html +2 -129
  81. nautobot/extras/templates/extras/customfield_edit.html +2 -108
  82. nautobot/extras/templates/extras/customfield_retrieve.html +129 -0
  83. nautobot/extras/templates/extras/customfield_update.html +108 -0
  84. nautobot/extras/templates/extras/inc/jobresult.html +7 -3
  85. nautobot/extras/templates/extras/jobresult.html +2 -155
  86. nautobot/extras/templates/extras/jobresult_retrieve.html +155 -0
  87. nautobot/extras/templates/extras/marketplace.html +5 -6
  88. nautobot/extras/templates/extras/note.html +2 -53
  89. nautobot/extras/templates/extras/note_retrieve.html +53 -0
  90. nautobot/extras/templates/extras/plugins_list.html +5 -6
  91. nautobot/extras/templates/extras/secretsgroup_retrieve.html +2 -29
  92. nautobot/extras/templatetags/custom_links.py +2 -2
  93. nautobot/extras/templatetags/job_buttons.py +1 -1
  94. nautobot/extras/templatetags/plugins.py +1 -1
  95. nautobot/extras/tests/integration/test_computedfields.py +2 -2
  96. nautobot/extras/tests/integration/test_customfields.py +14 -11
  97. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  98. nautobot/extras/tests/integration/test_notes.py +1 -1
  99. nautobot/extras/tests/integration/test_plugins.py +6 -6
  100. nautobot/extras/tests/integration/test_relationships.py +2 -2
  101. nautobot/extras/tests/test_filters.py +9 -0
  102. nautobot/extras/tests/test_forms.py +2 -2
  103. nautobot/extras/tests/test_plugins.py +14 -3
  104. nautobot/extras/tests/test_relationships.py +7 -7
  105. nautobot/extras/tests/test_views.py +172 -1
  106. nautobot/extras/urls.py +3 -59
  107. nautobot/extras/utils.py +1 -1
  108. nautobot/extras/views.py +96 -182
  109. nautobot/ipam/tables.py +8 -15
  110. nautobot/ipam/tests/migration/test_migrations.py +8 -8
  111. nautobot/ipam/tests/test_api.py +2 -2
  112. nautobot/ipam/tests/test_models.py +1 -1
  113. nautobot/project-static/docs/404.html +23 -0
  114. nautobot/project-static/docs/apps/index.html +23 -0
  115. nautobot/project-static/docs/apps/nautobot-apps.html +23 -0
  116. nautobot/project-static/docs/assets/_mkdocstrings.css +44 -6
  117. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +28 -0
  118. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +25 -0
  119. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +128 -20
  120. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +37 -4
  121. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +39 -6
  122. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +25 -0
  123. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +24 -0
  124. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +32 -5
  125. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +41 -8
  126. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +39 -7
  127. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +43 -10
  128. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +74 -59
  129. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +143 -28
  130. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +43 -12
  131. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +135 -53
  132. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +229 -36
  133. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +27 -1
  134. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +30 -1
  135. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +162 -18
  136. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +258 -51
  137. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5987 -2620
  138. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +25 -0
  139. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +154 -55
  140. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +150 -35
  141. nautobot/project-static/docs/development/apps/api/configuration-view.html +23 -0
  142. nautobot/project-static/docs/development/apps/api/database-backend-config.html +23 -0
  143. nautobot/project-static/docs/development/apps/api/models/django-admin.html +23 -0
  144. nautobot/project-static/docs/development/apps/api/models/global-search.html +23 -0
  145. nautobot/project-static/docs/development/apps/api/models/graphql.html +23 -0
  146. nautobot/project-static/docs/development/apps/api/models/index.html +23 -0
  147. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +23 -0
  148. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +23 -0
  149. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +23 -0
  150. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +23 -0
  151. nautobot/project-static/docs/development/apps/api/platform-features/index.html +23 -0
  152. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +23 -0
  153. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +23 -0
  154. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +23 -0
  155. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +23 -0
  156. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +23 -0
  157. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +23 -0
  158. nautobot/project-static/docs/development/apps/api/prometheus.html +23 -0
  159. nautobot/project-static/docs/development/apps/api/setup.html +23 -0
  160. nautobot/project-static/docs/development/apps/api/testing.html +23 -0
  161. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +23 -0
  162. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +23 -0
  163. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +23 -0
  164. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +23 -0
  165. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +23 -0
  166. nautobot/project-static/docs/development/apps/api/views/base-template.html +23 -0
  167. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +23 -0
  168. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +23 -0
  169. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +23 -0
  170. nautobot/project-static/docs/development/apps/api/views/index.html +23 -0
  171. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +23 -0
  172. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +31 -2
  173. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +23 -0
  174. nautobot/project-static/docs/development/apps/api/views/notes.html +23 -0
  175. nautobot/project-static/docs/development/apps/api/views/rest-api.html +23 -0
  176. nautobot/project-static/docs/development/apps/api/views/urls.html +23 -0
  177. nautobot/project-static/docs/development/apps/index.html +23 -0
  178. nautobot/project-static/docs/development/apps/migration/code-updates.html +23 -0
  179. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +23 -0
  180. nautobot/project-static/docs/development/apps/migration/from-v1.html +23 -0
  181. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +23 -0
  182. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +23 -0
  183. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +23 -0
  184. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +23 -0
  185. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +26 -3
  186. nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +10544 -0
  187. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +23 -0
  188. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +23 -0
  189. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +23 -0
  190. nautobot/project-static/docs/development/apps/porting-from-netbox.html +26 -3
  191. nautobot/project-static/docs/development/core/application-registry.html +23 -0
  192. nautobot/project-static/docs/development/core/best-practices.html +23 -0
  193. nautobot/project-static/docs/development/core/bootstrap-ui.html +23 -0
  194. nautobot/project-static/docs/development/core/caching.html +23 -0
  195. nautobot/project-static/docs/development/core/controllers.html +23 -0
  196. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +23 -0
  197. nautobot/project-static/docs/development/core/generic-views.html +23 -0
  198. nautobot/project-static/docs/development/core/getting-started.html +23 -0
  199. nautobot/project-static/docs/development/core/homepage.html +23 -0
  200. nautobot/project-static/docs/development/core/index.html +23 -0
  201. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +23 -0
  202. nautobot/project-static/docs/development/core/model-checklist.html +23 -0
  203. nautobot/project-static/docs/development/core/model-features.html +23 -0
  204. nautobot/project-static/docs/development/core/natural-keys.html +23 -0
  205. nautobot/project-static/docs/development/core/navigation-menu.html +23 -0
  206. nautobot/project-static/docs/development/core/release-checklist.html +23 -0
  207. nautobot/project-static/docs/development/core/role-internals.html +23 -0
  208. nautobot/project-static/docs/development/core/settings.html +23 -0
  209. nautobot/project-static/docs/development/core/style-guide.html +23 -0
  210. nautobot/project-static/docs/development/core/templates.html +23 -0
  211. nautobot/project-static/docs/development/core/testing.html +23 -0
  212. nautobot/project-static/docs/development/core/ui-component-framework.html +713 -255
  213. nautobot/project-static/docs/development/core/user-preferences.html +23 -0
  214. nautobot/project-static/docs/development/index.html +23 -0
  215. nautobot/project-static/docs/development/jobs/getting-started.html +23 -0
  216. nautobot/project-static/docs/development/jobs/index.html +23 -0
  217. nautobot/project-static/docs/development/jobs/installation.html +23 -0
  218. nautobot/project-static/docs/development/jobs/job-extensions.html +23 -0
  219. nautobot/project-static/docs/development/jobs/job-logging.html +23 -0
  220. nautobot/project-static/docs/development/jobs/job-patterns.html +23 -0
  221. nautobot/project-static/docs/development/jobs/job-structure.html +23 -0
  222. nautobot/project-static/docs/development/jobs/migration/from-v1.html +23 -0
  223. nautobot/project-static/docs/development/jobs/testing.html +23 -0
  224. nautobot/project-static/docs/index.html +23 -0
  225. nautobot/project-static/docs/media/development/core/ui-component-framework/breadcrumbs-titles-data-flow.png +0 -0
  226. nautobot/project-static/docs/media/power_distribution.png +0 -0
  227. nautobot/project-static/docs/objects.inv +0 -0
  228. nautobot/project-static/docs/overview/application_stack.html +23 -0
  229. nautobot/project-static/docs/overview/design_philosophy.html +23 -0
  230. nautobot/project-static/docs/release-notes/index.html +23 -0
  231. nautobot/project-static/docs/release-notes/version-1.0.html +23 -0
  232. nautobot/project-static/docs/release-notes/version-1.1.html +23 -0
  233. nautobot/project-static/docs/release-notes/version-1.2.html +23 -0
  234. nautobot/project-static/docs/release-notes/version-1.3.html +23 -0
  235. nautobot/project-static/docs/release-notes/version-1.4.html +23 -0
  236. nautobot/project-static/docs/release-notes/version-1.5.html +23 -0
  237. nautobot/project-static/docs/release-notes/version-1.6.html +23 -0
  238. nautobot/project-static/docs/release-notes/version-2.0.html +23 -0
  239. nautobot/project-static/docs/release-notes/version-2.1.html +23 -0
  240. nautobot/project-static/docs/release-notes/version-2.2.html +23 -0
  241. nautobot/project-static/docs/release-notes/version-2.3.html +23 -0
  242. nautobot/project-static/docs/release-notes/version-2.4.html +306 -0
  243. nautobot/project-static/docs/requirements.txt +2 -2
  244. nautobot/project-static/docs/search/search_index.json +1 -1
  245. nautobot/project-static/docs/sitemap.xml +303 -299
  246. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  247. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +23 -0
  248. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +23 -0
  249. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +23 -0
  250. nautobot/project-static/docs/user-guide/administration/configuration/index.html +23 -0
  251. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +23 -0
  252. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +23 -0
  253. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +23 -0
  254. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +23 -0
  255. nautobot/project-static/docs/user-guide/administration/guides/docker.html +23 -0
  256. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +23 -0
  257. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +23 -0
  258. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +23 -0
  259. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +23 -0
  260. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +23 -0
  261. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +23 -0
  262. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +23 -0
  263. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +23 -0
  264. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +23 -0
  265. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +23 -0
  266. nautobot/project-static/docs/user-guide/administration/installation/index.html +23 -0
  267. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +23 -0
  268. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +23 -0
  269. nautobot/project-static/docs/user-guide/administration/installation/services.html +23 -0
  270. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +23 -0
  271. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +23 -0
  272. nautobot/project-static/docs/user-guide/administration/security/index.html +23 -0
  273. nautobot/project-static/docs/user-guide/administration/security/notices.html +23 -0
  274. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +284 -219
  275. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +23 -0
  276. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +23 -0
  277. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +23 -0
  278. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +23 -0
  279. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +23 -0
  280. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +23 -0
  281. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +23 -0
  282. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +23 -0
  283. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +23 -0
  284. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +23 -0
  285. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +23 -0
  286. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +23 -0
  287. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +23 -0
  288. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +23 -0
  289. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +23 -0
  290. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +23 -0
  291. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +23 -0
  292. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +23 -0
  293. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +23 -0
  294. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +23 -0
  295. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +23 -0
  296. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +23 -0
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +23 -0
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +23 -0
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +23 -0
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +23 -0
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +23 -0
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +23 -0
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +23 -0
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +23 -0
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +23 -0
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +23 -0
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +23 -0
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +23 -0
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +23 -0
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +23 -0
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +23 -0
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +23 -0
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +23 -0
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +23 -0
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +23 -0
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +23 -0
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +23 -0
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +23 -0
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +23 -0
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +23 -0
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +23 -0
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +23 -0
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +23 -0
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +23 -0
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +305 -5
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +24 -1
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +23 -0
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +136 -3
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +41 -1
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +40 -0
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +23 -0
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +23 -0
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +23 -0
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +23 -0
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +23 -0
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +23 -0
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +23 -0
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +23 -0
  339. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +23 -0
  340. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +23 -0
  341. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +23 -0
  342. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +23 -0
  343. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +23 -0
  344. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +23 -0
  345. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +23 -0
  346. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +23 -0
  347. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +23 -0
  348. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +23 -0
  349. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +23 -0
  350. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +23 -0
  351. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +23 -0
  352. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +23 -0
  353. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +23 -0
  354. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +23 -0
  355. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +23 -0
  356. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +23 -0
  357. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +23 -0
  358. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +23 -0
  359. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +23 -0
  360. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +23 -0
  361. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +23 -0
  362. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +23 -0
  363. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +23 -0
  364. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +23 -0
  365. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +23 -0
  366. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +23 -0
  367. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +23 -0
  368. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +23 -0
  369. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +23 -0
  370. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +23 -0
  371. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +23 -0
  372. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +23 -0
  373. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +23 -0
  374. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +23 -0
  375. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +23 -0
  376. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +23 -0
  377. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +23 -0
  378. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +23 -0
  379. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +23 -0
  380. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +23 -0
  381. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +23 -0
  382. nautobot/project-static/docs/user-guide/index.html +23 -0
  383. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +23 -0
  384. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +23 -0
  385. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +23 -0
  386. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +23 -0
  387. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +23 -0
  388. nautobot/project-static/docs/user-guide/platform-functionality/events.html +23 -0
  389. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +23 -0
  390. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +23 -0
  391. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +23 -0
  392. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +23 -0
  393. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +23 -0
  394. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +23 -0
  395. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +23 -0
  396. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +24 -1
  397. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +23 -0
  398. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +23 -0
  399. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +23 -0
  400. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +23 -0
  401. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +23 -0
  402. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +23 -0
  403. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +23 -0
  404. nautobot/project-static/docs/user-guide/platform-functionality/note.html +23 -0
  405. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +23 -0
  406. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +23 -0
  407. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +23 -0
  408. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +23 -0
  409. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +23 -0
  410. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +23 -0
  411. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +23 -0
  412. nautobot/project-static/docs/user-guide/platform-functionality/role.html +23 -0
  413. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +23 -0
  414. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +23 -0
  415. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +23 -0
  416. nautobot/project-static/docs/user-guide/platform-functionality/status.html +23 -0
  417. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +23 -0
  418. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +23 -0
  419. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +23 -0
  420. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +23 -0
  421. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +23 -0
  422. nautobot/users/tests/test_api.py +2 -2
  423. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -252
  424. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -75
  425. nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +252 -0
  426. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +75 -0
  427. nautobot/virtualization/urls.py +3 -61
  428. nautobot/virtualization/views.py +48 -72
  429. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/METADATA +24 -24
  430. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/RECORD +434 -417
  431. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/LICENSE.txt +0 -0
  432. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/NOTICE +0 -0
  433. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/WHEEL +0 -0
  434. {nautobot-2.4.14.dist-info → nautobot-2.4.16.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,538 @@
1
+ from dataclasses import dataclass
2
+ import logging
3
+ from typing import Any, Callable, Literal, Optional, Protocol, Type, Union
4
+ from urllib.parse import urlencode
5
+
6
+ from django.db.models import Model
7
+ from django.template import Context
8
+ from django.urls import NoReverseMatch, reverse
9
+
10
+ from nautobot.core.templatetags import helpers
11
+ from nautobot.core.ui.utils import get_absolute_url, render_component_template
12
+ from nautobot.core.utils import lookup
13
+ from nautobot.core.utils.lookup import get_model_for_view_name, get_model_from_name
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class WithStr(Protocol):
19
+ def __str__(self) -> str: ...
20
+
21
+
22
+ ViewNameType = Union[str, Callable[[Context], str], None]
23
+ ModelLabelType = Literal["plural", "singular"]
24
+ ModelType = Union[str, Model, Type[Model], None]
25
+ LabelType = Union[Callable[[Context], str], WithStr, None]
26
+ BreadcrumbItemsType = dict[str, list["BaseBreadcrumbItem"]]
27
+ ReverseParams = Union[dict[str, Any], Callable[[Context], dict[str, Any]], None]
28
+
29
+
30
+ @dataclass
31
+ class BaseBreadcrumbItem:
32
+ """
33
+ Base interface for breadcrumb items.
34
+
35
+ Attributes:
36
+ should_render (Callable[[Context], bool]): Callable to decide whether this item should be rendered or not.
37
+ label (Union[Callable[[Context], str], WithStr, None]): Optional override for the display label in the breadcrumb.
38
+ label_key (Optional[str]): Optional key to take label from the context.
39
+ """
40
+
41
+ should_render: Callable[[Context], bool] = lambda context: True
42
+ label: LabelType = None
43
+ label_key: Optional[str] = None
44
+
45
+ def get_url(self, context: Context) -> Optional[str]:
46
+ """
47
+ Get the URL for the breadcrumb item.
48
+
49
+ Args:
50
+ context (Context): The current template context.
51
+
52
+ Returns:
53
+ Optional[str]: The URL as a string, or None.
54
+ """
55
+ return None
56
+
57
+ def get_label(self, context: Context) -> str:
58
+ """
59
+ Get the label (display text) for the breadcrumb.
60
+
61
+ Args:
62
+ context (Context): The current template context.
63
+
64
+ Returns:
65
+ str: Label as a string.
66
+ """
67
+
68
+ if self.label:
69
+ return str(self.label(context)) if callable(self.label) else str(self.label)
70
+ if self.label_key:
71
+ return str(context.get(self.label_key, ""))
72
+ return ""
73
+
74
+ def reverse_view_name(
75
+ self,
76
+ view_name: str,
77
+ context: Context,
78
+ reverse_kwargs: ReverseParams = None,
79
+ reverse_query_params: ReverseParams = None,
80
+ ) -> Optional[str]:
81
+ """
82
+ Reverse a Django view name into a URL, optionally adding query parameters.
83
+
84
+ Args:
85
+ view_name (str): Django view name to reverse.
86
+ context (Context): Template context, used to resolve params if needed.
87
+ reverse_kwargs (ReverseParams): URL kwargs for reversing.
88
+ reverse_query_params (ReverseParams): Query parameters to append.
89
+
90
+ Returns:
91
+ Optional[str]: The resolved URL as a string, or None if reversing fails.
92
+ """
93
+ if view_name == "":
94
+ return None
95
+
96
+ try:
97
+ # TODO: refactor after Django 5.2 upgrade
98
+ # query params can be passed directly to the `reverse` function instead of merging two strings
99
+ # reverse(view_name, query=query_params, kwargs=...)
100
+ # https://docs.djangoproject.com/en/5.2/ref/urlresolvers/#reverse
101
+ url = reverse(view_name, kwargs=self.resolve_reverse_params(reverse_kwargs, context))
102
+ if query_params := self.resolve_reverse_params(reverse_query_params, context):
103
+ return f"{url}?{urlencode(query_params)}"
104
+ return url
105
+ except NoReverseMatch as err:
106
+ logger.error('No reverse match for: "%s". Exc: %s', view_name, err)
107
+ return None
108
+
109
+ @staticmethod
110
+ def resolve_reverse_params(params: ReverseParams, context: Context) -> dict[str, Any]:
111
+ """
112
+ Resolves parameters for URL reversing, calling if callable, or returning as-is.
113
+
114
+ Args:
115
+ params (ReverseParams): Dict or callable to resolve.
116
+ context (Context): Context for callables.
117
+
118
+ Returns:
119
+ dict[str, Any]: Dictionary of parameters for URL reversing.
120
+ """
121
+ if callable(params):
122
+ return params(context)
123
+ if params:
124
+ return params
125
+ return {}
126
+
127
+ def as_pair(self, context: Context) -> tuple[str, str]:
128
+ """
129
+ Construct the (URL, label) pair for the breadcrumb.
130
+
131
+ Combines `get_url()` and `get_label()` and applies title casing to the label.
132
+
133
+ Args:
134
+ context (Context): Context object used to resolve the breadcrumb parts.
135
+
136
+ Returns:
137
+ tuple[str, Optional[str]]: A tuple of (URL, label), where URL may be an empty string
138
+ if unresolved, and label is title-cased.
139
+ """
140
+ url = self.get_url(context) or ""
141
+ label = helpers.bettertitle(self.get_label(context))
142
+ return url, label
143
+
144
+
145
+ @dataclass
146
+ class ViewNameBreadcrumbItem(BaseBreadcrumbItem):
147
+ """
148
+ Breadcrumb via raw view name and optional params.
149
+
150
+ From raw viewname string that will be passed to the reverse method. You can pass reverse kwargs or query params.
151
+ Label won't be generated automatically.
152
+
153
+ Attributes:
154
+ view_name (Union[str, Callable[[Context], str], None]): Django view name to reverse or callable taking context.
155
+ Can be used as fallback if `view_name_key` won't be found in the context.
156
+ view_name_key: (Optional[str]): Key to get the `view_name` from the context.
157
+ reverse_kwargs (Union[dict[str, Any], Callable[[Context], dict[str, Any]], None]): Keyword arguments passed to `reverse()`.
158
+ reverse_query_params (Union[dict[str, Any], Callable[[Context], dict[str, Any]], None]): Keyword arguments added to the url.
159
+ should_render (Callable[[Context], bool]): Callable to decide whether this item should be rendered or not.
160
+ label (Union[Callable[[Context], str], WithStr, None]): Optional override for the display label in the breadcrumb.
161
+ label_key (Optional[str]): Optional key to take label from the context.
162
+ label_from_view_name (bool): Try to resolve given view name and get the label from assosiacted model.
163
+
164
+ Examples:
165
+ >>> ViewNameBreadcrumbItem(view_name="dcim:device_list")
166
+ ("/dcim/devices/", "") # No label automatically generated
167
+ >>> ViewNameBreadcrumbItem(view_name="dcim:device_list", reverse_query_params={"filter": "some_value"}, label="Link")
168
+ ("/dcim/devices/?filter=some_value", "Link")
169
+ """
170
+
171
+ view_name: ViewNameType = None
172
+ view_name_key: Optional[str] = None
173
+ reverse_kwargs: ReverseParams = None
174
+ reverse_query_params: ReverseParams = None
175
+ label_from_view_name: bool = False
176
+
177
+ def get_url(self, context: Context) -> Optional[str]:
178
+ """
179
+ Get the URL for the breadcrumb item based on the configuration: view name, context, reverse kwargs, query params.
180
+
181
+ Args:
182
+ context (Context): The current template context.
183
+
184
+ Returns:
185
+ Optional[str]: The URL as a string, or None.
186
+ """
187
+ view_name = self.get_view_name(context)
188
+ if not view_name:
189
+ return None
190
+
191
+ return self.reverse_view_name(view_name, context, self.reverse_kwargs, self.reverse_query_params)
192
+
193
+ def get_label(self, context: Context) -> str:
194
+ if self.label_from_view_name:
195
+ model = get_model_for_view_name(self.get_view_name(context))
196
+ if model is not None:
197
+ return model._meta.verbose_name_plural
198
+ return super().get_label(context)
199
+
200
+ def get_view_name(self, context: Context) -> Optional[str]:
201
+ if self.view_name_key:
202
+ return context.get(self.view_name_key, self.view_name)
203
+ if callable(self.view_name):
204
+ return self.view_name(context)
205
+
206
+ return self.view_name
207
+
208
+
209
+ @dataclass
210
+ class ModelBreadcrumbItem(BaseBreadcrumbItem):
211
+ """
212
+ Breadcrumb via model class / instance / name.
213
+
214
+ Based on model class, content type or dotted model name passed directly or taken automatically from context.
215
+ It will generate label based on model `verbose_name` or `verbose_name_plural` depending on `model_label_type`.
216
+ If `label` is set explicitly, it's returned as-is or called if callable.
217
+
218
+ Attributes:
219
+ model (Union[str, Type[Model], None, Callable[[Context], Union[str, Type[Model], None]]): Django model class, instance, or dotted path string or callable that returns one of this.
220
+ model_key (Optional[str]): Context key to fetch a model class, instance or dotted path string.
221
+ action (str): Action to use when resolving a model-based route (default: "list").
222
+ label_type (Literal["singular", "plural"]): Whether to use `verbose_name` or `verbose_name_plural`.
223
+ reverse_kwargs (Union[dict[str, Any], Callable[[Context], dict[str, Any]], None]): Keyword arguments passed to `reverse()`.
224
+ reverse_query_params (Union[dict[str, Any], Callable[[Context], dict[str, Any]], None]): Keyword arguments added to the url.
225
+ should_render (Callable[[Context], bool]): Callable to decide whether this item should be rendered or not.
226
+ label (Union[Callable[[Context], str], WithStr, None]): Optional override for the display label in the breadcrumb.
227
+ label_key (Optional[str]): Optional key to take label from the context.
228
+
229
+ Examples:
230
+ >>> ModelBreadcrumbItem(model=Device)
231
+ ("/dcim/devices/", "Devices")
232
+ >>> ModelBreadcrumbItem(model="dcim.device")
233
+ ("/dcim/devices/", "Devices")
234
+ >>> ModelBreadcrumbItem(model="dcim.device", label_type="singular", action="add")
235
+ ("/dcim/devices/add", "Device")
236
+ """
237
+
238
+ model: Union[ModelType, Callable[[Context], ModelType]] = None
239
+ model_key: Optional[str] = None
240
+ action: str = "list"
241
+ label_type: ModelLabelType = "plural"
242
+ reverse_kwargs: ReverseParams = None
243
+ reverse_query_params: ReverseParams = None
244
+
245
+ def get_url(self, context: Context) -> Optional[str]:
246
+ """
247
+ Get the URL for the breadcrumb item based on the configuration: model, action, reverse kwargs, query params.
248
+
249
+ Args:
250
+ context (Context): The current template context.
251
+
252
+ Returns:
253
+ Optional[str]: The URL as a string, or None.
254
+ """
255
+ model_obj = self.get_model(context)
256
+ if not model_obj:
257
+ return None
258
+ view_name = lookup.get_route_for_model(model_obj, self.action)
259
+ return self.reverse_view_name(view_name, context, self.reverse_kwargs, self.reverse_query_params)
260
+
261
+ def get_label(self, context: Context) -> str:
262
+ """
263
+ Get the display name from the model's metadata.
264
+
265
+ Depending on the `model_label_type`, either the `verbose_name` or `verbose_name_plural`
266
+ will be returned. Accepts model class, instance or dotted path string.
267
+
268
+ Args:
269
+ context (Context): The current template context.
270
+
271
+ Returns:
272
+ str: The verbose name of the model class for use as a label.
273
+ """
274
+ if self.label or self.label_key:
275
+ return super().get_label(context)
276
+
277
+ model_obj = self.get_model(context)
278
+ name_attr = "verbose_name" if self.label_type == "singular" else "verbose_name_plural"
279
+
280
+ if model_obj is not None:
281
+ if isinstance(model_obj, str):
282
+ model_cls = get_model_from_name(model_obj)
283
+ return getattr(model_cls._meta, name_attr)
284
+ return getattr(model_obj._meta, name_attr)
285
+ return ""
286
+
287
+ def get_model(self, context: Context) -> ModelType:
288
+ if self.model_key:
289
+ return context.get(self.model_key)
290
+ if self.model:
291
+ if callable(self.model):
292
+ return self.model(context)
293
+ return self.model
294
+ return None
295
+
296
+
297
+ @dataclass
298
+ class InstanceBreadcrumbItem(BaseBreadcrumbItem):
299
+ """
300
+ Breadcrumb via object instance from context.
301
+
302
+ Detail url for object instance taken from context. By default, `instance_key` is set to `object`.
303
+ Label will be generated from object, but you can still override it.
304
+
305
+ Attributes:
306
+ instance_key (Optional[str]): Context key to fetch a Django model instance for building the breadcrumb.
307
+ instance (Callable[[Context], Optional[Model]): Callable to fetch the instance from context. If
308
+ should_render (Callable[[Context], bool]): Callable to decide whether this item should be rendered or not.
309
+ label (Union[Callable[[Context], str], WithStr, None]): Optional override for the display label in the breadcrumb.
310
+ label_key (Optional[str]): Optional key to take label from the context.
311
+
312
+ Examples:
313
+ >>> InstanceBreadcrumbItem()
314
+ ("/dcim/devices/1234", "My Device") # Assuming that under "object" there is a Device instance
315
+ >>> InstanceBreadcrumbItem(label="Custom Device Label")
316
+ ("/dcim/devices/1234", "Custom Device Label") # Assuming that under "object" there is a Device instance
317
+ """
318
+
319
+ instance_key: str = "object"
320
+ instance: Optional[Callable[[Context], Optional[Model]]] = None
321
+ label: Union[Callable[[Context], str], WithStr, None] = None
322
+
323
+ def get_url(self, context: Context) -> Optional[str]:
324
+ """
325
+ Resolve the URL for the breadcrumb item based on the instance.
326
+
327
+ Args:
328
+ context (Context): The current template context.
329
+
330
+ Returns:
331
+ Optional[str]: The URL as a string, or None.
332
+ """
333
+ instance = self.get_instance(context)
334
+ return get_absolute_url(instance) if instance else None
335
+
336
+ def get_label(self, context: Context) -> str:
337
+ """
338
+ Get the label (display text) for the breadcrumb from instance.
339
+
340
+ Args:
341
+ context (Context): The current template context.
342
+
343
+ Returns:
344
+ str: Label as a string.
345
+ """
346
+ if self.label or self.label_key:
347
+ return super().get_label(context)
348
+ instance = self.get_instance(context)
349
+ if not instance:
350
+ return ""
351
+ return getattr(instance, "display", str(instance))
352
+
353
+ def get_instance(self, context: Context) -> Optional[Model]:
354
+ """
355
+ Get the instance depending on the settings.
356
+
357
+ Args:
358
+ context (Context): The current template context.
359
+
360
+ Returns:
361
+ Optional[Model]: Instance from context.
362
+ """
363
+ if self.instance:
364
+ return self.instance(context)
365
+
366
+ return context.get(self.instance_key)
367
+
368
+
369
+ class Breadcrumbs:
370
+ """
371
+ Base class responsible for generating and rendering breadcrumbs for a page.
372
+
373
+ This class supports flexible breadcrumb configuration through:
374
+ - `items`: Default breadcrumb items per view action.
375
+
376
+ You can add more information to the breadcrumbs trail by passing appropriate
377
+ `BreadcrumbItem` objects grouped by view action (e.g., "*", "list", "add", "edit").
378
+
379
+ Special breadcrumb item actions:
380
+ - `*` - if no other action was found, items from `*` will be used
381
+ - `detail` action is used when there is no dedicated action for given request
382
+ and there is `context['detail'] = True` set in context
383
+
384
+ !!! important
385
+ This class automatically adds the:
386
+ - `InstanceBreadcrumbItem` at the end of `detail` breadcrumbs
387
+ - `ModelBreadcrumbItem` at the beginning of `list` and `detail` breadcrumbs
388
+
389
+ You can override this behavior by subclassing this class and updating
390
+ the `list_breadcrumb_item` or `detail_breadcrumb_item` attributes.
391
+
392
+ If you're using custom action other than `list` / `detail` you need to remember to add above breadcrumbs
393
+ if you need them in your custom action.
394
+
395
+ Attributes:
396
+ template (str): Path to the template used to render the breadcrumb component.
397
+ items (dict[str, list[BreadcrumbItem]]): Default breadcrumb items per view action.
398
+ """
399
+
400
+ breadcrumb_items: list[BaseBreadcrumbItem] = [
401
+ # Default breadcrumb if view defines `list_url` in the Context
402
+ ViewNameBreadcrumbItem(
403
+ view_name_key="list_url",
404
+ label_from_view_name=True,
405
+ should_render=lambda context: context.get("list_url") is not None,
406
+ ),
407
+ # Fallback if there is no `list_url` in the Context
408
+ ModelBreadcrumbItem(model_key="model", should_render=lambda context: context.get("list_url") is None),
409
+ ]
410
+
411
+ def __init__(
412
+ self,
413
+ items: BreadcrumbItemsType = None,
414
+ template: str = "inc/breadcrumbs.html",
415
+ ):
416
+ """
417
+ Initialize the Breadcrumbs configuration.
418
+
419
+ Args:
420
+ items (Optional[dict[str, list[BreadcrumbItem]]]): Default breadcrumb items for each action.
421
+ template (str): The template used to render the breadcrumbs.
422
+ """
423
+ self.template = template
424
+
425
+ # Set the default breadcrumbs
426
+ self.items = {
427
+ "list": [*self.breadcrumb_items],
428
+ "detail": [*self.breadcrumb_items],
429
+ }
430
+
431
+ # If custom items are present, merge with defaults
432
+ if items:
433
+ self.items = {**self.items, **items}
434
+
435
+ # Built-in feature: always add the instance details at the end of breadcrumbs path
436
+ self.items["detail"].append(InstanceBreadcrumbItem())
437
+
438
+ def get_breadcrumbs_items(self, context: Context) -> list[tuple[str, str]]:
439
+ """
440
+ Compute the list of breadcrumb items for the given context.
441
+
442
+ Items are determined based on the `view_action` in context.
443
+
444
+ Args:
445
+ context (Context): The view or template context that holds `view_action` and related state.
446
+
447
+ Returns:
448
+ (list[tuple[str, str]]): A list of (url, label) tuples representing breadcrumb entries.
449
+ """
450
+ action = context.get("view_action", "list")
451
+ detail = context.get("detail", False)
452
+ items = self.get_items_for_action(self.items, action, detail)
453
+ return [item.as_pair(context) for item in items if item.should_render(context)]
454
+
455
+ def filter_breadcrumbs_items(self, items: list[tuple[str, str]], context: Context) -> list[tuple[str, str]]:
456
+ """
457
+ Filters out all items that both label and url are None or empty str.
458
+
459
+ Args:
460
+ items (list[tuple[str, str]]): breadcrumb items pair.s
461
+ context (Context): The view or template context.
462
+
463
+ Returns:
464
+ (list[tuple[str, str]]): A list of filtered breadcrumb items pairs.
465
+ """
466
+ return [(url, label) for url, label in items if self.is_label_not_blank(label)]
467
+
468
+ @staticmethod
469
+ def is_label_not_blank(label: str) -> bool:
470
+ """
471
+ Check if label is not empty (only whitespace) or None.
472
+
473
+ Args:
474
+ label (str): The label to check.
475
+
476
+ Returns:
477
+ (bool): True if label is not None or empty (only whitespace), False otherwise.
478
+ """
479
+ return label and label.strip()
480
+
481
+ @staticmethod
482
+ def get_items_for_action(items: BreadcrumbItemsType, action: str, detail: bool) -> list[BaseBreadcrumbItem]:
483
+ """
484
+ Get the breadcrumb items for a specific action, with fallback to 'detail' if not found
485
+ and to asterisk (*) if present.
486
+
487
+ Args:
488
+ items (BreadcrumbItemsType): Dictionary mapping action names to breadcrumb item lists.
489
+ action (str): Current action name (e.g., "list", "detail").
490
+ detail (bool): Whether this is a detail view (for fallback).
491
+
492
+ Returns:
493
+ list[BaseBreadcrumbItem]: List of breadcrumb items for the action.
494
+ """
495
+ breadcrumbs_list = items.get(action, [])
496
+ if breadcrumbs_list:
497
+ return breadcrumbs_list
498
+
499
+ if detail:
500
+ return items.get("detail", [])
501
+
502
+ return items.get("*", [])
503
+
504
+ def render(self, context):
505
+ """
506
+ Render the breadcrumbs HTML.
507
+
508
+ This method updates the context with the generated breadcrumb items and any additional context from `get_extra_context`.
509
+
510
+ Args:
511
+ context (Context): The current rendering context.
512
+
513
+ Returns:
514
+ (str): Rendered HTML for the breadcrumb component.
515
+ """
516
+ with context.update(
517
+ {
518
+ **self.get_extra_context(context),
519
+ }
520
+ ):
521
+ breadcrumbs_items = self.get_breadcrumbs_items(context)
522
+ filtered_items = self.filter_breadcrumbs_items(breadcrumbs_items, context)
523
+ return render_component_template(self.template, context, breadcrumbs_items=filtered_items)
524
+
525
+ def get_extra_context(self, context: Context):
526
+ """
527
+ Provide additional data to include in the rendering context, based on the configuration of this component.
528
+
529
+ Context updated here will be applied to resolving url and labels.
530
+ Please ote that you can't override `breadcrumb_items` here because items are generated after this method call.
531
+
532
+ Args:
533
+ context (Context): The current context passed to `render()`.
534
+
535
+ Returns:
536
+ (dict): A dictionary of extra context variables.
537
+ """
538
+ return {}
@@ -0,0 +1,53 @@
1
+ from nautobot.core.choices import ButtonActionColorChoices
2
+ from nautobot.core.ui import object_detail
3
+
4
+
5
+ class BaseBulkButton(object_detail.FormButton):
6
+ """Base class for bulk action buttons."""
7
+
8
+ action = None
9
+ color = None
10
+ icon = None
11
+ label = None
12
+ weight = None
13
+
14
+ def __init__(self, *, form_id: str, model, **kwargs):
15
+ model_name = model.__name__.lower()
16
+ app_label = model._meta.app_label
17
+ link_name = f"{app_label}:{model_name}_bulk_{self.action}"
18
+
19
+ super().__init__(
20
+ link_name=link_name,
21
+ link_includes_pk=False,
22
+ label=self.label,
23
+ color=self.color,
24
+ icon=self.icon,
25
+ size="xs",
26
+ form_id=form_id,
27
+ weight=self.weight,
28
+ **kwargs,
29
+ )
30
+
31
+
32
+ class BulkRenameButton(BaseBulkButton):
33
+ action = "rename"
34
+ color = ButtonActionColorChoices.RENAME
35
+ icon = "mdi-pencil"
36
+ label = "Rename"
37
+ weight = 200
38
+
39
+
40
+ class BulkEditButton(BaseBulkButton):
41
+ action = "edit"
42
+ color = ButtonActionColorChoices.EDIT
43
+ icon = "mdi-pencil"
44
+ label = "Edit"
45
+ weight = 300
46
+
47
+
48
+ class BulkDeleteButton(BaseBulkButton):
49
+ action = "delete"
50
+ color = ButtonActionColorChoices.DELETE
51
+ icon = "mdi-trash-can-outline"
52
+ label = "Delete"
53
+ weight = 400
@@ -182,6 +182,7 @@ class Button(Component):
182
182
  attributes=None,
183
183
  size=None,
184
184
  link_includes_pk=True,
185
+ context_object_key=None,
185
186
  **kwargs,
186
187
  ):
187
188
  """
@@ -194,6 +195,7 @@ class Button(Component):
194
195
  This link will be reversed and will automatically include the current object's PK as a parameter to the
195
196
  `reverse()` call when the button is rendered. For more complex link construction, you can subclass this
196
197
  and override the `get_link()` method.
198
+ context_object_key (str, optional): The key in the render context that will contain the linked object.
197
199
  icon (str, optional): Material Design Icons icon, to include on the button, for example `"mdi-plus-bold"`.
198
200
  template_path (str): Template to render for this button.
199
201
  required_permissions (list, optional): Permissions such as `["dcim.add_consoleport"]`.
@@ -213,6 +215,7 @@ class Button(Component):
213
215
  self.attributes = attributes
214
216
  self.size = size
215
217
  self.link_includes_pk = link_includes_pk
218
+ self.context_object_key = context_object_key
216
219
  super().__init__(**kwargs)
217
220
 
218
221
  def should_render(self, context: Context):
@@ -227,7 +230,7 @@ class Button(Component):
227
230
  more advanced link construction.
228
231
  """
229
232
  if self.link_name and self.link_includes_pk:
230
- obj = get_obj_from_context(context)
233
+ obj = get_obj_from_context(context, self.context_object_key)
231
234
  return reverse(self.link_name, kwargs={"pk": obj.pk})
232
235
  elif self.link_name:
233
236
  return reverse(self.link_name)
@@ -685,6 +688,8 @@ class ObjectsTablePanel(Panel):
685
688
  order_by_fields=None,
686
689
  table_title=None,
687
690
  max_display_count=None,
691
+ paginate=True,
692
+ show_table_config_button=True,
688
693
  include_columns=None,
689
694
  exclude_columns=None,
690
695
  add_button_route="default",
@@ -725,6 +730,10 @@ class ObjectsTablePanel(Panel):
725
730
  order_by_fields (list, optional): list of fields to order the table queryset by.
726
731
  max_display_count (int, optional): Maximum number of items to display in the table.
727
732
  If None, defaults to the `get_paginate_count()` (which is user's preference or a global setting).
733
+ paginate (bool, optional): If False, do not attach a paginator to the table and render all rows
734
+ (or up to `max_display_count` if provided). Defaults to True.
735
+ show_table_config_button (bool, optional): If False, hide the small "Configure" button rendered in the
736
+ panel header for this table. Defaults to True.
728
737
  table_title (str, optional): The title to display in the panel heading for the table.
729
738
  If None, defaults to the plural verbose name of the table model.
730
739
  include_columns (list, optional): A list of field names to include in the table display.
@@ -775,6 +784,8 @@ class ObjectsTablePanel(Panel):
775
784
  self.order_by_fields = order_by_fields
776
785
  self.table_title = table_title
777
786
  self.max_display_count = max_display_count
787
+ self.paginate = paginate
788
+ self.show_table_config_button = show_table_config_button
778
789
  self.include_columns = include_columns
779
790
  self.exclude_columns = exclude_columns
780
791
  self.add_button_route = add_button_route
@@ -893,13 +904,24 @@ class ObjectsTablePanel(Panel):
893
904
  ):
894
905
  body_content_table.columns.show("pk")
895
906
 
896
- per_page = self.max_display_count if self.max_display_count is not None else get_paginate_count(request)
897
- paginate = {"paginator_class": EnhancedPaginator, "per_page": per_page}
898
- RequestConfig(request, paginate).configure(body_content_table)
899
- try:
900
- more_queryset_count = max(body_content_table.data.data.count() - per_page, 0)
901
- except TypeError:
902
- more_queryset_count = max(len(body_content_table.data.data) - per_page, 0)
907
+ more_queryset_count = 0
908
+ if self.paginate:
909
+ per_page = self.max_display_count if self.max_display_count is not None else get_paginate_count(request)
910
+ paginate = {"paginator_class": EnhancedPaginator, "per_page": per_page}
911
+ RequestConfig(request, paginate).configure(body_content_table)
912
+ try:
913
+ more_queryset_count = max(body_content_table.data.data.count() - per_page, 0)
914
+ except TypeError:
915
+ more_queryset_count = max(len(body_content_table.data.data) - per_page, 0)
916
+ elif self.max_display_count is not None:
917
+ # If not paginating but a cap is desired, slice the table's data source.
918
+ try:
919
+ more_queryset_count = max(body_content_table.data.data.count() - self.max_display_count, 0)
920
+ body_content_table.data.data = body_content_table.data.data[: self.max_display_count]
921
+ except TypeError:
922
+ # Non-queryset iterable; fall back to list slicing
923
+ more_queryset_count = max(len(body_content_table.data.data) - self.max_display_count, 0)
924
+ body_content_table.data.data = list(body_content_table.data.data)[: self.max_display_count]
903
925
 
904
926
  obj = get_obj_from_context(context)
905
927
  body_content_table_model = body_content_table.Meta.model
@@ -931,6 +953,7 @@ class ObjectsTablePanel(Panel):
931
953
  "footer_buttons": self.footer_buttons,
932
954
  "form_id": self.form_id,
933
955
  "more_queryset_count": more_queryset_count,
956
+ "show_table_config_button": self.show_table_config_button,
934
957
  }
935
958
 
936
959