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
@@ -16,6 +16,9 @@ logger = logging.getLogger(__name__)
16
16
  RESOLVER_PREFIX = "resolve_"
17
17
 
18
18
 
19
+ LIST_SEARCH_PARAMS_BY_SCHEMA_TYPE = {}
20
+
21
+
19
22
  def generate_restricted_queryset():
20
23
  """
21
24
  Generate a function to return a restricted queryset compatible with the internal permissions system.
@@ -248,6 +251,9 @@ def generate_schema_type(app_name: str, model: object) -> OptimizedNautobotObjec
248
251
  def generate_list_search_parameters(schema_type):
249
252
  """Generate list of query parameters for the list resolver based on a filterset."""
250
253
 
254
+ if schema_type in LIST_SEARCH_PARAMS_BY_SCHEMA_TYPE:
255
+ return LIST_SEARCH_PARAMS_BY_SCHEMA_TYPE[schema_type]
256
+
251
257
  search_params = {
252
258
  "limit": graphene.Int(),
253
259
  "offset": graphene.Int(),
@@ -259,6 +265,8 @@ def generate_list_search_parameters(schema_type):
259
265
  )
260
266
  )
261
267
 
268
+ LIST_SEARCH_PARAMS_BY_SCHEMA_TYPE[schema_type] = search_params
269
+
262
270
  return search_params
263
271
 
264
272
 
@@ -243,12 +243,12 @@ def extend_schema_type_custom_field(schema_type, model):
243
243
  (DjangoObjectType): The extended schema_type object
244
244
  """
245
245
 
246
- cfs = CustomField.objects.get_for_model(model)
246
+ custom_fields = CustomField.objects.get_for_model(model, get_queryset=False)
247
247
  prefix = ""
248
248
  if settings.GRAPHQL_CUSTOM_FIELD_PREFIX and isinstance(settings.GRAPHQL_CUSTOM_FIELD_PREFIX, str):
249
249
  prefix = f"{settings.GRAPHQL_CUSTOM_FIELD_PREFIX}_"
250
250
 
251
- for field in cfs:
251
+ for field in custom_fields:
252
252
  # Since we guaranteed cf.key's uniqueness in CustomField data migration
253
253
  # We can safely field_key this in our GraphQL without duplication
254
254
  # For new CustomField instances, we also make sure that duplicate key does not exist.
@@ -281,6 +281,7 @@ def extend_schema_type_custom_field(schema_type, model):
281
281
 
282
282
  def extend_schema_type_computed_field(schema_type, model):
283
283
  """Extend schema_type object to had attribute and resolver around computed_fields.
284
+
284
285
  Each computed field will be defined as a first level attribute.
285
286
 
286
287
  Args:
@@ -291,12 +292,12 @@ def extend_schema_type_computed_field(schema_type, model):
291
292
  (DjangoObjectType): The extended schema_type object
292
293
  """
293
294
 
294
- cfs = ComputedField.objects.get_for_model(model)
295
+ computed_fields = ComputedField.objects.get_for_model(model, get_queryset=False)
295
296
  prefix = ""
296
297
  if settings.GRAPHQL_COMPUTED_FIELD_PREFIX and isinstance(settings.GRAPHQL_COMPUTED_FIELD_PREFIX, str):
297
298
  prefix = f"{settings.GRAPHQL_COMPUTED_FIELD_PREFIX}_"
298
299
 
299
- for field in cfs:
300
+ for field in computed_fields:
300
301
  field_name = f"{prefix}{field.key}"
301
302
  try:
302
303
  check_if_key_is_graphql_safe("Computed Field", field.key)
@@ -396,12 +397,16 @@ def extend_schema_type_global_features(schema_type, model):
396
397
 
397
398
 
398
399
  def extend_schema_type_relationships(schema_type, model):
