nautobot 2.4.12__py3-none-any.whl → 2.4.14__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 (366) hide show
  1. nautobot/core/graphql/generators.py +8 -0
  2. nautobot/core/graphql/schema.py +30 -30
  3. nautobot/core/management/commands/migrate.py +90 -1
  4. nautobot/core/settings.yaml +3 -3
  5. nautobot/core/tables.py +4 -4
  6. nautobot/core/templates/inc/footer.html +4 -4
  7. nautobot/core/testing/api.py +7 -0
  8. nautobot/core/views/utils.py +1 -1
  9. nautobot/dcim/choices.py +2 -0
  10. nautobot/dcim/constants.py +0 -16
  11. nautobot/dcim/factory.py +1 -1
  12. nautobot/dcim/migrations/0071_alter_consoleport_options_and_more.py +42 -0
  13. nautobot/dcim/models/device_components.py +10 -2
  14. nautobot/dcim/models/devices.py +18 -0
  15. nautobot/dcim/templates/dcim/device.html +1 -34
  16. nautobot/dcim/templates/dcim/rack.html +2 -318
  17. nautobot/dcim/templates/dcim/rack_edit.html +2 -47
  18. nautobot/dcim/templates/dcim/rack_elevation_list.html +4 -1
  19. nautobot/dcim/templates/dcim/rack_retrieve.html +318 -0
  20. nautobot/dcim/templates/dcim/rack_update.html +47 -0
  21. nautobot/dcim/tests/test_models.py +1 -0
  22. nautobot/dcim/urls.py +3 -28
  23. nautobot/dcim/utils.py +4 -30
  24. nautobot/dcim/views.py +73 -71
  25. nautobot/extras/choices.py +12 -4
  26. nautobot/extras/filters/mixins.py +8 -6
  27. nautobot/extras/forms/forms.py +9 -0
  28. nautobot/extras/forms/mixins.py +4 -2
  29. nautobot/extras/migrations/0062_collect_roles_from_related_apps_roles.py +30 -7
  30. nautobot/extras/migrations/0124_add_joblogentry_index.py +16 -0
  31. nautobot/extras/models/customfields.py +52 -3
  32. nautobot/extras/models/jobs.py +6 -0
  33. nautobot/extras/models/relationships.py +55 -6
  34. nautobot/extras/templates/extras/graphqlquery.html +2 -97
  35. nautobot/extras/templates/extras/graphqlquery_list.html +1 -0
  36. nautobot/extras/templates/extras/graphqlquery_retrieve.html +97 -0
  37. nautobot/extras/templates/extras/secretsgroup.html +2 -29
  38. nautobot/extras/templates/extras/secretsgroup_edit.html +2 -82
  39. nautobot/extras/templates/extras/secretsgroup_retrieve.html +29 -0
  40. nautobot/extras/templates/extras/secretsgroup_update.html +82 -0
  41. nautobot/extras/tests/test_customfields.py +115 -7
  42. nautobot/extras/tests/test_relationships.py +7 -1
  43. nautobot/extras/tests/test_views.py +113 -1
  44. nautobot/extras/urls.py +2 -51
  45. nautobot/extras/utils.py +4 -1
  46. nautobot/extras/views.py +42 -135
  47. nautobot/ipam/api/views.py +69 -6
  48. nautobot/ipam/migrations/0052_alter_ipaddress_index_together_and_more.py +28 -0
  49. nautobot/ipam/models.py +13 -1
  50. nautobot/ipam/tests/test_api.py +351 -3
  51. nautobot/ipam/utils/testing.py +76 -29
  52. nautobot/project-static/docs/404.html +11 -34
  53. nautobot/project-static/docs/apps/index.html +11 -34
  54. nautobot/project-static/docs/apps/nautobot-apps.html +11 -34
  55. nautobot/project-static/docs/assets/javascripts/{bundle.56ea9cef.min.js → bundle.50899def.min.js} +2 -2
  56. nautobot/project-static/docs/assets/javascripts/{bundle.56ea9cef.min.js.map → bundle.50899def.min.js.map} +2 -2
  57. nautobot/project-static/docs/assets/stylesheets/{main.342714a4.min.css → main.7e37652d.min.css} +1 -1
  58. nautobot/project-static/docs/assets/stylesheets/{main.342714a4.min.css.map → main.7e37652d.min.css.map} +1 -1
  59. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +11 -34
  60. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +11 -34
  61. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +11 -34
  62. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +11 -34
  63. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +11 -34
  64. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +11 -34
  65. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +11 -34
  66. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +11 -34
  67. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +11 -34
  68. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +11 -34
  69. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +11 -34
  70. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +11 -34
  71. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +11 -34
  72. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +11 -34
  73. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +11 -34
  74. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +11 -34
  75. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +11 -34
  76. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +11 -34
  77. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +11 -34
  78. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +11 -34
  79. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +11 -34
  80. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +11 -34
  81. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +11 -34
  82. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +13 -34
  83. nautobot/project-static/docs/development/apps/api/configuration-view.html +11 -34
  84. nautobot/project-static/docs/development/apps/api/database-backend-config.html +11 -34
  85. nautobot/project-static/docs/development/apps/api/models/django-admin.html +11 -34
  86. nautobot/project-static/docs/development/apps/api/models/global-search.html +11 -34
  87. nautobot/project-static/docs/development/apps/api/models/graphql.html +11 -34
  88. nautobot/project-static/docs/development/apps/api/models/index.html +11 -34
  89. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +11 -34
  90. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +11 -34
  91. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +11 -34
  92. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +11 -34
  93. nautobot/project-static/docs/development/apps/api/platform-features/index.html +11 -34
  94. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +11 -34
  95. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +11 -34
  96. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +11 -34
  97. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +11 -34
  98. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +11 -34
  99. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +11 -34
  100. nautobot/project-static/docs/development/apps/api/prometheus.html +11 -34
  101. nautobot/project-static/docs/development/apps/api/setup.html +11 -34
  102. nautobot/project-static/docs/development/apps/api/testing.html +11 -34
  103. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +11 -34
  104. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +11 -34
  105. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +11 -34
  106. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +11 -34
  107. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +11 -34
  108. nautobot/project-static/docs/development/apps/api/views/base-template.html +11 -34
  109. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +11 -34
  110. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +11 -34
  111. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +11 -34
  112. nautobot/project-static/docs/development/apps/api/views/index.html +11 -34
  113. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +11 -34
  114. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +11 -34
  115. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +11 -34
  116. nautobot/project-static/docs/development/apps/api/views/notes.html +11 -34
  117. nautobot/project-static/docs/development/apps/api/views/rest-api.html +11 -34
  118. nautobot/project-static/docs/development/apps/api/views/urls.html +11 -34
  119. nautobot/project-static/docs/development/apps/index.html +11 -34
  120. nautobot/project-static/docs/development/apps/migration/code-updates.html +11 -34
  121. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +11 -34
  122. nautobot/project-static/docs/development/apps/migration/from-v1.html +11 -34
  123. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +11 -34
  124. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +11 -34
  125. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +11 -34
  126. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +11 -34
  127. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +11 -34
  128. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +11 -34
  129. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +11 -34
  130. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +11 -34
  131. nautobot/project-static/docs/development/apps/porting-from-netbox.html +11 -34
  132. nautobot/project-static/docs/development/core/application-registry.html +139 -133
  133. nautobot/project-static/docs/development/core/best-practices.html +11 -34
  134. nautobot/project-static/docs/development/core/bootstrap-ui.html +11 -34
  135. nautobot/project-static/docs/development/core/caching.html +11 -34
  136. nautobot/project-static/docs/development/core/controllers.html +11 -34
  137. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +11 -34
  138. nautobot/project-static/docs/development/core/generic-views.html +11 -34
  139. nautobot/project-static/docs/development/core/getting-started.html +11 -34
  140. nautobot/project-static/docs/development/core/homepage.html +11 -34
  141. nautobot/project-static/docs/development/core/index.html +11 -34
  142. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +11 -34
  143. nautobot/project-static/docs/development/core/model-checklist.html +11 -34
  144. nautobot/project-static/docs/development/core/model-features.html +11 -34
  145. nautobot/project-static/docs/development/core/natural-keys.html +11 -34
  146. nautobot/project-static/docs/development/core/navigation-menu.html +11 -34
  147. nautobot/project-static/docs/development/core/release-checklist.html +11 -34
  148. nautobot/project-static/docs/development/core/role-internals.html +11 -34
  149. nautobot/project-static/docs/development/core/settings.html +11 -34
  150. nautobot/project-static/docs/development/core/style-guide.html +11 -34
  151. nautobot/project-static/docs/development/core/templates.html +11 -34
  152. nautobot/project-static/docs/development/core/testing.html +11 -34
  153. nautobot/project-static/docs/development/core/ui-component-framework.html +11 -34
  154. nautobot/project-static/docs/development/core/user-preferences.html +11 -34
  155. nautobot/project-static/docs/development/index.html +11 -34
  156. nautobot/project-static/docs/development/jobs/getting-started.html +11 -34
  157. nautobot/project-static/docs/development/jobs/index.html +11 -34
  158. nautobot/project-static/docs/development/jobs/installation.html +11 -34
  159. nautobot/project-static/docs/development/jobs/job-extensions.html +11 -34
  160. nautobot/project-static/docs/development/jobs/job-logging.html +11 -34
  161. nautobot/project-static/docs/development/jobs/job-patterns.html +11 -34
  162. nautobot/project-static/docs/development/jobs/job-structure.html +11 -34
  163. nautobot/project-static/docs/development/jobs/migration/from-v1.html +11 -34
  164. nautobot/project-static/docs/development/jobs/testing.html +11 -34
  165. nautobot/project-static/docs/index.html +11 -34
  166. nautobot/project-static/docs/overview/application_stack.html +11 -34
  167. nautobot/project-static/docs/overview/design_philosophy.html +11 -34
  168. nautobot/project-static/docs/release-notes/index.html +11 -34
  169. nautobot/project-static/docs/release-notes/version-1.0.html +11 -34
  170. nautobot/project-static/docs/release-notes/version-1.1.html +11 -34
  171. nautobot/project-static/docs/release-notes/version-1.2.html +11 -34
  172. nautobot/project-static/docs/release-notes/version-1.3.html +11 -34
  173. nautobot/project-static/docs/release-notes/version-1.4.html +11 -34
  174. nautobot/project-static/docs/release-notes/version-1.5.html +11 -34
  175. nautobot/project-static/docs/release-notes/version-1.6.html +11 -34
  176. nautobot/project-static/docs/release-notes/version-2.0.html +11 -34
  177. nautobot/project-static/docs/release-notes/version-2.1.html +11 -34
  178. nautobot/project-static/docs/release-notes/version-2.2.html +11 -34
  179. nautobot/project-static/docs/release-notes/version-2.3.html +11 -34
  180. nautobot/project-static/docs/release-notes/version-2.4.html +271 -34
  181. nautobot/project-static/docs/requirements.txt +1 -1
  182. nautobot/project-static/docs/search/search_index.json +1 -1
  183. nautobot/project-static/docs/sitemap.xml +299 -299
  184. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  185. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +11 -34
  186. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +11 -34
  187. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +11 -34
  188. nautobot/project-static/docs/user-guide/administration/configuration/index.html +11 -34
  189. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +11 -34
  190. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +14 -37
  191. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +11 -34
  192. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +11 -34
  193. nautobot/project-static/docs/user-guide/administration/guides/docker.html +11 -34
  194. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +11 -34
  195. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +11 -34
  196. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +11 -34
  197. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +11 -34
  198. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +11 -34
  199. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +11 -34
  200. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +11 -34
  201. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +11 -34
  202. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +11 -34
  203. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +11 -34
  204. nautobot/project-static/docs/user-guide/administration/installation/index.html +11 -34
  205. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +11 -34
  206. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +11 -34
  207. nautobot/project-static/docs/user-guide/administration/installation/services.html +11 -34
  208. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +11 -34
  209. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +11 -34
  210. nautobot/project-static/docs/user-guide/administration/security/index.html +11 -34
  211. nautobot/project-static/docs/user-guide/administration/security/notices.html +11 -34
  212. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +19 -39
  213. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +11 -34
  214. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +11 -34
  215. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +11 -34
  216. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +11 -34
  217. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +11 -34
  218. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +11 -34
  219. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +11 -34
  220. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +11 -34
  221. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +11 -34
  222. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +11 -34
  223. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +11 -34
  224. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +11 -34
  225. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +11 -34
  226. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +11 -34
  227. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +11 -34
  228. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +11 -34
  229. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +11 -34
  230. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +11 -34
  231. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +11 -34
  232. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +11 -34
  233. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +11 -34
  234. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +11 -34
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +11 -34
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +11 -34
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +11 -34
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +11 -34
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +11 -34
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +11 -34
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +11 -34
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +11 -34
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +11 -34
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +11 -34
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +11 -34
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +11 -34
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +11 -34
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +11 -34
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +11 -34
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +14 -37
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +11 -34
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +11 -34
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +19 -52
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +11 -34
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +11 -34
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +11 -34
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +11 -34
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +14 -37
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +11 -34
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +11 -34
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +11 -34
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +11 -34
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +11 -34
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +11 -34
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +11 -34
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +11 -34
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +11 -34
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +11 -34
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +11 -34
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +11 -34
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +11 -34
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +11 -34
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +11 -34
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +11 -34
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +11 -34
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +11 -34
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +11 -34
  278. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +11 -34
  279. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +11 -34
  280. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +11 -34
  281. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +11 -34
  282. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +11 -34
  283. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +11 -34
  284. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +11 -34
  285. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +11 -34
  286. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +11 -34
  287. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +11 -34
  288. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +11 -34
  289. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +11 -34
  290. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +11 -34
  291. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +11 -34
  292. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +11 -34
  293. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +11 -34
  294. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +11 -34
  295. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +11 -34
  296. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +11 -34
  297. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +11 -34
  298. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +11 -34
  299. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +11 -34
  300. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +11 -34
  301. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +11 -34
  302. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +11 -34
  303. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -34
  304. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +11 -34
  305. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +11 -34
  306. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +11 -34
  307. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +11 -34
  308. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +11 -34
  309. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +11 -34
  310. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +11 -34
  311. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +11 -34
  312. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +11 -34
  313. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +11 -34
  314. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +11 -34
  315. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +11 -34
  316. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +11 -34
  317. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +11 -34
  318. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +11 -34
  319. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +11 -34
  320. nautobot/project-static/docs/user-guide/index.html +11 -34
  321. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +11 -34
  322. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +11 -34
  323. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +11 -34
  324. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +11 -34
  325. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +11 -34
  326. nautobot/project-static/docs/user-guide/platform-functionality/events.html +11 -34
  327. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +11 -34
  328. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +11 -34
  329. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +11 -34
  330. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +11 -34
  331. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +11 -34
  332. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +11 -34
  333. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +11 -34
  334. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +11 -34
  335. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +11 -34
  336. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +11 -34
  337. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +11 -34
  338. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +11 -34
  339. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +11 -34
  340. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +11 -34
  341. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +11 -34
  342. nautobot/project-static/docs/user-guide/platform-functionality/note.html +11 -34
  343. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +11 -34
  344. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +11 -34
  345. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +11 -34
  346. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +11 -34
  347. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +11 -34
  348. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +11 -34
  349. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +11 -34
  350. nautobot/project-static/docs/user-guide/platform-functionality/role.html +11 -34
  351. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +11 -34
  352. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +11 -34
  353. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +11 -34
  354. nautobot/project-static/docs/user-guide/platform-functionality/status.html +11 -34
  355. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +11 -34
  356. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +11 -34
  357. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +11 -34
  358. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +11 -34
  359. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +11 -34
  360. nautobot/tenancy/api/views.py +2 -1
  361. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/METADATA +5 -5
  362. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/RECORD +366 -357
  363. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/LICENSE.txt +0 -0
  364. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/NOTICE +0 -0
  365. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/WHEEL +0 -0
  366. {nautobot-2.4.12.dist-info → nautobot-2.4.14.dist-info}/entry_points.txt +0 -0
