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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (434) hide show
  1. nautobot/apps/utils.py +2 -0
  2. nautobot/apps/views.py +2 -0
  3. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -8
  4. nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +9 -0
  5. nautobot/circuits/tests/integration/test_circuit.py +2 -2
  6. nautobot/circuits/views.py +32 -15
  7. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +3 -3
  8. nautobot/cloud/views.py +7 -0
  9. nautobot/core/apps/__init__.py +1 -0
  10. nautobot/core/celery/__init__.py +2 -1
  11. nautobot/core/filters.py +2 -2
  12. nautobot/core/settings.py +1 -0
  13. nautobot/core/settings.yaml +9 -0
  14. nautobot/core/tables.py +21 -23
  15. nautobot/core/templates/components/breadcrumbs.html +19 -0
  16. nautobot/core/templates/components/panel/panel.html +1 -1
  17. nautobot/core/templates/generic/object_changelog.html +0 -2
  18. nautobot/core/templates/generic/object_list.html +15 -12
  19. nautobot/core/templates/generic/object_notes.html +0 -2
  20. nautobot/core/templates/generic/object_retrieve.html +16 -9
  21. nautobot/core/templates/inc/paginator.html +3 -3
  22. nautobot/core/templates/inc/table.html +2 -2
  23. nautobot/core/templatetags/helpers.py +104 -6
  24. nautobot/core/templatetags/ui_framework.py +40 -5
  25. nautobot/core/testing/filters.py +37 -21
  26. nautobot/core/testing/mixins.py +1 -1
  27. nautobot/core/testing/views.py +27 -4
  28. nautobot/core/tests/test_tables.py +43 -6
  29. nautobot/core/tests/test_templatetags_ui_framework.py +146 -0
  30. nautobot/core/tests/test_titles.py +2 -2
  31. nautobot/core/tests/test_ui.py +14 -1
  32. nautobot/core/tests/test_views.py +45 -0
  33. nautobot/core/ui/breadcrumbs.py +13 -8
  34. nautobot/core/ui/bulk_buttons.py +53 -53
  35. nautobot/core/ui/object_detail.py +52 -9
  36. nautobot/core/ui/titles.py +9 -5
  37. nautobot/core/utils/data.py +13 -0
  38. nautobot/core/utils/deprecation.py +2 -0
  39. nautobot/core/views/__init__.py +24 -3
  40. nautobot/core/views/generic.py +42 -17
  41. nautobot/core/views/mixins.py +146 -12
  42. nautobot/core/views/utils.py +117 -0
  43. nautobot/dcim/migrations/0073_alter_powerport_power_factor_and_more.py +41 -0
  44. nautobot/dcim/models/device_component_templates.py +4 -2
  45. nautobot/dcim/models/device_components.py +3 -2
  46. nautobot/dcim/models/devices.py +4 -0
  47. nautobot/dcim/tables/__init__.py +2 -0
  48. nautobot/dcim/tables/devices.py +24 -0
  49. nautobot/dcim/tables/power.py +2 -2
  50. nautobot/dcim/templates/dcim/device/base.html +1 -11
  51. nautobot/dcim/templates/dcim/device_component.html +0 -19
  52. nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -16
  53. nautobot/dcim/templates/dcim/rack_elevation_list.html +4 -4
  54. nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -50
  55. nautobot/dcim/tests/test_views.py +41 -0
  56. nautobot/dcim/views.py +169 -39
  57. nautobot/extras/filters/mixins.py +1 -1
  58. nautobot/extras/forms/forms.py +15 -0
  59. nautobot/extras/models/customfields.py +45 -9
  60. nautobot/extras/models/groups.py +10 -1
  61. nautobot/extras/models/jobs.py +2 -2
  62. nautobot/extras/plugins/views.py +18 -5
  63. nautobot/extras/tables.py +4 -2
  64. nautobot/extras/templates/extras/configcontext_retrieve.html +1 -1
  65. nautobot/extras/templates/extras/configcontext_update.html +49 -49
  66. nautobot/extras/templates/extras/configcontextschema_retrieve.html +47 -47
  67. nautobot/extras/templates/extras/configcontextschema_update.html +18 -18
  68. nautobot/extras/templates/extras/customfield_retrieve.html +1 -128
  69. nautobot/extras/templates/extras/dynamicgroup.html +2 -99
  70. nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -199
  71. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +99 -0
  72. nautobot/extras/templates/extras/dynamicgroup_update.html +199 -0
  73. nautobot/extras/templates/extras/gitrepository.html +2 -82
  74. nautobot/extras/templates/extras/gitrepository_object_edit.html +2 -13
  75. nautobot/extras/templates/extras/gitrepository_retrieve.html +82 -0
  76. nautobot/extras/templates/extras/gitrepository_update.html +13 -0
  77. nautobot/extras/templates/extras/inc/job_table.html +1 -1
  78. nautobot/extras/templates/extras/inc/object_contact_header.html +2 -2
  79. nautobot/extras/templates/extras/note_retrieve.html +1 -53
  80. nautobot/extras/templates/extras/plugin_detail.html +3 -7
  81. nautobot/extras/templates/extras/plugins_list.html +0 -2
  82. nautobot/extras/templates/extras/tag_retrieve.html +1 -1
  83. nautobot/extras/templates/extras/tag_update.html +14 -14
  84. nautobot/extras/templates/extras/team_retrieve.html +1 -1
  85. nautobot/extras/tests/test_dynamicgroups.py +73 -18
  86. nautobot/extras/tests/test_models.py +216 -0
  87. nautobot/extras/tests/test_views.py +7 -2
  88. nautobot/extras/urls.py +2 -94
  89. nautobot/extras/views.py +425 -430
  90. nautobot/ipam/apps.py +1 -0
  91. nautobot/ipam/jobs/__init__.py +10 -0
  92. nautobot/ipam/jobs/cleanup.py +296 -0
  93. nautobot/ipam/models.py +301 -178
  94. nautobot/ipam/querysets.py +3 -3
  95. nautobot/ipam/signals.py +6 -1
  96. nautobot/ipam/templates/ipam/inc/ipadress_edit_header.html +3 -3
  97. nautobot/ipam/templates/ipam/inc/toggle_available.html +2 -2
  98. nautobot/ipam/templates/ipam/ipaddress_assign.html +1 -1
  99. nautobot/ipam/templates/ipam/prefix.html +0 -8
  100. nautobot/ipam/templates/ipam/prefix_list.html +1 -1
  101. nautobot/ipam/templates/ipam/vlan_retrieve.html +1 -77
  102. nautobot/ipam/tests/test_api.py +5 -0
  103. nautobot/ipam/tests/test_jobs.py +454 -0
  104. nautobot/ipam/tests/test_models.py +677 -122
  105. nautobot/ipam/tests/test_querysets.py +46 -0
  106. nautobot/ipam/tests/test_views.py +40 -164
  107. nautobot/ipam/urls.py +0 -11
  108. nautobot/ipam/utils/migrations.py +1 -1
  109. nautobot/ipam/utils/testing.py +9 -4
  110. nautobot/ipam/views.py +175 -235
  111. nautobot/project-static/docs/404.html +9 -6
  112. nautobot/project-static/docs/apps/index.html +9 -6
  113. nautobot/project-static/docs/apps/nautobot-apps.html +9 -6
  114. nautobot/project-static/docs/assets/javascripts/bundle.92b07e13.min.js +16 -0
  115. nautobot/project-static/docs/assets/javascripts/{bundle.50899def.min.js.map → bundle.92b07e13.min.js.map} +2 -2
  116. nautobot/project-static/docs/assets/javascripts/workers/{search.d50fe291.min.js → search.973d3a69.min.js} +4 -4
  117. nautobot/project-static/docs/assets/javascripts/workers/{search.d50fe291.min.js.map → search.973d3a69.min.js.map} +1 -1
  118. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +9 -6
  119. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +9 -6
  120. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +9 -6
  121. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +9 -6
  122. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +10 -7
  123. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +9 -6
  124. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +9 -6
  125. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +9 -6
  126. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9 -6
  127. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +9 -6
  128. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +9 -6
  129. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +9 -6
  130. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +9 -6
  131. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +9 -6
  132. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +9 -6
  133. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +11 -8
  134. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +9 -6
  135. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +9 -6
  136. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +11 -8
  137. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +81 -6
  138. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +73 -18
  139. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +9 -6
  140. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +69 -7
  141. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +402 -21
  142. nautobot/project-static/docs/development/apps/api/configuration-view.html +13 -10
  143. nautobot/project-static/docs/development/apps/api/database-backend-config.html +11 -8
  144. nautobot/project-static/docs/development/apps/api/models/django-admin.html +13 -10
  145. nautobot/project-static/docs/development/apps/api/models/global-search.html +10 -7
  146. nautobot/project-static/docs/development/apps/api/models/graphql.html +18 -15
  147. nautobot/project-static/docs/development/apps/api/models/index.html +14 -11
  148. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +12 -9
  149. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +15 -12
  150. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +9 -6
  151. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +15 -12
  152. nautobot/project-static/docs/development/apps/api/platform-features/index.html +9 -6
  153. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +11 -8
  154. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +16 -13
  155. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +12 -10305
  156. nautobot/project-static/docs/development/apps/api/platform-features/prepopulating-data.html +10722 -0
  157. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +15 -12
  158. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +14 -11
  159. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +9 -6
  160. nautobot/project-static/docs/development/apps/api/prometheus.html +15 -12
  161. nautobot/project-static/docs/development/apps/api/setup.html +9 -6
  162. nautobot/project-static/docs/development/apps/api/testing.html +9 -6
  163. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +12 -9
  164. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +9 -6
  165. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +9 -6
  166. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +9 -6
  167. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +20 -17
  168. nautobot/project-static/docs/development/apps/api/views/base-template.html +9 -6
  169. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +15 -12
  170. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +14 -11
  171. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +9 -6
  172. nautobot/project-static/docs/development/apps/api/views/index.html +9 -6
  173. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +10 -7
  174. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +24 -21
  175. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +12 -9
  176. nautobot/project-static/docs/development/apps/api/views/notes.html +10 -7
  177. nautobot/project-static/docs/development/apps/api/views/rest-api.html +19 -16
  178. nautobot/project-static/docs/development/apps/api/views/urls.html +11 -8
  179. nautobot/project-static/docs/development/apps/index.html +9 -6
  180. nautobot/project-static/docs/development/apps/migration/code-updates.html +19 -16
  181. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +9 -6
  182. nautobot/project-static/docs/development/apps/migration/from-v1.html +9 -6
  183. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +22 -19
  184. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +9 -6
  185. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +9 -6
  186. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +9 -6
  187. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9 -6
  188. nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +14 -11
  189. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +27 -24
  190. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +20 -17
  191. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +20 -17
  192. nautobot/project-static/docs/development/apps/porting-from-netbox.html +9 -6
  193. nautobot/project-static/docs/development/core/application-registry.html +23 -20
  194. nautobot/project-static/docs/development/core/best-practices.html +23 -20
  195. nautobot/project-static/docs/development/core/bootstrap-ui.html +9 -6
  196. nautobot/project-static/docs/development/core/caching.html +9 -6
  197. nautobot/project-static/docs/development/core/controllers.html +9 -6
  198. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +10 -7
  199. nautobot/project-static/docs/development/core/generic-views.html +9 -6
  200. nautobot/project-static/docs/development/core/getting-started.html +9 -21
  201. nautobot/project-static/docs/development/core/homepage.html +12 -9
  202. nautobot/project-static/docs/development/core/index.html +9 -6
  203. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9 -6
  204. nautobot/project-static/docs/development/core/model-checklist.html +9 -6
  205. nautobot/project-static/docs/development/core/model-features.html +11 -8
  206. nautobot/project-static/docs/development/core/natural-keys.html +21 -18
  207. nautobot/project-static/docs/development/core/navigation-menu.html +10 -7
  208. nautobot/project-static/docs/development/core/release-checklist.html +9 -6
  209. nautobot/project-static/docs/development/core/role-internals.html +9 -6
  210. nautobot/project-static/docs/development/core/settings.html +9 -6
  211. nautobot/project-static/docs/development/core/style-guide.html +32 -29
  212. nautobot/project-static/docs/development/core/templates.html +9 -6
  213. nautobot/project-static/docs/development/core/testing.html +10 -7
  214. nautobot/project-static/docs/development/core/ui-component-framework.html +42 -44
  215. nautobot/project-static/docs/development/core/user-preferences.html +9 -6
  216. nautobot/project-static/docs/development/index.html +9 -6
  217. nautobot/project-static/docs/development/jobs/getting-started.html +13 -10
  218. nautobot/project-static/docs/development/jobs/index.html +9 -6
  219. nautobot/project-static/docs/development/jobs/installation.html +23 -20
  220. nautobot/project-static/docs/development/jobs/job-extensions.html +25 -22
  221. nautobot/project-static/docs/development/jobs/job-logging.html +12 -9
  222. nautobot/project-static/docs/development/jobs/job-patterns.html +45 -42
  223. nautobot/project-static/docs/development/jobs/job-structure.html +53 -50
  224. nautobot/project-static/docs/development/jobs/migration/from-v1.html +23 -20
  225. nautobot/project-static/docs/development/jobs/testing.html +14 -11
  226. nautobot/project-static/docs/index.html +9 -6
  227. nautobot/project-static/docs/objects.inv +0 -0
  228. nautobot/project-static/docs/overview/application_stack.html +9 -6
  229. nautobot/project-static/docs/overview/design_philosophy.html +9 -6
  230. nautobot/project-static/docs/release-notes/index.html +9 -6
  231. nautobot/project-static/docs/release-notes/version-1.0.html +9 -6
  232. nautobot/project-static/docs/release-notes/version-1.1.html +9 -6
  233. nautobot/project-static/docs/release-notes/version-1.2.html +10 -7
  234. nautobot/project-static/docs/release-notes/version-1.3.html +9 -6
  235. nautobot/project-static/docs/release-notes/version-1.4.html +9 -6
  236. nautobot/project-static/docs/release-notes/version-1.5.html +13 -10
  237. nautobot/project-static/docs/release-notes/version-1.6.html +9 -6
  238. nautobot/project-static/docs/release-notes/version-2.0.html +9 -6
  239. nautobot/project-static/docs/release-notes/version-2.1.html +9 -6
  240. nautobot/project-static/docs/release-notes/version-2.2.html +9 -6
  241. nautobot/project-static/docs/release-notes/version-2.3.html +9 -6
  242. nautobot/project-static/docs/release-notes/version-2.4.html +489 -6
  243. nautobot/project-static/docs/search/search_index.json +1 -1
  244. nautobot/project-static/docs/sitemap.xml +301 -301
  245. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  246. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +15 -12
  247. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +9 -6
  248. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +16 -13
  249. nautobot/project-static/docs/user-guide/administration/configuration/index.html +9 -6
  250. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +9 -6
  251. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +38 -8
  252. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +9 -6
  253. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +9 -6
  254. nautobot/project-static/docs/user-guide/administration/guides/docker.html +9 -6
  255. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +9 -6
  256. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +9 -6
  257. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +9 -6
  258. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +9 -6
  259. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +9 -6
  260. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +16 -13
  261. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +9 -6
  262. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +9 -6
  263. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +9 -6
  264. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +9 -6
  265. nautobot/project-static/docs/user-guide/administration/installation/index.html +9 -6
  266. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +9 -6
  267. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +9 -6
  268. nautobot/project-static/docs/user-guide/administration/installation/services.html +12 -9
  269. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +13 -10
  270. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +10 -7
  271. nautobot/project-static/docs/user-guide/administration/security/index.html +9 -6
  272. nautobot/project-static/docs/user-guide/administration/security/notices.html +9 -6
  273. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +9 -6
  274. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +10 -7
  275. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +9 -6
  276. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +9 -6
  277. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +9 -6
  278. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +9 -6
  279. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +9 -6
  280. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +9 -6
  281. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +9 -6
  282. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +15 -12
  283. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +9 -6
  284. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +9 -6
  285. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +9 -6
  286. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +9 -6
  287. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +9 -6
  288. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +9 -6
  289. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +9 -6
  290. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +9 -6
  291. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +9 -6
  292. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +9 -6
  293. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +9 -6
  294. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +9 -6
  295. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +9 -6
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +9 -6
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +9 -6
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +9 -6
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +9 -6
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +9 -6
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +9 -6
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +9 -6
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +9 -6
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +9 -6
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +9 -6
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +9 -6
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +13 -10
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +9 -6
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +9 -6
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +9 -6
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +9 -6
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +9 -6
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +9 -6
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +9 -6
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +9 -6
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +9 -6
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +9 -6
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +9 -6
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +9 -6
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +9 -6
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +9 -6
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +9 -6
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +9 -6
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +9 -6
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +9 -6
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +9 -6
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +9 -6
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +9 -6
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +9 -6
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +9 -6
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +9 -6
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +9 -6
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +9 -6
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +9 -6
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +9 -6
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +9 -6
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +9 -6
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9 -6
  339. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +9 -6
  340. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +9 -6
  341. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +11 -8
  342. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +11 -8
  343. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +41 -41
  344. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +9 -6
  345. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +197 -54
  346. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +9 -6
  347. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +9 -6
  348. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +9 -6
  349. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +9 -6
  350. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +9 -6
  351. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +9 -6
  352. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +9 -6
  353. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +9 -6
  354. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +9 -6
  355. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +9 -6
  356. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +9 -6
  357. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +9 -6
  358. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +9 -6
  359. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +9 -6
  360. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9 -6
  361. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9 -6
  362. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9 -6
  363. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9 -6
  364. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +9 -6
  365. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +9 -6
  366. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +9 -6
  367. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +9 -6
  368. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +9 -6
  369. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +9 -6
  370. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +9 -6
  371. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +9 -6
  372. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +9 -6
  373. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +9 -6
  374. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +9 -6
  375. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +9 -6
  376. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +13 -10
  377. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +9 -6
  378. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +9 -6
  379. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +9 -6
  380. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9 -6
  381. nautobot/project-static/docs/user-guide/index.html +9 -6
  382. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +9 -6
  383. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +9 -6
  384. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +10 -7
  385. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +9 -6
  386. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +9 -6
  387. nautobot/project-static/docs/user-guide/platform-functionality/events.html +11 -8
  388. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +9 -6
  389. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +9 -6
  390. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +9 -6
  391. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +9 -6
  392. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +9 -6
  393. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +9 -6
  394. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +9 -6
  395. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +9 -6
  396. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +9 -6
  397. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +9 -6
  398. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9 -6
  399. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9 -6
  400. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +9 -6
  401. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +9 -6
  402. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +9 -6
  403. nautobot/project-static/docs/user-guide/platform-functionality/note.html +9 -6
  404. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +12 -9
  405. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +9 -6
  406. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9 -6
  407. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +9 -6
  408. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +9 -6
  409. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +9 -6
  410. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +9 -6
  411. nautobot/project-static/docs/user-guide/platform-functionality/role.html +9 -6
  412. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9 -6
  413. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +11 -8
  414. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +9 -6
  415. nautobot/project-static/docs/user-guide/platform-functionality/status.html +9 -6
  416. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +9 -6
  417. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +9 -6
  418. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +9 -6
  419. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +9 -6
  420. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +9 -6
  421. nautobot/project-static/fonts/UFL.txt +96 -96
  422. nautobot/project-static/img/nautobot_icon.svg +32 -34
  423. nautobot/project-static/js/forms.js +35 -2
  424. nautobot/project-static/js/table_sorting_indicator.js +0 -2
  425. nautobot/virtualization/filters.py +7 -0
  426. {nautobot-2.4.16.dist-info → nautobot-2.4.18.dist-info}/METADATA +8 -8
  427. {nautobot-2.4.16.dist-info → nautobot-2.4.18.dist-info}/RECORD +431 -421
  428. nautobot/core/templates/inc/breadcrumbs.html +0 -14
  429. nautobot/project-static/docs/assets/javascripts/bundle.50899def.min.js +0 -16
  430. nautobot/project-static/docs/requirements.txt +0 -14
  431. {nautobot-2.4.16.dist-info → nautobot-2.4.18.dist-info}/LICENSE.txt +0 -0
  432. {nautobot-2.4.16.dist-info → nautobot-2.4.18.dist-info}/NOTICE +0 -0
  433. {nautobot-2.4.16.dist-info → nautobot-2.4.18.dist-info}/WHEEL +0 -0
  434. {nautobot-2.4.16.dist-info → nautobot-2.4.18.dist-info}/entry_points.txt +0 -0
