nautobot 2.4.9__py3-none-any.whl → 2.4.11__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (433) hide show
  1. nautobot/cloud/tests/test_views.py +13 -1
  2. nautobot/cloud/views.py +39 -9
  3. nautobot/core/api/parsers.py +56 -2
  4. nautobot/core/celery/__init__.py +21 -0
  5. nautobot/core/celery/encoders.py +3 -0
  6. nautobot/core/forms/forms.py +4 -1
  7. nautobot/core/jobs/bulk_actions.py +8 -8
  8. nautobot/core/jobs/cleanup.py +11 -0
  9. nautobot/core/management/commands/generate_test_data.py +2 -1
  10. nautobot/core/models/__init__.py +2 -0
  11. nautobot/core/templates/generic/object_retrieve.html +1 -1
  12. nautobot/core/testing/mixins.py +19 -1
  13. nautobot/core/testing/views.py +104 -8
  14. nautobot/core/tests/test_csv.py +92 -1
  15. nautobot/core/tests/test_jinja_filters.py +59 -0
  16. nautobot/core/tests/test_jobs.py +20 -4
  17. nautobot/core/tests/test_utils.py +193 -0
  18. nautobot/core/tests/test_views.py +73 -0
  19. nautobot/core/tests/test_views_utils.py +53 -2
  20. nautobot/core/ui/object_detail.py +4 -0
  21. nautobot/core/urls.py +2 -2
  22. nautobot/core/utils/lookup.py +4 -2
  23. nautobot/core/utils/module_loading.py +86 -58
  24. nautobot/core/views/__init__.py +21 -0
  25. nautobot/core/views/generic.py +2 -12
  26. nautobot/core/views/mixins.py +19 -1
  27. nautobot/core/views/renderers.py +4 -13
  28. nautobot/core/views/utils.py +16 -0
  29. nautobot/dcim/api/serializers.py +13 -0
  30. nautobot/dcim/api/urls.py +1 -0
  31. nautobot/dcim/api/views.py +20 -0
  32. nautobot/dcim/apps.py +1 -0
  33. nautobot/dcim/factory.py +11 -0
  34. nautobot/dcim/filters/__init__.py +110 -0
  35. nautobot/dcim/forms.py +205 -19
  36. nautobot/dcim/migrations/0070_modulefamily_models.py +92 -0
  37. nautobot/dcim/models/__init__.py +2 -0
  38. nautobot/dcim/models/device_component_templates.py +18 -0
  39. nautobot/dcim/models/device_components.py +25 -1
  40. nautobot/dcim/models/devices.py +68 -0
  41. nautobot/dcim/navigation.py +16 -0
  42. nautobot/dcim/tables/__init__.py +2 -0
  43. nautobot/dcim/tables/devices.py +48 -0
  44. nautobot/dcim/tables/devicetypes.py +35 -1
  45. nautobot/dcim/tables/template_code.py +2 -0
  46. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -90
  47. nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +1 -1
  48. nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +1 -63
  49. nautobot/dcim/templates/dcim/location.html +2 -249
  50. nautobot/dcim/templates/dcim/location_edit.html +2 -38
  51. nautobot/dcim/templates/dcim/location_retrieve.html +249 -0
  52. nautobot/dcim/templates/dcim/location_update.html +38 -0
  53. nautobot/dcim/templates/dcim/module_update.html +1 -0
  54. nautobot/dcim/templates/dcim/modulebay_retrieve.html +93 -1
  55. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +31 -0
  56. nautobot/dcim/templates/dcim/moduletype_retrieve.html +6 -0
  57. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -160
  58. nautobot/dcim/tests/test_api.py +35 -0
  59. nautobot/dcim/tests/test_filters.py +102 -3
  60. nautobot/dcim/tests/test_models.py +146 -0
  61. nautobot/dcim/tests/test_views.py +70 -97
  62. nautobot/dcim/urls.py +4 -22
  63. nautobot/dcim/views.py +439 -153
  64. nautobot/extras/api/views.py +9 -2
  65. nautobot/extras/context_managers.py +2 -2
  66. nautobot/extras/datasources/git.py +11 -3
  67. nautobot/extras/forms/forms.py +9 -5
  68. nautobot/extras/jobs.py +4 -2
  69. nautobot/extras/models/customfields.py +2 -0
  70. nautobot/extras/models/datasources.py +13 -8
  71. nautobot/extras/models/groups.py +18 -0
  72. nautobot/extras/models/jobs.py +19 -0
  73. nautobot/extras/models/metadata.py +2 -0
  74. nautobot/extras/models/models.py +4 -0
  75. nautobot/extras/models/secrets.py +7 -0
  76. nautobot/extras/plugins/__init__.py +3 -0
  77. nautobot/extras/secrets/__init__.py +14 -0
  78. nautobot/extras/tables.py +40 -3
  79. nautobot/extras/templates/extras/configcontext.html +2 -220
  80. nautobot/extras/templates/extras/configcontext_edit.html +2 -50
  81. nautobot/extras/templates/extras/configcontext_retrieve.html +2 -0
  82. nautobot/extras/templates/extras/configcontext_update.html +50 -0
  83. nautobot/extras/templates/extras/configcontextschema.html +2 -48
  84. nautobot/extras/templates/extras/configcontextschema_edit.html +2 -19
  85. nautobot/extras/templates/extras/configcontextschema_retrieve.html +48 -0
  86. nautobot/extras/templates/extras/configcontextschema_update.html +19 -0
  87. nautobot/extras/templates/extras/inc/configcontext_data.html +1 -0
  88. nautobot/extras/templates/extras/inc/json_data.html +1 -1
  89. nautobot/extras/templates/extras/inc/json_format.html +2 -2
  90. nautobot/extras/templates/extras/job_edit.html +12 -6
  91. nautobot/extras/templates/extras/tag.html +2 -52
  92. nautobot/extras/templates/extras/tag_edit.html +2 -15
  93. nautobot/extras/templates/extras/tag_retrieve.html +52 -0
  94. nautobot/extras/templates/extras/tag_update.html +15 -0
  95. nautobot/extras/templates/extras/team_retrieve.html +2 -2
  96. nautobot/extras/tests/test_api.py +15 -15
  97. nautobot/extras/tests/test_context_managers.py +20 -0
  98. nautobot/extras/tests/test_filters.py +4 -4
  99. nautobot/extras/tests/test_jobs.py +23 -10
  100. nautobot/extras/tests/test_models.py +45 -8
  101. nautobot/extras/tests/test_plugins.py +6 -3
  102. nautobot/extras/tests/test_views.py +66 -11
  103. nautobot/extras/urls.py +4 -134
  104. nautobot/extras/views.py +113 -158
  105. nautobot/ipam/models.py +51 -4
  106. nautobot/ipam/tables.py +19 -0
  107. nautobot/ipam/templates/ipam/vlan.html +2 -84
  108. nautobot/ipam/templates/ipam/vlan_edit.html +2 -24
  109. nautobot/ipam/templates/ipam/vlan_retrieve.html +84 -0
  110. nautobot/ipam/templates/ipam/vlan_update.html +24 -0
  111. nautobot/ipam/tests/test_views.py +5 -0
  112. nautobot/ipam/urls.py +1 -21
  113. nautobot/ipam/views.py +45 -70
  114. nautobot/project-static/docs/404.html +31 -8
  115. nautobot/project-static/docs/apps/index.html +31 -8
  116. nautobot/project-static/docs/apps/nautobot-apps.html +31 -8
  117. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +31 -8
  118. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +31 -8
  119. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +31 -8
  120. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +31 -8
  121. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +31 -8
  122. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +31 -8
  123. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +31 -8
  124. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +31 -8
  125. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +31 -8
  126. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +31 -8
  127. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +31 -8
  128. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +31 -8
  129. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +31 -8
  130. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +31 -8
  131. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +31 -8
  132. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +31 -8
  133. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +31 -8
  134. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +31 -8
  135. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +31 -8
  136. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +120 -8
  137. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +31 -8
  138. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +31 -8
  139. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +31 -8
  140. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +31 -8
  141. nautobot/project-static/docs/development/apps/api/configuration-view.html +31 -8
  142. nautobot/project-static/docs/development/apps/api/database-backend-config.html +31 -8
  143. nautobot/project-static/docs/development/apps/api/models/django-admin.html +31 -8
  144. nautobot/project-static/docs/development/apps/api/models/global-search.html +31 -8
  145. nautobot/project-static/docs/development/apps/api/models/graphql.html +31 -8
  146. nautobot/project-static/docs/development/apps/api/models/index.html +31 -8
  147. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +40 -8
  148. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +31 -8
  149. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +31 -8
  150. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +31 -8
  151. nautobot/project-static/docs/development/apps/api/platform-features/index.html +31 -8
  152. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +31 -8
  153. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +31 -8
  154. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +31 -8
  155. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +70 -46
  156. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +31 -8
  157. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +31 -8
  158. nautobot/project-static/docs/development/apps/api/prometheus.html +31 -8
  159. nautobot/project-static/docs/development/apps/api/setup.html +31 -8
  160. nautobot/project-static/docs/development/apps/api/testing.html +31 -8
  161. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +31 -8
  162. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +31 -8
  163. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +31 -8
  164. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +31 -8
  165. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +31 -8
  166. nautobot/project-static/docs/development/apps/api/views/base-template.html +31 -8
  167. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +31 -8
  168. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +31 -8
  169. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +31 -8
  170. nautobot/project-static/docs/development/apps/api/views/index.html +31 -8
  171. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +31 -8
  172. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +31 -8
  173. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +31 -8
  174. nautobot/project-static/docs/development/apps/api/views/notes.html +31 -8
  175. nautobot/project-static/docs/development/apps/api/views/rest-api.html +31 -8
  176. nautobot/project-static/docs/development/apps/api/views/urls.html +31 -8
  177. nautobot/project-static/docs/development/apps/index.html +31 -8
  178. nautobot/project-static/docs/development/apps/migration/code-updates.html +31 -8
  179. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +31 -8
  180. nautobot/project-static/docs/development/apps/migration/from-v1.html +31 -8
  181. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +31 -8
  182. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +31 -8
  183. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +31 -8
  184. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +31 -8
  185. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +31 -8
  186. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +31 -8
  187. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +31 -8
  188. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +31 -8
  189. nautobot/project-static/docs/development/apps/porting-from-netbox.html +31 -8
  190. nautobot/project-static/docs/development/core/application-registry.html +31 -8
  191. nautobot/project-static/docs/development/core/best-practices.html +31 -8
  192. nautobot/project-static/docs/development/core/bootstrap-ui.html +31 -8
  193. nautobot/project-static/docs/development/core/caching.html +31 -8
  194. nautobot/project-static/docs/development/core/controllers.html +31 -8
  195. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +31 -8
  196. nautobot/project-static/docs/development/core/generic-views.html +31 -8
  197. nautobot/project-static/docs/development/core/getting-started.html +31 -8
  198. nautobot/project-static/docs/development/core/homepage.html +31 -8
  199. nautobot/project-static/docs/development/core/index.html +31 -8
  200. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +31 -8
  201. nautobot/project-static/docs/development/core/model-checklist.html +31 -8
  202. nautobot/project-static/docs/development/core/model-features.html +31 -8
  203. nautobot/project-static/docs/development/core/natural-keys.html +31 -8
  204. nautobot/project-static/docs/development/core/navigation-menu.html +31 -8
  205. nautobot/project-static/docs/development/core/release-checklist.html +31 -8
  206. nautobot/project-static/docs/development/core/role-internals.html +31 -8
  207. nautobot/project-static/docs/development/core/settings.html +31 -8
  208. nautobot/project-static/docs/development/core/style-guide.html +31 -8
  209. nautobot/project-static/docs/development/core/templates.html +31 -8
  210. nautobot/project-static/docs/development/core/testing.html +31 -8
  211. nautobot/project-static/docs/development/core/ui-component-framework.html +31 -8
  212. nautobot/project-static/docs/development/core/user-preferences.html +31 -8
  213. nautobot/project-static/docs/development/index.html +31 -8
  214. nautobot/project-static/docs/development/jobs/getting-started.html +35 -8
  215. nautobot/project-static/docs/development/jobs/index.html +31 -8
  216. nautobot/project-static/docs/development/jobs/installation.html +31 -8
  217. nautobot/project-static/docs/development/jobs/job-extensions.html +31 -8
  218. nautobot/project-static/docs/development/jobs/job-logging.html +31 -8
  219. nautobot/project-static/docs/development/jobs/job-patterns.html +31 -8
  220. nautobot/project-static/docs/development/jobs/job-structure.html +31 -8
  221. nautobot/project-static/docs/development/jobs/migration/from-v1.html +31 -8
  222. nautobot/project-static/docs/development/jobs/testing.html +31 -8
  223. nautobot/project-static/docs/index.html +31 -8
  224. nautobot/project-static/docs/insert-analytics.sh +36 -0
  225. nautobot/project-static/docs/objects.inv +0 -0
  226. nautobot/project-static/docs/overview/application_stack.html +31 -8
  227. nautobot/project-static/docs/overview/design_philosophy.html +31 -8
  228. nautobot/project-static/docs/release-notes/index.html +31 -8
  229. nautobot/project-static/docs/release-notes/version-1.0.html +31 -8
  230. nautobot/project-static/docs/release-notes/version-1.1.html +31 -8
  231. nautobot/project-static/docs/release-notes/version-1.2.html +31 -8
  232. nautobot/project-static/docs/release-notes/version-1.3.html +31 -8
  233. nautobot/project-static/docs/release-notes/version-1.4.html +31 -8
  234. nautobot/project-static/docs/release-notes/version-1.5.html +31 -8
  235. nautobot/project-static/docs/release-notes/version-1.6.html +328 -8
  236. nautobot/project-static/docs/release-notes/version-2.0.html +31 -8
  237. nautobot/project-static/docs/release-notes/version-2.1.html +31 -8
  238. nautobot/project-static/docs/release-notes/version-2.2.html +31 -8
  239. nautobot/project-static/docs/release-notes/version-2.3.html +31 -8
  240. nautobot/project-static/docs/release-notes/version-2.4.html +353 -8
  241. nautobot/project-static/docs/search/search_index.json +1 -1
  242. nautobot/project-static/docs/sitemap.xml +302 -298
  243. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  244. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +31 -8
  245. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +31 -8
  246. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +31 -8
  247. nautobot/project-static/docs/user-guide/administration/configuration/index.html +31 -8
  248. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +31 -8
  249. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +31 -8
  250. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +31 -8
  251. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +31 -8
  252. nautobot/project-static/docs/user-guide/administration/guides/docker.html +31 -8
  253. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +31 -8
  254. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +31 -8
  255. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +31 -8
  256. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +31 -8
  257. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +31 -8
  258. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +31 -8
  259. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +31 -8
  260. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +31 -8
  261. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +31 -8
  262. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +31 -8
  263. nautobot/project-static/docs/user-guide/administration/installation/index.html +31 -8
  264. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +31 -8
  265. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +31 -8
  266. nautobot/project-static/docs/user-guide/administration/installation/services.html +31 -8
  267. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +31 -8
  268. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +31 -8
  269. nautobot/project-static/docs/user-guide/administration/security/index.html +31 -9
  270. nautobot/project-static/docs/user-guide/administration/security/notices.html +144 -9
  271. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +31 -8
  272. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +31 -8
  273. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +31 -8
  274. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +31 -8
  275. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +31 -8
  276. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +31 -8
  277. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +31 -8
  278. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +31 -8
  279. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +31 -8
  280. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +31 -8
  281. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +31 -8
  282. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +31 -8
  283. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +31 -8
  284. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +31 -8
  285. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +31 -8
  286. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +31 -8
  287. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +31 -8
  288. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +31 -8
  289. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +31 -8
  290. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +31 -8
  291. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +31 -8
  292. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +31 -8
  293. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +31 -8
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +31 -8
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +31 -8
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +31 -8
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +31 -8
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +31 -8
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +31 -8
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +31 -8
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +31 -8
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +31 -8
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +31 -8
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +31 -8
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +43 -20
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +31 -8
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +31 -8
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +31 -8
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +31 -8
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +31 -8
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +31 -8
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +31 -8
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +31 -8
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +31 -8
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +31 -8
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +35 -8
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +35 -8
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +35 -8
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +10261 -0
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +34 -11
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +31 -8
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +31 -8
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +31 -8
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +31 -8
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +31 -8
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +31 -8
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +31 -8
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +31 -8
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +31 -8
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +31 -8
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +31 -8
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +31 -8
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +31 -8
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +31 -8
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +31 -8
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +31 -8
  337. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +31 -8
  338. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +31 -8
  339. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +31 -8
  340. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +31 -8
  341. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +31 -8
  342. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +31 -8
  343. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +31 -8
  344. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +31 -8
  345. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +31 -8
  346. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +31 -8
  347. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +31 -8
  348. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +31 -8
  349. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +31 -8
  350. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +31 -8
  351. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +31 -8
  352. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +31 -8
  353. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +31 -8
  354. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +31 -8
  355. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +31 -8
  356. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +31 -8
  357. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +31 -8
  358. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +31 -8
  359. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +31 -8
  360. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +31 -8
  361. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +31 -8
  362. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +31 -8
  363. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +31 -8
  364. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +31 -8
  365. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +31 -8
  366. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +31 -8
  367. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +31 -8
  368. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +31 -8
  369. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +31 -8
  370. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +31 -8
  371. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +31 -8
  372. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +31 -8
  373. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +41 -15
  374. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +31 -8
  375. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +31 -8
  376. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +31 -8
  377. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +31 -8
  378. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +31 -8
  379. nautobot/project-static/docs/user-guide/index.html +31 -8
  380. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +31 -8
  381. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +31 -8
  382. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +31 -8
  383. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +31 -8
  384. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +31 -8
  385. nautobot/project-static/docs/user-guide/platform-functionality/events.html +31 -8
  386. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +31 -8
  387. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +31 -8
  388. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +37 -9
  389. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +31 -8
  390. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +31 -8
  391. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +31 -8
  392. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +31 -8
  393. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +31 -8
  394. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +31 -8
  395. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +31 -8
  396. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +31 -8
  397. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +31 -8
  398. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +31 -8
  399. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +31 -8
  400. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +31 -8
  401. nautobot/project-static/docs/user-guide/platform-functionality/note.html +31 -8
  402. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +31 -8
  403. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +31 -8
  404. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +31 -8
  405. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +31 -8
  406. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +31 -8
  407. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +31 -8
  408. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +31 -8
  409. nautobot/project-static/docs/user-guide/platform-functionality/role.html +31 -8
  410. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +31 -8
  411. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +31 -8
  412. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +31 -8
  413. nautobot/project-static/docs/user-guide/platform-functionality/status.html +31 -8
  414. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +31 -8
  415. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +31 -8
  416. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +31 -8
  417. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +31 -8
  418. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +31 -8
  419. nautobot/tenancy/tables.py +2 -0
  420. nautobot/users/models.py +4 -0
  421. nautobot/virtualization/models.py +4 -0
  422. nautobot/virtualization/tests/test_views.py +1 -1
  423. nautobot/wireless/forms.py +0 -1
  424. nautobot/wireless/models.py +1 -1
  425. nautobot/wireless/tables.py +7 -0
  426. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/METADATA +4 -4
  427. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/RECORD +433 -418
  428. /nautobot/dcim/templates/dcim/{platform_edit.html → platform_create.html} +0 -0
  429. /nautobot/extras/test_jobs/{pass.py → pass_job.py} +0 -0
  430. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/LICENSE.txt +0 -0
  431. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/NOTICE +0 -0
  432. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/WHEEL +0 -0
  433. {nautobot-2.4.9.dist-info → nautobot-2.4.11.dist-info}/entry_points.txt +0 -0