399
- """Extend the schema type with attributes and resolvers corresponding
400
- to the relationships associated with this model."""
400
+ """
401
+ Extend the schema type with attributes and resolvers for the relationships associated with this model.
401
402
 
403
+ Args:
404
+ schema_type (DjangoObjectType): GraphQL Object type for a given model
405
+ model (Model): Django model
406
+ """
402
407
  relationships_by_side = {
403
- "source": Relationship.objects.get_for_model_source(model),
404
- "destination": Relationship.objects.get_for_model_destination(model),
408
+ "source": Relationship.objects.get_for_model_source(model, get_queryset=False),
409
+ "destination": Relationship.objects.get_for_model_destination(model, get_queryset=False),
405
410
  }
406
411
 
407
412
  prefix = ""
@@ -499,30 +504,16 @@ def generate_query_mixin():
499
504
 
500
505
  logger.debug("Generating dynamic schemas for all models in the models_features graphql registry")
501
506
  # - Ensure an attribute/schematype with the same name doesn't already exist
502
- registered_models = registry.get("model_features", {}).get("graphql", {})
503
- for app_name, models in registered_models.items():
504
- for model_name in models:
505
- try:
506
- # Find the model class based on the content type
507
- ct = ContentType.objects.get(app_label=app_name, model=model_name)
508
- model = ct.model_class()
509
- except ContentType.DoesNotExist:
510
- logger.warning(
511
- 'Unable to generate a schema type for the model "%s.%s" in GraphQL, '
512
- "as this model doesn't have an associated ContentType. Please create the Object manually.",
513
- app_name,
514
- model_name,
515
- )
516
- continue
507
+ registered_models = registry.get("feature_models", {}).get("graphql", [])
508
+ for model in registered_models:
509
+ type_identifier = model._meta.label_lower
517
510
 
518
- type_identifier = f"{app_name}.{model_name}"
519
-
520
- if type_identifier in registry["graphql_types"].keys():
521
- # Skip models that have been added statically
522
- continue
511
+ if type_identifier in registry["graphql_types"].keys():
512
+ # Skip models that have been added statically
513
+ continue
523
514
 
524
- schema_type = generate_schema_type(app_name=app_name, model=model)
525
- registry["graphql_types"][type_identifier] = schema_type
515
+ schema_type = generate_schema_type(app_name=model._meta.app_label, model=model)
516
+ registry["graphql_types"][type_identifier] = schema_type
526
517
 
527
518
  logger.debug("Adding plugins' statically defined graphql schema types")
528
519
  # After checking for conflict
@@ -542,6 +533,15 @@ def generate_query_mixin():
542
533
  registry["graphql_types"][type_identifier] = schema_type
543
534
 
544
535
  logger.debug("Extending all registered schema types with dynamic attributes")
536
+
537
+ # Precache all content-types as we'll need them for filtering and the like
538
+ for content_type in ContentType.objects.all():
539
+ ContentType.objects._add_to_cache(ContentType.objects.db, content_type)
540
+
541
+ CustomField.objects.populate_list_caches()
542
+ ComputedField.objects.populate_list_caches()
543
+ Relationship.objects.populate_list_caches()
544
+
545
545
  for schema_type in registry["graphql_types"].values():
546
546
  if already_present(schema_type._meta.model):
547
547
  continue
@@ -1,8 +1,97 @@
1
1
  # noinspection PyUnresolvedReferences
2
- from django.core.management.commands.migrate import Command # noqa: F401 # unused-import
2
+ import contextlib
3
+ import time
4
+
5
+ from django.apps import apps
6
+ from django.core.management.commands.migrate import Command as _Command
3
7
  from django.db import models
8
+ from django.db.migrations.operations.fields import FieldOperation
9
+ from django.db.migrations.operations.models import ModelOperation
4
10
 
5
11
  from nautobot.core.management import commands
6
12
 
7
13
  # Overload deconstruct with our own.
8
14
  models.Field.deconstruct = commands.custom_deconstruct