@@ -48,7 +48,12 @@ from nautobot.core.utils.requests import (
48
48
  get_filterable_params_from_filter_params,
49
49
  normalize_querydict,
50
50
  )
51
- from nautobot.core.views.mixins import BulkEditAndBulkDeleteModelMixin, GetReturnURLMixin, ObjectPermissionRequiredMixin
51
+ from nautobot.core.views.mixins import (
52
+ BulkEditAndBulkDeleteModelMixin,
53
+ GetReturnURLMixin,
54
+ ObjectPermissionRequiredMixin,
55
+ UIComponentsMixin,
56
+ )
52
57
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
53
58
  from nautobot.core.views.utils import (
54
59
  check_filter_for_display,
@@ -63,7 +68,7 @@ from nautobot.core.views.utils import (
63
68
  from nautobot.extras.models import ExportTemplate, SavedView, UserSavedViewAssociation
64
69
 
65
70
 
66
- class GenericView(LoginRequiredMixin, View):
71
+ class GenericView(LoginRequiredMixin, UIComponentsMixin, View):
67
72
  """
68
73
  Base class for non-object-related views.
69
74
 
@@ -71,7 +76,7 @@ class GenericView(LoginRequiredMixin, View):
71
76
  """
72
77
 
73
78
 
74
- class ObjectView(ObjectPermissionRequiredMixin, View):
79
+ class ObjectView(ObjectPermissionRequiredMixin, UIComponentsMixin, View):
75
80
  """
76
81
  Retrieve a single object for display.
77
82
 
@@ -115,21 +120,28 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
115
120
  Generic GET handler for accessing an object.
116
121
  """
117
122
  instance = get_object_or_404(self.queryset, **kwargs)
123
+ model = self.queryset.model
118
124
  content_type = ContentType.objects.get_for_model(self.queryset.model)
119
125
  context = {
120
126
  "object": instance,
121
127
  "content_type": content_type,
122
- "verbose_name": self.queryset.model._meta.verbose_name,
123
- "verbose_name_plural": self.queryset.model._meta.verbose_name_plural,
128
+ "verbose_name": model._meta.verbose_name,
129
+ "verbose_name_plural": model._meta.verbose_name_plural,
124
130
  "object_detail_content": self.object_detail_content,
131
+ "breadcrumbs": self.get_breadcrumbs(model, view_type=""),
125
132
  **common_detail_view_context(request, instance),
126
133
  **self.get_extra_context(request, instance),
127
134
  }
128
135
 
136
+ # Some of the legacy views overriding title in `get_extra_context` method.
137
+ # But if not, we will generate the default `title` using the default `Titles` class or one set in class under `view_titles`.
138
+ if context.get("title") is None:
139
+ context["title"] = self.get_view_titles(model, view_type="").render(context)
140
+
129
141
  return render(request, self.get_template_name(), context)
130
142
 
131
143
 
132
- class ObjectListView(ObjectPermissionRequiredMixin, View):
144
+ class ObjectListView(ObjectPermissionRequiredMixin, UIComponentsMixin, View):
133
145
  """
134
146
  List a series of objects.
135
147
 
@@ -378,11 +390,13 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
378
390
  "dynamic_filter_form": dynamic_filter_form,
379
391
  "search_form": search_form,
380
392
  "list_url": list_url,
381
- "title": bettertitle(model._meta.verbose_name_plural),
382
393
  "new_changes_not_applied": new_changes_not_applied,
383
394
  "current_saved_view": current_saved_view,
384
395
  "saved_views": saved_views,
385
396
  "model": model,
397
+ "verbose_name_plural": model._meta.verbose_name_plural,
398
+ "view_action": "list",
399
+ "breadcrumbs": self.get_breadcrumbs(model),
386
400
  }
387
401
 
388
402
  # `extra_context()` would require `request` access, however `request` parameter cannot simply be
@@ -392,6 +406,11 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
392
406
  setattr(self, "request", request)
393
407
  context.update(self.extra_context())
394
408
 
409
+ # Some of the legacy views overriding title in `extra_context` method.
410
+ # But if not, we will generate the default `title` using the default `Titles` class or one set in class under `view_titles`.
411
+ if context.get("title") is None:
412
+ context["title"] = self.get_view_titles(model).render(context)
413
+
395
414
  return render(request, self.template_name, context)
396
415
 
397
416
  def alter_queryset(self, request):
@@ -402,7 +421,7 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
402
421
  return {}
403
422
 
404
423
 
405
- class ObjectEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
424
+ class ObjectEditView(GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, View):
406
425
  """
407
426
  Create or edit a single object.
408
427
 
@@ -550,7 +569,7 @@ class ObjectEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
550
569
  )
551
570
 
552
571
 
553
- class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
572
+ class ObjectDeleteView(GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, View):
554
573
  """
555
574
  Delete a single object.
556
575
 
@@ -627,7 +646,7 @@ class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
627
646
  )
628
647
 
629
648
 
630
- class BulkCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
649
+ class BulkCreateView(GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, View):
631
650
  """
632
651
  Create new objects in bulk.
633
652
 
@@ -741,7 +760,7 @@ class BulkCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
741
760
  )
