nautobot 2.4.5__py3-none-any.whl → 2.4.7__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 (410) hide show
  1. nautobot/apps/forms.py +2 -0
  2. nautobot/circuits/templates/circuits/providernetwork.html +1 -1
  3. nautobot/circuits/templates/circuits/providernetwork_retrieve.html +2 -55
  4. nautobot/circuits/views.py +20 -23
  5. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +2 -40
  6. nautobot/cloud/views.py +12 -0
  7. nautobot/core/api/mixins.py +10 -0
  8. nautobot/core/api/urls.py +2 -2
  9. nautobot/core/api/views.py +39 -1
  10. nautobot/core/celery/encoders.py +2 -2
  11. nautobot/core/forms/__init__.py +2 -0
  12. nautobot/core/forms/fields.py +23 -7
  13. nautobot/core/forms/utils.py +1 -0
  14. nautobot/core/forms/widgets.py +18 -0
  15. nautobot/core/jobs/bulk_actions.py +1 -1
  16. nautobot/core/management/commands/generate_test_data.py +1 -1
  17. nautobot/core/models/name_color_content_types.py +9 -0
  18. nautobot/core/models/validators.py +7 -0
  19. nautobot/core/settings.py +0 -14
  20. nautobot/core/settings.yaml +0 -28
  21. nautobot/core/tables.py +6 -1
  22. nautobot/core/templates/generic/object_retrieve.html +1 -1
  23. nautobot/core/templates/widgets/sluginput.html +5 -1
  24. nautobot/core/templatetags/helpers.py +15 -1
  25. nautobot/core/testing/api.py +18 -0
  26. nautobot/core/testing/integration.py +6 -2
  27. nautobot/core/tests/nautobot_config.py +0 -2
  28. nautobot/core/tests/runner.py +17 -140
  29. nautobot/core/tests/test_api.py +4 -4
  30. nautobot/core/tests/test_authentication.py +83 -4
  31. nautobot/core/tests/test_forms.py +11 -8
  32. nautobot/core/tests/test_graphql.py +9 -0
  33. nautobot/core/tests/test_jobs.py +7 -0
  34. nautobot/core/ui/object_detail.py +47 -3
  35. nautobot/core/utils/lookup.py +2 -2
  36. nautobot/dcim/factory.py +2 -0
  37. nautobot/dcim/filters/__init__.py +5 -0
  38. nautobot/dcim/forms.py +27 -1
  39. nautobot/dcim/migrations/0068_alter_softwareimagefile_download_url.py +19 -0
  40. nautobot/dcim/migrations/0069_softwareimagefile_external_integration.py +25 -0
  41. nautobot/dcim/models/devices.py +9 -2
  42. nautobot/dcim/models/locations.py +9 -0
  43. nautobot/dcim/tables/devices.py +1 -0
  44. nautobot/dcim/templates/dcim/device_list.html +1 -1
  45. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  46. nautobot/dcim/templates/dcim/manufacturer.html +1 -63
  47. nautobot/dcim/templates/dcim/module_list.html +1 -1
  48. nautobot/dcim/templates/dcim/moduletype_retrieve.html +1 -1
  49. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +4 -0
  50. nautobot/dcim/tests/integration/test_module_bay_position.py +125 -0
  51. nautobot/dcim/tests/test_api.py +74 -31
  52. nautobot/dcim/tests/test_filters.py +2 -0
  53. nautobot/dcim/tests/test_models.py +78 -0
  54. nautobot/dcim/tests/test_views.py +7 -1
  55. nautobot/dcim/urls.py +1 -45
  56. nautobot/dcim/views.py +35 -66
  57. nautobot/extras/choices.py +4 -0
  58. nautobot/extras/filters/__init__.py +6 -0
  59. nautobot/extras/forms/forms.py +83 -11
  60. nautobot/extras/models/customfields.py +10 -9
  61. nautobot/extras/plugins/marketplace_manifest.yml +18 -0
  62. nautobot/extras/signals.py +43 -4
  63. nautobot/extras/tables.py +4 -5
  64. nautobot/extras/tasks.py +4 -2
  65. nautobot/extras/templates/extras/contact_retrieve.html +1 -58
  66. nautobot/extras/templates/extras/exporttemplate.html +1 -53
  67. nautobot/extras/templates/extras/inc/panel_changelog.html +1 -1
  68. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  69. nautobot/extras/templates/extras/status.html +1 -37
  70. nautobot/extras/templates/extras/team_retrieve.html +1 -58
  71. nautobot/extras/tests/integration/test_notes.py +1 -1
  72. nautobot/extras/tests/test_api.py +22 -7
  73. nautobot/extras/tests/test_changelog.py +4 -4
  74. nautobot/extras/tests/test_customfields.py +27 -0
  75. nautobot/extras/tests/test_plugins.py +19 -13
  76. nautobot/extras/tests/test_relationships.py +9 -0
  77. nautobot/extras/tests/test_tags.py +2 -2
  78. nautobot/extras/tests/test_views.py +37 -6
  79. nautobot/extras/urls.py +3 -100
  80. nautobot/extras/views.py +111 -133
  81. nautobot/ipam/tables.py +7 -2
  82. nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -41
  83. nautobot/ipam/templates/ipam/service.html +2 -46
  84. nautobot/ipam/templates/ipam/service_edit.html +1 -17
  85. nautobot/ipam/templates/ipam/service_retrieve.html +7 -0
  86. nautobot/ipam/tests/migration/__init__.py +0 -0
  87. nautobot/ipam/tests/migration/test_migrations.py +510 -0
  88. nautobot/ipam/tests/test_api.py +66 -36
  89. nautobot/ipam/tests/test_filters.py +0 -10
  90. nautobot/ipam/tests/test_views.py +44 -2
  91. nautobot/ipam/urls.py +2 -47
  92. nautobot/ipam/utils/migrations.py +185 -152
  93. nautobot/ipam/utils/testing.py +177 -0
  94. nautobot/ipam/views.py +95 -157
  95. nautobot/project-static/css/base.css +5 -0
  96. nautobot/project-static/docs/404.html +0 -2
  97. nautobot/project-static/docs/apps/index.html +0 -2
  98. nautobot/project-static/docs/apps/nautobot-apps.html +0 -2
  99. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +0 -2
  100. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +0 -2
  101. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +0 -2
  102. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +0 -2
  103. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +0 -2
  104. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +0 -2
  105. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +0 -2
  106. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +0 -2
  107. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +0 -2
  108. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +0 -2
  109. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +0 -2
  110. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +0 -2
  111. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +62 -2
  112. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +0 -2
  113. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +0 -2
  114. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +47 -2
  115. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +0 -2
  116. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +0 -2
  117. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +18 -2
  118. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +0 -2
  119. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +70 -5
  120. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +0 -2
  121. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +0 -2
  122. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +0 -2
  123. nautobot/project-static/docs/development/apps/api/configuration-view.html +0 -2
  124. nautobot/project-static/docs/development/apps/api/database-backend-config.html +0 -2
  125. nautobot/project-static/docs/development/apps/api/models/django-admin.html +0 -2
  126. nautobot/project-static/docs/development/apps/api/models/global-search.html +0 -2
  127. nautobot/project-static/docs/development/apps/api/models/graphql.html +0 -2
  128. nautobot/project-static/docs/development/apps/api/models/index.html +0 -2
  129. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +0 -2
  130. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +0 -2
  131. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +0 -2
  132. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +0 -2
  133. nautobot/project-static/docs/development/apps/api/platform-features/index.html +0 -2
  134. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +0 -2
  135. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +0 -2
  136. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +0 -2
  137. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +0 -2
  138. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +0 -2
  139. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +0 -2
  140. nautobot/project-static/docs/development/apps/api/prometheus.html +0 -2
  141. nautobot/project-static/docs/development/apps/api/setup.html +0 -2
  142. nautobot/project-static/docs/development/apps/api/testing.html +0 -89
  143. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +0 -2
  144. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +0 -2
  145. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +0 -2
  146. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +0 -2
  147. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +0 -2
  148. nautobot/project-static/docs/development/apps/api/views/base-template.html +0 -2
  149. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +0 -2
  150. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +0 -2
  151. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +0 -2
  152. nautobot/project-static/docs/development/apps/api/views/index.html +0 -2
  153. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +0 -2
  154. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +0 -2
  155. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +0 -2
  156. nautobot/project-static/docs/development/apps/api/views/notes.html +0 -2
  157. nautobot/project-static/docs/development/apps/api/views/rest-api.html +0 -2
  158. nautobot/project-static/docs/development/apps/api/views/urls.html +0 -2
  159. nautobot/project-static/docs/development/apps/index.html +0 -2
  160. nautobot/project-static/docs/development/apps/migration/code-updates.html +0 -2
  161. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +1 -3
  162. nautobot/project-static/docs/development/apps/migration/from-v1.html +0 -2
  163. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +0 -2
  164. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +0 -2
  165. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +0 -2
  166. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +0 -2
  167. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +0 -2
  168. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +0 -2
  169. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +0 -2
  170. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +0 -2
  171. nautobot/project-static/docs/development/apps/porting-from-netbox.html +0 -2
  172. nautobot/project-static/docs/development/core/application-registry.html +0 -2
  173. nautobot/project-static/docs/development/core/best-practices.html +3 -5
  174. nautobot/project-static/docs/development/core/bootstrap-ui.html +0 -2
  175. nautobot/project-static/docs/development/core/caching.html +0 -2
  176. nautobot/project-static/docs/development/core/controllers.html +0 -2
  177. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +0 -2
  178. nautobot/project-static/docs/development/core/generic-views.html +0 -2
  179. nautobot/project-static/docs/development/core/getting-started.html +78 -109
  180. nautobot/project-static/docs/development/core/homepage.html +0 -2
  181. nautobot/project-static/docs/development/core/index.html +0 -2
  182. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +0 -2
  183. nautobot/project-static/docs/development/core/model-checklist.html +0 -2
  184. nautobot/project-static/docs/development/core/model-features.html +0 -2
  185. nautobot/project-static/docs/development/core/natural-keys.html +0 -2
  186. nautobot/project-static/docs/development/core/navigation-menu.html +0 -2
  187. nautobot/project-static/docs/development/core/release-checklist.html +1 -3
  188. nautobot/project-static/docs/development/core/role-internals.html +0 -2
  189. nautobot/project-static/docs/development/core/settings.html +0 -2
  190. nautobot/project-static/docs/development/core/style-guide.html +1 -3
  191. nautobot/project-static/docs/development/core/templates.html +0 -2
  192. nautobot/project-static/docs/development/core/testing.html +24 -200
  193. nautobot/project-static/docs/development/core/ui-component-framework.html +0 -2
  194. nautobot/project-static/docs/development/core/user-preferences.html +0 -2
  195. nautobot/project-static/docs/development/index.html +0 -2
  196. nautobot/project-static/docs/development/jobs/index.html +0 -2
  197. nautobot/project-static/docs/development/jobs/migration/from-v1.html +0 -2
  198. nautobot/project-static/docs/index.html +0 -2
  199. nautobot/project-static/docs/media/user-guide/administration/getting-started/nautobot-cloud.png +0 -0
  200. nautobot/project-static/docs/objects.inv +0 -0
  201. nautobot/project-static/docs/overview/application_stack.html +1 -3
  202. nautobot/project-static/docs/overview/design_philosophy.html +0 -2
  203. nautobot/project-static/docs/release-notes/index.html +0 -2
  204. nautobot/project-static/docs/release-notes/version-1.0.html +0 -2
  205. nautobot/project-static/docs/release-notes/version-1.1.html +0 -2
  206. nautobot/project-static/docs/release-notes/version-1.2.html +0 -2
  207. nautobot/project-static/docs/release-notes/version-1.3.html +0 -2
  208. nautobot/project-static/docs/release-notes/version-1.4.html +0 -2
  209. nautobot/project-static/docs/release-notes/version-1.5.html +0 -2
  210. nautobot/project-static/docs/release-notes/version-1.6.html +0 -2
  211. nautobot/project-static/docs/release-notes/version-2.0.html +0 -2
  212. nautobot/project-static/docs/release-notes/version-2.1.html +0 -2
  213. nautobot/project-static/docs/release-notes/version-2.2.html +0 -2
  214. nautobot/project-static/docs/release-notes/version-2.3.html +0 -2
  215. nautobot/project-static/docs/release-notes/version-2.4.html +364 -3
  216. nautobot/project-static/docs/search/search_index.json +1 -1
  217. nautobot/project-static/docs/sitemap.xml +290 -290
  218. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  219. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +0 -2
  220. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +0 -2
  221. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +0 -2
  222. nautobot/project-static/docs/user-guide/administration/configuration/index.html +0 -2
  223. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +0 -2
  224. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +2 -50
  225. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +0 -2
  226. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +0 -2
  227. nautobot/project-static/docs/user-guide/administration/guides/docker.html +0 -2
  228. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +0 -2
  229. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +71 -2
  230. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +0 -2
  231. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +0 -2
  232. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +0 -2
  233. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +0 -2
  234. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +0 -2
  235. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +0 -2
  236. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +0 -2
  237. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +3 -3
  238. nautobot/project-static/docs/user-guide/administration/installation/index.html +257 -18
  239. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +0 -2
  240. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +0 -2
  241. nautobot/project-static/docs/user-guide/administration/installation/services.html +0 -2
  242. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +0 -2
  243. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +0 -2
  244. nautobot/project-static/docs/user-guide/administration/security/index.html +0 -2
  245. nautobot/project-static/docs/user-guide/administration/security/notices.html +0 -2
  246. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +1 -3
  247. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +0 -2
  248. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +0 -2
  249. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +0 -2
  250. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +0 -2
  251. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +0 -2
  252. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +0 -2
  253. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +0 -2
  254. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +0 -2
  255. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +0 -2
  256. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +2 -4
  257. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +0 -2
  258. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +0 -2
  259. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +0 -2
  260. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +0 -2
  261. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +0 -2
  262. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +0 -2
  263. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +0 -2
  264. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +0 -2
  265. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +0 -2
  266. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +0 -2
  267. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +0 -2
  268. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +0 -2
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +0 -2
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +0 -2
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +0 -2
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +0 -2
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +0 -2
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +0 -2
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +0 -2
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +0 -2
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +0 -2
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +0 -2
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +0 -2
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +0 -2
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +0 -2
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +0 -2
  283. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +0 -2
  284. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +0 -2
  285. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +0 -2
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +0 -2
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +0 -2
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +0 -2
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +0 -2
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +0 -2
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +0 -2
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +0 -2
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +0 -2
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +0 -2
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +0 -2
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +0 -2
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +0 -2
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +0 -2
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +0 -2
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +0 -2
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +0 -2
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +0 -2
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +0 -2
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +0 -2
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +0 -2
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +0 -2
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -2
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +0 -2
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +0 -2
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +0 -2
  311. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +0 -2
  312. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +0 -2
  313. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +0 -2
  314. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +0 -2
  315. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +0 -2
  316. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +0 -2
  317. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +0 -2
  318. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +0 -2
  319. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +0 -2
  320. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +0 -2
  321. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +0 -2
  322. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +0 -2
  323. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +0 -2
  324. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +0 -2
  325. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +0 -2
  326. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +0 -2
  327. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +0 -2
  328. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +0 -2
  329. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +0 -2
  330. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +0 -2
  331. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +0 -2
  332. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +0 -2
  333. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +0 -2
  334. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +0 -2
  335. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +0 -2
  336. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -13
  337. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +0 -2
  338. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +8 -10
  339. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +1 -2
  340. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +0 -2
  341. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +40 -27
  342. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -6
  343. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +1 -3
  344. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +77 -7
  345. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +1 -3
  346. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +0 -3
  347. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +1 -3
  348. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +0 -2
  349. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +0 -2
  350. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +0 -2
  351. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +0 -2
  352. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +0 -2
  353. nautobot/project-static/docs/user-guide/index.html +89 -4
  354. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +0 -2
  355. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +0 -2
  356. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +0 -2
  357. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +0 -2
  358. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +0 -2
  359. nautobot/project-static/docs/user-guide/platform-functionality/events.html +0 -2
  360. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +0 -2
  361. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +0 -2
  362. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +0 -2
  363. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +0 -2
  364. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +0 -2
  365. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +0 -2
  366. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +0 -2
  367. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +0 -2
  368. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +0 -2
  369. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +0 -2
  370. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +0 -2
  371. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +0 -2
  372. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +0 -2
  373. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +0 -2
  374. nautobot/project-static/docs/user-guide/platform-functionality/note.html +0 -2
  375. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +0 -2
  376. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +0 -2
  377. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +0 -2
  378. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +0 -2
  379. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +0 -2
  380. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +0 -2
  381. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +0 -2
  382. nautobot/project-static/docs/user-guide/platform-functionality/role.html +0 -2
  383. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +0 -2
  384. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +0 -2
  385. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +0 -2
  386. nautobot/project-static/docs/user-guide/platform-functionality/status.html +0 -2
  387. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +0 -2
  388. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +0 -2
  389. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +0 -2
  390. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +0 -2
  391. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +207 -124
  392. nautobot/project-static/js/forms.js +88 -37
  393. nautobot/project-static/js/homepage_layout.js +12 -3
  394. nautobot/virtualization/forms.py +20 -0
  395. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -39
  396. nautobot/virtualization/templates/virtualization/clustertype.html +1 -0
  397. nautobot/virtualization/tests/test_api.py +14 -3
  398. nautobot/virtualization/tests/test_views.py +10 -2
  399. nautobot/virtualization/urls.py +10 -93
  400. nautobot/virtualization/views.py +33 -72
  401. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/METADATA +6 -5
  402. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/RECORD +407 -402
  403. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/WHEEL +1 -1
  404. nautobot/core/tests/performance_baselines.yml +0 -8900
  405. nautobot/dcim/templates/dcim/modulebay_create.html +0 -39
  406. nautobot/ipam/tests/test_migrations.py +0 -462
  407. /nautobot/ipam/templates/ipam/{namespace_ipaddresses.html → namespace_ip_addresses.html} +0 -0
  408. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/LICENSE.txt +0 -0
  409. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/NOTICE +0 -0
  410. {nautobot-2.4.5.dist-info → nautobot-2.4.7.dist-info}/entry_points.txt +0 -0