nautobot/extras/views.py CHANGED
@@ -2,6 +2,7 @@ import logging
2
2
  from typing import Optional
3
3
  from urllib.parse import parse_qs
4
4
 
5
+ from django.conf import settings
5
6
  from django.contrib import messages
6
7
  from django.contrib.contenttypes.models import ContentType
7
8
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
@@ -15,7 +16,7 @@ from django.urls import reverse
15
16
  from django.urls.exceptions import NoReverseMatch
16
17
  from django.utils import timezone
17
18
  from django.utils.encoding import iri_to_uri
18
- from django.utils.html import format_html
19
+ from django.utils.html import format_html, format_html_join
19
20
  from django.utils.http import url_has_allowed_host_and_scheme
20
21
  from django.utils.timezone import get_current_timezone
21
22
  from django.views.generic import View
@@ -86,7 +87,6 @@ from .choices import (
86
87
  JobExecutionType,
87
88
  JobQueueTypeChoices,
88
89
  JobResultStatusChoices,
89
- LogLevelChoices,
90
90
  )
91
91
  from .datasources import (
92
92
  enqueue_git_repository_diff_origin_and_local,
@@ -111,7 +111,6 @@ from .models import (
111
111
  Job as JobModel,
112
112
  JobButton,
113
113
  JobHook,
114
- JobLogEntry,
115
114
  JobQueue,
116
115
  JobResult,
117
116
  MetadataType,
@@ -180,55 +179,89 @@ class ComputedFieldUIViewSet(NautobotUIViewSet):
180
179
  # have an associated owner, such as a Git repository
181
180
 
182
181
 
183
- class ConfigContextListView(generic.ObjectListView):
182
+ class ConfigContextUIViewSet(NautobotUIViewSet):
183
+ bulk_update_form_class = forms.ConfigContextBulkEditForm
184
+ filterset_class = filters.ConfigContextFilterSet
185
+ filterset_form_class = forms.ConfigContextFilterForm
186
+ form_class = forms.ConfigContextForm
184
187
  queryset = ConfigContext.objects.all()
185
- filterset = filters.ConfigContextFilterSet
186
- filterset_form = forms.ConfigContextFilterForm
187
- table = tables.ConfigContextTable
188
- action_buttons = ("add",)
188
+ serializer_class = serializers.ConfigContextSerializer
189
+ table_class = tables.ConfigContextTable
189
190
 
191
+ class AssignmentObjectFieldsPanel(object_detail.ObjectFieldsPanel):
192
+ def render_value(self, key, value, context):
193
+ if key == "dynamic_groups" and not settings.CONFIG_CONTEXT_DYNAMIC_GROUPS_ENABLED:
194
+ return None
195
+ if not value:
196
+ return helpers.HTML_NONE
190
197
 
191
- class ConfigContextView(generic.ObjectView):
192
- queryset = ConfigContext.objects.all()
198
+ items = []
199
+ for val in value.all():
200
+ rendered_val = helpers.hyperlinked_object(val)
201
+ items.append(rendered_val)
202
+
203
+ if not items:
204
+ return helpers.HTML_NONE
205
+
206
+ return format_html("<ul>{}</ul>", format_html_join("", "<li>{}</li>", ((item,) for item in items)))
207
+
208
+ object_detail_content = object_detail.ObjectDetailContent(
209
+ panels=(
210
+ object_detail.ObjectFieldsPanel(
211
+ weight=100,
212
+ section=SectionChoices.LEFT_HALF,
213
+ fields="__all__",
214
+ exclude_fields=[
215
+ "data",
216
+ "owner_content_type",
217
+ "owner_object_id",
218
+ ],
219
+ hide_if_unset=[
220
+ "owner",
221
+ ],
222
+ ),
223
+ object_detail.Panel(
224
+ weight=100,
225
+ section=SectionChoices.FULL_WIDTH,
226
+ label="Data",
227
+ header_extra_content_template_path="extras/inc/json_format.html",
228
+ body_content_template_path="extras/inc/configcontext_data.html",
229
+ ),
230
+ AssignmentObjectFieldsPanel(
231
+ weight=200,
232
+ section=SectionChoices.RIGHT_HALF,
233
+ label="Assignment",
234
+ fields=[
235
+ "locations",
236
+ "roles",
237
+ "device_types",
238
+ "platforms",
239
+ "cluster_groups",
240
+ "clusters",
241
+ "tenant_groups",
242
+ "tenants",
243
+ "device_redundancy_groups",
244
+ "dynamic_groups",
245
+ ],
246
+ ),
247
+ )
248
+ )
193
249
 
194
250
  def get_extra_context(self, request, instance):
195
251
  context = super().get_extra_context(request, instance)
196
252
  # Determine user's preferred output format
197
- if request.GET.get("format") in ["json", "yaml"]:
198
- context["format"] = request.GET.get("format")
253
+ if request.GET.get("data_format") in ["json", "yaml"]:
254
+ context["data_format"] = request.GET.get("data_format")
199
255
  if request.user.is_authenticated:
200
- request.user.set_config("extras.configcontext.format", context["format"], commit=True)
256
+ request.user.set_config("extras.configcontext.format", context["data_format"], commit=True)
201
257
  elif request.user.is_authenticated:
202
- context["format"] = request.user.get_config("extras.configcontext.format", "json")
258
+ context["data_format"] = request.user.get_config("extras.configcontext.format", "json")
203
259
  else:
204
- context["format"] = "json"
260
+ context["data_format"] = "json"
205
261
 
206
262
  return context
207
263
 
208
264
 
209
- class ConfigContextEditView(generic.ObjectEditView):
210
- queryset = ConfigContext.objects.all()
211
- model_form = forms.ConfigContextForm
212
- template_name = "extras/configcontext_edit.html"
213
-
214
-
215
- class ConfigContextBulkEditView(generic.BulkEditView):
216
- queryset = ConfigContext.objects.all()
217
- filterset = filters.ConfigContextFilterSet
218
- table = tables.ConfigContextTable
219
- form = forms.ConfigContextBulkEditForm
220
-
221
-
222
- class ConfigContextDeleteView(generic.ObjectDeleteView):
223
- queryset = ConfigContext.objects.all()
224
-
225
-
226
- class ConfigContextBulkDeleteView(generic.BulkDeleteView):
227
- queryset = ConfigContext.objects.all()
228
- table = tables.ConfigContextTable
229
- filterset = filters.ConfigContextFilterSet
230
-
231
-
232
265
  class ObjectConfigContextView(generic.ObjectView):
233
266
  base_template = None
234
267
  template_name = "extras/object_configcontext.html"
@@ -263,28 +296,26 @@ class ObjectConfigContextView(generic.ObjectView):
263
296
  # have an associated owner, such as a Git repository
264
297
 
265
298
 
266
- class ConfigContextSchemaListView(generic.ObjectListView):
267
- queryset = ConfigContextSchema.objects.all()
268
- filterset = filters.ConfigContextSchemaFilterSet
269
- filterset_form = forms.ConfigContextSchemaFilterForm
270
- table = tables.ConfigContextSchemaTable
271
- action_buttons = ("add",)
272
-
273
-
274
- class ConfigContextSchemaView(generic.ObjectView):
299
+ class ConfigContextSchemaUIViewSet(NautobotUIViewSet):
300
+ bulk_update_form_class = forms.ConfigContextSchemaBulkEditForm
301
+ filterset_class = filters.ConfigContextSchemaFilterSet
302
+ filterset_form_class = forms.ConfigContextSchemaFilterForm
303
+ form_class = forms.ConfigContextSchemaForm
275
304
  queryset = ConfigContextSchema.objects.all()
305
+ serializer_class = serializers.ConfigContextSchemaSerializer
306
+ table_class = tables.ConfigContextSchemaTable
276
307
 
277
308
  def get_extra_context(self, request, instance):
278
309
  context = super().get_extra_context(request, instance)
279
310
  # Determine user's preferred output format
280
- if request.GET.get("format") in ["json", "yaml"]:
281
- context["format"] = request.GET.get("format")
311
+ if request.GET.get("data_format") in ["json", "yaml"]:
312
+ context["data_format"] = request.GET.get("data_format")
282
313
  if request.user.is_authenticated:
283
- request.user.set_config("extras.configcontextschema.format", context["format"], commit=True)
314
+ request.user.set_config("extras.configcontextschema.format", context["data_format"], commit=True)
284
315
  elif request.user.is_authenticated:
285
- context["format"] = request.user.get_config("extras.configcontextschema.format", "json")
316
+ context["data_format"] = request.user.get_config("extras.configcontextschema.format", "json")
286
317
  else:
287
- context["format"] = "json"
318
+ context["data_format"] = "json"
288
319
 
289
320
  return context
290
321
 
@@ -372,29 +403,6 @@ class ConfigContextSchemaObjectValidationView(generic.ObjectView):
372
403
  }
373
404
 
374
405
 
375
- class ConfigContextSchemaEditView(generic.ObjectEditView):
376
- queryset = ConfigContextSchema.objects.all()
377
- model_form = forms.ConfigContextSchemaForm
378
- template_name = "extras/configcontextschema_edit.html"
379
-
380
-
381
- class ConfigContextSchemaBulkEditView(generic.BulkEditView):
382
- queryset = ConfigContextSchema.objects.all()
383
- filterset = filters.ConfigContextSchemaFilterSet
384
- table = tables.ConfigContextSchemaTable
385
- form = forms.ConfigContextSchemaBulkEditForm
386
-
387
-
388
- class ConfigContextSchemaDeleteView(generic.ObjectDeleteView):
389
- queryset = ConfigContextSchema.objects.all()
390
-
391
-
392
- class ConfigContextSchemaBulkDeleteView(generic.BulkDeleteView):
393
- queryset = ConfigContextSchema.objects.all()
394
- table = tables.ConfigContextSchemaTable
395
- filterset = filters.ConfigContextSchemaFilterSet
396
-
397
-
398
406
  #
399
407
  # Contacts
400
408
  #
@@ -1553,6 +1561,11 @@ class JobEditView(generic.ObjectEditView):
1553
1561
  model_form = forms.JobEditForm
1554
1562
  template_name = "extras/job_edit.html"
1555
1563
 
1564
+ def alter_obj(self, obj, request, url_args, url_kwargs):
1565
+ # Reload the job class to ensure we have the latest version
1566
+ get_job(obj.class_path, reload=True)
1567
+ return obj
1568
+
1556
1569
 
1557
1570
  class JobBulkEditView(generic.BulkEditView):
1558
1571
  queryset = JobModel.objects.all()
@@ -2077,44 +2090,12 @@ class JobHookUIViewSet(NautobotUIViewSet):
2077
2090
  #
2078
2091
 
2079
2092
 
2080
- def get_annotated_jobresult_queryset():
2081
- return (
2082
- JobResult.objects.defer("result")
2083
- .select_related("job_model", "user")
2084
- .annotate(
2085
- debug_log_count=count_related(
2086
- JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_DEBUG}
2087
- ),
2088
- success_log_count=count_related(
2089
- JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_SUCCESS}
2090
- ),
2091
- info_log_count=count_related(
2092
- JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_INFO}
2093
- ),
2094
- warning_log_count=count_related(
2095
- JobLogEntry, "job_result", filter_dict={"log_level": LogLevelChoices.LOG_WARNING}
2096
- ),
2097
- error_log_count=count_related(
2098
- JobLogEntry,
2099
- "job_result",
2100
- filter_dict={
2101
- "log_level__in": [
2102
- LogLevelChoices.LOG_FAILURE,
2103
- LogLevelChoices.LOG_ERROR,
2104
- LogLevelChoices.LOG_CRITICAL,
2105
- ],
2106
- },
2107
- ),
2108
- )
2109
- )
2110
-
2111
-
2112
2093
  class JobResultListView(generic.ObjectListView):