742
761
 
743
762
 
744
- class ObjectImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
763
+ class ObjectImportView(GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, View):
745
764
  """
746
765
  Import a single object (YAML or JSON format).
747
766
 
@@ -888,7 +907,9 @@ class ObjectImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
888
907
  )
889
908
 
890
909
 
891
- class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): # 3.0 TODO: remove as it's no longer used
910
+ class BulkImportView(
911
+ GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, View
912
+ ): # 3.0 TODO: remove as it's no longer used
892
913
  """
893
914
  Import objects in bulk (CSV format).
894
915
 
@@ -997,7 +1018,9 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): #
997
1018
  )
998
1019
 
999
1020
 
1000
- class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, BulkEditAndBulkDeleteModelMixin, View):
1021
+ class BulkEditView(
1022
+ GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, BulkEditAndBulkDeleteModelMixin, View
1023
+ ):
1001
1024
  """
1002
1025
  Edit objects in bulk.
1003
1026
 
@@ -1093,7 +1116,7 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, BulkEditAnd
1093
1116
  return {}
1094
1117
 
1095
1118
 
1096
- class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
1119
+ class BulkRenameView(GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, View):
1097
1120
  """
1098
1121
  An extendable view for renaming objects in bulk.
1099
1122
  """
@@ -1193,7 +1216,9 @@ class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
1193
1216
  return ""
1194
1217
 
1195
1218
 
1196
- class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, BulkEditAndBulkDeleteModelMixin, View):
1219
+ class BulkDeleteView(
1220
+ GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, BulkEditAndBulkDeleteModelMixin, View
1221
+ ):
1197
1222
  """
