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
nautobot/apps/choices.py CHANGED
@@ -18,11 +18,15 @@ from nautobot.dcim.choices import (
18
18
  InterfaceRedundancyGroupProtocolChoices,
19
19
  InterfaceTypeChoices,
20
20
  PortTypeChoices,
21
+ PowerFeedBreakerPoleChoices,
21
22
  PowerFeedPhaseChoices,
23
+ PowerFeedStatusChoices,
22
24
  PowerFeedSupplyChoices,
23
25
  PowerFeedTypeChoices,
24
26
  PowerOutletFeedLegChoices,
25
27
  PowerOutletTypeChoices,
28
+ PowerPanelTypeChoices,
29
+ PowerPathChoices,
26
30
  PowerPortTypeChoices,
27
31
  RackDimensionUnitChoices,
28
32
  RackElevationDetailRenderChoices,
@@ -89,11 +93,15 @@ __all__ = (
89
93
  "ObjectChangeActionChoices",
90
94
  "ObjectChangeEventContextChoices",
91
95
  "PortTypeChoices",
96
+ "PowerFeedBreakerPoleChoices",
92
97
  "PowerFeedPhaseChoices",
98
+ "PowerFeedStatusChoices",
93
99
  "PowerFeedSupplyChoices",
94
100
  "PowerFeedTypeChoices",
95
101
  "PowerOutletFeedLegChoices",
96
102
  "PowerOutletTypeChoices",
103
+ "PowerPanelTypeChoices",
104
+ "PowerPathChoices",
97
105
  "PowerPortTypeChoices",
98
106
  "PrefixTypeChoices",
99
107
  "RackDimensionUnitChoices",
nautobot/apps/ui.py CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  from nautobot.core.choices import ButtonColorChoices
4
4
  from nautobot.core.ui.base import PermissionsMixin
5
+ from nautobot.core.ui.breadcrumbs import (
6
+ BaseBreadcrumbItem,
7
+ Breadcrumbs,
8
+ InstanceBreadcrumbItem,
9
+ ModelBreadcrumbItem,
10
+ ViewNameBreadcrumbItem,
11
+ )
5
12
  from nautobot.core.ui.choices import LayoutChoices, SectionChoices
6
13
  from nautobot.core.ui.homepage import (
7
14
  HomePageBase,
@@ -36,6 +43,7 @@ from nautobot.core.ui.object_detail import (
36
43
  Tab,
37
44
  TextPanel,
38
45
  )
46
+ from nautobot.core.ui.titles import Titles
39
47
  from nautobot.core.ui.utils import render_component_template
40
48
  from nautobot.extras.choices import BannerClassChoices
41
49
  from nautobot.extras.plugins import Banner, TemplateExtension
@@ -43,7 +51,9 @@ from nautobot.extras.plugins import Banner, TemplateExtension
43
51
  __all__ = (
44
52
  "Banner",
45
53
  "BannerClassChoices",
54
+ "BaseBreadcrumbItem",
46
55
  "BaseTextPanel",
56
+ "Breadcrumbs",
47
57
  "Button",
48
58
  "ButtonColorChoices",
49
59
  "Component",
@@ -55,8 +65,10 @@ __all__ = (
55
65
  "HomePageGroup",
56
66
  "HomePageItem",
57
67
  "HomePagePanel",
68
+ "InstanceBreadcrumbItem",
58
69
  "KeyValueTablePanel",
59
70
  "LayoutChoices",
71
+ "ModelBreadcrumbItem",
60
72
  "NavMenuAddButton",
61
73
  "NavMenuBase",
62
74
  "NavMenuButton",
@@ -75,5 +87,7 @@ __all__ = (
75
87
  "Tab",
76
88
  "TemplateExtension",
77
89
  "TextPanel",
90
+ "Titles",
91
+ "ViewNameBreadcrumbItem",
78
92
  "render_component_template",
79
93
  )
@@ -519,6 +519,8 @@ class StatusView(NautobotAPIVersionMixin, APIView):
519
519
  if version:
520
520
  if isinstance(version, tuple):
521
521
  version = ".".join(str(n) for n in version)
522
+ else:
523
+ version = str(version)
522
524
  installed_apps[app_config.name] = version
523
525
  installed_apps = dict(sorted(installed_apps.items()))
524
526
 
nautobot/core/choices.py CHANGED
@@ -194,6 +194,7 @@ class ButtonActionColorChoices(ChoiceSet):
194
194
  EXPORT = "success"
195
195
  IMPORT = "primary"
196
196
  INFO = "info"
197
+ RENAME = "warning"
197
198
  SUBMIT = "primary"
198
199
  SWAP = "primary"
199
200
 
@@ -210,6 +211,7 @@ class ButtonActionColorChoices(ChoiceSet):
210
211
  (EXPORT, "Export"),
211
212
  (IMPORT, "Import"),
212
213
  (INFO, "Info"),
214
+ (RENAME, "Rename"),
213
215
  (SUBMIT, "Submit"),
214
216
  (SWAP, "Swap"),
215
217
  )
@@ -236,6 +238,7 @@ class ButtonActionIconChoices(ChoiceSet):
236
238
  LOCK = "mdi-lock"
237
239
  MAGNIFY = "mdi-magnify"
238
240
  NOTE = "mdi-note-text"
241
+ RENAME = "mdi-pencil"
239
242
  SWAP = "mdi-swap-vertical"
240
243
  TRASH = "mdi-trash-can-outline"
241
244
 
@@ -256,6 +259,7 @@ class ButtonActionIconChoices(ChoiceSet):
256
259
  (LOCK, "Lock"),
257
260
  (MAGNIFY, "Magnify"),
258
261
  (NOTE, "Note"),
262
+ (RENAME, "Rename"),
259
263
  (SWAP, "Swap"),
260
264
  (TRASH, "Trash"),
261
265
  )
nautobot/core/filters.py CHANGED
@@ -209,14 +209,12 @@ class ContentTypeFilterMixin:
209
209
  return qs
210
210
 
211
211
  if value.isdigit():
212
- return qs.filter(**{f"{self.field_name}__pk": value})
213
-
212
+ return self.get_method(qs)(**{f"{self.field_name}__pk": value})
214
213
  try:
215
214
  app_label, model = value.lower().split(".")
216
215
  except ValueError:
217
216
  return qs.none()
218
-
219
- return qs.filter(
217
+ return self.get_method(qs)(
220
218
  **{
221
219
  f"{self.field_name}__app_label": app_label,
222
220
  f"{self.field_name}__model": model,
@@ -244,6 +242,8 @@ class ContentTypeChoiceFilter(ContentTypeFilterMixin, django_filters.ChoiceFilte
244
242
  content_type = ContentTypeChoiceFilter(
245
243
  choices=FeatureQuery("dynamic_groups").get_choices,
246
244
  )
245
+
246
+ In most cases you should use `ContentTypeMultipleChoiceFilter` instead.
247
247
  """
248
248
 
249
249
 
@@ -251,6 +251,9 @@ class ContentTypeMultipleChoiceFilter(django_filters.MultipleChoiceFilter):
251
251
  """
252
252
  Allows multiple-choice ContentType filtering by <app_label>.<model> (e.g. "dcim.location").
253
253
 
254
+ Does NOT allow filtering by PK at this time; it would need to be reimplemented similar to
255
+ NaturalKeyOrPKMultipleChoiceFilter as a breaking change.
256
+
254
257
  Defaults to joining multiple options with "AND". Pass `conjoined=False` to
255
258
  override this behavior to join with "OR" instead.
256
259
 
@@ -265,43 +268,14 @@ class ContentTypeMultipleChoiceFilter(django_filters.MultipleChoiceFilter):
265
268
  kwargs.setdefault("conjoined", True)
266
269
  super().__init__(*args, **kwargs)
267
270
 
268
- def filter(self, qs, value):
269
- """Filter on value, which should be list of content-type names.
270
-
271
- e.g. `['dcim.device', 'dcim.rack']`
272
- """
273
- if not self.conjoined:
274
- q = models.Q()
275
-
276
- for v in value:
277
- if self.conjoined:
278
- qs = ContentTypeFilter.filter(self, qs, v)
279
- else:
280
- if v.isdigit():
281
- q |= models.Q(**{f"{self.field_name}__pk": value})
282
- continue
283
- # Similar to the ContentTypeFilter.filter() call above, but instead of narrowing the query each time
284
- # (a AND b AND c ...) we broaden the query each time (a OR b OR c ...).
285
- # Specifically, we're mapping a value like ['dcim.device', 'ipam.vlan'] to a query like
286
- # Q((field__app_label="dcim" AND field__model="device") OR (field__app_label="ipam" AND field__model="VLAN"))
287
- try:
288
- app_label, model = v.lower().split(".")
289
- except ValueError:
290
- continue
291
- q |= models.Q(
292
- **{
293
- f"{self.field_name}__app_label": app_label,
294
- f"{self.field_name}__model": model,
295
- }
296
- )
297
-
298
- if not self.conjoined:
299
- qs = qs.filter(q)
300
-
301
- if self.distinct:
302
- qs = qs.distinct()
303
-
304
- return qs
271
+ def get_filter_predicate(self, v):
272
+ if v.isdigit():
273
+ return {f"{self.field_name}__pk": v}
274
+ try:
275
+ app_label, model = v.lower().split(".")
276
+ except ValueError:
277
+ return {f"{self.field_name}__pk": v}
278
+ return {f"{self.field_name}__app_label": app_label, f"{self.field_name}__model": model}
305
279
 
306
280
 
307
281
  class MappedPredicatesFilterMixin:
@@ -642,6 +616,9 @@ class BaseFilterSet(django_filters.FilterSet):
642
616
  (
643
617
  django_filters.ModelChoiceFilter,
644
618
  django_filters.ModelMultipleChoiceFilter,
619
+ ContentTypeFilter,
620
+ ContentTypeChoiceFilter,
621
+ ContentTypeMultipleChoiceFilter,
645
622
  MultiValueUUIDFilter,
646
623
  TagFilter,
647
624
  TreeNodeMultipleChoiceFilter,
@@ -820,6 +797,7 @@ class BaseFilterSet(django_filters.FilterSet):
820
797
  to_field_name="name",
821
798
  label="Contacts (name or ID)",
822
799
  )
800
+ cls.declared_filters["contacts"] = filters["contacts"] # pylint: disable=no-member
823
801
 
824
802
  if "teams" not in filters:
825
803
  filters["teams"] = NaturalKeyOrPKMultipleChoiceFilter(
@@ -828,6 +806,7 @@ class BaseFilterSet(django_filters.FilterSet):
828
806
  to_field_name="name",
829
807
  label="Teams (name or ID)",
830
808
  )
809
+ cls.declared_filters["teams"] = filters["teams"] # pylint: disable=no-member
831
810
 
832
811
  if "dynamic_groups" not in filters and getattr(cls._meta.model, "is_dynamic_group_associable_model", False): # pylint: disable=no-member
833
812
  if not hasattr(cls._meta.model, "static_group_association_set"): # pylint: disable=no-member
@@ -848,6 +827,7 @@ class BaseFilterSet(django_filters.FilterSet):
848
827
  query_params={"content_type": cls._meta.model._meta.label_lower}, # pylint: disable=no-member
849
828
  label="Dynamic groups (name or ID)",
850
829
  )
830
+ cls.declared_filters["dynamic_groups"] = filters["dynamic_groups"] # pylint: disable=no-member
851
831
 
852
832
  # django-filters has no concept of "abstract" filtersets, so we have to fake it
853
833
  if cls._meta.model is not None: # pylint: disable=no-member
@@ -0,0 +1,47 @@
1
+ from django.core.exceptions import ValidationError
2
+ from django.core.management.base import BaseCommand
3
+
4
+ from nautobot.extras.models import Job, ScheduledJob
5
+
6
+
7
+ class ApprovalRequiredScheduledJobsError(ValidationError):
8
+ """Raised when scheduled jobs requiring approval are found during managment command."""
9
+
10
+ def __init__(self, message_lines):
11
+ self.message_lines = message_lines
12
+ super().__init__(message_lines)
13
+
14
+ def __str__(self):
15
+ return "\n".join(self.message_lines)
16
+
17
+
18
+ class Command(BaseCommand):
19
+ help = "Checks for scheduled jobs and jobs that require approval."
20
+
21
+ def handle(self, *args, **options):
22
+ approval_required_scheduled_jobs = ScheduledJob.objects.filter(approval_required=True).values_list("id", "name")
23
+
24
+ if approval_required_scheduled_jobs:
25
+ message_lines = [
26
+ "These need to be approved (and run) or denied before upgrading to Nautobot v3, as the introduction of the approval workflows feature means that future scheduled-job approvals will be handled differently.",
27
+ "Refer to the documentation: https://docs.nautobot.com/projects/core/en/v2.4.14/user-guide/platform-functionality/jobs/job-scheduling-and-approvals/#approval-via-the-ui",
28
+ "Below is a list of affected scheduled jobs:",
29
+ ]
30
+ for schedule_job_id, scheduled_job_name in approval_required_scheduled_jobs:
31
+ message_lines.append(f" - ID: {schedule_job_id}, Name: {scheduled_job_name}")
32
+ raise ApprovalRequiredScheduledJobsError(message_lines)
33
+
34
+ approval_required_jobs = Job.objects.filter(approval_required=True).values_list("name", flat=True)
35
+ if approval_required_jobs:
36
+ message_lines = [
37
+ "Following jobs still have `approval_required=True`.",
38
+ "These jobs will no longer trigger approval automatically.",
39
+ "After upgrading to Nautobot 3.x, you should add an approval workflow definition(s) covering these jobs.",
40
+ "Refer to the documentation: https://docs.nautobot.com/projects/core/en/v3.0.0/user-guide/platform-functionality/approval-workflow/",
41
+ "Affected jobs (Names):",
42
+ ]
43
+ for job_name in approval_required_jobs:
44
+ message_lines.append(f" - {job_name}")
45
+ else:
46
+ message_lines = ["No approval_required jobs or scheduled jobs found."]
47
+ self.stdout.write("\n".join(message_lines))
@@ -375,7 +375,7 @@ class Command(BaseCommand):
375
375
  confirm = input(
376
376
  f"""\
377
377
  You have requested a flush of the database before generating new data.
378
- This will IRREVERSIBLY DESTROY all data in the "{connections[options['database']].settings_dict['NAME']}" database,
378
+ This will IRREVERSIBLY DESTROY all data in the "{connections[options["database"]].settings_dict["NAME"]}" database,
379
379
  including all user accounts, and return each table to an empty state.
380
380
  Are you SURE you want to do this?
381
381
 
@@ -92,6 +92,6 @@ class Command(_Command):
92
92
 
93
93
  for key, value in self.affected_models_count.items():
94
94
  if value:
95
- self.stdout.write(f" Affected {key:<38} {value: 8} rows {elapsed/value:6.3f}s/record")
95
+ self.stdout.write(f" Affected {key:<38} {value: 8} rows {elapsed / value:6.3f}s/record")
96
96
  else:
97
97
  self.stdout.write(f" Affected {key:<38} {value: 8} rows")
@@ -1,6 +1,7 @@
1
1
  from django.core.cache import cache
2
2
  from django.db.models import Case, When
3
3
  from django.db.models.signals import post_delete, post_save
4
+ from tree_queries.compiler import TreeQuery
4
5
  from tree_queries.models import TreeNode
5
6
  from tree_queries.query import TreeManager as TreeManager_, TreeQuerySet as TreeQuerySet_
6
7
 
@@ -61,6 +62,22 @@ class TreeQuerySet(TreeQuerySet_, querysets.RestrictedQuerySet):
61
62
  return deepest.tree_depth
62
63
  return 0
63
64
 
65
+ def count(self):
66
+ """Custom count method for optimization purposes.
67
+
68
+ TreeQuerySet instances in Nautobot are by default with tree fields. So if somewhere tree fields aren't
69
+ explicitly removed from the queryset and count is called, the whole tree is calculated. Since this is not
70
+ needed, this implementation calls `without_tree_fields` before issuing the count query and `with_tree_fields`
71
+ afterwards when applicable.
72
+ """
73
+ should_have_tree_fields = isinstance(self.query, TreeQuery)
74
+ if should_have_tree_fields:
75
+ self.without_tree_fields()
76
+ count = super().count()
77
+ if should_have_tree_fields:
78
+ self.with_tree_fields()
79
+ return count
80
+
64
81
 
65
82
  class TreeManager(TreeManager_, BaseManager.from_queryset(TreeQuerySet)):
66
83
  """
nautobot/core/settings.py CHANGED
@@ -765,7 +765,7 @@ CONSTANCE_CONFIG = {
765
765
  ),
766
766
  "JOB_CREATE_FILE_MAX_SIZE": ConstanceConfigItem(
767
767
  default=10 << 20,
768
- help_text=mark_safe( # noqa: S308 # suspicious-mark-safe-usage, but this is a static string so it's safe
768
+ help_text=mark_safe(
769
769
  "Maximum size (in bytes) of any single file generated by a <code>Job.create_file()</code> call."
770
770
  ),
771
771
  field_type=int,
@@ -798,7 +798,7 @@ CONSTANCE_CONFIG = {
798
798
  ),
799
799
  "NETWORK_DRIVERS": ConstanceConfigItem(
800
800
  default={},
801
- help_text=mark_safe( # noqa: S308 # suspicious-mark-safe-usage, but this is a static string so it's safe
801
+ help_text=mark_safe(
802
802
  "Extend or override default Platform.network_driver translations provided by "
803
803
  '<a href="https://netutils.readthedocs.io/en/latest/user/lib_use_cases_lib_mapper/">netutils</a>. '
804
804
  "Enter a dictionary in JSON format, for example:\n"
nautobot/core/tables.py CHANGED
@@ -5,7 +5,7 @@ from django.conf import settings
5
5
  from django.contrib.auth.models import AnonymousUser
6
6
  from django.contrib.contenttypes.fields import GenericForeignKey
7
7
  from django.core.exceptions import FieldDoesNotExist, FieldError
8
- from django.db.models import Prefetch
8
+ from django.db.models import Prefetch, QuerySet
9
9
  from django.db.models.fields.related import ForeignKey, RelatedField
10
10
  from django.db.models.fields.reverse_related import ManyToOneRel
11
11
  from django.urls import reverse
@@ -333,6 +333,29 @@ class BaseTable(django_tables2.Table):
333
333
  # Otherwise, use the default sorting method
334
334
  self.data.order_by(self._order_by)
335
335
 
336
+ def add_conditional_prefetch(self, table_field, db_column=None, prefetch=None):
337
+ """Conditionally prefetch the specified database column if the related table field is visible.
338
+
339
+ Args:
340
+ table_field (str): Name of the field on the table to check for visibility. Also used as the prefetch field
341
+ if neither db_column nor prefetch is specified.
342
+ db_column (str): Optionally specify the db column to prefetch. Mutually exclusive with prefetch.
343
+ prefetch (Prefetch): Optionally specify a prefetch object. Mutually exclusive with db_column.
344
+ """
345
+ if db_column and prefetch:
346
+ raise ValueError(
347
+ "BaseTable.add_conditional_prefetch called with both db_column and prefetch, this is not allowed."
348
+ )
349
+ if not db_column:
350
+ db_column = table_field
351
+ if table_field in self.columns and self.columns[table_field].visible and isinstance(self.data.data, QuerySet):
352
+ if prefetch:
353
+ self.data = TableData.from_data(self.data.data.prefetch_related(prefetch))
354
+ else:
355
+ self.data = TableData.from_data(self.data.data.prefetch_related(db_column))
356
+ self.data.set_table(self)
357
+ self.rows = BoundRows(data=self.data, table=self, pinned_data=self.pinned_data)
358
+
336
359
 
337
360
  #
338
361
  # Table columns
@@ -353,7 +376,7 @@ class ToggleColumn(django_tables2.CheckBoxColumn):
353
376
 
354
377
  @property
355
378
  def header(self):
356
- return mark_safe('<input type="checkbox" class="toggle" title="Toggle all" />') # noqa: S308 # suspicious-mark-safe-usage, but this is a static string so it's safe
379
+ return mark_safe('<input type="checkbox" class="toggle" title="Toggle all" />')
357
380
 
358
381
 
359
382
  class BooleanColumn(django_tables2.Column):
@@ -5,7 +5,7 @@
5
5
  <!DOCTYPE html>
6
6
  <html lang="en"{% if request.COOKIES|get_item:"theme" == 'dark' %} data-theme="dark"{% endif %}>
7
7
  <head>
8
- <title>{% block title %}Home{% endblock %} - {{ settings.BRANDING_TITLE }}</title>
8
+ <title>{% block title %}Home{% endblock %}{% block document_title_extra %}{% endblock %} - {{ settings.BRANDING_TITLE }}</title>
9
9
  {% include 'inc/media.html' %}
10
10
  {% block extra_styles %}{% endblock %}
11
11
  </head>
@@ -6,7 +6,7 @@
6
6
  {% endif %}
7
7
 
8
8
  <div class="pull-right noprint">
9
- {% if request.user.is_authenticated %}
9
+ {% if request.user.is_authenticated and show_table_config_button %}
10
10
  {% table_config_button_small body_content_table %}
11
11
  {% endif %}
12
12
  </div>
@@ -4,18 +4,26 @@
4
4
  {% load perms %}
5
5
  {% load plugins %}
6
6
  {% load static %}
7
+ {% load ui_framework %}
8
+
9
+ {% block document_title_extra %}{% saved_view_title "plain" %}{% endblock %}
7
10
 
8
11
  {% block header %}
9
12
  <div class="row noprint">
10
13
  <div class="{% if search_form %}col-sm-8 col-md-9 {% else %} col-md-12 {% endif %}">
11
- <ol class="breadcrumb">
12
- {% block breadcrumbs %}
13
- {% if list_url %}
14
- <li><a href="{% url list_url %}">{{ title }}</a></li>
15
- {% endif %}
16
- {% block extra_breadcrumbs %}{% endblock extra_breadcrumbs %}
17
- {% endblock breadcrumbs %}
18
- </ol>
14
+ {% if breadcrumbs is not None %}
15
+ {% render_breadcrumbs %}
16
+ {% else %}
17
+ {# TODO: remove this fragment after breadcrumbs implementation in generic views like ConsoleConnectionsListView #}
18
+ <ol class="breadcrumb">
19
+ {% block breadcrumbs %}
20
+ {% if list_url %}
21
+ <li><a href="{% url list_url %}">{{ title }}</a></li>
22
+ {% endif %}
23
+ {% block extra_breadcrumbs %}{% endblock extra_breadcrumbs %}
24
+ {% endblock breadcrumbs %}
25
+ </ol>
26
+ {% endif %}
19
27
  </div>
20
28
  {% if search_form %}
21
29
  <div class="col-sm-4 col-md-3">
@@ -146,18 +154,7 @@
146
154
  {% endblock export_button %}
147
155
  {% endif %}
148
156
  </div>
149
- <h1>{% block title %}
150
- {{ title }}
151
- {% if current_saved_view %}
152
-
153
- {% if new_changes_not_applied %}
154
- <i title="Pending changes not saved">{{ current_saved_view.name }}</i>
155
- {% else %}
156
- {{ current_saved_view.name }}
157
- {% endif %}
158
- {% endif %}
159
- {% endblock %}
160
- </h1>
157
+ <h1>{% block title %}{% render_title %}{% endblock %}{% saved_view_title %}</h1>
161
158
  {% block header_extra %}{% endblock %}
162
159
  {% if filter_params %}
163
160
  <div class="filters-applied">
@@ -0,0 +1,14 @@
1
+ <ol class="breadcrumb">
2
+ {% block breadcrumbs %}
3
+ {% for url, title in breadcrumbs_items %}
4
+ <li>
5
+ {% if url %}
6
+ <a href="{{ url }}">{{ title }}</a>
7
+ {% else %}
8
+ {{ title }}
9
+ {% endif %}
10
+ </li>
11
+ {% endfor %}
12
+ {% block extra_breadcrumbs %}{% endblock extra_breadcrumbs %}
13
+ {% endblock %}
14
+ </ol>
@@ -176,8 +176,6 @@ def consolidate_bulk_action_buttons(context):
176
176
  "bulk_action_buttons": bulk_action_buttons,
177
177
  }
178
178
 
179
- params = ("?" + context["request"].GET.urlencode()) if context["request"].GET else ""
180
-
181
179
  primary_button_fragment = child_button_fragment = """
182
180
  <button {attrs}>
183
181
  <span class="{icon}" aria-hidden="true"></span> {label}
@@ -203,7 +201,7 @@ def consolidate_bulk_action_buttons(context):
203
201
  attrs=render_tag_attrs(
204
202
  {
205
203
  "class": edit_button_classes,
206
- "formaction": reverse(context["bulk_edit_url"]) + params,
204
+ "formaction": reverse(context["bulk_edit_url"]),
207
205
  "type": "submit",
208
206
  }
209
207
  ),
@@ -242,7 +240,7 @@ def consolidate_bulk_action_buttons(context):
242
240
  "class": delete_button_classes,
243
241
  "type": "submit",
244
242
  "name": "_delete",
245
- "formaction": reverse(context["bulk_delete_url"]) + params,
243
+ "formaction": reverse(context["bulk_delete_url"]),
246
244
  }
247
245
  ),
248
246
  icon="mdi mdi-trash-can-outline",
@@ -2,6 +2,7 @@ import datetime
2
2
  import json
3
3
  import logging
4
4
  import re
5
+ from typing import Literal
5
6
  from urllib.parse import parse_qs, quote_plus
6
7
 
7
8
  from django import template
@@ -12,7 +13,7 @@ from django.contrib.staticfiles.finders import find
12
13
  from django.core.exceptions import ObjectDoesNotExist
13
14
  from django.templatetags.static import static, StaticNode
14
15
  from django.urls import NoReverseMatch, reverse
15
- from django.utils.html import format_html, format_html_join
16
+ from django.utils.html import format_html, format_html_join, strip_tags
16
17
  from django.utils.safestring import mark_safe
17
18
  from django.utils.text import slugify as django_slugify
18
19
  from django_jinja import library
@@ -25,10 +26,9 @@ from nautobot.core.constants import PAGINATE_COUNT_DEFAULT
25
26
  from nautobot.core.utils import color, config, data, logging as nautobot_logging, lookup
26
27
  from nautobot.core.utils.requests import add_nautobot_version_query_param_to_url
27
28
 
28
- # S308 is suspicious-mark-safe-usage, but these are all using static strings that we know to be safe
29
- HTML_TRUE = mark_safe('<span class="text-success"><i class="mdi mdi-check-bold" title="Yes"></i></span>') # noqa: S308
30
- HTML_FALSE = mark_safe('<span class="text-danger"><i class="mdi mdi-close-thick" title="No"></i></span>') # noqa: S308
31
- HTML_NONE = mark_safe('<span class="text-muted">&mdash;</span>') # noqa: S308
29
+ HTML_TRUE = mark_safe('<span class="text-success"><i class="mdi mdi-check-bold" title="Yes"></i></span>')
30
+ HTML_FALSE = mark_safe('<span class="text-danger"><i class="mdi mdi-close-thick" title="No"></i></span>')
31
+ HTML_NONE = mark_safe('<span class="text-muted">&mdash;</span>')
32
32
 
33
33
  DEFAULT_SUPPORT_MESSAGE = (
34
34
  "If further assistance is required, please join the `#nautobot` channel "
@@ -1124,7 +1124,7 @@ def custom_branding_or_static(branding_asset, static_asset):
1124
1124
  branding has been configured in settings, else it returns stock branding via static.
1125
1125
  """
1126
1126
  if settings.BRANDING_FILEPATHS.get(branding_asset):
1127
- url = f"{ settings.MEDIA_URL }{ settings.BRANDING_FILEPATHS.get(branding_asset) }"
1127
+ url = f"{settings.MEDIA_URL}{settings.BRANDING_FILEPATHS.get(branding_asset)}"
1128
1128
  else:
1129
1129
  url = StaticNode.handle_simple(static_asset)
1130
1130
  return add_nautobot_version_query_param_to_url(url)
@@ -1255,3 +1255,26 @@ def _build_hyperlink(value, field="", target="", rel=""):
1255
1255
  except AttributeError:
1256
1256
  pass
1257
1257
  return format_html("{}", display)
1258
+
1259
+
1260
+ @register.simple_tag(takes_context=True)
1261
+ def saved_view_title(context, mode: Literal["html", "plain"] = "html"):
1262
+ """
1263
+ Creates a formatted title that includes saved view information.
1264
+ Usage: <h1>{{ title }}{% saved_view_title "html" %}</h1>
1265
+ """
1266
+ new_changes_not_applied = context.get("new_changes_not_applied", False)
1267
+ current_saved_view = context.get("current_saved_view")
1268
+
1269
+ if not current_saved_view:
1270
+ return ""
1271
+
1272
+ if new_changes_not_applied:
1273
+ title = format_html(' — <i title="Pending changes not saved">{}</i>', current_saved_view.name)
1274
+ else:
1275
+ title = format_html(" — {}", current_saved_view.name)
1276
+
1277
+ if mode == "plain":
1278
+ return strip_tags(title)
1279
+
1280
+ return title
@@ -3,6 +3,8 @@ import logging
3
3
  from django import template
4
4
  from django.utils.html import format_html_join
5
5
 
6
+ from nautobot.core.ui.breadcrumbs import Breadcrumbs
7
+ from nautobot.core.ui.titles import Titles
6
8
  from nautobot.core.utils.lookup import get_view_for_model
7
9
  from nautobot.core.views.utils import get_obj_from_context
8
10
 
@@ -27,6 +29,25 @@ def render_components(context, components):
27
29
  return ""
28
30
 
29
31
 
32
+ @register.simple_tag(takes_context=True)
33
+ def render_title(context, mode="plain"):
34
+ title_obj = context.get("view_titles")
35
+ if title_obj is not None and isinstance(title_obj, Titles):
36
+ return title_obj.render(context, mode=mode)
37
+
38
+ if fallback_title := context.get("title"):
39
+ return fallback_title
40
+ return ""
41
+
42
+
43
+ @register.simple_tag(takes_context=True)
44
+ def render_breadcrumbs(context):
45
+ breadcrumbs_obj = context.get("breadcrumbs")
46
+ if breadcrumbs_obj is not None and isinstance(breadcrumbs_obj, Breadcrumbs):
47
+ return breadcrumbs_obj.render(context)
48
+ return ""
49
+
50
+
30
51
  @register.simple_tag(takes_context=True)
31
52
  def render_detail_view_extra_buttons(context):
32
53
  """