2113
2094
  """
2114
2095
  List JobResults
2115
2096
  """
2116
2097
 
2117
- queryset = get_annotated_jobresult_queryset()
2098
+ queryset = JobResult.objects.defer("result").select_related("job_model", "user")
2118
2099
  filterset = filters.JobResultFilterSet
2119
2100
  filterset_form = forms.JobResultFilterForm
2120
2101
  table = tables.JobResultTable
@@ -2126,7 +2107,7 @@ class JobResultDeleteView(generic.ObjectDeleteView):
2126
2107
 
2127
2108
 
2128
2109
  class JobResultBulkDeleteView(generic.BulkDeleteView):
2129
- queryset = get_annotated_jobresult_queryset()
2110
+ queryset = JobResult.objects.defer("result").select_related("job_model", "user")
2130
2111
  table = tables.JobResultTable
2131
2112
  filterset = filters.JobResultFilterSet
2132
2113
 
@@ -2519,24 +2500,15 @@ class RelationshipUIViewSet(NautobotUIViewSet):
2519
2500
  )
2520
2501
 
2521
2502
 
2522
- class RelationshipAssociationListView(generic.ObjectListView):
2503
+ class RelationshipAssociationUIViewSet(ObjectListViewMixin, ObjectDestroyViewMixin, ObjectBulkDestroyViewMixin):
2504
+ filterset_class = filters.RelationshipAssociationFilterSet
2505
+ filterset_form_class = forms.RelationshipAssociationFilterForm
2506
+ serializer_class = serializers.RelationshipAssociationSerializer
2507
+ table_class = tables.RelationshipAssociationTable
2523
2508
  queryset = RelationshipAssociation.objects.all()