1198
1223
  Delete objects in bulk.
1199
1224
 
@@ -1304,7 +1329,7 @@ class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, BulkEditA
1304
1329
 
1305
1330
 
1306
1331
  # TODO: Replace with BulkCreateView
1307
- class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
1332
+ class ComponentCreateView(GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, View):
1308
1333
  """
1309
1334
  Add one or more components (e.g. interfaces, console ports, etc.) to a Device or VirtualMachine.
1310
1335
  """
@@ -1410,7 +1435,7 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
1410
1435
  )
1411
1436
 
1412
1437
 
1413
- class BulkComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
1438
+ class BulkComponentCreateView(GetReturnURLMixin, UIComponentsMixin, ObjectPermissionRequiredMixin, View):
1414
1439
  """
1415
1440
  Add one or more components (e.g. interfaces, console ports, etc.) to a set of Devices or VirtualMachines.
1416
1441
  """
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import ClassVar, Optional
2
+ from typing import ClassVar, Optional, Type, Union
3
3
 
4
4
  from django.contrib import messages
5
5
  from django.contrib.auth.mixins import AccessMixin
@@ -12,7 +12,7 @@ from django.core.exceptions import (
12
12
  ValidationError,
13
13
  )
14
14
  from django.db import transaction
15
- from django.db.models import ManyToManyField, ProtectedError, Q
15
+ from django.db.models import ManyToManyField, Model, ProtectedError, Q
16
16
  from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
