nautobot 2.4.13__py3-none-any.whl → 2.4.15__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 (463) hide show
  1. nautobot/apps/choices.py +8 -0
  2. nautobot/apps/ui.py +14 -0
  3. nautobot/core/api/views.py +2 -0
  4. nautobot/core/choices.py +4 -0
  5. nautobot/core/filters.py +21 -41
  6. nautobot/core/graphql/generators.py +8 -0
  7. nautobot/core/graphql/schema.py +30 -30
  8. nautobot/core/management/commands/check_job_approval_status.py +47 -0
  9. nautobot/core/management/commands/generate_test_data.py +1 -1
  10. nautobot/core/management/commands/migrate.py +90 -1
  11. nautobot/core/models/tree_queries.py +17 -0
  12. nautobot/core/settings.py +2 -2
  13. nautobot/core/settings.yaml +3 -3
  14. nautobot/core/tables.py +29 -6
  15. nautobot/core/templates/base_django.html +1 -1
  16. nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
  17. nautobot/core/templates/generic/object_list.html +17 -20
  18. nautobot/core/templates/inc/breadcrumbs.html +14 -0
  19. nautobot/core/templatetags/buttons.py +2 -4
  20. nautobot/core/templatetags/helpers.py +29 -6
  21. nautobot/core/templatetags/ui_framework.py +21 -0
  22. nautobot/core/testing/api.py +7 -0
  23. nautobot/core/testing/filters.py +20 -3
  24. nautobot/core/testing/forms.py +1 -1
  25. nautobot/core/tests/integration/test_filters.py +2 -2
  26. nautobot/core/tests/test_breadcrumbs.py +366 -0
  27. nautobot/core/tests/test_commands.py +40 -0
  28. nautobot/core/tests/test_filters.py +51 -1
  29. nautobot/core/tests/test_forms.py +1 -1
  30. nautobot/core/tests/test_graphql.py +4 -4
  31. nautobot/core/tests/test_titles.py +183 -0
  32. nautobot/core/tests/test_tree_queries.py +30 -0
  33. nautobot/core/tests/test_views.py +2 -2
  34. nautobot/core/tests/test_views_utils.py +1 -1
  35. nautobot/core/ui/breadcrumbs.py +538 -0
  36. nautobot/core/ui/bulk_buttons.py +53 -0
  37. nautobot/core/ui/object_detail.py +31 -8
  38. nautobot/core/ui/titles.py +127 -0
  39. nautobot/core/ui/utils.py +25 -0
  40. nautobot/core/utils/migrations.py +1 -1
  41. nautobot/core/views/__init__.py +1 -1
  42. nautobot/core/views/mixins.py +26 -1
  43. nautobot/core/views/renderers.py +20 -2
  44. nautobot/core/views/utils.py +14 -13
  45. nautobot/dcim/api/serializers.py +9 -0
  46. nautobot/dcim/choices.py +55 -0
  47. nautobot/dcim/constants.py +0 -16
  48. nautobot/dcim/factory.py +1 -1
  49. nautobot/dcim/filters/__init__.py +15 -3
  50. nautobot/dcim/forms.py +120 -7
  51. nautobot/dcim/management/commands/trace_paths.py +1 -1
  52. nautobot/dcim/migrations/0072_alter_powerfeed_options_and_more.py +97 -0
  53. nautobot/dcim/models/device_component_templates.py +8 -0
  54. nautobot/dcim/models/device_components.py +31 -12
  55. nautobot/dcim/models/devices.py +1 -1
  56. nautobot/dcim/models/power.py +171 -10
  57. nautobot/dcim/models/racks.py +7 -4
  58. nautobot/dcim/tables/devices.py +2 -0
  59. nautobot/dcim/tables/devicetypes.py +1 -0
  60. nautobot/dcim/tables/power.py +30 -2
  61. nautobot/dcim/templates/dcim/device.html +2 -2
  62. nautobot/dcim/templates/dcim/devicetype_retrieve.html +1 -214
  63. nautobot/dcim/templates/dcim/location_retrieve.html +2 -2
  64. nautobot/dcim/templates/dcim/powerfeed_edit.html +8 -0
  65. nautobot/dcim/templates/dcim/powerfeed_retrieve.html +1 -1
  66. nautobot/dcim/templates/dcim/rack.html +2 -318
  67. nautobot/dcim/templates/dcim/rack_edit.html +2 -47
  68. nautobot/dcim/templates/dcim/rack_retrieve.html +318 -0
  69. nautobot/dcim/templates/dcim/rack_update.html +47 -0
  70. nautobot/dcim/tests/integration/test_device_bulk_operations.py +61 -0
  71. nautobot/dcim/tests/test_api.py +24 -4
  72. nautobot/dcim/tests/test_filters.py +91 -13
  73. nautobot/dcim/tests/test_models.py +262 -0
  74. nautobot/dcim/tests/test_views.py +20 -12
  75. nautobot/dcim/urls.py +2 -27
  76. nautobot/dcim/utils.py +13 -30
  77. nautobot/dcim/views.py +428 -146
  78. nautobot/extras/choices.py +12 -4
  79. nautobot/extras/factory.py +19 -20
  80. nautobot/extras/filters/__init__.py +3 -2
  81. nautobot/extras/filters/mixins.py +23 -7
  82. nautobot/extras/forms/__init__.py +2 -1
  83. nautobot/extras/forms/forms.py +71 -0
  84. nautobot/extras/forms/mixins.py +4 -2
  85. nautobot/extras/managers.py +4 -1
  86. nautobot/extras/migrations/0062_collect_roles_from_related_apps_roles.py +30 -7
  87. nautobot/extras/migrations/0124_add_joblogentry_index.py +16 -0
  88. nautobot/extras/migrations/0125_jobresult_date_started.py +18 -0
  89. nautobot/extras/models/customfields.py +53 -5
  90. nautobot/extras/models/datasources.py +1 -2
  91. nautobot/extras/models/jobs.py +13 -3
  92. nautobot/extras/models/relationships.py +55 -6
  93. nautobot/extras/plugins/views.py +24 -1
  94. nautobot/extras/secrets/__init__.py +1 -1
  95. nautobot/extras/tables.py +9 -0
  96. nautobot/extras/templates/extras/customfield.html +2 -129
  97. nautobot/extras/templates/extras/customfield_edit.html +2 -108
  98. nautobot/extras/templates/extras/customfield_retrieve.html +129 -0
  99. nautobot/extras/templates/extras/customfield_update.html +108 -0
  100. nautobot/extras/templates/extras/graphqlquery.html +2 -97
  101. nautobot/extras/templates/extras/graphqlquery_list.html +1 -0
  102. nautobot/extras/templates/extras/graphqlquery_retrieve.html +97 -0
  103. nautobot/extras/templates/extras/inc/jobresult.html +7 -3
  104. nautobot/extras/templates/extras/jobresult.html +2 -155
  105. nautobot/extras/templates/extras/jobresult_retrieve.html +155 -0
  106. nautobot/extras/templates/extras/marketplace.html +5 -6
  107. nautobot/extras/templates/extras/note.html +2 -53
  108. nautobot/extras/templates/extras/note_retrieve.html +53 -0
  109. nautobot/extras/templates/extras/plugins_list.html +5 -6
  110. nautobot/extras/templates/extras/secretsgroup.html +2 -29
  111. nautobot/extras/templates/extras/secretsgroup_edit.html +2 -82
  112. nautobot/extras/templates/extras/secretsgroup_retrieve.html +29 -0
  113. nautobot/extras/templates/extras/secretsgroup_update.html +82 -0
  114. nautobot/extras/templatetags/custom_links.py +2 -2
  115. nautobot/extras/templatetags/job_buttons.py +1 -1
  116. nautobot/extras/templatetags/plugins.py +1 -1
  117. nautobot/extras/tests/integration/test_computedfields.py +2 -2
  118. nautobot/extras/tests/integration/test_customfields.py +14 -11
  119. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  120. nautobot/extras/tests/integration/test_notes.py +1 -1
  121. nautobot/extras/tests/integration/test_plugins.py +6 -6
  122. nautobot/extras/tests/integration/test_relationships.py +2 -2
  123. nautobot/extras/tests/test_customfields.py +115 -7
  124. nautobot/extras/tests/test_filters.py +9 -0
  125. nautobot/extras/tests/test_forms.py +2 -2
  126. nautobot/extras/tests/test_plugins.py +2 -3
  127. nautobot/extras/tests/test_relationships.py +14 -8
  128. nautobot/extras/tests/test_views.py +285 -2
  129. nautobot/extras/urls.py +5 -110
  130. nautobot/extras/utils.py +5 -2
  131. nautobot/extras/views.py +116 -311
  132. nautobot/ipam/api/views.py +69 -6
  133. nautobot/ipam/tables.py +8 -15
  134. nautobot/ipam/tests/migration/test_migrations.py +8 -8
  135. nautobot/ipam/tests/test_api.py +352 -2
  136. nautobot/ipam/tests/test_models.py +1 -1
  137. nautobot/project-static/docs/404.html +34 -34
  138. nautobot/project-static/docs/apps/index.html +34 -34
  139. nautobot/project-static/docs/apps/nautobot-apps.html +34 -34
  140. nautobot/project-static/docs/assets/_mkdocstrings.css +44 -6
  141. nautobot/project-static/docs/assets/javascripts/{bundle.56ea9cef.min.js → bundle.50899def.min.js} +2 -2
  142. nautobot/project-static/docs/assets/javascripts/{bundle.56ea9cef.min.js.map → bundle.50899def.min.js.map} +2 -2
  143. nautobot/project-static/docs/assets/stylesheets/{main.342714a4.min.css → main.7e37652d.min.css} +1 -1
  144. nautobot/project-static/docs/assets/stylesheets/{main.342714a4.min.css.map → main.7e37652d.min.css.map} +1 -1
  145. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +39 -34
  146. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +36 -34
  147. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +139 -54
  148. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +48 -38
  149. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +50 -40
  150. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +36 -34
  151. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +35 -34
  152. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +43 -39
  153. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +52 -42
  154. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +50 -41
  155. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +54 -44
  156. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +85 -93
  157. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +154 -62
  158. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +54 -46
  159. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +146 -87
  160. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +240 -70
  161. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +38 -35
  162. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +41 -35
  163. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +173 -52
  164. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +269 -85
  165. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5987 -2643
  166. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +36 -34
  167. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +165 -89
  168. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +161 -69
  169. nautobot/project-static/docs/development/apps/api/configuration-view.html +34 -34
  170. nautobot/project-static/docs/development/apps/api/database-backend-config.html +34 -34
  171. nautobot/project-static/docs/development/apps/api/models/django-admin.html +34 -34
  172. nautobot/project-static/docs/development/apps/api/models/global-search.html +34 -34
  173. nautobot/project-static/docs/development/apps/api/models/graphql.html +34 -34
  174. nautobot/project-static/docs/development/apps/api/models/index.html +34 -34
  175. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +34 -34
  176. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +34 -34
  177. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +34 -34
  178. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +34 -34
  179. nautobot/project-static/docs/development/apps/api/platform-features/index.html +34 -34
  180. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +34 -34
  181. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +34 -34
  182. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +34 -34
  183. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +34 -34
  184. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +34 -34
  185. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +34 -34
  186. nautobot/project-static/docs/development/apps/api/prometheus.html +34 -34
  187. nautobot/project-static/docs/development/apps/api/setup.html +34 -34
  188. nautobot/project-static/docs/development/apps/api/testing.html +34 -34
  189. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +34 -34
  190. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +34 -34
  191. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +34 -34
  192. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +34 -34
  193. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +34 -34
  194. nautobot/project-static/docs/development/apps/api/views/base-template.html +34 -34
  195. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +34 -34
  196. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +34 -34
  197. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +34 -34
  198. nautobot/project-static/docs/development/apps/api/views/index.html +34 -34
  199. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +34 -34
  200. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +42 -36
  201. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +34 -34
  202. nautobot/project-static/docs/development/apps/api/views/notes.html +34 -34
  203. nautobot/project-static/docs/development/apps/api/views/rest-api.html +34 -34
  204. nautobot/project-static/docs/development/apps/api/views/urls.html +34 -34
  205. nautobot/project-static/docs/development/apps/index.html +34 -34
  206. nautobot/project-static/docs/development/apps/migration/code-updates.html +34 -34
  207. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +34 -34
  208. nautobot/project-static/docs/development/apps/migration/from-v1.html +34 -34
  209. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +34 -34
  210. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +34 -34
  211. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +34 -34
  212. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +34 -34
  213. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +37 -37
  214. nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +10544 -0
  215. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +34 -34
  216. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +34 -34
  217. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +34 -34
  218. nautobot/project-static/docs/development/apps/porting-from-netbox.html +37 -37
  219. nautobot/project-static/docs/development/core/application-registry.html +162 -133
  220. nautobot/project-static/docs/development/core/best-practices.html +34 -34
  221. nautobot/project-static/docs/development/core/bootstrap-ui.html +34 -34
  222. nautobot/project-static/docs/development/core/caching.html +34 -34
  223. nautobot/project-static/docs/development/core/controllers.html +34 -34
  224. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +34 -34
  225. nautobot/project-static/docs/development/core/generic-views.html +34 -34
  226. nautobot/project-static/docs/development/core/getting-started.html +34 -34
  227. nautobot/project-static/docs/development/core/homepage.html +34 -34
  228. nautobot/project-static/docs/development/core/index.html +34 -34
  229. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +34 -34
  230. nautobot/project-static/docs/development/core/model-checklist.html +34 -34
  231. nautobot/project-static/docs/development/core/model-features.html +34 -34
  232. nautobot/project-static/docs/development/core/natural-keys.html +34 -34
  233. nautobot/project-static/docs/development/core/navigation-menu.html +34 -34
  234. nautobot/project-static/docs/development/core/release-checklist.html +34 -34
  235. nautobot/project-static/docs/development/core/role-internals.html +34 -34
  236. nautobot/project-static/docs/development/core/settings.html +34 -34
  237. nautobot/project-static/docs/development/core/style-guide.html +34 -34
  238. nautobot/project-static/docs/development/core/templates.html +34 -34
  239. nautobot/project-static/docs/development/core/testing.html +34 -34
  240. nautobot/project-static/docs/development/core/ui-component-framework.html +724 -289
  241. nautobot/project-static/docs/development/core/user-preferences.html +34 -34
  242. nautobot/project-static/docs/development/index.html +34 -34
  243. nautobot/project-static/docs/development/jobs/getting-started.html +34 -34
  244. nautobot/project-static/docs/development/jobs/index.html +34 -34
  245. nautobot/project-static/docs/development/jobs/installation.html +34 -34
  246. nautobot/project-static/docs/development/jobs/job-extensions.html +34 -34
  247. nautobot/project-static/docs/development/jobs/job-logging.html +34 -34
  248. nautobot/project-static/docs/development/jobs/job-patterns.html +34 -34
  249. nautobot/project-static/docs/development/jobs/job-structure.html +34 -34
  250. nautobot/project-static/docs/development/jobs/migration/from-v1.html +34 -34
  251. nautobot/project-static/docs/development/jobs/testing.html +34 -34
  252. nautobot/project-static/docs/index.html +34 -34
  253. nautobot/project-static/docs/media/development/core/ui-component-framework/breadcrumbs-titles-data-flow.png +0 -0
  254. nautobot/project-static/docs/media/power_distribution.png +0 -0
  255. nautobot/project-static/docs/objects.inv +0 -0
  256. nautobot/project-static/docs/overview/application_stack.html +34 -34
  257. nautobot/project-static/docs/overview/design_philosophy.html +34 -34
  258. nautobot/project-static/docs/release-notes/index.html +34 -34
  259. nautobot/project-static/docs/release-notes/version-1.0.html +34 -34
  260. nautobot/project-static/docs/release-notes/version-1.1.html +34 -34
  261. nautobot/project-static/docs/release-notes/version-1.2.html +34 -34
  262. nautobot/project-static/docs/release-notes/version-1.3.html +34 -34
  263. nautobot/project-static/docs/release-notes/version-1.4.html +34 -34
  264. nautobot/project-static/docs/release-notes/version-1.5.html +34 -34
  265. nautobot/project-static/docs/release-notes/version-1.6.html +34 -34
  266. nautobot/project-static/docs/release-notes/version-2.0.html +34 -34
  267. nautobot/project-static/docs/release-notes/version-2.1.html +34 -34
  268. nautobot/project-static/docs/release-notes/version-2.2.html +34 -34
  269. nautobot/project-static/docs/release-notes/version-2.3.html +34 -34
  270. nautobot/project-static/docs/release-notes/version-2.4.html +402 -34
  271. nautobot/project-static/docs/requirements.txt +3 -3
  272. nautobot/project-static/docs/search/search_index.json +1 -1
  273. nautobot/project-static/docs/sitemap.xml +303 -299
  274. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  275. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +34 -34
  276. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +34 -34
  277. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +34 -34
  278. nautobot/project-static/docs/user-guide/administration/configuration/index.html +34 -34
  279. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +34 -34
  280. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +37 -37
  281. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +34 -34
  282. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +34 -34
  283. nautobot/project-static/docs/user-guide/administration/guides/docker.html +34 -34
  284. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +34 -34
  285. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +34 -34
  286. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +34 -34
  287. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +34 -34
  288. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +34 -34
  289. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +34 -34
  290. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +34 -34
  291. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +34 -34
  292. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +34 -34
  293. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +34 -34
  294. nautobot/project-static/docs/user-guide/administration/installation/index.html +34 -34
  295. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +34 -34
  296. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +34 -34
  297. nautobot/project-static/docs/user-guide/administration/installation/services.html +34 -34
  298. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +34 -34
  299. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +34 -34
  300. nautobot/project-static/docs/user-guide/administration/security/index.html +34 -34
  301. nautobot/project-static/docs/user-guide/administration/security/notices.html +34 -34
  302. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +296 -251
  303. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +34 -34
  304. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +34 -34
  305. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +34 -34
  306. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +34 -34
  307. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +34 -34
  308. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +34 -34
  309. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +34 -34
  310. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +34 -34
  311. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +34 -34
  312. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +34 -34
  313. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +34 -34
  314. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +34 -34
  315. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +34 -34
  316. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +34 -34
  317. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +34 -34
  318. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +34 -34
  319. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +34 -34
  320. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +34 -34
  321. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +34 -34
  322. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +34 -34
  323. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +34 -34
  324. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +34 -34
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +34 -34
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +34 -34
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +34 -34
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +34 -34
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +34 -34
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +34 -34
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +34 -34
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +34 -34
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +34 -34
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +34 -34
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +34 -34
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +34 -34
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +34 -34
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +34 -34
  339. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +34 -34
  340. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +37 -37
  341. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +34 -34
  342. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +34 -34
  343. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +42 -52
  344. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +34 -34
  345. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +34 -34
  346. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +34 -34
  347. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +34 -34
  348. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +37 -37
  349. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +34 -34
  350. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +34 -34
  351. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +34 -34
  352. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +34 -34
  353. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +316 -39
  354. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +35 -35
  355. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +34 -34
  356. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +147 -37
  357. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +52 -35
  358. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +51 -34
  359. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +34 -34
  360. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +34 -34
  361. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +34 -34
  362. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +34 -34
  363. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +34 -34
  364. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +34 -34
  365. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +34 -34
  366. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +34 -34
  367. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +34 -34
  368. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +34 -34
  369. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +34 -34
  370. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +34 -34
  371. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +34 -34
  372. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +34 -34
  373. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +34 -34
  374. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +34 -34
  375. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +34 -34
  376. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +34 -34
  377. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +34 -34
  378. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +34 -34
  379. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +34 -34
  380. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +34 -34
  381. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +34 -34
  382. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +34 -34
  383. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +34 -34
  384. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +34 -34
  385. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +34 -34
  386. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +34 -34
  387. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +34 -34
  388. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +34 -34
  389. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +34 -34
  390. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +34 -34
  391. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +34 -34
  392. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +34 -34
  393. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +34 -34
  394. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +34 -34
  395. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +34 -34
  396. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +34 -34
  397. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +34 -34
  398. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +34 -34
  399. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +34 -34
  400. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +34 -34
  401. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +34 -34
  402. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +34 -34
  403. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +34 -34
  404. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +34 -34
  405. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +34 -34
  406. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +34 -34
  407. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +34 -34
  408. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +34 -34
  409. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +34 -34
  410. nautobot/project-static/docs/user-guide/index.html +34 -34
  411. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +34 -34
  412. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +34 -34
  413. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +34 -34
  414. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +34 -34
  415. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +34 -34
  416. nautobot/project-static/docs/user-guide/platform-functionality/events.html +34 -34
  417. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +34 -34
  418. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +34 -34
  419. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +34 -34
  420. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +34 -34
  421. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +34 -34
  422. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +34 -34
  423. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +34 -34
  424. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +35 -35
  425. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +34 -34
  426. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +34 -34
  427. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +34 -34
  428. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +34 -34
  429. nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +34 -34
  430. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +34 -34
  431. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +34 -34
  432. nautobot/project-static/docs/user-guide/platform-functionality/note.html +34 -34
  433. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +34 -34
  434. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +34 -34
  435. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +34 -34
  436. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +34 -34
  437. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +34 -34
  438. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +34 -34
  439. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +34 -34
  440. nautobot/project-static/docs/user-guide/platform-functionality/role.html +34 -34
  441. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +34 -34
  442. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +34 -34
  443. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +34 -34
  444. nautobot/project-static/docs/user-guide/platform-functionality/status.html +34 -34
  445. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +34 -34
  446. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +34 -34
  447. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +34 -34
  448. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +34 -34
  449. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +34 -34
  450. nautobot/tenancy/api/views.py +2 -1
  451. nautobot/users/tests/test_api.py +2 -2
  452. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -252
  453. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -75
  454. nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +252 -0
  455. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +75 -0
  456. nautobot/virtualization/urls.py +3 -61
  457. nautobot/virtualization/views.py +48 -72
  458. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/METADATA +27 -27
  459. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/RECORD +463 -439
  460. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/LICENSE.txt +0 -0
  461. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/NOTICE +0 -0
  462. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/WHEEL +0 -0
  463. {nautobot-2.4.13.dist-info → nautobot-2.4.15.dist-info}/entry_points.txt +0 -0