2524
- filterset = filters.RelationshipAssociationFilterSet
2525
- filterset_form = forms.RelationshipAssociationFilterForm
2526
- table = tables.RelationshipAssociationTable
2527
2509
  action_buttons = ()
2528
2510
 
2529
2511
 
2530
- class RelationshipAssociationBulkDeleteView(generic.BulkDeleteView):
2531
- queryset = RelationshipAssociation.objects.all()
2532
- table = tables.RelationshipAssociationTable
2533
- filterset = filters.RelationshipAssociationFilterSet
2534
-
2535
-
2536
- class RelationshipAssociationDeleteView(generic.ObjectDeleteView):
2537
- queryset = RelationshipAssociation.objects.all()
2538
-
2539
-
2540
2512
  #
2541
2513
  # Roles
2542
2514
  #
@@ -3005,17 +2977,28 @@ class StatusUIViewSet(NautobotUIViewSet):
3005
2977
  #
3006
2978
 
3007
2979
 
3008
- class TagListView(generic.ObjectListView):
3009
- queryset = Tag.objects.annotate(items=count_related(TaggedItem, "tag"))
3010
- filterset = filters.TagFilterSet
3011
- filterset_form = forms.TagFilterForm
3012
- table = tables.TagTable
2980
+ class TagUIViewSet(NautobotUIViewSet):
2981
+ bulk_update_form_class = forms.TagBulkEditForm
2982
+ filterset_class = filters.TagFilterSet
2983
+ filterset_form_class = forms.TagFilterForm
2984
+ form_class = forms.TagForm
2985
+ queryset = Tag.objects.all()
2986
+ serializer_class = serializers.TagSerializer
2987
+ table_class = tables.TagTable
2988
+
2989
+ def alter_queryset(self, request):
2990
+ queryset = super().alter_queryset(request)
3013
2991
 