17
17
  from django.http import HttpResponse
18
18
  from django.shortcuts import get_object_or_404, redirect
@@ -217,10 +217,152 @@ class GetReturnURLMixin:
217
217
  return reverse("home")
218
218
 
219
219
 
220
+ class UIComponentsMixin:
221
+ """
222
+ Mixin that resolves UI components (e.g., breadcrumbs, titles) either from:
223
+ 1) the current view class (preferred),
224
+ 2) a related view class for a given model (via `lookup.get_view_for_model()`),
225
+ 3) or a default component class.
226
+
227
+ The public helpers (`get_view_titles()`, `get_breadcrumbs()`) return concrete
228
+ component *instances* ready to use in renderers/templates.
229
+
230
+ It should be used in views that use the `nautobot.apps.views.GenericView` and standard Nautobot templates.
231
+
232
+ Example usage:
233
+ ```
234
+ def get(self, request, *args, **kwargs):
235
+ context = {
236
+ ...
237
+ "breadcrumbs": self.get_breadcrumbs(model),
238
+ }
239
+ context["title"] = self.get_view_titles(model).render(context)
240
+
241
+ return render(
242
+ request,
243
+ "extras/plugins_list.html",
244
+ context,
245
+ )
246
+ ```
247
+
248
+ Attributes:
249
+ breadcrumbs (ClassVar[Optional[Breadcrumbs]]): Optional component declared on the view class. May be:
250
+ - `None` (no local definition, fall back),
251
+ - a component *class* (will be instantiated),
252
+ - or a pre-instantiated component (returned as-is).
253
+ view_titles (ClassVar[Optional[Titles]]): Same contract as `breadcrumbs`,
254
+ but for titles.
255
+ """
256
+
257
+ breadcrumbs: ClassVar[Optional[Breadcrumbs]] = None
258
+ view_titles: ClassVar[Optional[Titles]] = None
259
+
260
+ def get_view_titles(self, model: Union[None, str, Type[Model], Model] = None, view_type: str = "List") -> Titles:
261
+ """
262
+ Resolve and return the `Titles` component instance.
263
+
264
+ Resolution order:
265
+ 1) If `self.view_titles` is set on the current view, use it.
266
+ 2) Else, if `model` is provided, copy the `view_titles` from the view
267
+ class associated with that model via `lookup.get_view_for_model(model, action)`.
268
+ 3) Else, instantiate and return the default `Titles()`.
269
+
270
+ Args:
271
+ model: A Django model **class**, **instance**, dotted name string, or `None`.
272
+ Passed to `lookup.get_view_for_model()` to find the related view class.
273
+ If `None`, only local/default resolution is used.
274
+ view_type: Logical view type used by `lookup.get_view_for_model()` (e.g., `"List"` or empty to construct `"DeviceView"` string).
275
+
276
+ Returns:
277
+ Titles: A concrete `Titles` component instance ready to use.
278
+ """
279
+ return self._resolve_component("view_titles", Titles, model, view_type)
280
+
281
+ def get_breadcrumbs(
282
+ self, model: Union[None, str, Type[Model], Model] = None, view_type: str = "List"
283
+ ) -> Breadcrumbs:
284
+ """
285
+ Resolve and return the `Breadcrumbs` component instance.
286
+
287
+ Resolution order mirrors `get_view_titles()`:
288
+ 1) Use `self.breadcrumbs` if set locally.
289
+ 2) Else, if `model` is provided, copy the `breadcrumbs` from the view
290
+ class associated with that model via `lookup.get_view_for_model(model, action)`.
291
+ 3) Else return a new default `Breadcrumbs()`.
292
+
293
+ Args:
294
+ model: A Django model **class**, **instance**, dotted name string, or `None`.
295
+ Passed to `lookup.get_view_for_model()` to find the related view class.
296
+ If `None`, only local/default resolution is used.
297
+ view_type: Logical view type used by `lookup.get_view_for_model()` (e.g., `"List"` or empty to construct `"DeviceView"` string).
298
+
299
+ Returns:
300
+ Breadcrumbs: A concrete `Breadcrumbs` component instance.
301
+ """
302
+ return self._resolve_component("breadcrumbs", Breadcrumbs, model, view_type)
303
+
304
+ def _resolve_component(
305
+ self,
306
+ attr_name: str,
307
+ default_cls: Type[Union[Breadcrumbs, Titles]],
308
+ model: Union[None, str, Type[Model], Model] = None,
309
+ view_type: str = "List",
310
+ ) -> Union[Breadcrumbs, Titles]:
311
+ """
312
+ Resolve a UI component by name.
313
+
314
+ Return local view's attribute defined via `attr_name` or
315
+ the `attr_name` defined on the view class for model via lookup.get_view_for_model(model, view_type) or
316
+ instantiates `default_cls`.
317
+
318
+ Args:
319
+ attr_name (str): Attribute to resolve (e.g., "breadcrumbs").
320
+ default_cls: Default Breadcrumbs/Title class to instantiate if not found.
321
+ model (Union[None, str, Type[Model], Model]): Django model (class/instance/dotted string) to locate a related view class.
322
+ view_type (str): View type for lookup (e.g., "List", or empty to resolve like "DeviceView").
323
+ Returns:
324
+ Breadcrumbs/Title instance.
325
+ """
326
+ local = getattr(self, attr_name, None)
327
+ if local is not None:
328
+ return self._instantiate_if_needed(local, default_cls)
329
+
330
+ if model is not None:
331
+ view_class = lookup.get_view_for_model(model, view_type)
332
+ view_component = getattr(view_class, attr_name, None)
333
+ return self._instantiate_if_needed(view_component, default_cls)
334
+
335
+ return default_cls()
336
+
337
+ @staticmethod
338
+ def _instantiate_if_needed(
339
+ attr: Union[None, Type[Union[Breadcrumbs, Titles]], Breadcrumbs, Titles],
340
+ default_cls: Type[Union[Breadcrumbs, Titles]],
341
+ ) -> Union[Breadcrumbs, Titles]:
342
+ """
343
+ Normalize a value into a component instance.
344
+
345
+ If attr is None - return default_cls().
346
+ If attr is a class - instantiate it.
347
+ Otherwise, return as is.
348
+
349
+ Args:
350
+ attr: None, a Breadcrumbs/Title class or an instance.
351
+ default_cls: Fallback class to instantiate when attr is None.
352
+ Returns:
353
+ Breadcrumbs/Title instance.
354
+ """
355
+ if attr is None:
356
+ return default_cls()
357
+ if isinstance(attr, type):
358
+ return attr()
359
+ return attr
360
+
361
+
220
362
  @extend_schema(exclude=True)
