nautobot 2.4.8__py3-none-any.whl → 2.4.10__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 (387) hide show
  1. nautobot/circuits/templates/circuits/circuittype.html +1 -1
  2. nautobot/circuits/templates/circuits/circuittype_retrieve.html +1 -39
  3. nautobot/circuits/views.py +18 -23
  4. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +4 -111
  5. nautobot/cloud/views.py +56 -25
  6. nautobot/core/api/parsers.py +56 -2
  7. nautobot/core/celery/schedulers.py +1 -0
  8. nautobot/core/filters.py +1 -1
  9. nautobot/core/graphql/schema.py +1 -0
  10. nautobot/core/jobs/__init__.py +14 -3
  11. nautobot/core/models/__init__.py +2 -0
  12. nautobot/core/tables.py +13 -6
  13. nautobot/core/testing/views.py +27 -2
  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 +113 -0
  17. nautobot/core/tests/test_ui.py +53 -1
  18. nautobot/core/tests/test_utils.py +11 -0
  19. nautobot/core/tests/test_views.py +73 -0
  20. nautobot/core/ui/object_detail.py +19 -12
  21. nautobot/core/urls.py +2 -2
  22. nautobot/core/utils/filtering.py +3 -0
  23. nautobot/core/views/__init__.py +21 -0
  24. nautobot/core/views/renderers.py +1 -1
  25. nautobot/dcim/forms.py +10 -0
  26. nautobot/dcim/models/device_component_templates.py +4 -0
  27. nautobot/dcim/models/device_components.py +12 -0
  28. nautobot/dcim/models/devices.py +6 -0
  29. nautobot/dcim/templates/dcim/devicefamily_retrieve.html +1 -43
  30. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +1 -59
  31. nautobot/dcim/templates/dcim/devicetype.html +2 -217
  32. nautobot/dcim/templates/dcim/devicetype_edit.html +2 -32
  33. nautobot/dcim/templates/dcim/devicetype_retrieve.html +217 -0
  34. nautobot/dcim/templates/dcim/devicetype_update.html +32 -0
  35. nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
  36. nautobot/dcim/templates/dcim/modulebay_retrieve.html +1 -84
  37. nautobot/dcim/templates/dcim/rack_elevation.html +14 -0
  38. nautobot/dcim/templates/dcim/rackreservation_retrieve.html +0 -68
  39. nautobot/dcim/tests/integration/test_fileinputpicker.py +1 -1
  40. nautobot/dcim/urls.py +1 -36
  41. nautobot/dcim/views.py +133 -81
  42. nautobot/extras/api/views.py +4 -6
  43. nautobot/extras/context_managers.py +2 -2
  44. nautobot/extras/migrations/0024_job_data_migration.py +1 -1
  45. nautobot/extras/models/customfields.py +2 -0
  46. nautobot/extras/models/datasources.py +8 -0
  47. nautobot/extras/models/groups.py +18 -0
  48. nautobot/extras/models/jobs.py +92 -62
  49. nautobot/extras/models/metadata.py +2 -0
  50. nautobot/extras/models/models.py +4 -0
  51. nautobot/extras/models/secrets.py +7 -0
  52. nautobot/extras/secrets/__init__.py +14 -0
  53. nautobot/extras/tables.py +11 -1
  54. nautobot/extras/templates/extras/computedfield_retrieve.html +1 -55
  55. nautobot/extras/templates/extras/inc/job_tiles.html +1 -1
  56. nautobot/extras/templates/extras/jobresult.html +1 -1
  57. nautobot/extras/templates/extras/metadatatype_retrieve.html +1 -66
  58. nautobot/extras/templates/extras/scheduledjob.html +25 -9
  59. nautobot/extras/tests/test_api.py +1 -1
  60. nautobot/extras/tests/test_context_managers.py +20 -0
  61. nautobot/extras/tests/test_models.py +26 -0
  62. nautobot/extras/tests/test_views.py +15 -2
  63. nautobot/extras/utils.py +18 -16
  64. nautobot/extras/views.py +65 -26
  65. nautobot/ipam/models.py +32 -0
  66. nautobot/ipam/tables.py +3 -4
  67. nautobot/project-static/docs/404.html +4 -4
  68. nautobot/project-static/docs/apps/index.html +4 -4
  69. nautobot/project-static/docs/apps/nautobot-apps.html +4 -4
  70. nautobot/project-static/docs/assets/javascripts/{bundle.c8b220af.min.js → bundle.13a4f30d.min.js} +4 -4
  71. nautobot/project-static/docs/assets/javascripts/{bundle.c8b220af.min.js.map → bundle.13a4f30d.min.js.map} +2 -2
  72. nautobot/project-static/docs/assets/javascripts/workers/{search.f8cc74c7.min.js → search.d50fe291.min.js} +2 -2
  73. nautobot/project-static/docs/assets/javascripts/workers/{search.f8cc74c7.min.js.map → search.d50fe291.min.js.map} +1 -1
  74. nautobot/project-static/docs/assets/stylesheets/{main.2afb09e1.min.css → main.342714a4.min.css} +1 -1
  75. nautobot/project-static/docs/assets/stylesheets/{main.2afb09e1.min.css.map → main.342714a4.min.css.map} +1 -1
  76. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +4 -4
  77. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +4 -4
  78. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +4 -4
  79. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +4 -4
  80. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +4 -4
  81. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +4 -4
  82. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +4 -4
  83. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +4 -4
  84. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +4 -4
  85. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +4 -4
  86. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +4 -4
  87. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +4 -4
  88. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +4 -4
  89. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +4 -4
  90. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +4 -4
  91. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +4 -4
  92. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +4 -4
  93. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +4 -4
  94. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +4 -4
  95. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +4 -4
  96. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +6 -8
  97. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +4 -4
  98. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +36 -6
  99. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +4 -4
  100. nautobot/project-static/docs/development/apps/api/configuration-view.html +4 -4
  101. nautobot/project-static/docs/development/apps/api/database-backend-config.html +4 -4
  102. nautobot/project-static/docs/development/apps/api/models/django-admin.html +4 -4
  103. nautobot/project-static/docs/development/apps/api/models/global-search.html +4 -4
  104. nautobot/project-static/docs/development/apps/api/models/graphql.html +4 -4
  105. nautobot/project-static/docs/development/apps/api/models/index.html +4 -4
  106. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +4 -4
  107. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +4 -4
  108. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +4 -4
  109. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +4 -4
  110. nautobot/project-static/docs/development/apps/api/platform-features/index.html +4 -4
  111. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +4 -4
  112. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +4 -4
  113. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +4 -4
  114. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +43 -42
  115. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +4 -4
  116. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +4 -4
  117. nautobot/project-static/docs/development/apps/api/prometheus.html +4 -4
  118. nautobot/project-static/docs/development/apps/api/setup.html +4 -4
  119. nautobot/project-static/docs/development/apps/api/testing.html +4 -4
  120. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +4 -4
  121. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +4 -4
  122. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +4 -4
  123. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +4 -4
  124. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +4 -4
  125. nautobot/project-static/docs/development/apps/api/views/base-template.html +4 -4
  126. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +4 -4
  127. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +4 -4
  128. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +4 -4
  129. nautobot/project-static/docs/development/apps/api/views/index.html +4 -4
  130. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +4 -4
  131. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +4 -4
  132. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +4 -4
  133. nautobot/project-static/docs/development/apps/api/views/notes.html +4 -4
  134. nautobot/project-static/docs/development/apps/api/views/rest-api.html +4 -4
  135. nautobot/project-static/docs/development/apps/api/views/urls.html +4 -4
  136. nautobot/project-static/docs/development/apps/index.html +4 -4
  137. nautobot/project-static/docs/development/apps/migration/code-updates.html +4 -4
  138. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +4 -4
  139. nautobot/project-static/docs/development/apps/migration/from-v1.html +4 -4
  140. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +4 -4
  141. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +4 -4
  142. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +4 -4
  143. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +4 -4
  144. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +4 -4
  145. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +4 -4
  146. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +4 -4
  147. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +4 -4
  148. nautobot/project-static/docs/development/apps/porting-from-netbox.html +4 -4
  149. nautobot/project-static/docs/development/core/application-registry.html +4 -4
  150. nautobot/project-static/docs/development/core/best-practices.html +4 -4
  151. nautobot/project-static/docs/development/core/bootstrap-ui.html +4 -4
  152. nautobot/project-static/docs/development/core/caching.html +4 -4
  153. nautobot/project-static/docs/development/core/controllers.html +4 -4
  154. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +41 -55
  155. nautobot/project-static/docs/development/core/generic-views.html +4 -4
  156. nautobot/project-static/docs/development/core/getting-started.html +4 -4
  157. nautobot/project-static/docs/development/core/homepage.html +4 -4
  158. nautobot/project-static/docs/development/core/index.html +4 -4
  159. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +4 -4
  160. nautobot/project-static/docs/development/core/model-checklist.html +4 -4
  161. nautobot/project-static/docs/development/core/model-features.html +4 -4
  162. nautobot/project-static/docs/development/core/natural-keys.html +4 -4
  163. nautobot/project-static/docs/development/core/navigation-menu.html +4 -4
  164. nautobot/project-static/docs/development/core/release-checklist.html +4 -4
  165. nautobot/project-static/docs/development/core/role-internals.html +4 -4
  166. nautobot/project-static/docs/development/core/settings.html +4 -4
  167. nautobot/project-static/docs/development/core/style-guide.html +4 -4
  168. nautobot/project-static/docs/development/core/templates.html +4 -4
  169. nautobot/project-static/docs/development/core/testing.html +4 -4
  170. nautobot/project-static/docs/development/core/ui-component-framework.html +4 -4
  171. nautobot/project-static/docs/development/core/user-preferences.html +4 -4
  172. nautobot/project-static/docs/development/index.html +4 -4
  173. nautobot/project-static/docs/development/jobs/getting-started.html +4 -4
  174. nautobot/project-static/docs/development/jobs/index.html +4 -4
  175. nautobot/project-static/docs/development/jobs/installation.html +4 -4
  176. nautobot/project-static/docs/development/jobs/job-extensions.html +4 -4
  177. nautobot/project-static/docs/development/jobs/job-logging.html +4 -4
  178. nautobot/project-static/docs/development/jobs/job-patterns.html +4 -4
  179. nautobot/project-static/docs/development/jobs/job-structure.html +4 -4
  180. nautobot/project-static/docs/development/jobs/migration/from-v1.html +4 -4
  181. nautobot/project-static/docs/development/jobs/testing.html +4 -4
  182. nautobot/project-static/docs/index.html +4 -4
  183. nautobot/project-static/docs/overview/application_stack.html +4 -4
  184. nautobot/project-static/docs/overview/design_philosophy.html +4 -4
  185. nautobot/project-static/docs/release-notes/index.html +4 -4
  186. nautobot/project-static/docs/release-notes/version-1.0.html +4 -4
  187. nautobot/project-static/docs/release-notes/version-1.1.html +4 -4
  188. nautobot/project-static/docs/release-notes/version-1.2.html +4 -4
  189. nautobot/project-static/docs/release-notes/version-1.3.html +4 -4
  190. nautobot/project-static/docs/release-notes/version-1.4.html +4 -4
  191. nautobot/project-static/docs/release-notes/version-1.5.html +4 -4
  192. nautobot/project-static/docs/release-notes/version-1.6.html +301 -4
  193. nautobot/project-static/docs/release-notes/version-2.0.html +4 -4
  194. nautobot/project-static/docs/release-notes/version-2.1.html +4 -4
  195. nautobot/project-static/docs/release-notes/version-2.2.html +4 -4
  196. nautobot/project-static/docs/release-notes/version-2.3.html +4 -4
  197. nautobot/project-static/docs/release-notes/version-2.4.html +291 -4
  198. nautobot/project-static/docs/requirements.txt +1 -1
  199. nautobot/project-static/docs/search/search_index.json +1 -1
  200. nautobot/project-static/docs/sitemap.xml +298 -298
  201. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  202. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +4 -4
  203. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +4 -4
  204. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +4 -4
  205. nautobot/project-static/docs/user-guide/administration/configuration/index.html +4 -4
  206. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +4 -4
  207. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +4 -4
  208. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +4 -4
  209. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +4 -4
  210. nautobot/project-static/docs/user-guide/administration/guides/docker.html +4 -4
  211. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +4 -4
  212. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +4 -4
  213. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +4 -4
  214. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +4 -4
  215. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +4 -4
  216. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +4 -4
  217. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +4 -4
  218. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +4 -4
  219. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +4 -4
  220. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +4 -4
  221. nautobot/project-static/docs/user-guide/administration/installation/index.html +4 -4
  222. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +4 -4
  223. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +4 -4
  224. nautobot/project-static/docs/user-guide/administration/installation/services.html +4 -4
  225. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +4 -4
  226. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +4 -4
  227. nautobot/project-static/docs/user-guide/administration/security/index.html +4 -5
  228. nautobot/project-static/docs/user-guide/administration/security/notices.html +117 -5
  229. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +4 -4
  230. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +4 -4
  231. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +4 -4
  232. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +4 -4
  233. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +4 -4
  234. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +4 -4
  235. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +4 -4
  236. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +4 -4
  237. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +4 -4
  238. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +4 -4
  239. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +4 -4
  240. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +4 -4
  241. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +4 -4
  242. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +4 -4
  243. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +4 -4
  244. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +4 -4
  245. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +4 -4
  246. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +4 -4
  247. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +4 -4
  248. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +4 -4
  249. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +4 -4
  250. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +4 -4
  251. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +4 -4
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +4 -4
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +4 -4
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +4 -4
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +4 -4
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +4 -4
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +4 -4
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +4 -4
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +4 -4
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +4 -4
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +4 -4
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +4 -4
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +4 -4
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +4 -4
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +4 -4
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +4 -4
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +4 -4
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +4 -4
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +4 -4
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +4 -4
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +4 -4
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +4 -4
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +4 -4
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +4 -4
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +4 -4
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +4 -4
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +4 -4
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +4 -4
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +4 -4
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +4 -4
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +4 -4
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +4 -4
  283. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +4 -4
  284. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +4 -4
  285. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +4 -4
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +4 -4
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +4 -4
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +4 -4
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +4 -4
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -4
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +4 -4
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +4 -4
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +4 -4
  294. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +4 -4
  295. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +4 -4
  296. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +4 -4
  297. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +4 -4
  298. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +4 -4
  299. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +4 -4
  300. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +4 -4
  301. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +4 -4
  302. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +4 -4
  303. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +4 -4
  304. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +4 -4
  305. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +4 -4
  306. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +4 -4
  307. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +4 -4
  308. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +4 -4
  309. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +4 -4
  310. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +4 -4
  311. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +4 -4
  312. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +4 -4
  313. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +4 -4
  314. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +4 -4
  315. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +4 -4
  316. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +4 -4
  317. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +4 -4
  318. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +4 -4
  319. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +4 -4
  320. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +4 -4
  321. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +4 -4
  322. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +4 -4
  323. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +4 -4
  324. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +4 -4
  325. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -4
  326. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +4 -4
  327. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +4 -4
  328. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +4 -4
  329. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +4 -4
  330. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +4 -4
  331. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +4 -4
  332. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +4 -4
  333. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +4 -4
  334. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +4 -4
  335. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +4 -4
  336. nautobot/project-static/docs/user-guide/index.html +4 -4
  337. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +4 -4
  338. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +4 -4
  339. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +4 -4
  340. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +4 -4
  341. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +4 -4
  342. nautobot/project-static/docs/user-guide/platform-functionality/events.html +4 -4
  343. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +4 -4
  344. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +4 -4
  345. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +4 -4
  346. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +4 -4
  347. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +4 -4
  348. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +4 -4
  349. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +4 -4
  350. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +4 -4
  351. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +4 -4
  352. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +4 -4
  353. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +4 -4
  354. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +4 -4
  355. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +4 -4
  356. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +4 -4
  357. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +4 -4
  358. nautobot/project-static/docs/user-guide/platform-functionality/note.html +4 -4
  359. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +4 -4
  360. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +4 -4
  361. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +4 -4
  362. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +4 -4
  363. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +4 -4
  364. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +4 -4
  365. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +4 -4
  366. nautobot/project-static/docs/user-guide/platform-functionality/role.html +4 -4
  367. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +4 -4
  368. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +4 -4
  369. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +4 -4
  370. nautobot/project-static/docs/user-guide/platform-functionality/status.html +4 -4
  371. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +4 -4
  372. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +4 -4
  373. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +4 -4
  374. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +4 -4
  375. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +4 -4
  376. nautobot/project-static/js/forms.js +0 -14
  377. nautobot/users/models.py +4 -0
  378. nautobot/virtualization/models.py +4 -0
  379. nautobot/wireless/tables.py +1 -0
  380. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +1 -55
  381. nautobot/wireless/views.py +33 -28
  382. {nautobot-2.4.8.dist-info → nautobot-2.4.10.dist-info}/METADATA +4 -4
  383. {nautobot-2.4.8.dist-info → nautobot-2.4.10.dist-info}/RECORD +387 -384
  384. {nautobot-2.4.8.dist-info → nautobot-2.4.10.dist-info}/LICENSE.txt +0 -0
  385. {nautobot-2.4.8.dist-info → nautobot-2.4.10.dist-info}/NOTICE +0 -0
  386. {nautobot-2.4.8.dist-info → nautobot-2.4.10.dist-info}/WHEEL +0 -0
  387. {nautobot-2.4.8.dist-info → nautobot-2.4.10.dist-info}/entry_points.txt +0 -0