nautobot/core/tables.py CHANGED
@@ -5,7 +5,7 @@ from django.conf import settings
5
5
  from django.contrib.auth.models import AnonymousUser
6
6
  from django.contrib.contenttypes.fields import GenericForeignKey
7
7
  from django.core.exceptions import FieldDoesNotExist, FieldError
8
- from django.db.models import Prefetch
8
+ from django.db.models import Prefetch, QuerySet
9
9
  from django.db.models.fields.related import ForeignKey, RelatedField
10
10
  from django.db.models.fields.reverse_related import ManyToOneRel
11
11
  from django.urls import reverse
@@ -80,14 +80,14 @@ class BaseTable(django_tables2.Table):
80
80
  reverse_lookup="static_group_associations__associated_object_id",
81
81
  )
82
82
 
83
- for cf in models.CustomField.objects.get_for_model(model):
83
+ for cf in models.CustomField.objects.get_for_model(model, get_queryset=False):
84
84
  name = cf.add_prefix_to_cf_key()
85
85
  self.base_columns[name] = CustomFieldColumn(cf)
86
86
 
87
- for cpf in models.ComputedField.objects.get_for_model(model):
87
+ for cpf in models.ComputedField.objects.get_for_model(model, get_queryset=False):
88
88
  self.base_columns[f"cpf_{cpf.key}"] = ComputedFieldColumn(cpf)