nautobot/dcim/views.py CHANGED
@@ -58,7 +58,9 @@ from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
58
58
  from nautobot.core.views.viewsets import NautobotUIViewSet
59
59
  from nautobot.dcim.choices import LocationDataToContactActionChoices
60
60
  from nautobot.dcim.forms import LocationMigrateDataToContactForm
61
+ from nautobot.dcim.utils import get_all_network_driver_mappings
61
62
  from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Team
63
+ from nautobot.extras.tables import DynamicGroupTable
62
64
  from nautobot.extras.views import ObjectChangeLogView, ObjectConfigContextView, ObjectDynamicGroupsView
63
65
  from nautobot.ipam.models import IPAddress, Prefix, Service, VLAN
64
66
  from nautobot.ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable, VRFDeviceAssignmentTable, VRFTable
@@ -523,11 +525,41 @@ class RackGroupUIViewSet(NautobotUIViewSet):
523
525
  #
524
526
 
525
527
 
526
- class RackListView(generic.ObjectListView):
527
- queryset = Rack.objects.all()
528
- filterset = filters.RackFilterSet
529
- filterset_form = forms.RackFilterForm
530
- table = tables.RackDetailTable
528
+ class RackUIViewSet(NautobotUIViewSet):
529
+ bulk_update_form_class = forms.RackBulkEditForm
530
+ filterset_class = filters.RackFilterSet
531
+ filterset_form_class = forms.RackFilterForm
532
+ form_class = forms.RackForm
533
+ serializer_class = serializers.RackSerializer
534
+ table_class = tables.RackDetailTable
535
+ queryset = Rack.objects.select_related("location", "tenant__tenant_group", "rack_group", "role")
536
+
537
+ def get_extra_context(self, request, instance):
538
+ context = super().get_extra_context(request, instance)
539
+
540
+ if self.action == "retrieve":
541
+ # Get 0U and child devices located within the rack
542
+ context["nonracked_devices"] = Device.objects.filter(rack=instance, position__isnull=True).select_related(
543
+ "device_type__manufacturer"
544
+ )
545
+
546
+ peer_racks = Rack.objects.restrict(request.user, "view").filter(location=instance.location)
547
+
548
+ if instance.rack_group:
549
+ peer_racks = peer_racks.filter(rack_group=instance.rack_group)
550
+ else:
551
+ peer_racks = peer_racks.filter(rack_group__isnull=True)
552
+
553
+ context["next_rack"] = peer_racks.filter(name__gt=instance.name).order_by("name").first()
554
+ context["prev_rack"] = peer_racks.filter(name__lt=instance.name).order_by("-name").first()
555
+
556
+ context["reservations"] = RackReservation.objects.restrict(request.user, "view").filter(rack=instance)
557
+ context["power_feeds"] = (
558
+ PowerFeed.objects.restrict(request.user, "view").filter(rack=instance).select_related("power_panel")
559
+ )
560
+ context["device_count"] = Device.objects.restrict(request.user, "view").filter(rack=instance).count()
561
+
562
+ return context
531
563
 