@@ -1,2 +1,2 @@
1
1
  {% extends 'circuits/circuittype_retrieve.html' %}
2
- {% comment %}2.0 TODO: remove this template, which only exists for backward compatibility with 1.3 and earlier{% endcomment %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -1,40 +1,2 @@
1
1
  {% extends 'generic/object_retrieve.html' %}
2
- {% load helpers %}
3
-
4
- {% block content_left_page %}
5
- <div class="panel panel-default">
6
- <div class="panel-heading">
7
- <strong>Circuit Type</strong>
8
- </div>
9
- <table class="table table-hover panel-body attr-table">
10
- <tr>
11
- <td>Description</td>
12
- <td>{{ object.description|placeholder }}</td>
13
- </tr>
14
- <tr>
15
- <td>Circuits</td>
16
- <td>
17
- <a href="{% url 'circuits:circuit_list' %}?circuit_type={{ object.pk }}">{{ circuits_table.rows|length }}</a>
18
- </td>
19
- </tr>
20
- </table>
21
- </div>
22
- {% endblock content_left_page %}
23
-
24
- {% block content_full_width_page %}
25
- <div class="panel panel-default">
26
- <div class="panel-heading">
27
- <strong>Circuits</strong>
28
- </div>
29
- {% include 'inc/table.html' with table=circuits_table %}
30
- {% if perms.circuits.add_circuit %}
31
- <div class="panel-footer text-right noprint">
32
- <a href="{% url 'circuits:circuit_add' %}?type={{ object.pk }}" class="btn btn-xs btn-primary">
33
- <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add circuit
34
- </a>
35
- </div>
36
- {% endif %}
37
- </div>
38
- {% include 'inc/paginator.html' with paginator=circuits_table.paginator page=circuits_table.page %}
39
- <div class="row"></div>
40
- {% endblock content_full_width_page %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -2,7 +2,6 @@ from django.contrib import messages
2
2
  from django.db import transaction
3
3
  from django.shortcuts import get_object_or_404, redirect, render
4
4
  from django.utils.html import format_html
5
- from django_tables2 import RequestConfig
6
5
 
7
6
  from nautobot.core.forms import ConfirmationForm
8
7
  from nautobot.core.templatetags.helpers import bettertitle, humanize_speed, placeholder
@@ -14,7 +13,6 @@ from nautobot.core.ui.object_detail import (
14
13
  )
15
14
  from nautobot.core.ui.utils import render_component_template
16
15
  from nautobot.core.views import generic
17
- from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
18
16
  from nautobot.core.views.viewsets import NautobotUIViewSet
19
17
 
20
18
  from . import filters, forms, tables
@@ -32,27 +30,24 @@ class CircuitTypeUIViewSet(NautobotUIViewSet):
32
30
  serializer_class = serializers.CircuitTypeSerializer
33
31
  table_class = tables.CircuitTypeTable
34
32
 
35
- def get_extra_context(self, request, instance):
36
- # Circuits
37
- context = super().get_extra_context(request, instance)
38
- if self.action == "retrieve":
39
- circuits = (
40
- Circuit.objects.restrict(request.user, "view")
41
- .filter(circuit_type=instance)
42
- .select_related("circuit_type", "tenant")
43
- .prefetch_related("circuit_terminations__location")
44
- )
45
-
46
- circuits_table = tables.CircuitTable(circuits)
47
- circuits_table.columns.hide("circuit_type")
48
-
49
- paginate = {
50
- "paginator_class": EnhancedPaginator,
51
- "per_page": get_paginate_count(request),
52
- }
53
- RequestConfig(request, paginate).configure(circuits_table)
54
- context["circuits_table"] = circuits_table
55
- return context
33
+ object_detail_content = ObjectDetailContent(
34
+ panels=(
35
+ ObjectFieldsPanel(
36
+ section=SectionChoices.LEFT_HALF,
37
+ weight=100,
38
+ fields="__all__",
39
+ ),
40
+ ObjectsTablePanel(
41
+ section=SectionChoices.FULL_WIDTH,
42
+ weight=100,
43
+ table_class=tables.CircuitTable,
44
+ table_filter="circuit_type",
45
+ select_related_fields=["circuit_type", "tenant"],
46
+ prefetch_related_fields=["circuit_terminations__location"],
47
+ exclude_columns=["circuit_type"],
48
+ ),
49
+ ),
50
+ )
56
51
 
57
52
 
58
53
  class CircuitTerminationUIViewSet(NautobotUIViewSet):
@@ -1,111 +1,4 @@
1
- {% extends 'generic/object_retrieve.html' %}
2
- {% load helpers %}
3
-
4
- {% block extra_breadcrumbs %}
5
- <li><a href="{% url 'cloud:cloudresourcetype_list' %}?provider={{ object.provider.pk }}">{{ object.provider }}</a></li>
6
- {% endblock extra_breadcrumbs %}
7
-
8
- {% block extra_nav_tabs %}
9
- {% if networks_count %}
10
- <li role="presentation"{% if request.GET.tab == 'networks' %} class="active"{% endif %}>
11
- <a href="{{ object.get_absolute_url }}#networks" onclick="switch_tab(this.href, reload=false)" aria-controls="networks" role="tab" data-toggle="tab">
12
- Cloud Networks {% badge networks_count %}
13
- </a>
14
- </li>
15
- {% endif %}
16
- {% if services_count %}
17
- <li role="presentation"{% if request.GET.tab == 'services' %} class="active"{% endif %}>
18
- <a href="{{ object.get_absolute_url }}#services" onclick="switch_tab(this.href, reload=false)" aria-controls="services" role="tab" data-toggle="tab">
19
- Cloud Services {% badge services_count %}
20
- </a>
21
- </li>
22
- {% endif %}
23
- {% endblock extra_nav_tabs %}
24
-
25
- {% block content_left_page %}
26
- <div class="panel panel-default">
27
- <div class="panel-heading">
28
- <strong>Cloud Resource Type</strong>
29
- </div>
30
- <table class="table table-hover panel-body attr-table">
31
- <tr>
32
- <td>Name</td>
33
- <td>
34
- {{ object.name }}
35
- </td>
36
- </tr>
37
- <tr>
38
- <td>Provider</td>
39
- <td>{{ object.provider|hyperlinked_object }}</td>
40
- </tr>
41
- <tr>
42
- <td>Content Type(s)</td>
43
- <td>
44
- {% if object.content_types.all %}
45
- <ul>
46
- {% for content_type in object.content_types.all %}
47
- <li>{{ content_type }}</li>
48
- {% endfor %}
49
- </ul>
50
- {% else %}
51
- {{ None|placeholder }}
52
- {% endif %}
53
- </td>
54
- </tr>
55
- <tr>
56
- <td>Description</td>
57
- <td>{{ object.description|placeholder }}</td>
58
- </tr>
59
- </table>
60
- </div>
61
- {% endblock content_left_page %}
62
-
63
- {% block content_right_page %}
64
- <div class="panel panel-default">
65
- <div class="panel-heading">
66
- <strong>Config Schema</strong>
67
- </div>
68
- <div class="panel-body">
69
- <pre>{{ object.config_schema|render_json }}</pre>
70
- </div>
71
- </div>
72
- {% endblock content_right_page %}
73
-
74
- {% block extra_tab_content %}
75
- {% if networks_count %}
76
- <div id="networks" role="tabpanel" class="tab-pane {% if not active_tab and not request.GET.tab or request.GET.tab == "networks" %}active{% else %}fade{% endif %}">
77
- <div class="panel panel-default">
78
- <div class="panel-heading">
79
- <strong>Cloud Networks</strong>
80
- </div>
81
- {% include 'inc/table.html' with table=networks_table %}
82
- {% if perms.cloud.add_cloudnetwork %}
83
- <div class="panel-footer text-right noprint">
84
- <a href="{% url 'cloud:cloudnetwork_add' %}?cloud_resource_type={{ object.pk }}" class="btn btn-xs btn-primary">
85
- <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add cloud network
86
- </a>
87
- </div>
88
- {% endif %}
89
- </div>
90
- {% include 'inc/paginator.html' with paginator=networks_table.paginator page=networks_table.page %}
91
- </div>
92
- {% endif %}
93
- {% if services_count %}
94
- <div id="services" role="tabpanel" class="tab-pane {% if not active_tab and not request.GET.tab or request.GET.tab == "services" %}active{% else %}fade{% endif %}">
95
- <div class="panel panel-default">
96
- <div class="panel-heading">
97
- <strong>Cloud Services</strong>
98
- </div>
99
- {% include 'inc/table.html' with table=services_table %}
100
- {% if perms.cloud.add_cloudservice %}
101
- <div class="panel-footer text-right noprint">
102
- <a href="{% url 'cloud:cloudservice_add' %}?cloud_resource_type={{ object.pk }}" class="btn btn-xs btn-primary">
103
- <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add cloud service
104
- </a>
105
- </div>
106
- {% endif %}
107
- </div>
108
- {% include 'inc/paginator.html' with paginator=services_table.paginator page=services_table.page %}
109
- </div>
110
- {% endif %}
111
- {% endblock extra_tab_content %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
+ {% block extra_breadcrumbs %}
3
+ <li><a href="{% url 'cloud:cloudresourcetype_list' %}?provider={{ object.provider.pk }}">{{ object.provider }}</a></li>
4
+ {% endblock extra_breadcrumbs %}
nautobot/cloud/views.py CHANGED
@@ -1,4 +1,3 @@
1
- from django_tables2 import RequestConfig
2
1
  from rest_framework.decorators import action
3
2
  from rest_framework.response import Response
4
3
 
@@ -33,7 +32,6 @@ from nautobot.cloud.models import CloudAccount, CloudNetwork, CloudResourceType,
33
32
  from nautobot.cloud.tables import CloudAccountTable, CloudNetworkTable, CloudResourceTypeTable, CloudServiceTable
34
33
  from nautobot.core.ui import object_detail
35
34
  from nautobot.core.ui.choices import SectionChoices
36
- from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
37
35
  from nautobot.core.views.viewsets import NautobotUIViewSet
38
36
  from nautobot.ipam.tables import PrefixTable
39
37
 
@@ -181,31 +179,64 @@ class CloudResourceTypeUIViewSet(NautobotUIViewSet):
181
179
  form_class = CloudResourceTypeForm
182
180
  bulk_update_form_class = CloudResourceTypeBulkEditForm
183
181
 
184
- def get_extra_context(self, request, instance=None):
185
- context = super().get_extra_context(request, instance)
186
- if self.action == "retrieve":
187
- paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
188
-
189
- networks = instance.cloud_networks.restrict(request.user, "view")
190
- networks_table = CloudNetworkTable(networks)
191
- networks_table.columns.hide("cloud_resource_type")
192
- RequestConfig(request, paginate).configure(networks_table)
193
-
194
- services = instance.cloud_services.restrict(request.user, "view")
195
- services_table = CloudServiceTable(services)
196
- services_table.columns.hide("cloud_resource_type")
197
- RequestConfig(request, paginate).configure(services_table)
182
+ object_detail_content = object_detail.ObjectDetailContent(
183
+ panels=(
184
+ object_detail.ObjectFieldsPanel(
185
+ section=SectionChoices.LEFT_HALF,
186
+ weight=100,
187
+ fields=("name", "provider", "content_types", "description"),
188
+ ),
189
+ object_detail.ObjectTextPanel(
190
+ section=SectionChoices.RIGHT_HALF,
191
+ weight=100,
192
+ object_field="config_schema",
193
+ label="Config Schema",
194
+ render_as=object_detail.ObjectTextPanel.RenderOptions.JSON,
195
+ ),
196
+ ),
197
+ extra_tabs=(
198
+ object_detail.DistinctViewTab(
199
+ weight=900,
200
+ tab_id="networks",
201
+ label="Cloud Networks",
202
+ url_name="cloud:cloudresourcetype_networks",
203
+ related_object_attribute="cloud_networks",
204
+ panels=(
205
+ object_detail.ObjectsTablePanel(
206
+ section=SectionChoices.FULL_WIDTH,
207
+ weight=100,
208
+ table_class=CloudNetworkTable,
209
+ table_filter="cloud_resource_type",
210
+ tab_id="networks",
211
+ ),
212
+ ),
213
+ ),
214
+ object_detail.DistinctViewTab(
215
+ weight=1000,
216
+ tab_id="services",
217
+ label="Cloud Services",
218
+ url_name="cloud:cloudresourcetype_services",
219
+ related_object_attribute="cloud_services",
220
+ panels=(
221
+ object_detail.ObjectsTablePanel(
222
+ section=SectionChoices.FULL_WIDTH,
223
+ weight=100,
224
+ table_class=CloudServiceTable,
225
+ table_filter="cloud_resource_type",
226
+ tab_id="services",
227
+ ),
228
+ ),
229
+ ),
230
+ ),
231
+ )
198
232
 
199
- context.update(
200
- {
201
- "networks_count": networks.count(),
202
- "networks_table": networks_table,
203
- "services_count": services.count(),
204
- "services_table": services_table,
205
- }
206
- )
233
+ @action(detail=True, url_path="networks")
234
+ def networks(self, request, *args, **kwargs):
235
+ return Response({})
207
236
 
208
- return context
237
+ @action(detail=True, url_path="services")
238
+ def services(self, request, *args, **kwargs):
239
+ return Response({})
209
240
 
210
241
 
211
242
  class CloudServiceUIViewSet(NautobotUIViewSet):
@@ -136,6 +136,56 @@ class NautobotCSVParser(BaseParser):
136
136
 
137
137
  return data_without_missing_field_lookups_values
138
138
 
139
+ def _convert_m2m_dict_to_list_of_dicts(self, data, field):
140
+ """
141
+ Converts a nested dictionary into list of flat dictionaries for M2M serializer.
142
+
143
+ Args:
144
+ data (dict): Nested dictionary with comma-separated string values.
145
+ field (str): Field name used in error messages.
146
+
147
+ Returns:
148
+ list: List of dictionaries, each containing one set of related values.
149
+
150
+ Raises:
151
+ ParseError: If the number of comma-separated values is inconsistent
152
+ across different keys.
153
+
154
+ Examples:
155
+ >>> data = {'manufacturer': {'name': 'Cisco,Cisco,Aruba'}, 'model': 'C9300,C9500,CX 6300'}
156
+ >>> field = "device_type"
157
+ >>> value = self.convert_m2m_dict_to_list_of_dicts(data, field)
158
+ >>> value
159
+ [
160
+ {'manufacturer': {'name': 'Cisco'},'model': 'C9300'},
161
+ {'manufacturer': {'name': 'Cisco'},'model': 'C9500'},
162
+ {'manufacturer': {'name': 'Aruba'},'model': 'CX 6300'}
163
+ ]
164
+ """
165
+
166
+ def flatten_dict(d, parent_key=""):
167
+ """Flatten nested dictionary with __ separated keys"""
168
+ items = []
169
+ for k, v in d.items():
170
+ new_key = f"{parent_key}__{k}" if parent_key else k
171
+ if isinstance(v, dict):
172
+ items.extend(flatten_dict(v, new_key).items())
173
+ else:
174
+ items.append((new_key, v.split(",")))
175
+ return dict(items)
176
+
177
+ flat_data = flatten_dict(data)
178
+
179
+ # Convert dictionary to list of dictionaries
180
+ values_count = {len(value) for value in flat_data.values()}
181
+ if len(values_count) > 1:
182
+ raise ParseError(f"Incorrect number of values provided for the {field} field")
183
+ values_count = values_count.pop()
184
+ return [
185
+ self._group_data_by_field_name({key: value[i] for key, value in flat_data.items()})
186
+ for i in range(values_count)
187
+ ]
188
+
139
189
  def row_elements_to_data(self, counter, row, serializer):
140
190
  """
141
191
  Parse a single row of CSV data (represented as a dict) into a dict suitable for consumption by the serializer.
@@ -171,9 +221,13 @@ class NautobotCSVParser(BaseParser):
171
221
  continue
172
222
 
173
223
  if isinstance(serializer_field, serializers.ManyRelatedField):
174
- # A list of related objects, represented as a list of composite-keys
175
224
  if value:
176
- value = value.split(",")
225
+ # A list of related objects, represented as a list of composite-keys
226
+ if isinstance(value, str):
227
+ value = value.split(",")
228
+ # A dictionary of fields identifying the objects
229
+ elif isinstance(value, dict):
230
+ value = self._convert_m2m_dict_to_list_of_dicts(value, key)
177
231
  else:
178
232
  value = []
179
233
  elif isinstance(serializer_field, serializers.RelatedField):
@@ -76,6 +76,7 @@ class NautobotScheduleEntry(ModelEntry):
76
76
  self._disable(model)
77
77
 
78
78
  if isinstance(model.celery_kwargs, Mapping):
79
+ # TODO: this allows model.celery_kwargs to override keys like `nautobot_job_user_id`; is that desirable?
79
80
  self.options.update(model.celery_kwargs)
80
81
 
81
82
  # copy-paste from django_celery_beat.schedulers
nautobot/core/filters.py CHANGED
@@ -397,7 +397,7 @@ class MappedPredicatesFilterMixin:
397
397
  lookup_expr = lookup_info.get("lookup_expr")
398
398
  preprocessor = lookup_info.get("preprocessor")
399
399
  if not callable(preprocessor):
400
- raise TypeError("Preprocessor {preprocessor} must be callable!")
400
+ raise TypeError(f"Preprocessor {preprocessor} must be callable!")
401
401
  else:
402
402
  raise TypeError(f"Predicate value must be a str or a dict! Got: {type(lookup_info)}")
403
403
 
@@ -95,6 +95,7 @@ CUSTOM_FIELD_MAPPING = {
95
95
  CustomFieldTypeChoices.TYPE_URL: graphene.String(),
96
96
  CustomFieldTypeChoices.TYPE_SELECT: graphene.String(),
97
97
  CustomFieldTypeChoices.TYPE_JSON: JSON(),
98
+ CustomFieldTypeChoices.TYPE_MULTISELECT: graphene.List(graphene.String),
98
99
  }
99
100
 
100
101
 
@@ -38,7 +38,7 @@ from nautobot.extras.jobs import (
38
38
  StringVar,
39
39
  TextVar,
40
40
  )
41
- from nautobot.extras.models import ExportTemplate, GitRepository
41
+ from nautobot.extras.models import ExportTemplate, GitRepository, SavedView
42
42
 
43
43
  name = "System Jobs"
44
44
 
@@ -150,6 +150,16 @@ class ExportObjectList(Job):
150
150
  soft_time_limit = 1800
151
151
  time_limit = 2000
152
152
 
153
+ def _get_saved_view_filter_params(self, query_params):
154
+ """Extract filter params from saved view if applicable."""
155
+ if "saved_view" in query_params and "all_filters_removed" not in query_params:
156
+ saved_view_filters = SavedView.objects.get(pk=query_params["saved_view"]).config.get("filter_params", {})
157
+ if len(query_params) > 1:
158
+ # Retain only filters also present in query_params
159
+ saved_view_filters = {key: value for key, value in saved_view_filters.items() if key in query_params}
160
+ return saved_view_filters
161
+ return {}
162
+
153
163
  def run(self, *, content_type, query_string="", export_format="csv", export_template=None): # pylint:disable=arguments-differ
154
164
  if not self.user.has_perm(f"{content_type.app_label}.view_{content_type.model}"):
155
165
  self.logger.error('User "%s" does not have permission to view %s objects', self.user, content_type.model)
@@ -178,8 +188,9 @@ class ExportObjectList(Job):
178
188
  "sort",
179
189
  "table_changes_pending",
180
190
  )
181
- filter_params = get_filterable_params_from_filter_params(
182
- query_params, default_non_filter_params, filterset_class()
191
+ filter_params = self._get_saved_view_filter_params(query_params)
192
+ filter_params.update(
193
+ get_filterable_params_from_filter_params(query_params, default_non_filter_params, filterset_class())
183
194
  )
184
195
  self.logger.debug("Filterset params: `%s`", filter_params)
185
196
  filterset = filterset_class(filter_params, queryset)
@@ -132,6 +132,8 @@ class BaseModel(models.Model):
132
132
  self.full_clean()
133
133
  self.save(*args, **kwargs)
134
134
 
135
+ validated_save.alters_data = True
136
+
135
137
  def natural_key(self) -> list:
136
138
  """
137
139
  Smarter default implementation of natural key construction.
nautobot/core/tables.py CHANGED
@@ -704,11 +704,17 @@ class RelationshipColumn(django_tables2.Column):
704
704
  if len(value) < 1:
705
705
  return "—"
706
706
 
707
+ v = value[0]
708
+ peer = v.get_peer(record)
709
+
707
710
  # Handle Relationships on the many side.
708
711
  if self.relationship.has_many(self.peer_side):
709
- v = value[0]
710
- meta = type(v.get_peer(record))._meta
711
- name = meta.verbose_name_plural if len(value) > 1 else meta.verbose_name
712
+ if peer is not None:
713
+ meta = type(peer)._meta
714
+ name = meta.verbose_name_plural if len(value) > 1 else meta.verbose_name
715
+ else: # Perhaps a relationship to an uninstalled App's models?
716
+ peer_type = getattr(self.relationship, f"{self.peer_side}_type")
717
+ name = f"{peer_type} object(s)"
712
718
  return format_html(
713
719
  '<a href="{}?relationship={}&{}_id={}">{} {}</a>',
714
720
  reverse("extras:relationshipassociation_list"),
@@ -720,6 +726,7 @@ class RelationshipColumn(django_tables2.Column):
720
726
  )
721
727
  # Handle Relationships on the one side.
722
728
  else:
723
- v = value[0]
724
- peer = v.get_peer(record)
725
- return format_html('<a href="{}">{}</a>', peer.get_absolute_url(), peer)
729
+ if peer is not None:
730
+ return format_html('<a href="{}">{}</a>', peer.get_absolute_url(), peer)
731
+ else:
732
+ return format_html("(unknown)")
@@ -302,6 +302,11 @@ class ViewTestCases:
302
302
  slug_source = None
303
303
  slugify_function = staticmethod(slugify)
304
304
  slug_test_object = ""
305
+ expected_create_form_buttons = [
306
+ '<button type="submit" name="_create" class="btn btn-primary">Create</button>',
307
+ '<button type="submit" name="_addanother" class="btn btn-primary">Create and Add Another</button>',
308
+ ]
309
+ expected_edit_form_buttons = ['<button type="submit" name="_update" class="btn btn-primary">Update</button>']
305
310
 
306
311
  def test_create_object_without_permission(self):
307
312
  # Try GET without permission
@@ -325,7 +330,14 @@ class ViewTestCases:
325
330
  self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
326
331
 
327
332
  # Try GET with model-level permission
328
- self.assertHttpStatus(self.client.get(self._get_url("add")), 200)
333
+ response = self.client.get(self._get_url("add"))
334
+ self.assertHttpStatus(response, 200)
335
+ # The response content should contain the expected form buttons
336
+ for button in self.expected_create_form_buttons:
337
+ self.assertBodyContains(response, button)
338
+ # The response content should not contain the expected form buttons
339
+ for button in self.expected_edit_form_buttons:
340
+ self.assertNotContains(response, button)
329
341
 
330
342
  # Try POST with model-level permission
331
343
  request = {
@@ -439,6 +451,11 @@ class ViewTestCases:
439
451
 
440
452
  form_data = {}
441
453
  update_data = {}
454
+ expected_edit_form_buttons = ['<button type="submit" name="_update" class="btn btn-primary">Update</button>']
455
+ expected_create_form_buttons = [
456
+ '<button type="submit" name="_create" class="btn btn-primary">Create</button>',
457
+ '<button type="submit" name="_addanother" class="btn btn-primary">Create and Add Another</button>',
458
+ ]
442
459
 
443
460
  def test_edit_object_without_permission(self):
444
461
  instance = self._get_queryset().first()
@@ -464,7 +481,15 @@ class ViewTestCases:
464
481
  self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
465
482
 
466
483
  # Try GET with model-level permission
467
- self.assertHttpStatus(self.client.get(self._get_url("edit", instance)), 200)
484
+ response = self.client.get(self._get_url("edit", instance))
485
+ self.assertHttpStatus(response, 200)
486
+ # The response content should contain the expected form buttons
487
+ for button in self.expected_edit_form_buttons:
488
+ self.assertBodyContains(response, button)
489
+
490
+ # The response content should not contain the unexpected form buttons
491
+ for button in self.expected_create_form_buttons:
492
+ self.assertNotContains(response, button)
468
493
 
469
494
  # Try POST with model-level permission
470
495
  update_data = self.update_data or self.form_data