221
- class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormView):
363
+ class NautobotViewSetMixin(GenericViewSet, UIComponentsMixin, AccessMixin, GetReturnURLMixin, FormView):
222
364
  """
223
- NautobotViewSetMixin is an aggregation of various mixins from DRF, Django and Nautobot to acheive the desired behavior pattern for NautobotUIViewSet
365
+ NautobotViewSetMixin is an aggregation of various mixins from DRF, Django and Nautobot to achieve the desired behavior pattern for NautobotUIViewSet
224
366
  """
225
367
 
226
368
  renderer_classes = [NautobotHTMLRenderer]
@@ -244,14 +386,6 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
244
386
  # custom view attributes used for permission checks and handling
245
387
  custom_view_base_action = None
246
388
  custom_view_additional_permissions = None
247
- view_titles = None
248
- breadcrumbs = None
249
-
250
- def get_view_titles(self):
251
- return self.instantiate_if_needed(self.view_titles, Titles)
252
-
253
- def get_breadcrumbs(self):
254
- return self.instantiate_if_needed(self.breadcrumbs, Breadcrumbs)
255
389
 
256
390
  @staticmethod
257
391
  def instantiate_if_needed(attr, default_cls):
@@ -2,12 +2,16 @@ import datetime
2
2
  from io import BytesIO
3
3
  import urllib.parse
4
4
 
5
+ from django.conf import settings
5
6
  from django.contrib import messages
7
+ from django.core.cache import cache
6
8
  from django.core.exceptions import FieldError, ValidationError
7
9
  from django.db.models import ForeignKey
8
10
  from django.utils.html import format_html, format_html_join
9
11
  from django.utils.safestring import mark_safe
10
12
  from django_tables2 import RequestConfig
13
+ from prometheus_client import REGISTRY
14
+ from prometheus_client.utils import floatToGoString
11
15
  from rest_framework import exceptions, serializers
12
16
 
13
17
  from nautobot.core.api.fields import ChoiceField, ContentTypeField, TimeZoneSerializerField
@@ -20,6 +24,11 @@ from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
20
24
  from nautobot.extras.models import SavedView
21
25
  from nautobot.extras.tables import AssociatedContactsTable, DynamicGroupTable, ObjectMetadataTable
22
26
 
27
+ METRICS_CACHE_KEY = "nautobot_app_metrics_cache"
28
+ always_generated_metrics = [
29
+ "nautobot_app_metrics_processing_ms" # Always generate this metric to track the processing time of Nautobot App metrics, improved with caching.
30
+ ]
31
+
23
32
 