2992
+ # Only annotate for list, bulk_edit, bulk_delete views
2993
+ if self.action in ["list", "bulk_update", "bulk_destroy"]:
2994
+ queryset = queryset.annotate(items=count_related(TaggedItem, "tag"))
3014
2995
 
3015
- class TagView(generic.ObjectView):
3016
- queryset = Tag.objects.all()
2996
+ return queryset
3017
2997
 
3018
2998
  def get_extra_context(self, request, instance):
2999
+ # Only run this logic when retrieving a single object
3000
+ if instance is None or self.action != "retrieve":
3001
+ return super().get_extra_context(request, instance)
3019
3002
  tagged_items = (
3020
3003
  TaggedItem.objects.filter(tag=instance).select_related("content_type").prefetch_related("content_object")
3021
3004
  )
@@ -3036,34 +3019,6 @@ class TagView(generic.ObjectView):
3036
3019
  }
3037
3020
 
3038
3021
 
3039
- class TagEditView(generic.ObjectEditView):
3040
- queryset = Tag.objects.all()
3041
- model_form = forms.TagForm
3042
- template_name = "extras/tag_edit.html"
3043
-
3044
-
3045
- class TagDeleteView(generic.ObjectDeleteView):
3046
- queryset = Tag.objects.all()
3047
-
3048
-
3049
- class TagBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
3050
- queryset = Tag.objects.all()
3051
- table = tables.TagTable
3052
-
3053
-
3054
- class TagBulkEditView(generic.BulkEditView):
3055
- queryset = Tag.objects.annotate(items=count_related(TaggedItem, "tag"))
3056
- table = tables.TagTable
3057
- form = forms.TagBulkEditForm
3058
- filterset = filters.TagFilterSet
3059
-
3060
-
3061
- class TagBulkDeleteView(generic.BulkDeleteView):
3062
- queryset = Tag.objects.annotate(items=count_related(TaggedItem, "tag"))
3063
- table = tables.TagTable
3064
- filterset = filters.TagFilterSet
3065
-
3066
-
3067
3022
  #