532
564
 
533
565
  class RackElevationListView(generic.ObjectListView):
@@ -583,70 +615,6 @@ class RackElevationListView(generic.ObjectListView):
583
615
  }
584
616
 
585
617
 
586
- class RackView(generic.ObjectView):
587
- queryset = Rack.objects.select_related("location", "tenant__tenant_group", "rack_group", "role")
588
-
589
- def get_extra_context(self, request, instance):
590
- # Get 0U and child devices located within the rack
591
- nonracked_devices = Device.objects.filter(rack=instance, position__isnull=True).select_related(
592
- "device_type__manufacturer"
593
- )
594
-
595
- peer_racks = Rack.objects.restrict(request.user, "view").filter(location=instance.location)
596
-
597
- if instance.rack_group:
598
- peer_racks = peer_racks.filter(rack_group=instance.rack_group)
599
- else:
600
- peer_racks = peer_racks.filter(rack_group__isnull=True)
601
- next_rack = peer_racks.filter(name__gt=instance.name).order_by("name").first()
602
- prev_rack = peer_racks.filter(name__lt=instance.name).order_by("-name").first()
603
-
604
- reservations = RackReservation.objects.restrict(request.user, "view").filter(rack=instance)
605
- power_feeds = (
606
- PowerFeed.objects.restrict(request.user, "view").filter(rack=instance).select_related("power_panel")
607
- )
608
-
609
- device_count = Device.objects.restrict(request.user, "view").filter(rack=instance).count()
610
-
611
- return {
612
- "device_count": device_count,
613
- "reservations": reservations,
614
- "power_feeds": power_feeds,
615
- "nonracked_devices": nonracked_devices,
616
- "next_rack": next_rack,
617
- "prev_rack": prev_rack,
618
- **super().get_extra_context(request, instance),
619
- }
620
-
621
-
622
- class RackEditView(generic.ObjectEditView):
623
- queryset = Rack.objects.all()
624
- model_form = forms.RackForm
625
- template_name = "dcim/rack_edit.html"
626
-
627
-
628
- class RackDeleteView(generic.ObjectDeleteView):
629
- queryset = Rack.objects.all()
630
-
631
-
632
- class RackBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
633
- queryset = Rack.objects.all()
634
- table = tables.RackTable
635
-
636
-
637
- class RackBulkEditView(generic.BulkEditView):
638
- queryset = Rack.objects.all()
639
- filterset = filters.RackFilterSet
640
- table = tables.RackTable
641
- form = forms.RackBulkEditForm
642
-
643
-
644
- class RackBulkDeleteView(generic.BulkDeleteView):
645
- queryset = Rack.objects.all()
646
- filterset = filters.RackFilterSet
647
- table = tables.RackTable
648
-
649
-
650
618
  #