24
33
  def check_filter_for_display(filters, field_name, values):
25
34
  """
@@ -399,3 +408,111 @@ def get_saved_views_for_user(user, list_url):
399
408
  return shared_saved_views | user_owned_saved_views
400
409
 
401
410
  return shared_saved_views
411
+
412
+
413
+ def is_metrics_experimental_caching_enabled():
414
+ """Return True if METRICS_EXPERIMENTAL_CACHING_DURATION is set to a positive integer."""
415
+ return settings.METRICS_EXPERIMENTAL_CACHING_DURATION > 0
416
+
417
+
418
+ def generate_latest_with_cache(registry=REGISTRY):
419
+ """A vendored version of prometheus_client.generate_latest that caches Nautobot App metrics."""
420
+
421
+ def sample_line(line):
422
+ if line.labels:
423
+ labelstr = "{{{0}}}".format(
424
+ ",".join(
425
+ [
426
+ '{}="{}"'.format(k, v.replace("\\", r"\\").replace("\n", r"\n").replace('"', r"\""))
427
+ for k, v in sorted(line.labels.items())
428
+ ]
429
+ )
430
+ )
431
+ else:
432
+ labelstr = ""
433
+ timestamp = ""
434
+ if line.timestamp is not None:
435
+ # Convert to milliseconds.
436
+ timestamp = f" {int(float(line.timestamp) * 1000):d}"
437
+ return f"{line.name}{labelstr} {floatToGoString(line.value)}{timestamp}\n"
438
+
439
+ cached_lines = []
440
+ output = []
441
+ # NOTE: In the original prometheus_client code the lines are written to output directly,
442
+ # here we are going to cache some lines so we need to build each metric's output separately.
443
+ # So instead of `output.append(line)` we do
444
+ # `this_metric_output.append(line)` and then `output.extend(this_metric_output)`
445
+ for metric in registry.collect():
446
+ this_metric_output = []
447
+ try:
448
+ mname = metric.name
449
+ mtype = metric.type
450
+ # Munging from OpenMetrics into Prometheus format.
451
+ if mtype == "counter":
452
+ mname = mname + "_total"
453
+ elif mtype == "info":
454
+ mname = mname + "_info"
455
+ mtype = "gauge"
456
+ elif mtype == "stateset":
457
+ mtype = "gauge"
458
+ elif mtype == "gaugehistogram":
459
+ # A gauge histogram is really a gauge,
460
+ # but this captures the structure better.
461
+ mtype = "histogram"
462
+ elif mtype == "unknown":
463
+ mtype = "untyped"
464
+
465
+ this_metric_output.append(
466
+ "# HELP {} {}\n".format(mname, metric.documentation.replace("\\", r"\\").replace("\n", r"\n"))
467
+ )
468
+ this_metric_output.append(f"# TYPE {mname} {mtype}\n")
469
+
470
+ om_samples = {}
471
+ for s in metric.samples:
472
+ for suffix in ["_created", "_gsum", "_gcount"]:
473
+ if s.name == metric.name + suffix:
474
+ # OpenMetrics specific sample, put in a gauge at the end.
475
+ om_samples.setdefault(suffix, []).append(sample_line(s))
476
+ break
477
+ else:
478
+ this_metric_output.append(sample_line(s))
479
+ except Exception as exception:
480
+ exception.args = (exception.args or ("",)) + (metric,)
481
+ raise
482
+
483
+ for suffix, lines in sorted(om_samples.items()):
484
+ this_metric_output.append(
485
+ "# HELP {}{} {}\n".format(
486
+ metric.name, suffix, metric.documentation.replace("\\", r"\\").replace("\n", r"\n")
487
+ )
488
+ )
489
+ this_metric_output.append(f"# TYPE {metric.name}{suffix} gauge\n")
490
+ this_metric_output.extend(lines)
491
+
492
+ # BEGIN Nautobot-specific logic
493
+ # If the metric name starts with nautobot_, we cache the lines
494
+ if metric.name.startswith("nautobot_") and metric.name not in always_generated_metrics:
495
+ cached_lines.extend(this_metric_output)
496
+ # END Nautobot-specific logic
497
+
498
+ # Always add the metric output to the final output
499
+ output.extend(this_metric_output)
500
+
501
+ # BEGIN Nautobot-specific logic
502
+ # Add in any previously cached metrics.
503
+ # Note that this is mutually-exclusive with the above block, that is to say,
504
+ # either cached_lines will be populated OR collector.local_cache will be populated,
505
+ # never both at the same time.
506
+ for collector in registry._collector_to_names:
507
+ # This is to avoid a race condition where between the time to collect the metrics and
508
+ # the time to generate the output, the cache is expired and we miss some metrics.
509
+ if hasattr(collector, "local_cache") and collector.local_cache:
510
+ output.extend(collector.local_cache)
511
+ del collector.local_cache # avoid re-using stale data on next call
512
+
513
+ # If we have any cached lines, and the cache is empty or expired, update the cache.
514
+ if cached_lines and not cache.get(METRICS_CACHE_KEY):
515
+ cache.set(METRICS_CACHE_KEY, cached_lines, timeout=settings.METRICS_EXPERIMENTAL_CACHING_DURATION)
516
+ # END Nautobot-specific logic
517
+
518
+ return "".join(output).encode("utf-8")
@@ -0,0 +1,41 @@
1
+ # Generated by Django 4.2.23 on 2025-08-22 21:03
2
+
3
+ from decimal import Decimal
4
+
5
+ import django.core.validators
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+ dependencies = [
11
+ ("dcim", "0072_alter_powerfeed_options_and_more"),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AlterField(
16
+ model_name="powerport",
17
+ name="power_factor",
18
+ field=models.DecimalField(
19
+ decimal_places=2,
20
+ default=Decimal("0.95"),
21
+ max_digits=4,
22
+ validators=[
23
+ django.core.validators.MinValueValidator(Decimal("0.01")),
24
+ django.core.validators.MaxValueValidator(Decimal("1.00")),
25
+ ],
26
+ ),
27
+ ),
28
+ migrations.AlterField(
29
+ model_name="powerporttemplate",
30
+ name="power_factor",
31
+ field=models.DecimalField(
32
+ decimal_places=2,
33
+ default=Decimal("0.95"),
34
+ max_digits=4,
35
+ validators=[
36
+ django.core.validators.MinValueValidator(Decimal("0.01")),
37
+ django.core.validators.MaxValueValidator(Decimal("1.00")),
38
+ ],
39
+ ),
40
+ ),
41
+ ]
@@ -1,3 +1,5 @@
1
+ from decimal import Decimal
2
+
1
3
  from django.contrib.contenttypes.models import ContentType
2
4
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
3
5
  from django.core.validators import MaxValueValidator, MinValueValidator
@@ -252,8 +254,8 @@ class PowerPortTemplate(ModularComponentTemplateModel):
252
254
  power_factor = models.DecimalField(
253
255
  max_digits=4,
254
256
  decimal_places=2,
255
- default="0.95",
256
- validators=[MinValueValidator(0.01), MaxValueValidator(1.00)],
257
+ default=Decimal("0.95"),
258
+ validators=[MinValueValidator(Decimal("0.01")), MaxValueValidator(Decimal("1.00"))],
257
259
  help_text="Power factor (0.01-1.00) for converting between watts (W) and volt-amps (VA). Defaults to 0.95.",
258
260
  )
259
261
 
@@ -1,3 +1,4 @@
1
+ from decimal import Decimal
1
2
  import re
2
3
 
3
4
  from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
@@ -402,8 +403,8 @@ class PowerPort(ModularComponentModel, CableTermination, PathEndpoint):
402
403
  power_factor = models.DecimalField(
403
404
  max_digits=4,
404
405
  decimal_places=2,
405
- default="0.95",
406
- validators=[MinValueValidator(0.01), MaxValueValidator(1.00)],
406
+ default=Decimal("0.95"),
407
+ validators=[MinValueValidator(Decimal("0.01")), MaxValueValidator(Decimal("1.00"))],
407
408
  help_text="Power factor (0.01-1.00) for converting between watts (W) and volt-amps (VA). Defaults to 0.95.",
408
409
  )
409
410
 
@@ -940,6 +940,10 @@ class Device(PrimaryModel, ConfigContextModel):
940
940
  """