3068
3023
  # Teams
3069
3024
  #
nautobot/ipam/models.py CHANGED
@@ -200,6 +200,8 @@ class VRF(PrimaryModel):
200
200
  instance.validated_save()
201
201
  return instance
202
202
 
203
+ add_device.alters_data = True
204
+
203
205
  def remove_device(self, device):
204
206
  """
205
207
  Remove a `device` from this VRF.
@@ -213,6 +215,8 @@ class VRF(PrimaryModel):
213
215
  instance = self.devices.through.objects.get(vrf=self, device=device)
214
216
  return instance.delete()
215
217
 
218
+ remove_device.alters_data = True
219
+
216
220
  def add_virtual_machine(self, virtual_machine, rd="", name=""):
217
221
  """
218
222
  Add a `virtual_machine` to this VRF, optionally overloading `rd` and `name`.
@@ -231,6 +235,8 @@ class VRF(PrimaryModel):
231
235
  instance.validated_save()
232
236
  return instance
233
237
 
238
+ add_virtual_machine.alters_data = True
239
+
234
240
  def remove_virtual_machine(self, virtual_machine):
235
241
  """
236
242
  Remove a `virtual_machine` from this VRF.
@@ -244,6 +250,8 @@ class VRF(PrimaryModel):
244
250
  instance = self.virtual_machines.through.objects.get(vrf=self, virtual_machine=virtual_machine)
245
251
  return instance.delete()
246
252
 
253
+ remove_virtual_machine.alters_data = True
254
+
247
255
  def add_virtual_device_context(self, virtual_device_context, rd="", name=""):
248
256
  """
