nautobot 2.4.6__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 (347) 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/urls.py +2 -2
  8. nautobot/core/api/views.py +39 -1
  9. nautobot/core/forms/__init__.py +2 -0
  10. nautobot/core/forms/fields.py +2 -2
  11. nautobot/core/forms/widgets.py +18 -0
  12. nautobot/core/templates/widgets/sluginput.html +5 -1
  13. nautobot/core/templatetags/helpers.py +15 -1
  14. nautobot/core/testing/integration.py +6 -2
  15. nautobot/core/ui/object_detail.py +16 -3
  16. nautobot/core/utils/lookup.py +2 -2
  17. nautobot/dcim/forms.py +10 -0
  18. nautobot/dcim/models/locations.py +9 -0
  19. nautobot/dcim/templates/dcim/device_list.html +1 -1
  20. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  21. nautobot/dcim/templates/dcim/manufacturer.html +1 -63
  22. nautobot/dcim/templates/dcim/module_list.html +1 -1
  23. nautobot/dcim/templates/dcim/moduletype_retrieve.html +1 -1
  24. nautobot/dcim/tests/integration/test_module_bay_position.py +125 -0
  25. nautobot/dcim/tests/test_models.py +13 -0
  26. nautobot/dcim/tests/test_views.py +4 -1
  27. nautobot/dcim/urls.py +1 -45
  28. nautobot/dcim/views.py +35 -66
  29. nautobot/extras/choices.py +4 -0
  30. nautobot/extras/filters/__init__.py +6 -0
  31. nautobot/extras/forms/forms.py +76 -8
  32. nautobot/extras/models/customfields.py +10 -9
  33. nautobot/extras/signals.py +43 -4
  34. nautobot/extras/tasks.py +4 -2
  35. nautobot/extras/templates/extras/contact_retrieve.html +1 -58
  36. nautobot/extras/templates/extras/exporttemplate.html +1 -53
  37. nautobot/extras/templates/extras/team_retrieve.html +1 -58
  38. nautobot/extras/tests/test_customfields.py +24 -0
  39. nautobot/extras/tests/test_views.py +22 -0
  40. nautobot/extras/urls.py +2 -70
  41. nautobot/extras/views.py +101 -79
  42. nautobot/ipam/tables.py +1 -0
  43. nautobot/project-static/css/base.css +5 -0
  44. nautobot/project-static/docs/404.html +0 -2
  45. nautobot/project-static/docs/apps/index.html +0 -2
  46. nautobot/project-static/docs/apps/nautobot-apps.html +0 -2
  47. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +0 -2
  48. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +0 -2
  49. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +0 -2
  50. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +0 -2
  51. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +0 -2
  52. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +0 -2
  53. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +0 -2
  54. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +0 -2
  55. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +0 -2
  56. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +0 -2
  57. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +0 -2
  58. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +0 -2
  59. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +62 -2
  60. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +0 -2
  61. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +0 -2
  62. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +0 -2
  63. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +0 -2
  64. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +0 -2
  65. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +0 -2
  66. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +0 -2
  67. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +7 -5
  68. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +0 -2
  69. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +0 -2
  70. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +0 -2
  71. nautobot/project-static/docs/development/apps/api/configuration-view.html +0 -2
  72. nautobot/project-static/docs/development/apps/api/database-backend-config.html +0 -2
  73. nautobot/project-static/docs/development/apps/api/models/django-admin.html +0 -2
  74. nautobot/project-static/docs/development/apps/api/models/global-search.html +0 -2
  75. nautobot/project-static/docs/development/apps/api/models/graphql.html +0 -2
  76. nautobot/project-static/docs/development/apps/api/models/index.html +0 -2
  77. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +0 -2
  78. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +0 -2
  79. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +0 -2
  80. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +0 -2
  81. nautobot/project-static/docs/development/apps/api/platform-features/index.html +0 -2
  82. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +0 -2
  83. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +0 -2
  84. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +0 -2
  85. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +0 -2
  86. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +0 -2
  87. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +0 -2
  88. nautobot/project-static/docs/development/apps/api/prometheus.html +0 -2
  89. nautobot/project-static/docs/development/apps/api/setup.html +0 -2
  90. nautobot/project-static/docs/development/apps/api/testing.html +0 -2
  91. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +0 -2
  92. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +0 -2
  93. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +0 -2
  94. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +0 -2
  95. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +0 -2
  96. nautobot/project-static/docs/development/apps/api/views/base-template.html +0 -2
  97. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +0 -2
  98. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +0 -2
  99. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +0 -2
  100. nautobot/project-static/docs/development/apps/api/views/index.html +0 -2
  101. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +0 -2
  102. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +0 -2
  103. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +0 -2
  104. nautobot/project-static/docs/development/apps/api/views/notes.html +0 -2
  105. nautobot/project-static/docs/development/apps/api/views/rest-api.html +0 -2
  106. nautobot/project-static/docs/development/apps/api/views/urls.html +0 -2
  107. nautobot/project-static/docs/development/apps/index.html +0 -2
  108. nautobot/project-static/docs/development/apps/migration/code-updates.html +0 -2
  109. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +0 -2
  110. nautobot/project-static/docs/development/apps/migration/from-v1.html +0 -2
  111. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +0 -2
  112. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +0 -2
  113. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +0 -2
  114. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +0 -2
  115. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +0 -2
  116. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +0 -2
  117. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +0 -2
  118. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +0 -2
  119. nautobot/project-static/docs/development/apps/porting-from-netbox.html +0 -2
  120. nautobot/project-static/docs/development/core/application-registry.html +0 -2
  121. nautobot/project-static/docs/development/core/best-practices.html +0 -2
  122. nautobot/project-static/docs/development/core/bootstrap-ui.html +0 -2
  123. nautobot/project-static/docs/development/core/caching.html +0 -2
  124. nautobot/project-static/docs/development/core/controllers.html +0 -2
  125. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +0 -2
  126. nautobot/project-static/docs/development/core/generic-views.html +0 -2
  127. nautobot/project-static/docs/development/core/getting-started.html +0 -2
  128. nautobot/project-static/docs/development/core/homepage.html +0 -2
  129. nautobot/project-static/docs/development/core/index.html +0 -2
  130. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +0 -2
  131. nautobot/project-static/docs/development/core/model-checklist.html +0 -2
  132. nautobot/project-static/docs/development/core/model-features.html +0 -2
  133. nautobot/project-static/docs/development/core/natural-keys.html +0 -2
  134. nautobot/project-static/docs/development/core/navigation-menu.html +0 -2
  135. nautobot/project-static/docs/development/core/release-checklist.html +0 -2
  136. nautobot/project-static/docs/development/core/role-internals.html +0 -2
  137. nautobot/project-static/docs/development/core/settings.html +0 -2
  138. nautobot/project-static/docs/development/core/style-guide.html +0 -2
  139. nautobot/project-static/docs/development/core/templates.html +0 -2
  140. nautobot/project-static/docs/development/core/testing.html +0 -2
  141. nautobot/project-static/docs/development/core/ui-component-framework.html +0 -2
  142. nautobot/project-static/docs/development/core/user-preferences.html +0 -2
  143. nautobot/project-static/docs/development/index.html +0 -2
  144. nautobot/project-static/docs/development/jobs/index.html +0 -2
  145. nautobot/project-static/docs/development/jobs/migration/from-v1.html +0 -2
  146. nautobot/project-static/docs/index.html +0 -2
  147. nautobot/project-static/docs/objects.inv +0 -0
  148. nautobot/project-static/docs/overview/application_stack.html +0 -2
  149. nautobot/project-static/docs/overview/design_philosophy.html +0 -2
  150. nautobot/project-static/docs/release-notes/index.html +0 -2
  151. nautobot/project-static/docs/release-notes/version-1.0.html +0 -2
  152. nautobot/project-static/docs/release-notes/version-1.1.html +0 -2
  153. nautobot/project-static/docs/release-notes/version-1.2.html +0 -2
  154. nautobot/project-static/docs/release-notes/version-1.3.html +0 -2
  155. nautobot/project-static/docs/release-notes/version-1.4.html +0 -2
  156. nautobot/project-static/docs/release-notes/version-1.5.html +0 -2
  157. nautobot/project-static/docs/release-notes/version-1.6.html +0 -2
  158. nautobot/project-static/docs/release-notes/version-2.0.html +0 -2
  159. nautobot/project-static/docs/release-notes/version-2.1.html +0 -2
  160. nautobot/project-static/docs/release-notes/version-2.2.html +0 -2
  161. nautobot/project-static/docs/release-notes/version-2.3.html +0 -2
  162. nautobot/project-static/docs/release-notes/version-2.4.html +138 -2
  163. nautobot/project-static/docs/search/search_index.json +1 -1
  164. nautobot/project-static/docs/sitemap.xml +290 -290
  165. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  166. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +0 -2
  167. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +0 -2
  168. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +0 -2
  169. nautobot/project-static/docs/user-guide/administration/configuration/index.html +0 -2
  170. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +0 -2
  171. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +0 -2
  172. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +0 -2
  173. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +0 -2
  174. nautobot/project-static/docs/user-guide/administration/guides/docker.html +0 -2
  175. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +0 -2
  176. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +0 -2
  177. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +0 -2
  178. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +0 -2
  179. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +0 -2
  180. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +0 -2
  181. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +0 -2
  182. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +0 -2
  183. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +0 -2
  184. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +0 -2
  185. nautobot/project-static/docs/user-guide/administration/installation/index.html +0 -2
  186. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +0 -2
  187. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +0 -2
  188. nautobot/project-static/docs/user-guide/administration/installation/services.html +0 -2
  189. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +0 -2
  190. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +0 -2
  191. nautobot/project-static/docs/user-guide/administration/security/index.html +0 -2
  192. nautobot/project-static/docs/user-guide/administration/security/notices.html +0 -2
  193. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +0 -2
  194. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +0 -2
  195. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +0 -2
  196. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +0 -2
  197. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +0 -2
  198. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +0 -2
  199. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +0 -2
  200. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +0 -2
  201. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +0 -2
  202. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +0 -2
  203. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +0 -2
  204. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +0 -2
  205. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +0 -2
  206. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +0 -2
  207. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +0 -2
  208. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +0 -2
  209. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +0 -2
  210. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +0 -2
  211. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +0 -2
  212. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +0 -2
  213. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +0 -2
  214. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +0 -2
  215. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +0 -2
  216. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +0 -2
  217. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +0 -2
  218. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +0 -2
  219. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +0 -2
  220. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +0 -2
  221. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +0 -2
  222. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +0 -2
  223. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +0 -2
  224. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +0 -2
  225. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +0 -2
  226. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +0 -2
  227. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +0 -2
  228. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +0 -2
  229. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +0 -2
  230. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +0 -2
  231. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +0 -2
  232. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +0 -2
  233. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +0 -2
  234. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +0 -2
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +0 -2
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +0 -2
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +0 -2
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +0 -2
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +0 -2
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +0 -2
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +0 -2
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +0 -2
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +0 -2
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +0 -2
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +0 -2
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +0 -2
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +0 -2
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +0 -2
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +0 -2
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +0 -2
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +0 -2
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +0 -2
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +0 -2
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +0 -2
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +0 -2
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +0 -2
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +0 -2
  258. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +0 -2
  259. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +0 -2
  260. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +0 -2
  261. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +0 -2
  262. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +0 -2
  263. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +0 -2
  264. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +0 -2
  265. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +0 -2
  266. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +0 -2
  267. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +0 -2
  268. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +0 -2
  269. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +0 -2
  270. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +0 -2
  271. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +0 -2
  272. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +0 -2
  273. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +0 -2
  274. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +0 -2
  275. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +0 -2
  276. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +0 -2
  277. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +0 -2
  278. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +0 -2
  279. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +0 -2
  280. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +0 -2
  281. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +0 -2
  282. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +0 -2
  283. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +0 -2
  284. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +0 -2
  285. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +0 -2
  286. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +0 -2
  287. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +0 -2
  288. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +0 -2
  289. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +0 -2
  290. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +0 -2
  291. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +0 -2
  292. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +0 -2
  293. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +0 -2
  294. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +0 -2
  295. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +0 -2
  296. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +0 -2
  297. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +0 -2
  298. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +0 -2
  299. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +0 -2
  300. nautobot/project-static/docs/user-guide/index.html +0 -2
  301. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +0 -2
  302. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +0 -2
  303. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +0 -2
  304. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +0 -2
  305. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +0 -2
  306. nautobot/project-static/docs/user-guide/platform-functionality/events.html +0 -2
  307. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +0 -2
  308. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +0 -2
  309. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +0 -2
  310. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +0 -2
  311. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +0 -2
  312. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +0 -2
  313. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +0 -2
  314. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +0 -2
  315. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +0 -2
  316. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +0 -2
  317. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +0 -2
  318. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +0 -2
  319. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +0 -2
  320. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +0 -2
  321. nautobot/project-static/docs/user-guide/platform-functionality/note.html +0 -2
  322. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +0 -2
  323. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +0 -2
  324. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +0 -2
  325. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +0 -2
  326. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +0 -2
  327. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +0 -2
  328. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +0 -2
  329. nautobot/project-static/docs/user-guide/platform-functionality/role.html +0 -2
  330. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +0 -2
  331. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +0 -2
  332. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +0 -2
  333. nautobot/project-static/docs/user-guide/platform-functionality/status.html +0 -2
  334. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +0 -2
  335. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +0 -2
  336. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +0 -2
  337. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +0 -2
  338. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +0 -2
  339. nautobot/project-static/js/forms.js +88 -37
  340. nautobot/project-static/js/homepage_layout.js +12 -3
  341. {nautobot-2.4.6.dist-info → nautobot-2.4.7.dist-info}/METADATA +1 -1
  342. {nautobot-2.4.6.dist-info → nautobot-2.4.7.dist-info}/RECORD +346 -346
  343. nautobot/dcim/templates/dcim/modulebay_create.html +0 -39
  344. {nautobot-2.4.6.dist-info → nautobot-2.4.7.dist-info}/LICENSE.txt +0 -0
  345. {nautobot-2.4.6.dist-info → nautobot-2.4.7.dist-info}/NOTICE +0 -0
  346. {nautobot-2.4.6.dist-info → nautobot-2.4.7.dist-info}/WHEEL +0 -0
  347. {nautobot-2.4.6.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()
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
  #
@@ -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",
@@ -457,7 +457,7 @@ class AutoPositionField(django_forms.CharField):
457
457
  source (str, tuple): Name of the field (or a list of field names) that will be used to suggest a position.
458
458
  """
459
459
  kwargs.setdefault("label", "Position")
460
- kwargs.setdefault("widget", forms.SlugWidget)
460
+ kwargs.setdefault("widget", forms.AutoPopulateWidget)
461
461
  super().__init__(*args, **kwargs)
462
462
  if isinstance(source, (tuple, list)):
463
463
  source = " ".join(source)
@@ -473,7 +473,7 @@ class AutoPositionPatternField(ExpandableNameField):
473
473
  source (str, tuple): Name pattern of the field (or a list of field names) that will be used to suggest a position pattern.
474
474
  """
475
475
  kwargs.setdefault("label", "Position")
476
- kwargs.setdefault("widget", forms.SlugWidget)
476
+ kwargs.setdefault("widget", forms.AutoPopulateWidget(attrs={"title": "Regenerate position"}))
477
477
  super().__init__(*args, **kwargs)
478
478
  if isinstance(source, (tuple, list)):
479
479
  source = " ".join(source)
@@ -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
  """
@@ -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>
@@ -2,7 +2,7 @@ import datetime
2
2
  import json
3
3
  import logging
4
4
  import re
5
- from urllib.parse import parse_qs
5
+ from urllib.parse import parse_qs, quote_plus
6
6
 
7
7
  from django import template
8
8
  from django.conf import settings
@@ -711,6 +711,20 @@ def render_ancestor_hierarchy(value):
711
711
  return result
712
712
 
713
713
 
714
+ @library.filter()
715
+ @register.filter()
716
+ def render_address(address):
717
+ if address:
718
+ map_link = format_html(
719
+ '<a href="https://maps.google.com/?q={}" target="_blank" class="btn btn-primary btn-xs">'
720
+ '<i class="mdi mdi-map-marker"></i> Map it</a>',
721
+ quote_plus(address),
722
+ )
723
+ address = format_html_join("", "{}<br>", ((line,) for line in address.split("\n")))
724
+ return format_html('<div class="pull-right noprint">{}</div>{}', map_link, address)
725
+ return HTML_NONE
726
+
727
+
714
728
  #
715
729
  # Tags
716
730
  #
@@ -35,11 +35,15 @@ class ObjectsListMixin:
35
35
  """
36
36
  self.browser.find_by_css("#object_list_form input.toggle").click()
37
37
 
38
- def select_one_item(self):
38
+ def select_one_item(self, pk=None):
39
39
  """
40
40
  Click first row checkbox on items table list to select one row.
41
41
  """
42
- self.browser.find_by_css('#object_list_form input[name="pk"]').click()
42
+ selector = '#object_list_form input[name="pk"]'
43
+ if pk:
44
+ selector = f'{selector}[value="{pk}"]'
45
+
46
+ self.browser.find_by_css(selector).click()
43
47
 
44
48
  def set_per_page(self, per_page=1):
45
49
  """
@@ -8,7 +8,7 @@ import logging
8
8
  from django.contrib.contenttypes.models import ContentType
9
9
  from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
10
10
  from django.db import models
11
- from django.db.models import CharField, JSONField, URLField
11
+ from django.db.models import CharField, JSONField, Q, URLField
12
12
  from django.db.models.fields.related import ManyToManyField
13
13
  from django.template import Context
14
14
  from django.template.defaultfilters import truncatechars
@@ -665,10 +665,14 @@ class ObjectsTablePanel(Panel):
665
665
  Table (`BaseTable`) instance. Mutually exclusive with `table_class`, `table_filter`, `table_attribute`.
666
666
  table_class (obj): The table class that will be instantiated and rendered e.g. CircuitTable, DeviceTable.
667
667
  Mutually exclusive with `context_table_key`.
668
- table_filter (str, optional): The name of the filter to apply to the queryset to initialize the table class.
668
+ table_filter (str, list, optional): The filter(s) to apply to the queryset to initialize the table class.
669
669
  For example, in a LocationType detail view, for an ObjectsTablePanel of related Locations, this would
670
670
  be `location_type`, because `Location.objects.filter(location_type=obj)` gives the desired queryset.
671
671
  Mutually exclusive with `table_attribute`.
672
+ For example, in ProviderNetwork detail view, for an ObjectsTablePanel of related Circuits, this would
673
+ be `["circuit_termination_a__provider_network", "circuit_termination_z__provider_network"]` because
674
+ `Circuit.objects.filter(Q(circuit_termination_a__provider_network=instance)
675
+ | Q(circuit_termination_z__provider_network=instance))` gives the desired queryset.
672
676
  table_attribute (str, optional): The attribute of the detail view instance that contains the queryset to
673
677
  initialize the table class. e.g. `dynamic_groups`.
674
678
  Mutually exclusive with `table_filter`.
@@ -789,7 +793,16 @@ class ObjectsTablePanel(Panel):
789
793
  if self.table_attribute:
790
794
  body_content_table_queryset = getattr(instance, self.table_attribute)
791
795
  else:
792
- body_content_table_queryset = body_content_table_model.objects.filter(**{self.table_filter: instance})
796
+ if isinstance(self.table_filter, str):
797
+ table_filters = [self.table_filter]
798
+ elif isinstance(self.table_filter, list):
799
+ table_filters = self.table_filter
800
+ else:
801
+ table_filters = []
802
+ query = Q()
803
+ for table_filter in table_filters:
804
+ query = query | Q(**{table_filter: instance})
805
+ body_content_table_queryset = body_content_table_model.objects.filter(query)
793
806
 
794
807
  body_content_table_queryset = body_content_table_queryset.restrict(request.user, "view")
795
808
  if self.select_related_fields:
@@ -291,9 +291,9 @@ def get_table_class_string_from_view_name(view_name):
291
291
 
292
292
  view_func = resolve(reverse(view_name)).func
293
293
  view_class = getattr(view_func, "cls", getattr(view_func, "view_class", None))
294
- if hasattr(view_class, "table_class"):
294
+ if hasattr(view_class, "table_class") and view_class.table_class:
295
295
  return view_class.table_class.__name__
296
- if hasattr(view_class, "table"):
296
+ if hasattr(view_class, "table") and view_class.table:
297
297
  return view_class.table.__name__
298
298
  return None
299
299
 
nautobot/dcim/forms.py CHANGED
@@ -768,6 +768,16 @@ class RackReservationFilterForm(NautobotFilterForm, TenancyFilterForm):
768
768
  #
769
769
 
770
770
 
771
+ class ManufacturerBulkEditForm(NautobotBulkEditForm):
772
+ pk = forms.ModelMultipleChoiceField(queryset=Manufacturer.objects.all(), widget=forms.MultipleHiddenInput())
773
+ description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
774
+
775
+ class Meta:
776
+ nullable_fields = [
777
+ "description",
778
+ ]
779
+
780
+
771
781
  class ManufacturerForm(NautobotModelForm):
772
782
  class Meta:
773
783
  model = Manufacturer
@@ -220,6 +220,15 @@ class Location(TreeModel, PrimaryModel):
220
220
  def __str__(self):
221
221
  return self.name
222
222
 
223
+ @property
224
+ def display(self):
225
+ """
226
+ Honor LOCATION_NAME_AS_NATURAL_KEY for display value rendering, else fallback to TreeModel.display().
227
+ """
228
+ if get_settings_or_config("LOCATION_NAME_AS_NATURAL_KEY"):
229
+ return self.name
230
+ return super().display
231
+
223
232
  @classproperty # https://github.com/PyCQA/pylint-django/issues/240
224
233
  def natural_key_field_lookups(cls): # pylint: disable=no-self-argument
225
234
  """
@@ -3,7 +3,7 @@
3
3
  {% block bulk_buttons %}
4
4
  {% if perms.dcim.change_device %}
5
5
  <div class="btn-group dropup">
6
- <button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
6
+ <button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="device-bulk-add-components-button">
7
7
  <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
8
8
  </button>
9
9
  <ul class="dropdown-menu">
@@ -10,7 +10,7 @@
10
10
  {% block extra_buttons %}
11
11
  {% if perms.dcim.change_devicetype %}
12
12
  <div class="btn-group">
13
- <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
13
+ <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="device-type-add-components-button">
14
14
  <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
15
15
  </button>
16
16
  <ul class="dropdown-menu">
@@ -1,64 +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>Manufacturer</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>Devices</td>
16
- <td>
17
- <a href="{% url 'dcim:device_list' %}?manufacturer={{ object.name }}">{{ device_table.rows|length }}</a>
18
- </td>
19
- </tr>
20
- {% if cloud_account_table.rows %}
21
- <tr>
22
- <td>Cloud Accounts</td>
23
- <td>
24
- <a href="{% url 'cloud:cloudaccount_list' %}?provider={{ object.name }}">{{ cloud_account_table.rows|length }}</a>
25
- </td>
26
- </tr>
27
- {% endif %}
28
- </table>
29
- </div>
30
- {% endblock content_left_page %}
31
-
32
- {% block content_full_width_page %}
33
- <div class="panel panel-default">
34
- <div class="panel-heading">
35
- <strong>Devices</strong>
36
- </div>
37
- {% include 'inc/table.html' with table=device_table %}
38
- {% if perms.dcim.add_device %}
39
- <div class="panel-footer text-right noprint">
40
- <a href="{% url 'dcim:device_add' %}?manufacturer={{ object.pk }}" class="btn btn-xs btn-primary">
41
- <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add device
42
- </a>
43
- </div>
44
- {% endif %}
45
- </div>
46
- {% include 'inc/paginator.html' with paginator=device_table.paginator page=device_table.page %}
47
- {% if cloud_account_table.rows %}
48
- <div class="panel panel-default">
49
- <div class="panel-heading">
50
- <strong>Cloud Accounts</strong>
51
- </div>
52
- {% include 'inc/table.html' with table=cloud_account_table %}
53
- {% if perms.cloud.add_cloudaccount %}
54
- <div class="panel-footer text-right noprint">
55
- <a href="{% url 'cloud:cloudaccount_add' %}?provider={{ object.pk }}" class="btn btn-xs btn-primary">
56
- <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Cloud Account
57
- </a>
58
- </div>
59
- {% endif %}
60
- </div>
61
- {% include 'inc/paginator.html' with paginator=cloud_account_table.paginator page=cloud_account_table.page %}
62
- {% endif %}
63
- <div class="row"></div>
64
- {% 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 %}
@@ -3,7 +3,7 @@
3
3
  {% block bulk_buttons %}
4
4
  {% if perms.dcim.change_module %}
5
5
  <div class="btn-group dropup">
6
- <button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
6
+ <button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="module-bulk-add-components-button">
7
7
  <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
8
8
  </button>
9
9
  <ul class="dropdown-menu">
@@ -9,7 +9,7 @@
9
9
  {% block extra_buttons %}
10
10
  {% if perms.dcim.change_moduletype %}
11
11
  <div class="btn-group">
12
- <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
12
+ <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="module-type-add-components-button">
13
13
  <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
14
14
  </button>
15
15
  <ul class="dropdown-menu">