15
+
16
+
17
+ class Command(_Command):
18
+ def migration_progress_callback(self, action, migration=None, fake=False):
19
+ """
20
+ Enhanced version of Django's built-in callback.
21
+
22
+ Differences in behavior:
23
+ - Measures and reports elapsed time whenever verbosity >= 1 (default value)
24
+ - Measures and reports affected record counts and elapsed time per record where applicable at verbosity >= 2.
25
+ - Aligns output to 80-character terminal width in most cases
26
+
27
+ Examples:
28
+ # nautobot-server migrate
29
+ Running migrations:
30
+ Applying dcim.0065_controller_capabilities_and_more... OK 0.10s
31
+ Applying wireless.0001_initial... OK 0.31s
32
+ Applying dcim.0066_controllermanageddevicegroup_radio_profi... OK 0.12s
33
+
34
+ # nautobot-server migrate -v=2 extras 0100
35
+ Operations to perform:
36
+ Target specific migration: 0100_fileproxy_job_result, from extras
37
+ Running migrations:
38
+ Rendering model states... DONE 19.10s
39
+ Unapplying ipam.0052_alter_ipaddress_index_together_and_mor... OK 0.10s
40
+ Affected ipam.ipaddress 75 rows 0.001s/record
41
+ Affected ipam.prefix 184 rows 0.000s/record
42
+ Affected ipam.vrf 20 rows 0.004s/record
43
+ Unapplying ipam.0051_added_optional_vrf_relationship_to_vdc... OK 0.10s
44
+ Affected ipam.vrf 20 rows 0.006s/record
45
+ Affected ipam.vrfdeviceassignment 140 rows 0.001s/record
46
+ Unapplying virtualization.0030_alter_virtualmachine_local_c... OK 0.30s
47
+ Affected virtualization.virtualmachine 0 rows
48
+ Affected virtualization.vminterface 0 rows
49
+ Unapplying virtualization.0029_add_role_field_to_interface_... OK 0.10s
50
+ Affected virtualization.vminterface 0 rows
51
+ """
52
+ if self.verbosity < 1:
53
+ return
54
+
55
+ if action in ["apply_start", "render_start", "unapply_start"]:
56
+ self.start = time.monotonic()
57
+ self.affected_models = set()
58
+ self.affected_models_count = {}
59
+ self.migration = migration
60
+ if self.verbosity >= 2 and self.migration is not None:
61
+ for operation in self.migration.operations:
62
+ if isinstance(operation, FieldOperation):
63
+ self.affected_models.add((self.migration.app_label, operation.model_name.lower()))
64
+ elif isinstance(operation, ModelOperation):
65
+ self.affected_models.add((self.migration.app_label, operation.name.lower()))
66
+
67
+ for app_label, model_name in sorted(self.affected_models):
68
+ self.affected_models_count[f"{app_label}.{model_name}"] = 0
69
+ with contextlib.suppress(Exception):
70
+ model = apps.get_model(app_label, model_name)
71
+ self.affected_models_count[model._meta.label_lower] = model.objects.count()
72
+
73
+ if action == "apply_start":
74
+ msg = f" Applying {str(migration)[:50]}..."
75
+ elif action == "render_start":
76
+ msg = " Rendering model states..."
77
+ else:
78
+ msg = f" Unapplying {str(migration)[:48]}..."
79
+ self.stdout.write(f"{msg:<64}", ending="")
80
+ self.stdout.flush()
81
+
82
+ elif action in ["apply_success", "render_success", "unapply_success"]:
83
+ elapsed = time.monotonic() - self.start
84
+ outcome = "DONE" if action == "render_success" else "FAKED" if fake else "OK"
85
+ self.stdout.write(self.style.SUCCESS(f" {outcome:<5} {elapsed: 8.2f}s"))
86
+ if self.verbosity >= 2 and self.migration is not None:
87
+ for app_label, model_name in sorted(self.affected_models):
88
+ if self.affected_models_count[f"{app_label}.{model_name}"] == 0:
89
+ with contextlib.suppress(Exception):
90
+ model = apps.get_model(app_label, model_name)
91
+ self.affected_models_count[model._meta.label_lower] = model.objects.count()
92
+
93
+ for key, value in self.affected_models_count.items():
94
+ if value:
95
+ self.stdout.write(f" Affected {key:<38} {value: 8} rows {elapsed/value:6.3f}s/record")
96
+ else:
97
+ self.stdout.write(f" Affected {key:<38} {value: 8} rows")
@@ -1487,9 +1487,9 @@ properties:
1487
1487
  }