941
941
  return self.virtual_chassis.master if self.virtual_chassis else None
942
942
 
943
+ @property
944
+ def is_vc_master(self):
945
+ return self == self.get_vc_master()
946
+
943
947
  @property
944
948
  def vc_interfaces(self):
945
949
  """
@@ -39,6 +39,7 @@ from .devices import (
39
39
  RearPortTable,
40
40
  SoftwareImageFileTable,
41
41
  SoftwareVersionTable,
42
+ VirtualChassisMembersTable,
42
43
  VirtualChassisTable,
43
44
  VirtualDeviceContextTable,
44
45
  )
@@ -125,6 +126,7 @@ __all__ = (
125
126
  "RearPortTemplateTable",
126
127
  "SoftwareImageFileTable",
127
128
  "SoftwareVersionTable",
129
+ "VirtualChassisMembersTable",
128
130
  "VirtualChassisTable",
129
131
  "VirtualDeviceContextTable",
130
132
  )
@@ -154,6 +154,30 @@ class PlatformTable(BaseTable):
154
154
  #
155
155
 
156
156
 
157
+ class VirtualChassisMembersTable(BaseTable):
158
+ name = tables.TemplateColumn(order_by=("_name",), template_code=DEVICE_LINK, verbose_name="Device")
159
+ vc_position = tables.TemplateColumn(
160
+ verbose_name="Position", template_code='<span class="badge badge-default">{{ record.vc_position }}</span>'
161
+ )
162
+ master = BooleanColumn(accessor="is_vc_master", verbose_name="Master")
163
+ vc_priority = tables.Column(verbose_name="Priority")
164
+
165
+ class Meta(BaseTable.Meta):
166
+ model = Device
167
+ fields = (
168
+ "name",
169
+ "vc_position",
170
+ "master",
171
+ "vc_priority",
172
+ )
173
+ default_columns = (
174
+ "name",
175
+ "vc_position",
176
+ "master",
177
+ "vc_priority",
178
+ )
179
+
180
+
157
181
  class DeviceTable(StatusTableMixin, RoleTableMixin, BaseTable):
158
182
  pk = ToggleColumn()
159
183
  name = tables.TemplateColumn(order_by=("_name",), template_code=DEVICE_LINK)
@@ -76,8 +76,8 @@ class PowerFeedTable(StatusTableMixin, CableTerminationTable):
76
76
  rack = tables.Column(linkify=True)
77
77
  type = ChoiceFieldColumn()
78
78
  power_path = tables.Column()
79
- occupied_positions = tables.Column(accessor="occupied_positions", verbose_name="Position")
80
- phase_designation = tables.Column(accessor="phase_designation", verbose_name="Phase Designation")
79
+ occupied_positions = tables.Column(accessor="occupied_positions", verbose_name="Position", orderable=False)
80
+ phase_designation = tables.Column(accessor="phase_designation", verbose_name="Phase Designation", orderable=False)
81
81
  max_utilization = tables.TemplateColumn(template_code="{{ value }}%")
82
82
  available_power = tables.Column(verbose_name="Available power (VA)")
83
83
  tags = TagColumn(url_name="dcim:powerfeed_list")
@@ -2,20 +2,10 @@
2
2
  {% load helpers %}
3
3
  {% load static %}
4
4
 
5
- {% block breadcrumbs %}
6
- <li><a href="{% url 'dcim:device_list' %}">Devices</a></li>
7
- <li><a href="{% url 'dcim:device_list' %}?location={{ object.location.pk }}">{{ object.location }}</a></li>
8
- {% if object.parent_bay %}
9
- <li>{{ object.parent_bay.device|hyperlinked_object }}</li>
10
- <li>{{ object.parent_bay }}</li>
11
- {% endif %}
12
- <li>{{ object|hyperlinked_object }}</li>
13
- {% endblock breadcrumbs %}
14
-
15
5
  {% block masthead %}
16
6
  <span class="hover_copy">
17
7
  <h1>
18
- <span id="devicename">{% block title %}{{ object }}{% endblock title %}</span>
8
+ <span id="devicename">{% block title %}{{ block.super }}{% endblock title %}</span>
19
9
  <button class="btn btn-xs btn-default hover_copy_button" data-clipboard-text="{{ object }}">
20
10
  <span class="mdi mdi-content-copy"></span>
21
11
  </button>