249
257
  Add a `virtual_device_context` to this VRF, optionally overloading `rd` and `name`.
@@ -264,6 +272,8 @@ class VRF(PrimaryModel):
264
272
  instance.validated_save()
265
273
  return instance
266
274
 
275
+ add_virtual_device_context.alters_data = True
276
+
267
277
  def remove_virtual_device_context(self, virtual_device_context):
268
278
  """
269
279
  Remove a `virtual_device_context` from this VRF.
@@ -279,6 +289,8 @@ class VRF(PrimaryModel):
279
289
  )
280
290
  return instance.delete()
281
291
 
292
+ remove_virtual_device_context.alters_data = True
293
+
282
294
  def add_prefix(self, prefix):
283
295
  """
284
296
  Add a `prefix` to this VRF. Each object must be in the same Namespace.
@@ -293,6 +305,8 @@ class VRF(PrimaryModel):
293
305
  instance.validated_save()
294
306
  return instance
295
307
 
308
+ add_prefix.alters_data = True
309
+
296
310
  def remove_prefix(self, prefix):
297
311
  """
298
312
  Remove a `prefix` from this VRF.
@@ -306,6 +320,8 @@ class VRF(PrimaryModel):
306
320
  instance = self.prefixes.through.objects.get(vrf=self, prefix=prefix)
307
321
  return instance.delete()
308
322
 
323
+ remove_prefix.alters_data = True
324
+
309
325
 
310
326
  @extras_features("graphql")
311
327
  class VRFDeviceAssignment(BaseModel):
@@ -374,6 +390,8 @@ class VRFDeviceAssignment(BaseModel):
374
390
  "A VRFDeviceAssignment entry must be associated with a device, a virtual machine, or a virtual device context."
375
391
  )
376
392
 
393
+ clean.alters_data = True
394
+
377
395
 
378
396
  @extras_features("graphql")
379
397
  class VRFPrefixAssignment(BaseModel):
@@ -640,6 +658,8 @@ class Prefix(PrimaryModel):
640
658
  self.prefix_length = prefix.prefixlen
641
659
  self.ip_version = prefix.version
642
660
 
661
+ _deconstruct_prefix.alters_data = True
662
+
643
663
  def delete(self, *args, **kwargs):
644
664
  """
645
665
  A Prefix with children will be impossible to delete and raise a `ProtectedError`.
@@ -705,6 +725,8 @@ class Prefix(PrimaryModel):
705
725
  return parent
706
726
  return None
707
727
 
728
+ get_parent.alters_data = True
729
+
708
730
  def clean(self):
709
731
  if self.prefix is not None: # missing network/prefix_length will be caught by super().clean()
710
732
  # Clear host bits from prefix
@@ -716,6 +738,8 @@ class Prefix(PrimaryModel):
716
738
 
717
739
  super().clean()
718
740
 
741
+ clean.alters_data = True
742
+
719
743
  def save(self, *args, **kwargs):
720
744
  self.clean()
721
745
 
@@ -821,6 +845,8 @@ class Prefix(PrimaryModel):
821
845
 
822
846
  return query.update(parent=self)
823
847
 
848
+ reparent_subnets.alters_data = True
849
+
824
850
  def reparent_ips(self):
825
851
  """Determine the list of child IPAddresses and set the parent to self."""