1488
1488
  ```
1489
1489
 
1490
- The default top-level keys are `ansible`, `hier_config`, `napalm`, `netmiko`, `netutils_parser`,
1491
- `ntc_templates`, `pyats`, `pyntc`, and `scrapli`, but you can also add additional keys if you have
1492
- an alternative network driver that you want your Nautobot instance to include.
1490
+ The default top-level keys consists of `ansible`, `hier_config`, `napalm`, etc, and comes from netutils, but
1491
+ you can also add additional keys if you have an alternative network driver that you want your Nautobot instance
1492
+ to include.
1493
1493
  is_constance_config: true
1494
1494
  type: "object"
1495
1495
  version_added: "1.6.0"
nautobot/core/tables.py CHANGED
@@ -80,14 +80,14 @@ class BaseTable(django_tables2.Table):
80
80
  reverse_lookup="static_group_associations__associated_object_id",
81
81
  )
82
82
 
83
- for cf in models.CustomField.objects.get_for_model(model):
83
+ for cf in models.CustomField.objects.get_for_model(model, get_queryset=False):
84
84
  name = cf.add_prefix_to_cf_key()
85
85
  self.base_columns[name] = CustomFieldColumn(cf)
86
86
 
87
- for cpf in models.ComputedField.objects.get_for_model(model):
87
+ for cpf in models.ComputedField.objects.get_for_model(model, get_queryset=False):
88
88
  self.base_columns[f"cpf_{cpf.key}"] = ComputedFieldColumn(cpf)
89
89
 
90
- for relationship in models.Relationship.objects.get_for_model_source(model):
90
+ for relationship in models.Relationship.objects.get_for_model_source(model, get_queryset=False):
91
91
  if not relationship.symmetric:
92
92
  self.base_columns[f"cr_{relationship.key}_src"] = RelationshipColumn(
93
93
  relationship, side=choices.RelationshipSideChoices.SIDE_SOURCE
@@ -97,7 +97,7 @@ class BaseTable(django_tables2.Table):
97
97
  relationship, side=choices.RelationshipSideChoices.SIDE_PEER
98
98
  )
99
99
 
100
- for relationship in models.Relationship.objects.get_for_model_destination(model):
100
+ for relationship in models.Relationship.objects.get_for_model_destination(model, get_queryset=False):
101
101
  if not relationship.symmetric:
102
102
  self.base_columns[f"cr_{relationship.key}_dst"] = RelationshipColumn(
103
103
  relationship, side=choices.RelationshipSideChoices.SIDE_DESTINATION
@@ -36,16 +36,16 @@
36
36
  <a href="#theme_modal" data-toggle="modal" data-target="#theme_modal" id="btn-theme-modal"><i class="mdi mdi-theme-light-dark text-primary"></i>Theme</a> &middot;
37
37
  <i class="mdi mdi-book-open-page-variant text-primary"></i>
38
38
  {% if settings.BRANDING_URLS.docs %}
39
- <a href="{{ settings.BRANDING_URLS.docs }}">Docs</a>
39
+ <a href="{{ settings.BRANDING_URLS.docs }}" target="_blank">Docs</a>
40
40
  {% else %}
41
- <a href="{% static 'docs/index.html' %}">Docs</a>
41
+ <a href="{% static 'docs/index.html' %}" target="_blank">Docs</a>
42
42
  {% endif %}
43
43
  &middot;
44
44
  <img src="{% static 'img/jinja_logo.svg' %}" style="height:20px"> <a href="{% url 'render_jinja_template' %}">Jinja Renderer</a> &middot;
45
45
  <i class="mdi mdi-cloud-braces text-primary"></i> <a href="{% url 'api_docs' %}">API</a> &middot;
46
46
  <i class="mdi mdi-graphql text-primary"></i> <a href="{% url 'graphql' %}">GraphQL</a> &middot;
47
- <i class="mdi mdi-xml text-primary"></i> <a href="{{ settings.BRANDING_URLS.code }}">Code</a> &middot;
48
- <i class="mdi mdi-lifebuoy text-primary"></i> <a href="{{ settings.BRANDING_URLS.help }}">Help</a>
47
+ <i class="mdi mdi-xml text-primary"></i> <a href="{{ settings.BRANDING_URLS.code }}" target="_blank">Code</a> &middot;
48
+ <i class="mdi mdi-lifebuoy text-primary"></i> <a href="{{ settings.BRANDING_URLS.help }}" target="_blank">Help</a>
49
49
  </p>
50
50
  {% endif %}
51
51
  </div>
@@ -7,6 +7,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
7
7
  from django.contrib.contenttypes.models import ContentType
8
8
  from django.db import connections, DEFAULT_DB_ALIAS
9
9
  from django.db.models import ForeignKey, ManyToManyField, QuerySet
10
+ from django.http import QueryDict
10
11
  from django.test import override_settings, tag
11
12
  from django.test.utils import CaptureQueriesContext
12
13
  from django.urls import reverse
@@ -105,6 +106,12 @@ class APITestCase(views.ModelTestCase):
105
106
  m2m_fields.append(field_name)
106
107
  return m2m_fields
107
108
 
109
+ @staticmethod
110
+ def add_query_params_to_url(url: str, query_dict: dict) -> str:
111
+ query = QueryDict(mutable=True)
112
+ query.update(query_dict)
113
+ return f"{url}?{query.urlencode()}"
114
+
108
115
 
109
116
  @tag("unit")
110
117
  class APIViewTestCases:
@@ -123,7 +123,7 @@ def get_csv_form_fields_from_serializer_class(serializer_class):
123
123
  from nautobot.extras.choices import CustomFieldTypeChoices
124
124
  from nautobot.extras.models import CustomField
125
125
 
126
- cfs = CustomField.objects.get_for_model(serializer_class.Meta.model)
126
+ cfs = CustomField.objects.get_for_model(serializer_class.Meta.model, get_queryset=False)
127
127
  for cf in cfs:
128
128
  cf_form_field = cf.to_form_field(set_initial=False)
129
129
  field_info = {
nautobot/dcim/choices.py CHANGED
@@ -589,6 +589,7 @@ class PowerOutletTypeChoices(ChoiceSet):
589
589
  TYPE_NEUTRIK_POWERCON_TRUE1 = "neutrik-powercon-true1"
590
590
  TYPE_NEUTRIK_POWERCON_TRUE1_TOP = "neutrik-powercon-true1-top"
591
591
  TYPE_UBIQUITI_SMARTPOWER = "ubiquiti-smartpower"
592
+ TYPE_EATON_C39 = "eaton-c39"
592
593
  # Other
593
594
  TYPE_HARDWIRED = "hardwired"
594
595
  TYPE_OTHER = "other"
@@ -728,6 +729,7 @@ class PowerOutletTypeChoices(ChoiceSet):
728
729
  (TYPE_NEUTRIK_POWERCON_TRUE1, "Neutrik powerCON TRUE1"),
729
730
  (TYPE_NEUTRIK_POWERCON_TRUE1_TOP, "Neutrik powerCON TRUE1 TOP"),
730
731
  (TYPE_UBIQUITI_SMARTPOWER, "Ubiquiti SmartPower"),
732
+ (TYPE_EATON_C39, "Eaton C39"),
731
733
  ),
732
734
  ),
733
735
  (
@@ -96,22 +96,6 @@ COMPATIBLE_TERMINATION_TYPES = {
96
96
  ],
97
97
  }
98
98
 
99
- #
100
- # Platforms
101
- #
102
-
103
- NETUTILS_NETWORK_DRIVER_MAPPING_NAMES = {
104
- "ansible",
105
- "hier_config",
106
- "napalm",
107
- "netmiko",
108
- "netutils_parser",
109
- "ntc_templates",
110
- "pyats",
111
- "pyntc",
112
- "scrapli",
113
- }
114
-
115
99
  #
116
100
  # Modules
117
101
  #
nautobot/dcim/factory.py CHANGED
@@ -616,7 +616,7 @@ class RackFactory(PrimaryModelFactory):
616
616
  has_role = NautobotBoolIterator()
617
617
  role = factory.Maybe("has_role", random_instance(lambda: Role.objects.get_for_model(Rack)), None)
618
618
 
619
- location = random_instance(lambda: Location.objects.get_for_model(VLANGroup), allow_null=False)
619
+ location = random_instance(lambda: Location.objects.get_for_model(Rack), allow_null=False)
620
620
 
621
621
  has_rack_group = NautobotBoolIterator() # TODO there's no RackGroupFactory yet...
622
622
  rack_group = factory.Maybe("has_rack_group", random_instance(RackGroup), None)
@@ -0,0 +1,42 @@
1
+ # Generated by Django 4.2.20 on 2025-07-17 20:24
2
+
3
+ from django.db import migrations
4
+
5
+ import nautobot.core.models.query_functions
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+ dependencies = [
10
+ ("dcim", "0070_modulefamily_models"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterModelOptions(
15
+ name="consoleport",
16
+ options={"ordering": ("device", "module__id", "_name")},
17
+ ),
18
+ migrations.AlterModelOptions(
19
+ name="consoleserverport",
20
+ options={"ordering": ("device", "module__id", "_name")},
21
+ ),
22
+ migrations.AlterModelOptions(
23
+ name="frontport",
24
+ options={"ordering": ("device", "module__id", "_name")},
25
+ ),
26
+ migrations.AlterModelOptions(
27
+ name="interface",
28
+ options={"ordering": ("device", "module__id", nautobot.core.models.query_functions.CollateAsChar("_name"))},
29
+ ),
30
+ migrations.AlterModelOptions(
31
+ name="poweroutlet",
32
+ options={"ordering": ("device", "module__id", "_name")},
33
+ ),
34
+ migrations.AlterModelOptions(
35
+ name="powerport",
36
+ options={"ordering": ("device", "module__id", "_name")},
37
+ ),
38
+ migrations.AlterModelOptions(
39
+ name="rearport",
40
+ options={"ordering": ("device", "module__id", "_name")},
41
+ ),
42
+ ]
@@ -2,6 +2,7 @@ import re
2
2
 
3
3
  from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
4
4
  from django.contrib.contenttypes.models import ContentType
5
+ from django.core.cache import cache
5
6
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
6
7
  from django.core.validators import MaxValueValidator, MinValueValidator
7
8
  from django.db import models, transaction
@@ -123,7 +124,7 @@ class ModularComponentModel(ComponentModel):
123
124
 
124
125
  class Meta:
125
126
  abstract = True
126
- ordering = ("device", "module", "_name")
127
+ ordering = ("device", "module__id", "_name") # Module.ordering is complex/expensive so don't order by module
127
128
  constraints = [
128
129
  models.UniqueConstraint(
129
130
  fields=("device", "name"),
@@ -646,7 +647,7 @@ class Interface(ModularComponentModel, CableTermination, PathEndpoint, BaseInter
646
647
  )
647
648
 
648
649
  class Meta(ModularComponentModel.Meta):
649
- ordering = ("device", "module", CollateAsChar("_name"))
650
+ ordering = ("device", "module__id", CollateAsChar("_name")) # Module.ordering is complex; don't order by module
650
651
 
651
652
  def clean(self):
652
653
  super().clean()
@@ -1311,3 +1312,10 @@ class ModuleBay(PrimaryModel):
1311
1312
  self.position = self.name
1312
1313
 
1313
1314
  clean.alters_data = True
1315
+
1316
+ def save(self, *args, **kwargs):
1317
+ super().save(*args, **kwargs)
1318
+
1319
+ if self.parent_device is not None:
1320
+ # Set the has_module_bays cache key on the parent device - see Device.has_module_bays()
1321
+ cache.set(f"nautobot.dcim.device.{self.parent_device.pk}.has_module_bays", True, timeout=5)
@@ -2,6 +2,7 @@ from collections import OrderedDict
2
2
 
3
3
  from django.contrib.contenttypes.fields import GenericRelation
4
4
  from django.contrib.contenttypes.models import ContentType
5
+ from django.core.cache import cache
5
6
  from django.core.exceptions import ValidationError
6
7
  from django.core.serializers.json import DjangoJSONEncoder
7
8
  from django.core.validators import MaxValueValidator, MinValueValidator
@@ -896,6 +897,8 @@ class Device(PrimaryModel, ConfigContextModel):
896
897
  instantiated_components = []
897
898
  for model, templates in component_models:
898
899
  model.objects.bulk_create([x.instantiate(device=self) for x in templates])
900
+ cache_key = f"nautobot.dcim.device.{self.pk}.has_module_bays"
901
+ cache.delete(cache_key)
899
902
  return instantiated_components
900
903
 
901
904
  create_components.alters_data = True
@@ -988,6 +991,18 @@ class Device(PrimaryModel, ConfigContextModel):
988
991
  """
989
992
  return Device.objects.filter(parent_bay__device=self.pk)
990
993
 
994
+ @property
995
+ def has_module_bays(self) -> bool:
996
+ """
997
+ Cacheable property for determining whether this Device has any ModuleBays, and therefore may contain Modules.
998
+ """
999
+ cache_key = f"nautobot.dcim.device.{self.pk}.has_module_bays"
1000
+ module_bays_exists = cache.get(cache_key)
1001
+ if module_bays_exists is None:
1002
+ module_bays_exists = self.module_bays.exists()
1003
+ cache.set(cache_key, module_bays_exists, timeout=5)
1004
+ return module_bays_exists
1005
+
991
1006
  @property
992
1007
  def all_modules(self):
993
1008
  """
@@ -998,6 +1013,9 @@ class Device(PrimaryModel, ConfigContextModel):
998
1013
  # We artificially limit the recursion to 4 levels or we would be stuck in an infinite loop.
999
1014
  recursion_depth = MODULE_RECURSION_DEPTH_LIMIT
1000
1015
  qs = Module.objects.all()
1016
+ if not self.has_module_bays:
1017
+ # Short-circuit to avoid an expensive nested query
1018
+ return qs.none()
1001
1019
  query = Q()
1002
1020
  for level in range(recursion_depth):
1003
1021
  recursive_query = "parent_module_bay__parent_module__" * level
@@ -398,40 +398,7 @@
398
398
  </div>
399
399
  </div>
400
400
  {% endif %}
401
- {% if object.is_dynamic_group_associable_model and perms.extras.view_dynamicgroup %}
402
- <div id="dynamic_groups" role="tabpanel" class="tab-pane {% if request.GET.tab == 'dynamic_groups' %}active{% else %}fade{% endif %}">
403
- <div class="row">
404
- <div class="col-md-12">
405
- <div class="alert alert-warning">
406
- Dynamic group membership is cached for performance reasons,
407
- therefore this table may not always be up-to-date.
408
- <br>You can refresh the membership of any specific group by navigating to it from the list below
409
- or from the <a href="{% url 'extras:dynamicgroup_list' %}">Dynamic Groups list view</a>.
410
- <br>You can also refresh the membership of all groups by running the
411
- <a href="{% url 'extras:job_run_by_class_path' class_path='nautobot.core.jobs.groups.RefreshDynamicGroupCaches' %}">Refresh Dynamic Group Caches job</a>.
412
- </div>
413
- </div>
414
- </div>
415
- <div class="row">
416
- <div class="col-md-12">
417
- <form method="post">
418
- {% csrf_token %}
419
- <div class="panel panel-default">
420
- <div class="panel-heading">
421
- <strong>Dynamic Groups</strong>
422
- <div class="pull-right noprint">
423
- <!-- Insert table config button here -->
424
- </div>
425
- </div>
426
- <div class="table-responsive">
427
- {% render_table associated_dynamic_groups_table 'inc/table.html' %}
428
- </div>
429
- </div>
430
- </form>
431
- </div>
432
- </div>
433
- </div>
434
- {% endif %}
401
+ {% comment %}The dynamic_groups tab is intentionally omitted here for performance reasons.{% endcomment %}
435
402
  {% if object.is_metadata_associable_model and perms.extras.view_objectmetadata %}
436
403
  <div id="object_metadata" role="tabpanel" class="tab-pane {% if request.GET.tab == 'object_metadata' %}active{% else %}fade{% endif %}">
437
404
  <div class="row">