89
89
 
90
- for relationship in models.Relationship.objects.get_for_model_source(model):
90
+ for relationship in models.Relationship.objects.get_for_model_source(model, get_queryset=False):
91
91
  if not relationship.symmetric:
92
92
  self.base_columns[f"cr_{relationship.key}_src"] = RelationshipColumn(
93
93
  relationship, side=choices.RelationshipSideChoices.SIDE_SOURCE
@@ -97,7 +97,7 @@ class BaseTable(django_tables2.Table):
97
97
  relationship, side=choices.RelationshipSideChoices.SIDE_PEER
98
98
  )
99
99
 
100
- for relationship in models.Relationship.objects.get_for_model_destination(model):
100
+ for relationship in models.Relationship.objects.get_for_model_destination(model, get_queryset=False):
101
101
  if not relationship.symmetric:
102
102
  self.base_columns[f"cr_{relationship.key}_dst"] = RelationshipColumn(
103
103
  relationship, side=choices.RelationshipSideChoices.SIDE_DESTINATION
@@ -333,6 +333,29 @@ class BaseTable(django_tables2.Table):
333
333
  # Otherwise, use the default sorting method
334
334
  self.data.order_by(self._order_by)
335
335
 
336
+ def add_conditional_prefetch(self, table_field, db_column=None, prefetch=None):
337
+ """Conditionally prefetch the specified database column if the related table field is visible.
338
+
339
+ Args:
340
+ table_field (str): Name of the field on the table to check for visibility. Also used as the prefetch field
341
+ if neither db_column nor prefetch is specified.
342
+ db_column (str): Optionally specify the db column to prefetch. Mutually exclusive with prefetch.
343
+ prefetch (Prefetch): Optionally specify a prefetch object. Mutually exclusive with db_column.
344
+ """
345
+ if db_column and prefetch:
346
+ raise ValueError(
347
+ "BaseTable.add_conditional_prefetch called with both db_column and prefetch, this is not allowed."
348
+ )
349
+ if not db_column:
350
+ db_column = table_field
351
+ if table_field in self.columns and self.columns[table_field].visible and isinstance(self.data.data, QuerySet):
352
+ if prefetch:
353
+ self.data = TableData.from_data(self.data.data.prefetch_related(prefetch))
354
+ else:
355
+ self.data = TableData.from_data(self.data.data.prefetch_related(db_column))
356
+ self.data.set_table(self)
357
+ self.rows = BoundRows(data=self.data, table=self, pinned_data=self.pinned_data)
358
+
336
359
 
337
360
  #
338
361
  # Table columns
@@ -353,7 +376,7 @@ class ToggleColumn(django_tables2.CheckBoxColumn):
353
376
 
354
377
  @property
355
378
  def header(self):
356
- return mark_safe('<input type="checkbox" class="toggle" title="Toggle all" />') # noqa: S308 # suspicious-mark-safe-usage, but this is a static string so it's safe
379
+ return mark_safe('<input type="checkbox" class="toggle" title="Toggle all" />')
357
380
 
358
381
 
359
382
  class BooleanColumn(django_tables2.Column):
@@ -5,7 +5,7 @@
5
5
  <!DOCTYPE html>
6
6
  <html lang="en"{% if request.COOKIES|get_item:"theme" == 'dark' %} data-theme="dark"{% endif %}>
7
7
  <head>
8
- <title>{% block title %}Home{% endblock %} - {{ settings.BRANDING_TITLE }}</title>
8
+ <title>{% block title %}Home{% endblock %}{% block document_title_extra %}{% endblock %} - {{ settings.BRANDING_TITLE }}</title>
9
9
  {% include 'inc/media.html' %}
10
10
  {% block extra_styles %}{% endblock %}
11
11
  </head>
@@ -6,7 +6,7 @@
6
6
  {% endif %}
7
7
 
8
8
  <div class="pull-right noprint">
9
- {% if request.user.is_authenticated %}
9
+ {% if request.user.is_authenticated and show_table_config_button %}
10
10
  {% table_config_button_small body_content_table %}
11
11
  {% endif %}
12
12
  </div>
@@ -4,18 +4,26 @@
4
4
  {% load perms %}
5
5
  {% load plugins %}
6
6
  {% load static %}
7
+ {% load ui_framework %}
8
+
9
+ {% block document_title_extra %}{% saved_view_title "plain" %}{% endblock %}
7
10
 
8
11
  {% block header %}
9
12
  <div class="row noprint">
10
13
  <div class="{% if search_form %}col-sm-8 col-md-9 {% else %} col-md-12 {% endif %}">
11
- <ol class="breadcrumb">
12
- {% block breadcrumbs %}
13
- {% if list_url %}
14
- <li><a href="{% url list_url %}">{{ title }}</a></li>
15
- {% endif %}
16
- {% block extra_breadcrumbs %}{% endblock extra_breadcrumbs %}
17
- {% endblock breadcrumbs %}
18
- </ol>
14
+ {% if breadcrumbs is not None %}
15
+ {% render_breadcrumbs %}
16
+ {% else %}
17
+ {# TODO: remove this fragment after breadcrumbs implementation in generic views like ConsoleConnectionsListView #}
18
+ <ol class="breadcrumb">
19
+ {% block breadcrumbs %}
20
+ {% if list_url %}
21
+ <li><a href="{% url list_url %}">{{ title }}</a></li>
22
+ {% endif %}
23
+ {% block extra_breadcrumbs %}{% endblock extra_breadcrumbs %}
24
+ {% endblock breadcrumbs %}
25
+ </ol>
26
+ {% endif %}
19
27
  </div>
20
28
  {% if search_form %}
21
29
  <div class="col-sm-4 col-md-3">
@@ -146,18 +154,7 @@
146
154
  {% endblock export_button %}
147
155
  {% endif %}
148
156
  </div>
149
- <h1>{% block title %}
150
- {{ title }}
151
- {% if current_saved_view %}
152
-
153
- {% if new_changes_not_applied %}
154
- <i title="Pending changes not saved">{{ current_saved_view.name }}</i>
155
- {% else %}
156
- {{ current_saved_view.name }}
157
- {% endif %}
158
- {% endif %}
159
- {% endblock %}
160
- </h1>
157
+ <h1>{% block title %}{% render_title %}{% endblock %}{% saved_view_title %}</h1>
161
158
  {% block header_extra %}{% endblock %}
162
159
  {% if filter_params %}
163
160
  <div class="filters-applied">
@@ -0,0 +1,14 @@
1
+ <ol class="breadcrumb">
2
+ {% block breadcrumbs %}
3
+ {% for url, title in breadcrumbs_items %}
4
+ <li>
5
+ {% if url %}
6
+ <a href="{{ url }}">{{ title }}</a>
7
+ {% else %}
8
+ {{ title }}
9
+ {% endif %}
10
+ </li>
11
+ {% endfor %}
12
+ {% block extra_breadcrumbs %}{% endblock extra_breadcrumbs %}
13
+ {% endblock %}
14
+ </ol>
@@ -176,8 +176,6 @@ def consolidate_bulk_action_buttons(context):
176
176
  "bulk_action_buttons": bulk_action_buttons,
177
177
  }
178
178
 
179
- params = ("?" + context["request"].GET.urlencode()) if context["request"].GET else ""
180
-
181
179
  primary_button_fragment = child_button_fragment = """
182
180
  <button {attrs}>
183
181
  <span class="{icon}" aria-hidden="true"></span> {label}
@@ -203,7 +201,7 @@ def consolidate_bulk_action_buttons(context):
203
201
  attrs=render_tag_attrs(
204
202
  {
205
203
  "class": edit_button_classes,
206
- "formaction": reverse(context["bulk_edit_url"]) + params,
204
+ "formaction": reverse(context["bulk_edit_url"]),
207
205
  "type": "submit",
208
206
  }
209
207
  ),
@@ -242,7 +240,7 @@ def consolidate_bulk_action_buttons(context):
242
240
  "class": delete_button_classes,
243
241
  "type": "submit",
244
242
  "name": "_delete",
245
- "formaction": reverse(context["bulk_delete_url"]) + params,
243
+ "formaction": reverse(context["bulk_delete_url"]),
246
244
  }
247
245
  ),
248
246
  icon="mdi mdi-trash-can-outline",
@@ -2,6 +2,7 @@ import datetime
2
2
  import json
3
3
  import logging
4
4
  import re
5
+ from typing import Literal
5
6
  from urllib.parse import parse_qs, quote_plus
6
7
 
7
8
  from django import template
@@ -12,7 +13,7 @@ from django.contrib.staticfiles.finders import find
12
13
  from django.core.exceptions import ObjectDoesNotExist
13
14
  from django.templatetags.static import static, StaticNode
14
15
  from django.urls import NoReverseMatch, reverse
15
- from django.utils.html import format_html, format_html_join
16
+ from django.utils.html import format_html, format_html_join, strip_tags
16
17
  from django.utils.safestring import mark_safe
17
18
  from django.utils.text import slugify as django_slugify
18
19
  from django_jinja import library
@@ -25,10 +26,9 @@ from nautobot.core.constants import PAGINATE_COUNT_DEFAULT
25
26
  from nautobot.core.utils import color, config, data, logging as nautobot_logging, lookup
26
27
  from nautobot.core.utils.requests import add_nautobot_version_query_param_to_url
27
28
 
28
- # S308 is suspicious-mark-safe-usage, but these are all using static strings that we know to be safe
29
- HTML_TRUE = mark_safe('<span class="text-success"><i class="mdi mdi-check-bold" title="Yes"></i></span>') # noqa: S308
30
- HTML_FALSE = mark_safe('<span class="text-danger"><i class="mdi mdi-close-thick" title="No"></i></span>') # noqa: S308
31
- HTML_NONE = mark_safe('<span class="text-muted">&mdash;</span>') # noqa: S308
29
+ HTML_TRUE = mark_safe('<span class="text-success"><i class="mdi mdi-check-bold" title="Yes"></i></span>')
30
+ HTML_FALSE = mark_safe('<span class="text-danger"><i class="mdi mdi-close-thick" title="No"></i></span>')
31
+ HTML_NONE = mark_safe('<span class="text-muted">&mdash;</span>')
32
32
 
33
33
  DEFAULT_SUPPORT_MESSAGE = (
34
34
  "If further assistance is required, please join the `#nautobot` channel "
@@ -1124,7 +1124,7 @@ def custom_branding_or_static(branding_asset, static_asset):
1124
1124
  branding has been configured in settings, else it returns stock branding via static.
1125
1125
  """
1126
1126
  if settings.BRANDING_FILEPATHS.get(branding_asset):
1127
- url = f"{ settings.MEDIA_URL }{ settings.BRANDING_FILEPATHS.get(branding_asset) }"
1127
+ url = f"{settings.MEDIA_URL}{settings.BRANDING_FILEPATHS.get(branding_asset)}"
1128
1128
  else:
1129
1129
  url = StaticNode.handle_simple(static_asset)
1130
1130
  return add_nautobot_version_query_param_to_url(url)
@@ -1255,3 +1255,26 @@ def _build_hyperlink(value, field="", target="", rel=""):
1255
1255
  except AttributeError:
1256
1256
  pass
1257
1257
  return format_html("{}", display)
1258
+
1259
+
1260
+ @register.simple_tag(takes_context=True)
1261
+ def saved_view_title(context, mode: Literal["html", "plain"] = "html"):
1262
+ """
1263
+ Creates a formatted title that includes saved view information.
1264
+ Usage: <h1>{{ title }}{% saved_view_title "html" %}</h1>
1265
+ """
1266
+ new_changes_not_applied = context.get("new_changes_not_applied", False)
1267
+ current_saved_view = context.get("current_saved_view")
1268
+
1269
+ if not current_saved_view:
1270
+ return ""
1271
+
1272
+ if new_changes_not_applied:
1273
+ title = format_html(' — <i title="Pending changes not saved">{}</i>', current_saved_view.name)
1274
+ else:
1275
+ title = format_html(" — {}", current_saved_view.name)
1276
+
1277
+ if mode == "plain":
1278
+ return strip_tags(title)
1279
+
1280
+ return title
@@ -3,6 +3,8 @@ import logging
3
3
  from django import template
4
4
  from django.utils.html import format_html_join
5
5
 
6
+ from nautobot.core.ui.breadcrumbs import Breadcrumbs
7
+ from nautobot.core.ui.titles import Titles
6
8
  from nautobot.core.utils.lookup import get_view_for_model
7
9
  from nautobot.core.views.utils import get_obj_from_context
8
10
 
@@ -27,6 +29,25 @@ def render_components(context, components):
27
29
  return ""
28
30
 
29
31
 
32
+ @register.simple_tag(takes_context=True)
33
+ def render_title(context, mode="plain"):
34
+ title_obj = context.get("view_titles")
35
+ if title_obj is not None and isinstance(title_obj, Titles):
36
+ return title_obj.render(context, mode=mode)
37
+
38
+ if fallback_title := context.get("title"):
39
+ return fallback_title
40
+ return ""
41
+
42
+
43
+ @register.simple_tag(takes_context=True)
44
+ def render_breadcrumbs(context):
45
+ breadcrumbs_obj = context.get("breadcrumbs")
46
+ if breadcrumbs_obj is not None and isinstance(breadcrumbs_obj, Breadcrumbs):
47
+ return breadcrumbs_obj.render(context)
48
+ return ""
49
+
50
+
30
51
  @register.simple_tag(takes_context=True)
31
52
  def render_detail_view_extra_buttons(context):
32
53
  """
@@ -7,6 +7,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
7
7
  from django.contrib.contenttypes.models import ContentType
8
8
  from django.db import connections, DEFAULT_DB_ALIAS
9
9
  from django.db.models import ForeignKey, ManyToManyField, QuerySet
10
+ from django.http import QueryDict
10
11
  from django.test import override_settings, tag
11
12
  from django.test.utils import CaptureQueriesContext
12
13
  from django.urls import reverse
@@ -105,6 +106,12 @@ class APITestCase(views.ModelTestCase):
105
106
  m2m_fields.append(field_name)
106
107
  return m2m_fields
107
108
 
109
+ @staticmethod
110
+ def add_query_params_to_url(url: str, query_dict: dict) -> str:
111
+ query = QueryDict(mutable=True)
112
+ query.update(query_dict)
113
+ return f"{url}?{query.urlencode()}"
114
+
108
115
 
109
116
  @tag("unit")
110
117
  class APIViewTestCases:
@@ -15,6 +15,7 @@ from nautobot.core.filters import (
15
15
  ContentTypeChoiceFilter,
16
16
  ContentTypeFilter,
17
17
  ContentTypeMultipleChoiceFilter,
18
+ NaturalKeyOrPKMultipleChoiceFilter,
18
19
  RelatedMembershipBooleanFilter,
19
20
  SearchFilter,
20
21
  )
@@ -207,15 +208,29 @@ class FilterTestCases:
207
208
  qs_result = self.queryset.filter(**{f"{field_name}__in": test_data}).distinct()
208
209
  self.assertQuerysetEqualAndNotEmpty(filterset_result, qs_result, ordered=False)
209
210
 
211
+ def test_automagic_filters(self):
212
+ """https://github.com/nautobot/nautobot/issues/6656"""
213
+ self.assertIsNotNone(self.filterset)
214
+ fs = self.filterset() # pylint: disable=not-callable
215
+ if getattr(self.queryset.model, "is_contact_associable_model", False):
216
+ self.assertIsInstance(fs.filters["contacts"], NaturalKeyOrPKMultipleChoiceFilter)
217
+ self.assertIsInstance(fs.filters["contacts__n"], NaturalKeyOrPKMultipleChoiceFilter)
218
+ self.assertIsInstance(fs.filters["teams"], NaturalKeyOrPKMultipleChoiceFilter)
219
+ self.assertIsInstance(fs.filters["teams__n"], NaturalKeyOrPKMultipleChoiceFilter)
220
+
221
+ if getattr(self.queryset.model, "is_dynamic_group_associable_model", False):
222
+ self.assertIsInstance(fs.filters["dynamic_groups"], NaturalKeyOrPKMultipleChoiceFilter)
223
+ self.assertIsInstance(fs.filters["dynamic_groups__n"], NaturalKeyOrPKMultipleChoiceFilter)
224
+
210
225
  def test_boolean_filters_generic(self):
211
- """Test all `RelatedMembershipBooleanFilter` filters found in `self.filterset.get_filters()`
226
+ """Test all `RelatedMembershipBooleanFilter` filters found in `self.filterset.filters`
212
227
  except for the ones with custom filter logic defined in its `method` attribute.
213
228
 
214
229
  This test asserts that `filter=True` matches `self.queryset.filter(field__isnull=False)` and
215
230
  that `filter=False` matches `self.queryset.filter(field__isnull=True)`.
216
231
  """
217
232
  self.assertIsNotNone(self.filterset)
218
- for filter_name, filter_object in self.filterset.get_filters().items():
233
+ for filter_name, filter_object in self.filterset().filters.items(): # pylint: disable=not-callable
219
234
  if not isinstance(filter_object, RelatedMembershipBooleanFilter):
220
235
  continue
221
236
  if filter_object.method is not None:
@@ -380,6 +395,8 @@ class FilterTestCases:
380
395
  self._assert_q_filter_predicate_validity(obj, obj_field_name, filter_field_name, lookup_method)
381
396
 
382
397
  def test_content_type_related_fields_uses_content_type_filter(self):
398
+ self.assertIsNotNone(self.filterset)
399
+ fs = self.filterset() # pylint: disable=not-callable
383
400
  for field in self.queryset.model._meta.fields:
384
401
  related_model = getattr(field, "related_model", None)
385
402
  if not related_model or related_model != ContentType:
@@ -387,7 +404,7 @@ class FilterTestCases:
387
404
  with self.subTest(
388
405
  f"Assert {self.filterset.__class__.__name__}.{field.name} implements ContentTypeFilter"
389
406
  ):
390
- filter_field = self.filterset.get_filters().get(field.name)
407
+ filter_field = fs.filters.get(field.name)
391
408
  if not filter_field:
392
409
  # This field is not part of the Filterset.
393
410
  continue
@@ -24,7 +24,7 @@ class FormTestCases:
24
24
  self.skipTest(f"{self.form_class.__name__}.{field_name} has no query_params")
25
25
  field_model = field_class.queryset.model
26
26
  filterset_class = get_filterset_for_model(field_model)
27
- filterset_fields = set(filterset_class.get_filters().keys())
27
+ filterset_fields = set(filterset_class().filters.keys())
28
28
  invalid_query_params = query_params_fields - filterset_fields
29
29
  self.assertFalse(
30
30
  invalid_query_params,
@@ -148,7 +148,7 @@ class ListViewFilterTestCase(SeleniumTestCase):
148
148
 
149
149
  def test_input_field_gets_updated(self):
150
150
  """Assert that a filter input/select field on Dynamic Filter Form updates if same field is updated."""
151
- self.browser.visit(f'{self.live_server_url}{reverse("dcim:location_list")}')
151
+ self.browser.visit(f"{self.live_server_url}{reverse('dcim:location_list')}")
152
152
 
153
153
  text_field_name = self.custom_fields[0].add_prefix_to_cf_key()
154
154
  integer_field_name = self.custom_fields[1].add_prefix_to_cf_key()
@@ -225,7 +225,7 @@ class ListViewFilterTestCase(SeleniumTestCase):
225
225
  def test_advanced_filter_application(self):
226
226
  """Assert that filters are applied successfully when using the advanced filter."""
227
227
  # Go to the location list view
228
- self.browser.visit(f'{self.live_server_url}{reverse("dcim:location_list")}')
228
+ self.browser.visit(f"{self.live_server_url}{reverse('dcim:location_list')}")
229
229
  # create a new tag
230
230
  tag = Tag.objects.create(name="Tag1")
231
231
  tag.content_types.set([ContentType.objects.get_for_model(Location)])