651
619
  # Rack reservations
652
620
  #
@@ -1665,6 +1633,8 @@ class PlatformUIViewSet(NautobotUIViewSet):
1665
1633
  context = super().get_extra_context(request, instance)
1666
1634
  if self.action == "retrieve":
1667
1635
  context["network_driver_tool_names"] = instance.fetch_network_driver_mappings()
1636
+ if self.action in ["create", "update"]:
1637
+ context["network_driver_names"] = sorted(get_all_network_driver_mappings().keys())
1668
1638
  return context
1669
1639
 
1670
1640
 
@@ -1701,7 +1671,39 @@ class DeviceView(generic.ObjectView):
1701
1671
  "tenant__tenant_group",
1702
1672
  ).prefetch_related("images", "software_image_files")
1703
1673
 
1704
- object_detail_content = object_detail.ObjectDetailContent(
1674
+ class DeviceDetailContent(object_detail.ObjectDetailContent):
1675
+ """
1676
+ Override base ObjectDetailContent to render dynamic-groups table as a separate view/tab instead of inline.
1677
+ """
1678
+
1679
+ def __init__(self, **kwargs):
1680
+ super().__init__(**kwargs)
1681
+ # Remove inline tab definition
1682
+ for tab in list(self._tabs):
1683
+ if isinstance(tab, object_detail._ObjectDetailGroupsTab):
1684
+ self._tabs.remove(tab)
1685
+ # Add distinct-view tab definition
1686
+ self._tabs.append(
1687
+ object_detail.DistinctViewTab(
1688
+ weight=object_detail.Tab.WEIGHT_GROUPS_TAB,
1689
+ tab_id="dynamic_groups",
1690
+ label="Dynamic Groups",
1691
+ url_name="dcim:device_dynamicgroups",
1692
+ related_object_attribute="dynamic_groups",
1693
+ panels=(
1694
+ object_detail.ObjectsTablePanel(
1695
+ weight=100,
1696
+ table_class=DynamicGroupTable,
1697
+ table_attribute="dynamic_groups",
1698
+ exclude_columns=["content_type"],
1699
+ add_button_route=None,
1700
+ related_field_name="member_id",
1701
+ ),
1702
+ ),
1703
+ )
1704
+ )
1705
+
1706
+ object_detail_content = DeviceDetailContent(
1705
1707
  extra_buttons=(
1706
1708
  object_detail.DropdownButton(
1707
1709
  weight=100,
@@ -2188,7 +2190,7 @@ class DeviceChangeLogView(ObjectChangeLogView):
2188
2190
  base_template = "dcim/device/base.html"
2189
2191
 
2190
2192
 
2191
- class DeviceDynamicGroupsView(ObjectDynamicGroupsView): # 3.0 TODO: remove, deprecated in 2.3
2193
+ class DeviceDynamicGroupsView(ObjectDynamicGroupsView):
2192
2194
  base_template = "dcim/device/base.html"
2193
2195
 
2194
2196
 
@@ -483,22 +483,30 @@ class SecretsGroupAccessTypeChoices(ChoiceSet):
483
483
 
484
484
 
485
485
  class SecretsGroupSecretTypeChoices(ChoiceSet):
486
+ TYPE_AUTHKEY = "authentication-key"
487
+ TYPE_AUTHPROTOCOL = "authentication-protocol"
486
488
  TYPE_KEY = "key"
489
+ TYPE_NOTES = "notes"
487
490
  TYPE_PASSWORD = "password" # noqa: S105 # hardcoded-password-string -- false positive
491
+ TYPE_PRIVALGORITHM = "private-algorithm"
492
+ TYPE_PRIVKEY = "private-key"
488
493
  TYPE_SECRET = "secret" # noqa: S105 # hardcoded-password-string -- false positive
489
494
  TYPE_TOKEN = "token" # noqa: S105 # hardcoded-password-string -- false positive
490
- TYPE_USERNAME = "username"
491
495
  TYPE_URL = "url"
492
- TYPE_NOTES = "notes"
496
+ TYPE_USERNAME = "username"
493
497
 
494
498
  CHOICES = (
499
+ (TYPE_AUTHKEY, "Authentication Key"),
500
+ (TYPE_AUTHPROTOCOL, "Authentication Protocol"),
495
501
  (TYPE_KEY, "Key"),
502
+ (TYPE_NOTES, "Notes"),
496
503
  (TYPE_PASSWORD, "Password"),
504
+ (TYPE_PRIVALGORITHM, "Private Algorithm"),
505
+ (TYPE_PRIVKEY, "Private Key"),
497
506
  (TYPE_SECRET, "Secret"),
498
507
  (TYPE_TOKEN, "Token"),
499
- (TYPE_USERNAME, "Username"),
500
508
  (TYPE_URL, "URL"),
501
- (TYPE_NOTES, "Notes"),
509
+ (TYPE_USERNAME, "Username"),
502
510
  )
503
511
 
504
512
 
@@ -74,7 +74,9 @@ class CustomFieldModelFilterSetMixin(django_filters.FilterSet):
74
74
  CustomFieldTypeChoices.TYPE_SELECT: CustomFieldSelectFilter,
75
75
  }
76
76
 
77
- custom_fields = CustomField.objects.get_for_model(self._meta.model, exclude_filter_disabled=True)
77
+ custom_fields = CustomField.objects.get_for_model(
78
+ self._meta.model, exclude_filter_disabled=True, get_queryset=False
79
+ )
78
80
  for cf in custom_fields:
79
81
  # Determine filter class for this CustomField type, default to CustomFieldCharFilter
80
82
  new_filter_name = cf.add_prefix_to_cf_key()
@@ -220,13 +222,13 @@ class RelationshipModelFilterSetMixin(django_filters.FilterSet):
220
222
  """
221
223
  Append form fields for all Relationships assigned to this model.
222
224
  """
223
- src_relationships, dst_relationships = Relationship.objects.get_for_model(model=model, hidden=False)
225
+ src_relationships, dst_relationships = Relationship.objects.get_for_model(
226
+ model=model, hidden=False, get_queryset=False
227
+ )
224
228
 
225
- for rel in src_relationships:
226
- self._append_relationships_side([rel], RelationshipSideChoices.SIDE_SOURCE, model)
229
+ self._append_relationships_side(src_relationships, RelationshipSideChoices.SIDE_SOURCE, model)
227
230
 
228
- for rel in dst_relationships:
229
- self._append_relationships_side([rel], RelationshipSideChoices.SIDE_DESTINATION, model)
231
+ self._append_relationships_side(dst_relationships, RelationshipSideChoices.SIDE_DESTINATION, model)
230
232
 
231
233
  def _append_relationships_side(self, relationships, initial_side, model):
232
234
  """
@@ -197,6 +197,7 @@ __all__ = (
197
197
  "SecretFilterForm",
198
198
  "SecretForm",
199
199
  "SecretsGroupAssociationFormSet",
200
+ "SecretsGroupBulkEditForm",
200
201
  "SecretsGroupFilterForm",
201
202
  "SecretsGroupForm",
202
203
  "StaticGroupAssociationFilterForm",
@@ -2082,6 +2083,14 @@ class RoleFilterForm(NautobotFilterForm):
2082
2083
  #
2083
2084
 
2084
2085
 
2086
+ class SecretsGroupBulkEditForm(NautobotBulkEditForm):
2087
+ pk = forms.ModelMultipleChoiceField(queryset=SecretsGroup.objects.all(), widget=forms.MultipleHiddenInput())
2088
+ description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
2089
+
2090
+ class Meta:
2091
+ model = SecretsGroup
2092
+
2093
+
2085
2094
  def provider_choices():
2086
2095
  return sorted([(slug, provider.name) for slug, provider in registry["secrets_providers"].items()])
2087
2096
 
@@ -95,7 +95,7 @@ class CustomFieldModelFilterFormMixin(forms.Form):
95
95
  def __init__(self, *args, **kwargs):
96
96
  super().__init__(*args, **kwargs)
97
97
 
98
- custom_fields = CustomField.objects.get_for_model(self.model, exclude_filter_disabled=True)
98
+ custom_fields = CustomField.objects.get_for_model(self.model, exclude_filter_disabled=True, get_queryset=False)
99
99
  self.custom_fields = []
100
100
  for cf in custom_fields:
101
101
  field_name = cf.add_prefix_to_cf_key()
@@ -714,7 +714,9 @@ class RelationshipModelFilterFormMixin(forms.Form):
714
714
  """
715
715
  Append form fields for all Relationships assigned to this model.
716
716
  """
717
- src_relationships, dst_relationships = Relationship.objects.get_for_model(model=self.model, hidden=False)
717
+ src_relationships, dst_relationships = Relationship.objects.get_for_model(
718
+ model=self.model, hidden=False, get_queryset=False
719
+ )
718
720
 
719
721
  for rel in src_relationships:
720
722
  self._append_relationships_side([rel], RelationshipSideChoices.SIDE_SOURCE)
@@ -1,7 +1,6 @@
1
1
  # Generated by Django 3.2.16 on 2022-11-19 23:15
2
2
 
3
3
  from collections import namedtuple
4
- import logging
5
4
 
6
5
  from django.db import migrations, models
7
6
 
@@ -24,8 +23,6 @@ color_map = {
24
23
  "success": ColorChoices.COLOR_GREEN,
25
24
  }
26
25
 
27
- logger = logging.getLogger(__name__)
28
-
29
26
 
30
27
  def create_equivalent_roles_of_virtualmachine_device_role(apps):
31
28
  """Create equivalent roles for the VirtualMachine DeviceRole."""
@@ -82,17 +79,43 @@ def create_roles(apps, roles_to_create, content_types):
82
79
  "description": old_role.description,
83
80
  "color": getattr(old_role, "color", color_map["default"]),
84
81
  "weight": getattr(old_role, "weight", None),
82
+ "_custom_field_data": getattr(old_role, "_custom_field_data", {}),
85
83
  },
86
84
  )
87
85
  if created:
88
- logger.info(f'Created Role "{new_role.name}"')
86
+ print(f" Created Role {new_role.name!r}")
87
+ else:
88
+ updated = False
89
+ # Fix up in the case of overlapping roles
90
+ if new_role.weight is None and hasattr(old_role, "weight"):
91
+ new_role.weight = old_role.weight
92
+ updated = True
93
+ if old_role.description and not new_role.description:
94
+ new_role.description = old_role.description
95
+ updated = True
96
+ if hasattr(old_role, "_custom_field_data"):
97
+ for field, value in old_role._custom_field_data.items():
98
+ if field in new_role._custom_field_data:
99
+ if new_role._custom_field_data[field] != value:
100
+ print(
101
+ f" Value conflict for custom field {field!r} for role {new_role.name!r} - DATA LOSS"
102
+ )
103
+ else:
104
+ new_role._custom_field_data[field] = value
105
+ updated = True
106
+ if updated:
107
+ print(f" Updated Role {new_role.name!r}")
108
+ new_role.save()
109
+ else:
110
+ print(f" No changes to already-created Role {new_role.name!r}")
111
+
89
112
  if old_role_ct and hasattr(old_role, "pk"):
90
113
  # Move over existing object change records to the new role we created
91
114
  updated_count = ObjectChange.objects.filter(
92
115
  changed_object_type=old_role_ct, changed_object_id=old_role.pk
93
116
  ).update(changed_object_type=new_role_ct, changed_object_id=new_role.pk)
94
- logger.info(
95
- f'Updated {updated_count} ObjectChanges from the existing "{old_role.name}" {old_role._meta.label}'
117
+ print(
118
+ f" Updated {updated_count} ObjectChanges from the existing {old_role.name!r} {old_role._meta.label}"
96
119
  )
97
120
 
98
121
  # This is for all of the change records for roles which no longer exist
@@ -100,7 +123,7 @@ def create_roles(apps, roles_to_create, content_types):
100
123
  updated_count = ObjectChange.objects.filter(changed_object_type=old_role_ct).update(
101
124
  changed_object_type=new_role_ct
102
125
  )
103
- logger.info(f"Updated {updated_count} leftover ObjectChanges from deleted {old_role_ct.model} records.")
126
+ print(f" Updated {updated_count} leftover ObjectChanges from deleted {old_role_ct.model} records.")
104
127
 
105
128
  roles = Role.objects.filter(name__in=[roles.name for roles in roles_to_create])
106
129
 
@@ -0,0 +1,16 @@
1
+ # Generated by Django 4.2.23 on 2025-07-31 23:49
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("extras", "0123_alter_joblogentry_created"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AddIndex(
13
+ model_name="joblogentry",
14
+ index=models.Index(fields=["job_result", "created"], name="extras_joblog_jr_created_idx"),
15
+ ),
16
+ ]
@@ -1,4 +1,4 @@
1
- from collections import OrderedDict
1
+ from collections import defaultdict, OrderedDict
2
2
  from datetime import date, datetime
3
3
  import json
4
4
  import logging
@@ -48,21 +48,42 @@ logger = logging.getLogger(__name__)
48
48
  class ComputedFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
49
49
  use_in_migrations = True
50
50
 
51
- def get_for_model(self, model):
51
+ def get_for_model(self, model, get_queryset=True):
52
52
  """
53
53
  Return all ComputedFields assigned to the given model.
54
+
55
+ Returns a queryset by default, or a list if `get_queryset` param is False.
54
56
  """
55
57
  concrete_model = model._meta.concrete_model
56
58
  cache_key = f"{self.get_for_model.cache_key_prefix}.{concrete_model._meta.label_lower}"
59
+ list_cache_key = f"{cache_key}.list"
60
+ if not get_queryset:
61
+ listing = cache.get(list_cache_key)
62
+ if listing is not None:
63
+ return listing
57
64
  queryset = cache.get(cache_key)
58
65
  if queryset is None:
59
66
  content_type = ContentType.objects.get_for_model(concrete_model)
60
67
  queryset = self.get_queryset().filter(content_type=content_type)
61
68
  cache.set(cache_key, queryset)
69
+ if not get_queryset:
70
+ listing = list(queryset)
71
+ cache.set(list_cache_key, listing)
72
+ return listing
62
73
  return queryset
63
74
 
64
75
  get_for_model.cache_key_prefix = "nautobot.extras.computedfield.get_for_model"
65
76
 
77
+ def populate_list_caches(self):
78
+ """Populate all caches for `get_for_model(..., get_queryset=False)` lookups."""
79
+ queryset = self.all().select_related("content_type")
80
+ listings = defaultdict(list)
81
+ for cf in queryset:
82
+ listings[f"{cf.content_type.app_label}.{cf.content_type.model}"].append(cf)
83
+ for ct in ContentType.objects.all():
84
+ label = f"{ct.app_label}.{ct.model}"
85
+ cache.set(f"{self.get_for_model.cache_key_prefix}.{label}.list", listings[label])
86
+
66
87
 
67
88
  @extras_features("graphql")
68
89
  class ComputedField(
@@ -370,18 +391,24 @@ class CustomFieldModel(models.Model):
370
391
  class CustomFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
371
392
  use_in_migrations = True
372
393
 
373
- def get_for_model(self, model, exclude_filter_disabled=False):
394
+ def get_for_model(self, model, exclude_filter_disabled=False, get_queryset=True):
374
395
  """
375
396
  Return (and cache) all CustomFields assigned to the given model.
376
397
 
377
398
  Args:
378
399
  model (Model): The django model to which custom fields are registered
379
400
  exclude_filter_disabled (bool): Exclude any custom fields which have filter logic disabled
401
+ get_queryset (bool): Whether to return a QuerySet or a list.
380
402
  """
381
403
  concrete_model = model._meta.concrete_model
382
404
  cache_key = (
383
405
  f"{self.get_for_model.cache_key_prefix}.{concrete_model._meta.label_lower}.{exclude_filter_disabled}"
384
406
  )
407
+ list_cache_key = f"{cache_key}.list"
408
+ if not get_queryset:
409
+ listing = cache.get(list_cache_key)
410
+ if listing is not None:
411
+ return listing
385
412
  queryset = cache.get(cache_key)
386
413
  if queryset is None:
387
414
  content_type = ContentType.objects.get_for_model(concrete_model)
@@ -389,6 +416,10 @@ class CustomFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
389
416
  if exclude_filter_disabled:
390
417
  queryset = queryset.exclude(filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED)
391
418
  cache.set(cache_key, queryset)
419
+ if not get_queryset:
420
+ listing = list(queryset)
421
+ cache.set(list_cache_key, listing)
422
+ return listing
392
423
  return queryset
393
424
 
394
425
  get_for_model.cache_key_prefix = "nautobot.extras.customfield.get_for_model"
@@ -405,6 +436,24 @@ class CustomFieldManager(BaseManager.from_queryset(RestrictedQuerySet)):
405
436
 
406
437
  keys_for_model.cache_key_prefix = "nautobot.extras.customfield.keys_for_model"
407
438
 
439
+ def populate_list_caches(self):
440
+ """Populate all caches for `get_for_model(..., get_queryset=False)` and `keys_for_model` lookups."""
441
+ queryset = self.all().prefetch_related("content_types")
442
+ cf_listings = defaultdict(lambda: defaultdict(list))
443
+ key_listings = defaultdict(list)
444
+ for cf in queryset:
445
+ for ct in cf.content_types.all():
446
+ label = f"{ct.app_label}.{ct.model}"
447
+ cf_listings[label][False].append(cf)
448
+ if cf.filter_logic != CustomFieldFilterLogicChoices.FILTER_DISABLED:
449
+ cf_listings[label][True].append(cf)
450
+ key_listings[label].append(cf.key)
451
+ for ct in ContentType.objects.all():
452
+ label = f"{ct.app_label}.{ct.model}"
453
+ cache.set(f"{self.get_for_model.cache_key_prefix}.{label}.True.list", cf_listings[label][True])
454
+ cache.set(f"{self.get_for_model.cache_key_prefix}.{label}.False.list", cf_listings[label][False])
455
+ cache.set(f"{self.keys_for_model.cache_key_prefix}.{label}", key_listings[label])
456
+
408
457
 
409
458
  @extras_features("webhooks")
410
459
  class CustomField(
@@ -543,6 +543,12 @@ class JobLogEntry(BaseModel):
543
543
  ordering = ["created"]
544
544
  get_latest_by = "created"
545
545
  verbose_name_plural = "job log entries"
546
+ indexes = [
547
+ models.Index(
548
+ name="extras_joblog_jr_created_idx",
549
+ fields=["job_result", "created"],
550
+ )
551
+ ]
546
552
 
547
553
 
548
554
  #
@@ -1,3 +1,4 @@
1
+ from collections import defaultdict
1
2
  import logging
2
3
 
3
4
  from django import forms
@@ -422,31 +423,38 @@ class RelationshipModel(models.Model):
422
423
  class RelationshipManager(BaseManager.from_queryset(RestrictedQuerySet)):
423
424
  use_in_migrations = True
424
425
 
425
- def get_for_model(self, model, hidden=None):
426
+ def get_for_model(self, model, hidden=None, get_queryset=True):
426
427
  """
427
428
  Return all Relationships assigned to the given model.
428
429
 
429
430
  Args:
430
431
  model (Model): The django model to which relationships are registered
431
432
  hidden (bool): Filter based on the value of the hidden flag, or None to not apply this filter
433
+ get_queryset (bool): Whether to return querysets or object lists.
432
434
 
433
- Returns a tuple of source and destination scoped relationship querysets.
435
+ Returns a tuple of source and destination scoped relationship record querysets/lists.
434
436
  """
435
437
  return (
436
- self.get_for_model_source(model, hidden=hidden),
437
- self.get_for_model_destination(model, hidden=hidden),
438
+ self.get_for_model_source(model, hidden=hidden, get_queryset=get_queryset),
439
+ self.get_for_model_destination(model, hidden=hidden, get_queryset=get_queryset),
438
440
  )
439
441
 
440
- def get_for_model_source(self, model, hidden=None):
442
+ def get_for_model_source(self, model, hidden=None, get_queryset=True):
441
443
  """
442
444
  Return all Relationships assigned to the given model for the source side only.
443
445
 
444
446
  Args:
445
447
  model (Model): The django model to which relationships are registered
446
448
  hidden (bool): Filter based on the value of the hidden flag, or None to not apply this filter
449
+ get_queryset (bool): Whether to return a queryset or an object list.
447
450
  """
448
451
  concrete_model = model._meta.concrete_model
449
452
  cache_key = f"{self.get_for_model_source.cache_key_prefix}.{concrete_model._meta.label_lower}.{hidden}"
453
+ list_cache_key = f"{cache_key}.list"
454
+ if not get_queryset:
455
+ listing = cache.get(list_cache_key)
456
+ if listing is not None:
457
+ return listing
450
458
  queryset = cache.get(cache_key)
451
459
  if queryset is None:
452
460
  content_type = ContentType.objects.get_for_model(concrete_model)
@@ -456,20 +464,30 @@ class RelationshipManager(BaseManager.from_queryset(RestrictedQuerySet)):
456
464
  if hidden is not None:
457
465
  queryset = queryset.filter(source_hidden=hidden)
458
466
  cache.set(cache_key, queryset)
467
+ if not get_queryset:
468
+ listing = list(queryset)
469
+ cache.set(list_cache_key, listing)
470
+ return listing
459
471
  return queryset
460
472
 
461
473
  get_for_model_source.cache_key_prefix = "nautobot.extras.relationship.get_for_model_source"
462
474
 
463
- def get_for_model_destination(self, model, hidden=None):
475
+ def get_for_model_destination(self, model, hidden=None, get_queryset=True):
464
476
  """
465
477
  Return all Relationships assigned to the given model for the destination side only.
466
478
 
467
479
  Args:
468
480
  model (Model): The django model to which relationships are registered
469
481
  hidden (bool): Filter based on the value of the hidden flag, or None to not apply this filter
482
+ get_queryset (bool): Whether to return a queryset or an object list.
470
483
  """
471
484
  concrete_model = model._meta.concrete_model
472
485
  cache_key = f"{self.get_for_model_destination.cache_key_prefix}.{concrete_model._meta.label_lower}.{hidden}"
486
+ list_cache_key = f"{cache_key}.list"
487
+ if not get_queryset:
488
+ listing = cache.get(list_cache_key)
489
+ if listing is not None:
490
+ return listing
473
491
  queryset = cache.get(cache_key)
474
492
  if queryset is None:
475
493
  content_type = ContentType.objects.get_for_model(concrete_model)
@@ -481,6 +499,10 @@ class RelationshipManager(BaseManager.from_queryset(RestrictedQuerySet)):
481
499
  if hidden is not None:
482
500
  queryset = queryset.filter(destination_hidden=hidden)
483
501
  cache.set(cache_key, queryset)
502
+ if not get_queryset:
503
+ listing = list(queryset)
504
+ cache.set(list_cache_key, listing)
505
+ return listing
484
506
  return queryset
485
507
 
486
508
  get_for_model_destination.cache_key_prefix = "nautobot.extras.relationship.get_for_model_destination"
@@ -495,6 +517,33 @@ class RelationshipManager(BaseManager.from_queryset(RestrictedQuerySet)):
495
517
  | Q(destination_type=content_type, required_on=RelationshipRequiredSideChoices.DESTINATION_SIDE_REQUIRED)
496
518
  )
497
519
 
520
+ def populate_list_caches(self):
521
+ """Populate all relevant caches for `get_for_model(..., get_queryset=False)` and related lookups."""
522
+ queryset = self.all().select_related("source_type", "destination_type")
523
+ listings = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
524
+ for rel in queryset:
525
+ listings["source"][f"{rel.source_type.app_label}.{rel.source_type.model}"]["None"].append(rel)
526
+ listings["source"][f"{rel.source_type.app_label}.{rel.source_type.model}"][str(rel.source_hidden)].append(
527
+ rel
528
+ )
529
+ listings["destination"][f"{rel.destination_type.app_label}.{rel.destination_type.model}"]["None"].append(
530
+ rel
531
+ )
532
+ listings["destination"][f"{rel.destination_type.app_label}.{rel.destination_type.model}"][
533
+ str(rel.destination_hidden)
534
+ ].append(rel)
535
+ for ct in ContentType.objects.all():
536
+ label = f"{ct.app_label}.{ct.model}"
537
+ for hidden in ["None", "True", "False"]:
538
+ cache.set(
539
+ f"{self.get_for_model_source.cache_key_prefix}.{label}.{hidden}.list",
540
+ listings["source"][label][hidden],
541
+ )
542
+ cache.set(
543
+ f"{self.get_for_model_destination.cache_key_prefix}.{label}.{hidden}.list",
544
+ listings["destination"][label][hidden],
545
+ )
546
+
498
547
 
499
548
  class Relationship(
500
549
  ChangeLoggedModel,