nautobot/apps/forms.py CHANGED
@@ -53,6 +53,7 @@ from nautobot.core.forms.utils import (
53
53
  from nautobot.core.forms.widgets import (
54
54
  APISelect,
55
55
  APISelectMultiple,
56
+ AutoPopulateWidget,
56
57
  BulkEditNullBooleanSelect,
57
58
  ColorSelect,
58
59
  ContentTypeSelect,
@@ -102,6 +103,7 @@ __all__ = (
102
103
  "APISelect",
103
104
  "APISelectMultiple",
104
105
  "AddressFieldMixin",
106
+ "AutoPopulateWidget",
105
107
  "BootstrapMixin",
106
108
  "BulkEditForm",
107
109
  "BulkEditNullBooleanSelect",
@@ -1,2 +1,2 @@
1
1
  {% extends 'circuits/providernetwork_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,57 +1,4 @@
1
1
  {% extends 'generic/object_retrieve.html' %}
2
- {% load static %}
3
- {% load helpers %}
4
- {% load plugins %}
5
-
6
- {% block breadcrumbs %}
7
- <li><a href="{% url 'circuits:providernetwork_list' %}">Provider Networks</a></li>
2
+ {% block extra_breadcrumbs %}
8
3
  <li><a href="{% url 'circuits:providernetwork_list' %}?provider={{ object.provider_id }}">{{ object.provider }}</a></li>
9
- <li>{{ object }}</li>
10
- {% endblock %}
11
-
12
- {% block content_left_page %}
13
- <div class="panel panel-default">
14
- <div class="panel-heading">
15
- <strong>Provider Network</strong>
16
- </div>
17
- <table class="table table-hover panel-body attr-table">
18
- <tr>
19
- <td>Provider</td>
20
- <td>{{ object.provider|hyperlinked_object }}</td>
21
- </tr>
22
- <tr>
23
- <td>Name</td>
24
- <td>{{ object.name }}</td>
25
- </tr>
26
- <tr>
27
- <td>Description</td>
28
- <td>{{ object.description }}</td>
29
- </tr>
30
- </table>
31
- </div>
32
- {% endblock content_left_page %}
33
-
34
- {% block content_right_page %}
35
- <div class="panel panel-default">
36
- <div class="panel-heading">
37
- <strong>Comments</strong>
38
- </div>
39
- <div class="panel-body rendered-markdown">
40
- {% if object.comments %}
41
- {{ object.comments|render_markdown }}
42
- {% else %}
43
- <span class="text-muted">None</span>
44
- {% endif %}
45
- </div>
46
- </div>
47
- {% endblock content_right_page %}
48
-
49
- {% block content_full_width_page %}
50
- <div class="panel panel-default">
51
- <div class="panel-heading">
52
- <strong>Circuits</strong>
53
- </div>
54
- {% include 'inc/table.html' with table=circuits_table %}
55
- </div>
56
- {% include 'inc/paginator.html' with paginator=circuits_table.paginator page=circuits_table.page %}
57
- {% endblock content_full_width_page %}
4
+ {% endblock extra_breadcrumbs %}
@@ -1,6 +1,5 @@
1
1
  from django.contrib import messages
2
2
  from django.db import transaction
3
- from django.db.models import Q
4
3
  from django.shortcuts import get_object_or_404, redirect, render
5
4
  from django.utils.html import format_html
6
5
  from django_tables2 import RequestConfig
@@ -300,28 +299,26 @@ class ProviderNetworkUIViewSet(NautobotUIViewSet):
300
299
  serializer_class = serializers.ProviderNetworkSerializer
301
300
  table_class = tables.ProviderNetworkTable
302
301
 
303
- def get_extra_context(self, request, instance):
304
- context = super().get_extra_context(request, instance)
305
- if self.action == "retrieve":
306
- circuits = (
307
- Circuit.objects.restrict(request.user, "view")
308
- .filter(
309
- Q(circuit_termination_a__provider_network=instance.pk)
310
- | Q(circuit_termination_z__provider_network=instance.pk)
311
- )
312
- .select_related("circuit_type", "tenant")
313
- .prefetch_related("circuit_terminations__location")
314
- )
315
-
316
- circuits_table = tables.CircuitTable(circuits)
317
- circuits_table.columns.hide("circuit_termination_a")
318
- circuits_table.columns.hide("circuit_termination_z")
319
-
320
- paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
321
- RequestConfig(request, paginate).configure(circuits_table)
322
-
323
- context["circuits_table"] = circuits_table
324
- return context
302
+ object_detail_content = ObjectDetailContent(
303
+ panels=(
304
+ ObjectFieldsPanel(
305
+ weight=100,
306
+ section=SectionChoices.LEFT_HALF,
307
+ fields="__all__",
308
+ ),
309
+ ObjectsTablePanel(
310
+ weight=200,
311
+ section=SectionChoices.FULL_WIDTH,
312
+ table_class=tables.CircuitTable,
313
+ table_filter=["circuit_termination_a__provider_network", "circuit_termination_z__provider_network"],
314
+ prefetch_related_fields=["circuit_terminations__location"],
315
+ select_related_fields=["circuit_type", "tenant"],
316
+ exclude_columns=["provider_network", "circuit_termination_a", "circuit_termination_z"],
317
+ related_field_name="provider_network",
318
+ add_button_route=None,
319
+ ),
320
+ )
321
+ )
325
322
 
326
323
 
327
324
  class CircuitSwapTerminations(generic.ObjectEditView):
@@ -1,43 +1,5 @@
1
1
  {% extends 'generic/object_retrieve.html' %}
2
- {% load helpers %}
3
-
4
2
  {% block extra_breadcrumbs %}
5
- <li><a href="{% url 'cloud:cloudaccount_list' %}?provider={{ object.provider.pk }}">{{ object.provider }}</a></li>
3
+ <li><a href="{% url 'cloud:cloudaccount_list' %}?provider={{ object.provider.pk }}">{{ object.provider }}</a></li>
6
4
  {% endblock extra_breadcrumbs %}
7
-
8
- {% block content_left_page %}
9
- <div class="panel panel-default">
10
- <div class="panel-heading">
11
- <strong>Cloud Account</strong>
12
- </div>
13
- <table class="table table-hover panel-body attr-table">
14
- <tr>
15
- <td>Name</td>
16
- <td>
17
- {{ object.name }}
18
- </td>
19
- </tr>
20
- <tr>
21
- <td>Provider</td>
22
- <td>{{ object.provider|hyperlinked_object }}</td>
23
- </tr>
24
- <tr>
25
- <td>Account Number</td>
26
- <td>
27
- <span class="hover_copy"><span id="copy_accountnumber">{{ object.account_number }}</span><button type="button" class="btn btn-inline btn-default hover_copy_button" data-clipboard-target="#copy_accountnumber">
28
- <span class="mdi mdi-content-copy"></span>
29
- </button>
30
- </span>
31
- </td>
32
- </tr>
33
- <tr>
34
- <td>Secrets Group</td>
35
- <td>{{ object.secrets_group|hyperlinked_object }}</td>
36
- </tr>
37
- <tr>
38
- <td>Description</td>
39
- <td>{{ object.description|placeholder }}</td>
40
- </tr>
41
- </table>
42
- </div>
43
- {% endblock content_left_page %}
5
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
nautobot/cloud/views.py CHANGED
@@ -31,6 +31,8 @@ from nautobot.cloud.forms import (
31
31
  )
32
32
  from nautobot.cloud.models import CloudAccount, CloudNetwork, CloudResourceType, CloudService
33
33
  from nautobot.cloud.tables import CloudAccountTable, CloudNetworkTable, CloudResourceTypeTable, CloudServiceTable
34
+ from nautobot.core.ui import object_detail
35
+ from nautobot.core.ui.choices import SectionChoices
34
36
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
35
37
  from nautobot.core.views.viewsets import NautobotUIViewSet
36
38
  from nautobot.ipam.tables import PrefixTable
@@ -45,6 +47,16 @@ class CloudAccountUIViewSet(NautobotUIViewSet):
45
47
  table_class = CloudAccountTable
46
48
  form_class = CloudAccountForm
47
49
 
50
+ object_detail_content = object_detail.ObjectDetailContent(
51
+ panels=(
52
+ object_detail.ObjectFieldsPanel(
53
+ weight=100,
54
+ section=SectionChoices.LEFT_HALF,
55
+ fields="__all__",
56
+ ),
57
+ )
58
+ )
59
+
48
60
 
49
61
  class CloudNetworkUIViewSet(NautobotUIViewSet):
50
62
  queryset = CloudNetwork.objects.all()
@@ -109,6 +109,16 @@ class WritableSerializerMixin:
109
109
  queryset = self.queryset
110
110
  else:
111
111
  queryset = self.Meta.model.objects
112
+
113
+ # Apply user permission on related objects
114
+ if (
115
+ "request" in self.context
116
+ and self.context["request"]
117
+ and self.context["request"].user
118
+ and hasattr(queryset, "restrict")
119
+ ):
120
+ queryset = queryset.restrict(self.context["request"].user, "view")
121
+
112
122
  if isinstance(data, list):
113
123
  return [self.get_object(data=entry, queryset=queryset) for entry in data]
114
124
  return self.get_object(data=data, queryset=queryset)
nautobot/core/api/urls.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from django.conf.urls import include
2
2
  from django.urls import path
3
3
  from drf_spectacular.views import (
4
- SpectacularAPIView,
5
4
  SpectacularJSONAPIView,
6
5
  SpectacularYAMLAPIView,
7
6
  )
@@ -12,6 +11,7 @@ from nautobot.core.api.views import (
12
11
  GetFilterSetFieldDOMElementAPIView,
13
12
  GetFilterSetFieldLookupExpressionChoicesAPIView,
14
13
  GraphQLDRFAPIView,
14
+ NautobotSpectacularAPIView,
15
15
  NautobotSpectacularRedocView,
16
16
  NautobotSpectacularSwaggerView,
17
17
  RenderJinjaView,
@@ -55,7 +55,7 @@ urlpatterns = [
55
55
  path("docs/", NautobotSpectacularSwaggerView.as_view(url_name="schema"), name="api_docs"),
56
56
  path("redoc/", NautobotSpectacularRedocView.as_view(url_name="schema"), name="api_redocs"),
57
57
  path("settings-schema/", SettingsJSONSchemaView.as_view(), name="setting_schema_json"),
58
- path("swagger/", SpectacularAPIView.as_view(), name="schema"),
58
+ path("swagger/", NautobotSpectacularAPIView.as_view(), name="schema"),
59
59
  path("swagger.json", SpectacularJSONAPIView.as_view(), name="schema_json"),
60
60
  path("swagger.yaml", SpectacularYAMLAPIView.as_view(), name="schema_yaml"),
61
61
  # GraphQL
@@ -7,6 +7,7 @@ from django import __version__ as DJANGO_VERSION, forms
7
7
  from django.apps import apps
8
8
  from django.conf import settings
9
9
  from django.contrib.contenttypes.models import ContentType
10
+ from django.core.cache import cache
10
11
  from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError
11
12
  from django.db import transaction
12
13
  from django.db.models import ProtectedError
@@ -14,10 +15,12 @@ from django.db.models.fields.related import ForeignKey, ManyToManyField, Related
14
15
  from django.db.models.fields.reverse_related import ManyToManyRel, ManyToOneRel
15
16
  from django.http.response import HttpResponseBadRequest
16
17
  from django.shortcuts import get_object_or_404, redirect
18
+ from django.utils.decorators import method_decorator
19
+ from django.views.decorators.gzip import gzip_page
17
20
  from drf_spectacular.plumbing import get_relative_url, set_query_parameters
18
21
  from drf_spectacular.renderers import OpenApiJsonRenderer
19
22
  from drf_spectacular.utils import extend_schema
20
- from drf_spectacular.views import SpectacularRedocView, SpectacularSwaggerView
23
+ from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
21
24
  from graphene_django.settings import graphene_settings
22
25
  from graphene_django.views import GraphQLView, HttpError, instantiate_middleware
23
26
  from graphql import get_default_backend
@@ -608,6 +611,41 @@ class NautobotSpectacularRedocView(APIVersioningGetSchemaURLMixin, SpectacularRe
608
611
  """Extend SpectacularRedocView to support Nautobot's ?api_version=<version> query parameter."""
609
612
 
610
613
 
614
+ @method_decorator(gzip_page, name="dispatch")
615
+ class NautobotSpectacularAPIView(SpectacularAPIView):
616
+ def _get_schema_response(self, request):
617
+ # version specified as parameter to the view always takes precedence. after
618
+ # that we try to source version through the schema view's own versioning_class.
619
+ version = self.api_version or request.version or self._get_version_parameter(request)
620
+ cache_key = f"openapi_schema_cache_{version}_{settings.VERSION}" # Invalidate cache on Nautobot release
621
+ etag = f'W/"{hash(cache_key)}"'
622
+
623
+ # With combined browser cache and backend cache, we have three options:
624
+ # - cache expired on browser, but Etag is the same (no changes in nautobot) -> 70-100ms response
625
+ # - cache expired on browser, Etag is different, cache present on backend -> 400-600ms response
626
+ # - cache expired on browser, Etag is different, no cache on backend -> 3-4s response
627
+
628
+ if_none_match = request.META.get("HTTP_IF_NONE_MATCH", "")
629
+ if if_none_match == etag:
630
+ return Response(status=304)
631
+
632
+ schema = cache.get(cache_key)
633
+ if not schema:
634
+ generator = self.generator_class(urlconf=self.urlconf, api_version=version, patterns=self.patterns)
635
+ schema = generator.get_schema(request=request, public=self.serve_public)
636
+ cache.set(cache_key, schema, 60 * 60 * 24 * 7)
637
+
638
+ return Response(
639
+ data=schema,
640
+ headers={
641
+ "Content-Disposition": f'inline; filename="{self._get_filename(request, version)}"',
642
+ "Cache-Control": f"max-age={3 * 24 * 60 * 60}, public",
643
+ "ETag": etag,
644
+ "Vary": "Accept, Accept-Encoding",
645
+ },
646
+ )
647
+
648
+
611
649
  #
612
650
  # GraphQL
613
651
  #
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
 
3
+ from django.db import models
3
4
  from rest_framework.utils.encoders import JSONEncoder
4
5
 
5
6
  logger = logging.getLogger(__name__)
@@ -26,10 +27,9 @@ class NautobotKombuJSONEncoder(JSONEncoder):
26
27
  def default(self, obj):
27
28
  # Import here to avoid django.core.exceptions.ImproperlyConfigured Error.
28
29
  # Core App is not set up yet if we import this at the top of the file.
29
- from nautobot.core.models import BaseModel
30
30
  from nautobot.core.models.managers import TagsManager
31
31
 
32
- if isinstance(obj, BaseModel):
32
+ if isinstance(obj, models.Model):
33
33
  cls = obj.__class__
34
34
  module = cls.__module__
35
35
  qual_name = ".".join([module, cls.__qualname__]) # fully qualified dotted import path
@@ -60,6 +60,7 @@ from nautobot.core.forms.utils import (
60
60
  from nautobot.core.forms.widgets import (
61
61
  APISelect,
62
62
  APISelectMultiple,
63
+ AutoPopulateWidget,
63
64
  BulkEditNullBooleanSelect,
64
65
  ClearableFileInput,
65
66
  ColorSelect,
@@ -86,6 +87,7 @@ __all__ = (
86
87
  "APISelect",
87
88
  "APISelectMultiple",
88
89
  "AddressFieldMixin",
90
+ "AutoPopulateWidget",
89
91
  "AutoPositionField",
90
92
  "AutoPositionPatternField",
91
93
  "BootstrapMixin",
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import logging
2
3
  import re
3
4
 
4
5
  from django import forms as django_forms
@@ -11,6 +12,7 @@ from django.db.models import Q
11
12
  from django.forms.fields import BoundField, CallableChoiceIterator, InvalidJSONInput, JSONField as _JSONField
12
13
  from django.templatetags.static import static
13
14
  from django.urls import reverse
15
+ from django.urls.exceptions import NoReverseMatch
14
16
  from django.utils.html import format_html
15
17
  import django_filters
16
18
  from netaddr import EUI
@@ -21,6 +23,8 @@ from nautobot.core.forms import widgets
21
23
  from nautobot.core.models import validators
22
24
  from nautobot.core.utils import data as data_utils, lookup
23
25
 
26
+ logger = logging.getLogger(__name__)
27
+
24
28
  __all__ = (
25
29
  "CSVChoiceField",
26
30
  "CSVContentTypeField",
@@ -453,7 +457,7 @@ class AutoPositionField(django_forms.CharField):
453
457
  source (str, tuple): Name of the field (or a list of field names) that will be used to suggest a position.
454
458
  """
455
459
  kwargs.setdefault("label", "Position")
456
- kwargs.setdefault("widget", forms.SlugWidget)
460
+ kwargs.setdefault("widget", forms.AutoPopulateWidget)
457
461
  super().__init__(*args, **kwargs)
458
462
  if isinstance(source, (tuple, list)):
459
463
  source = " ".join(source)
@@ -469,7 +473,7 @@ class AutoPositionPatternField(ExpandableNameField):
469
473
  source (str, tuple): Name pattern of the field (or a list of field names) that will be used to suggest a position pattern.
470
474
  """
471
475
  kwargs.setdefault("label", "Position")
472
- kwargs.setdefault("widget", forms.SlugWidget)
476
+ kwargs.setdefault("widget", forms.AutoPopulateWidget(attrs={"title": "Regenerate position"}))
473
477
  super().__init__(*args, **kwargs)
474
478
  if isinstance(source, (tuple, list)):
475
479
  source = " ".join(source)
@@ -591,8 +595,16 @@ class DynamicModelChoiceMixin:
591
595
  widget = bound_field.field.widget
592
596
  if not widget.attrs.get("data-url"):
593
597
  route = lookup.get_route_for_model(self.queryset.model, "list", api=True)
594
- data_url = reverse(route)
595
- widget.attrs["data-url"] = data_url
598
+ try:
599
+ data_url = reverse(route)
600
+ widget.attrs["data-url"] = data_url
601
+ except NoReverseMatch:
602
+ logger.error(
603
+ 'API route lookup "%s" failed for model %s, form field "%s" will not work properly',
604
+ route,
605
+ self.queryset.model.__name__,
606
+ bound_field.name,
607
+ )
596
608
 
597
609
  return bound_field
598
610
 
@@ -800,9 +812,13 @@ class NumericArrayField(SimpleArrayField):
800
812
  def to_python(self, value):
801
813
  try:
802
814
  if not value:
803
- value = ""
804
- else:
805
- value = ",".join([str(n) for n in forms.parse_numeric_range(value)])
815
+ return []
816
+
817
+ if isinstance(value, list):
818
+ value = ",".join([str(n) for n in value])
819
+
820
+ value = ",".join([str(n) for n in forms.parse_numeric_range(value)])
821
+
806
822
  except (TypeError, ValueError) as error:
807
823
  raise ValidationError(error)
808
824
  return super().to_python(value)
@@ -41,6 +41,7 @@ def parse_numeric_range(input_string, base=10):
41
41
  raise TypeError("Input value must be a string using a range format.")
42
42
  except ValueError:
43
43
  begin, end = dash_range, dash_range
44
+
44
45
  begin, end = int(begin.strip(), base=base), int(end.strip(), base=base) + 1
45
46
  values.extend(range(begin, end))
46
47
  # Remove duplicates and sort
@@ -12,6 +12,7 @@ from nautobot.core.forms import utils
12
12
  __all__ = (
13
13
  "APISelect",
14
14
  "APISelectMultiple",
15
+ "AutoPopulateWidget",
15
16
  "BulkEditNullBooleanSelect",
16
17
  "ClearableFileInput",
17
18
  "ColorSelect",
@@ -41,6 +42,23 @@ class SlugWidget(forms.TextInput):
41
42
 
42
43
  template_name = "widgets/sluginput.html"
43
44
 
45
+ def get_context(self, name, value, attrs):
46
+ custom_title = self.attrs.pop("title", None)
47
+ context = super().get_context(name, value, attrs)
48
+ context["widget"]["custom_title"] = custom_title
49
+ return context
50
+
51
+
52
+ class AutoPopulateWidget(SlugWidget):
53
+ """
54
+ Subclass SlugWidget and add support for auto-populate JavaScript logic from `form.js`.
55
+ """
56
+
57
+ def get_context(self, name, value, attrs):
58
+ attrs["data-autopopulate"] = ""
59
+ context = super().get_context(name, value, attrs)
60
+ return context
61
+
44
62
 
45
63
  class ColorSelect(forms.Select):
46
64
  """
@@ -108,7 +108,7 @@ class BulkEditObjects(Job):
108
108
  if form.cleaned_data[field_name]:
109
109
  getattr(obj, field_name).set(form.cleaned_data[field_name])
110
110
  # Normal fields
111
- elif form.cleaned_data[field_name] not in (None, ""):
111
+ elif form.cleaned_data[field_name] not in (None, "", []):
112
112
  setattr(obj, field_name, form.cleaned_data[field_name])
113
113
 
114
114
  # Update custom fields
@@ -213,6 +213,7 @@ class Command(BaseCommand):
213
213
  _create_batch(PlatformFactory, 20, description="with Manufacturers", has_manufacturer=True)
214
214
  _create_batch(PlatformFactory, 5, description="without Manufacturers", has_manufacturer=False)
215
215
  _create_batch(SoftwareVersionFactory, 20, description="to be usable by Devices")
216
+ _create_batch(ExternalIntegrationFactory, 20)
216
217
  _create_batch(SoftwareImageFileFactory, 25, description="to be usable by DeviceTypes")
217
218
  _create_batch(ManufacturerFactory, 4, description="without Platforms") # 4 more hard-coded Manufacturers
218
219
  _create_batch(DeviceTypeFactory, 30)
@@ -312,7 +313,6 @@ class Command(BaseCommand):
312
313
  has_pp_info=True,
313
314
  has_description=True,
314
315
  )
315
- _create_batch(ExternalIntegrationFactory, 20)
316
316
  _create_batch(ControllerFactory, 10, description="with Devices or DeviceRedundancyGroups")
317
317
  _create_batch(ControllerManagedDeviceGroupFactory, 5, description="without any Devices")
318
318
  _create_batch(SupportedDataRateFactory, 20)
@@ -1,10 +1,12 @@
1
1
  from django.contrib.contenttypes.models import ContentType
2
2
  from django.db import models
3
+ from django.utils.html import format_html
3
4
 
4
5
  from nautobot.core.choices import ColorChoices
5
6
  from nautobot.core.constants import CHARFIELD_MAX_LENGTH
6
7
  from nautobot.core.models import BaseManager, BaseModel, ContentTypeRelatedQuerySet
7
8
  from nautobot.core.models.fields import ColorField
9
+ from nautobot.core.templatetags import helpers
8
10
  from nautobot.extras.models.change_logging import ChangeLoggedModel
9
11
 
10
12
  # Importing CustomFieldModel, ChangeLoggedModel, RelationshipModel from nautobot.extras.models
@@ -54,3 +56,10 @@ class NameColorContentTypesModel(
54
56
 
55
57
  def get_content_types(self):
56
58
  return ",".join(f"{ct.app_label}.{ct.model}" for ct in self.content_types.all())
59
+
60
+ def get_color_display(self):
61
+ if self.color:
62
+ return format_html(
63
+ '<span class="label color-block" style="background-color: #{}">&nbsp;</span>', self.color
64
+ )
65
+ return helpers.placeholder(self.color)
@@ -27,8 +27,15 @@ class EnhancedURLValidator(URLValidator):
27
27
  r"\Z",
28
28
  re.IGNORECASE,
29
29
  )
30
+
30
31
  schemes = settings.ALLOWED_URL_SCHEMES
31
32
 
33
+ def __getattribute__(self, name):
34
+ """Dynamically fetch schemes each time it's accessed."""
35
+ if name == "schemes":
36
+ self.schemes = settings.ALLOWED_URL_SCHEMES # Always return the latest list
37
+ return super().__getattribute__(name)
38
+
32
39
 
33
40
  class ExclusionValidator(BaseValidator):
34
41
  """
nautobot/core/settings.py CHANGED
@@ -287,20 +287,6 @@ TEST_USE_FACTORIES = is_truthy(os.getenv("NAUTOBOT_TEST_USE_FACTORIES", "False")
287
287
  # Pseudo-random number generator seed, for reproducibility of test results.
288
288
  TEST_FACTORY_SEED = os.getenv("NAUTOBOT_TEST_FACTORY_SEED", None)
289
289
 
290
- #
291
- # django-slowtests
292
- #
293
-
294
- # Performance test uses `NautobotPerformanceTestRunner` to run, which is only available once you have `django-slowtests` installed in your dev environment.
295
- # `invoke performance-test` and adding `--performance-report` or `--performance-snapshot` at the end of the `invoke` command
296
- # will automatically opt to NautobotPerformanceTestRunner to run the tests.
297
-
298
- # The baseline file that the performance test is running against
299
- # TODO we need to replace the baselines in this file with more consistent results at least for CI
300
- TEST_PERFORMANCE_BASELINE_FILE = os.getenv(
301
- "NAUTOBOT_TEST_PERFORMANCE_BASELINE_FILE", "nautobot/core/tests/performance_baselines.yml"
302
- )
303
-
304
290
  #
305
291
  # Django Prometheus
306
292
  #
@@ -1926,34 +1926,6 @@ properties:
1926
1926
  version_added: "1.5.0"
1927
1927
  see_also:
1928
1928
  "`TEST_USE_FACTORIES`": "#test_use_factories"
1929
- TEST_PERFORMANCE_BASELINE_FILE:
1930
- default: "nautobot/core/tests/performance_baselines.yml"
1931
- description: "File path of a YAML file providing baseline times for all performance-related tests."
1932
- details: |-
1933
- The YAML file should conform to the following format:
1934
-
1935
- ```yaml
1936
- tests:
1937
- - name: >-
1938
- test_run_job_with_sensitive_variables_and_requires_approval
1939
- (nautobot.extras.tests.test_views.JobTestCase)
1940
- execution_time: 4.799533
1941
- - name: test_run_missing_schedule (nautobot.extras.tests.test_views.JobTestCase)
1942
- execution_time: 4.367563
1943
- - name: test_run_now_missing_args (nautobot.extras.tests.test_views.JobTestCase)
1944
- execution_time: 4.363194
1945
- - name: >-
1946
- test_create_object_with_constrained_permission
1947
- (nautobot.extras.tests.test_views.GraphQLQueriesTestCase)
1948
- execution_time: 3.474244
1949
- - name: >-
1950
- test_run_now_constrained_permissions
1951
- (nautobot.extras.tests.test_views.JobTestCase)
1952
- execution_time: 2.727531
1953
- ```
1954
- environment_variable: "NAUTOBOT_TEST_PERFORMANCE_BASELINE_FILE"
1955
- type: "string"
1956
- version_added: "1.5.0"
1957
1929
  TEST_USE_FACTORIES:
1958
1930
  default: false
1959
1931
  description: >-
nautobot/core/tables.py CHANGED
@@ -493,6 +493,9 @@ class LinkedCountColumn(django_tables2.Column):
493
493
  reverse_lookup (str, optional): The reverse lookup parameter to use to derive the count.
494
494
  If not specified, the first key in `url_params` will be implicitly used as the `reverse_lookup` value.
495
495
  distinct (bool, optional): Parameter passed through to `count_related()`.
496
+ display_field (str, optional): Name of the field to use when displaying an object rather than just a count.
497
+ This will be passed to hyperlinked_object() as the `field` parameter
498
+ If not specified, it will use the "display" field.
496
499
  **kwargs (dict, optional): As the parent Column class.
497
500
 
498
501
  Examples:
@@ -536,6 +539,7 @@ class LinkedCountColumn(django_tables2.Column):
536
539
  reverse_lookup=None,
537
540
  distinct=False,
538
541
  default=None,
542
+ display_field="display",
539
543
  **kwargs,
540
544
  ):
541
545
  self.viewname = viewname
@@ -544,6 +548,7 @@ class LinkedCountColumn(django_tables2.Column):
544
548
  self.url_params = url_params
545
549
  self.reverse_lookup = reverse_lookup or next(iter(url_params.keys()))
546
550
  self.distinct = distinct
551
+ self.display_field = display_field
547
552
  self.model = get_model_for_view_name(self.viewname)
548
553
  super().__init__(*args, default=default, **kwargs)
549
554
 
@@ -565,7 +570,7 @@ class LinkedCountColumn(django_tables2.Column):
565
570
  if value > 1:
566
571
  return format_html('<a href="{}" class="badge">{}</a>', url, value)
567
572
  if related_record is not None:
568
- return helpers.hyperlinked_object(related_record)
573
+ return helpers.hyperlinked_object(related_record, self.display_field)
569
574
  if value == 1:
570
575
  return format_html('<a href="{}" class="badge">{}</a>', url, value)
571
576
  return helpers.placeholder(value)
@@ -117,7 +117,7 @@
117
117
  {% if perms.extras.view_note %}
118
118
  {% if active_tab != 'notes' and object.get_notes_url or active_tab == 'notes' %}
119
119
  <li role="presentation"{% if active_tab == 'notes' %} class="active"{% endif %}>
120
- <a href="{{ object.get_notes_url }}">Notes</a>
120
+ <a href="{{ object.get_notes_url }}">Notes {% badge object.notes.count %}</a>
121
121
  </li>
122
122
  {% endif %}
123
123
  {% endif %}
@@ -1,7 +1,11 @@
1
1
  <div class="input-group">
2
2
  {% include "django/forms/widgets/input.html" %}
3
3
  <span class="input-group-btn">
4
- <button class="btn btn-default reslugify" type="button" title="Regenerate slug">
4
+ <button class="btn btn-default reslugify"
5
+ type="button"
6
+ title="{% if widget.custom_title %}{{ widget.custom_title }}{% else %}Regenerate slug{% endif %}"
7
+ data-regenerate="{{ widget.attrs.id }}"
8
+ >
5
9
  <i class="mdi mdi-refresh"></i>
6
10
  </button>
7
11
  </span>