826
852
  query = IPAddress.objects.select_for_update().filter(
@@ -832,6 +858,8 @@ class Prefix(PrimaryModel):
832
858
 
833
859
  return query.update(parent=self)
834
860
 
861
+ reparent_ips.alters_data = True
862
+
835
863
  def supernets(self, direct=False, include_self=False, for_update=False):
836
864
  """
837
865
  Return supernets of this Prefix.
@@ -861,7 +889,7 @@ class Prefix(PrimaryModel):
861
889
  prefix_length__lte=self.prefix_length,
862
890
  network__lte=self.network,
863
891
  broadcast__gte=self.broadcast,
864
- namespace=self.namespace,
892
+ namespace_id=self.namespace_id,
865
893
  )
866
894
 
867
895
  return supernets
@@ -893,7 +921,7 @@ class Prefix(PrimaryModel):
893
921
  ip_version=self.ip_version,
894
922
  network__gte=self.network,
895
923
  broadcast__lte=self.broadcast,
896
- namespace=self.namespace,
924
+ namespace_id=self.namespace_id,
897
925
  )
898
926
 
899
927
  def is_child_node(self):
@@ -1065,6 +1093,17 @@ class Prefix(PrimaryModel):
1065
1093
  For prefixes containing IP addresses and/or pools, pools are considered fully utilized while
1066
1094
  only IP addresses that are not contained within pools are added to the utilization.
1067
1095
 
1096
+ It is recommended that when using this method you add the following prefetch to the queryset when dealing with
1097
+ multiple prefixes to ensure good performance:
1098
+
1099
+ ```
1100
+ prefetch_related(
1101
+ Prefetch(
1102
+ "children", queryset=Prefix.objects.only("network", "prefix_length", "parent_id").order_by()
1103
+ )
1104
+ )
1105
+ ```
1106
+
1068
1107
  Returns:
1069
1108
  UtilizationData (namedtuple): (numerator, denominator)
1070
1109
  """
@@ -1077,7 +1116,7 @@ class Prefix(PrimaryModel):
1077
1116
  # change this when that is the case, see #3873 for historical context.
1078
1117
  if self.type != choices.PrefixTypeChoices.TYPE_CONTAINER:
1079
1118
  pool_ips = IPAddress.objects.filter(
1080
- parent__namespace=self.namespace,
1119
+ parent__namespace_id=self.namespace_id,
1081
1120
  ip_version=self.ip_version,
1082
1121
  host__gte=self.network,
1083
1122
  host__lte=self.broadcast,
@@ -1085,7 +1124,11 @@ class Prefix(PrimaryModel):
1085
1124
  child_ips = netaddr.IPSet(pool_ips)
1086
1125
 
1087
1126
  if self.type != choices.PrefixTypeChoices.TYPE_POOL:
1088
- child_prefixes = netaddr.IPSet(p.prefix for p in self.children.only("network", "prefix_length").iterator())
1127
+ # Using self.children.all over self.children.iterator (with chunk_size given or not) consistently shaves
1128
+ # off around 200 extra SQL queries and shows better performance.
1129
+ # Also note that this is meant to be used in conjunction with a Prefetch on an only query. This query is
1130
+ # performed in nautobot.ipam.tables.PrefixDetailTable.
1131
+ child_prefixes = netaddr.IPSet(p.prefix for p in self.children.all())
1089
1132
 
1090
1133
  numerator_set = child_ips | child_prefixes
1091
1134
 
@@ -1228,6 +1271,8 @@ class IPAddress(PrimaryModel):
1228
1271
  self.mask_length = address.prefixlen
1229
1272
  self.ip_version = address.version
1230
1273
 
1274
+ _deconstruct_address.alters_data = True
1275
+
1231
1276
  natural_key_field_names = ["parent__namespace", "host"]
1232
1277
 
1233
1278
  def _get_closest_parent(self):
@@ -1289,6 +1334,8 @@ class IPAddress(PrimaryModel):
1289
1334
 
1290
1335
  super().clean()
1291
1336
 
1337
+ clean.alters_data = True
1338
+
1292
1339
  def save(self, *args, **kwargs):
1293
1340
  self.clean() # MUST do data fixup as above
1294
1341
 
nautobot/ipam/tables.py CHANGED
@@ -1,5 +1,8 @@
1
+ from django.db.models import Prefetch, QuerySet
1
2
  from django.utils.safestring import mark_safe
2
3
  import django_tables2 as tables
4
+ from django_tables2.data import TableData
5
+ from django_tables2.rows import BoundRows
3
6
  from django_tables2.utils import Accessor
4
7
 
5
8
  from nautobot.core.tables import (
@@ -47,7 +50,9 @@ UTILIZATION_GRAPH = """
47
50
  # object: the base ancestor Prefix, in the case of PrefixDetailTable, else None
48
51
  PREFIX_COPY_LINK = """
49
52
  {% load helpers %}
53
+ {% if not table.hide_hierarchy_ui %}
50
54
  {% tree_hierarchy_ui_representation record.ancestors.count|as_range table.hide_hierarchy_ui base_tree_depth|default:0 %}
55
+ {% endif %}
51
56
  <span class="hover_copy">
52
57
  <a href="\
53
58
  {% if record.present_in_database %}\
@@ -418,6 +423,20 @@ class PrefixDetailTable(PrefixTable):
418
423
  tenant = TenantColumn()
419
424
  tags = TagColumn(url_name="ipam:prefix_list")
420
425
 
426
+ def __init__(self, *args, **kwargs):
427
+ super().__init__(*args, **kwargs)
428
+ # Conditionally prefetch children for the utilization calculation if that column is visible.
429
+ if self.columns["utilization"].visible and isinstance(self.data.data, QuerySet):
430
+ self.data = TableData.from_data(
431
+ self.data.data.prefetch_related(
432
+ Prefetch(
433
+ "children", queryset=Prefix.objects.only("network", "prefix_length", "parent_id").order_by()
434
+ )
435
+ )
436
+ )
437
+ self.data.set_table(self)
438
+ self.rows = BoundRows(data=self.data, table=self, pinned_data=self.pinned_data)
439
+
421
440
  class Meta(PrefixTable.Meta):
422
441
  fields = (
423
442
  "pk",