nautobot 2.4.1__py3-none-any.whl → 2.4.3__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 (461) hide show
  1. nautobot/circuits/templates/circuits/inc/circuit_termination.html +1 -1
  2. nautobot/circuits/tests/integration/test_circuit.py +135 -0
  3. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +43 -0
  4. nautobot/circuits/tests/integration/test_relationships.py +1 -1
  5. nautobot/circuits/views.py +4 -1
  6. nautobot/cloud/api/views.py +3 -3
  7. nautobot/core/apps/__init__.py +0 -5
  8. nautobot/core/constants.py +0 -1
  9. nautobot/core/forms/__init__.py +2 -0
  10. nautobot/core/forms/forms.py +2 -1
  11. nautobot/core/forms/widgets.py +8 -0
  12. nautobot/core/management/commands/generate_performance_test_endpoints.py +268 -0
  13. nautobot/core/templates/generic/object_bulk_delete.html +1 -1
  14. nautobot/core/templates/generic/object_bulk_destroy.html +1 -1
  15. nautobot/core/templates/generic/object_bulk_edit.html +1 -1
  16. nautobot/core/templates/generic/object_bulk_import.html +1 -1
  17. nautobot/core/templates/generic/object_create.html +5 -0
  18. nautobot/core/templates/generic/object_delete.html +1 -1
  19. nautobot/core/templates/generic/object_detail.html +1 -1
  20. nautobot/core/templates/generic/object_edit.html +1 -1
  21. nautobot/core/templates/inc/javascript.html +2 -0
  22. nautobot/core/templates/widgets/clearable_file.html +5 -0
  23. nautobot/core/templatetags/helpers.py +3 -3
  24. nautobot/core/testing/integration.py +469 -12
  25. nautobot/core/tests/test_commands.py +31 -0
  26. nautobot/core/tests/test_jobs.py +34 -2
  27. nautobot/core/tests/test_utils.py +17 -2
  28. nautobot/core/utils/git.py +7 -2
  29. nautobot/core/utils/lookup.py +12 -1
  30. nautobot/core/views/generic.py +10 -2
  31. nautobot/core/views/mixins.py +22 -7
  32. nautobot/core/views/utils.py +2 -2
  33. nautobot/dcim/api/views.py +11 -10
  34. nautobot/dcim/forms.py +15 -6
  35. nautobot/dcim/models/devices.py +1 -2
  36. nautobot/dcim/tables/devices.py +2 -1
  37. nautobot/dcim/templates/dcim/cable.html +1 -1
  38. nautobot/dcim/templates/dcim/cable_trace.html +4 -4
  39. nautobot/dcim/templates/dcim/consoleport.html +14 -4
  40. nautobot/dcim/templates/dcim/consoleserverport.html +14 -4
  41. nautobot/dcim/templates/dcim/device/base.html +1 -1
  42. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +3 -3
  43. nautobot/dcim/templates/dcim/device.html +2 -2
  44. nautobot/dcim/templates/dcim/device_component.html +1 -1
  45. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  46. nautobot/dcim/templates/dcim/frontport.html +7 -2
  47. nautobot/dcim/templates/dcim/interface.html +9 -4
  48. nautobot/dcim/templates/dcim/location.html +1 -1
  49. nautobot/dcim/templates/dcim/locationtype.html +1 -1
  50. nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
  51. nautobot/dcim/templates/dcim/manufacturer.html +1 -1
  52. nautobot/dcim/templates/dcim/platform.html +1 -1
  53. nautobot/dcim/templates/dcim/powerfeed.html +9 -4
  54. nautobot/dcim/templates/dcim/poweroutlet.html +14 -4
  55. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  56. nautobot/dcim/templates/dcim/powerport.html +14 -4
  57. nautobot/dcim/templates/dcim/rack.html +1 -1
  58. nautobot/dcim/templates/dcim/rackgroup.html +1 -1
  59. nautobot/dcim/templates/dcim/rackreservation.html +2 -2
  60. nautobot/dcim/templates/dcim/rearport.html +7 -2
  61. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  62. nautobot/dcim/tests/integration/test_device_bulk_operations.py +30 -0
  63. nautobot/dcim/tests/integration/test_fileinputpicker.py +87 -0
  64. nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -0
  65. nautobot/dcim/tests/test_models.py +1 -1
  66. nautobot/dcim/tests/test_views.py +9 -1
  67. nautobot/dcim/views.py +12 -15
  68. nautobot/extras/api/serializers.py +33 -0
  69. nautobot/extras/api/views.py +13 -5
  70. nautobot/extras/constants.py +1 -0
  71. nautobot/extras/datasources/git.py +125 -0
  72. nautobot/extras/forms/forms.py +4 -0
  73. nautobot/extras/jobs.py +8 -1
  74. nautobot/extras/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
  75. nautobot/extras/models/customfields.py +29 -12
  76. nautobot/extras/models/datasources.py +85 -0
  77. nautobot/extras/models/models.py +15 -0
  78. nautobot/extras/models/relationships.py +17 -5
  79. nautobot/extras/signals.py +15 -1
  80. nautobot/extras/templates/extras/computedfield.html +1 -1
  81. nautobot/extras/templates/extras/configcontext.html +1 -1
  82. nautobot/extras/templates/extras/configcontextschema.html +1 -1
  83. nautobot/extras/templates/extras/customfield.html +1 -1
  84. nautobot/extras/templates/extras/customlink.html +1 -1
  85. nautobot/extras/templates/extras/dynamicgroup.html +1 -1
  86. nautobot/extras/templates/extras/exporttemplate.html +1 -1
  87. nautobot/extras/templates/extras/gitrepository.html +1 -1
  88. nautobot/extras/templates/extras/graphqlquery.html +1 -1
  89. nautobot/extras/templates/extras/job.html +1 -0
  90. nautobot/extras/templates/extras/job_detail.html +1 -1
  91. nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
  92. nautobot/extras/templates/extras/jobhook.html +1 -1
  93. nautobot/extras/templates/extras/jobresult.html +1 -1
  94. nautobot/extras/templates/extras/objectchange.html +1 -1
  95. nautobot/extras/templates/extras/plugin_detail.html +1 -1
  96. nautobot/extras/templates/extras/relationship.html +1 -63
  97. nautobot/extras/templates/extras/role_retrieve.html +1 -1
  98. nautobot/extras/templates/extras/scheduledjob.html +1 -1
  99. nautobot/extras/templates/extras/secret.html +1 -1
  100. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  101. nautobot/extras/templates/extras/status.html +1 -1
  102. nautobot/extras/templates/extras/tag.html +1 -1
  103. nautobot/extras/templates/extras/webhook.html +1 -1
  104. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
  105. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
  106. nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
  107. nautobot/extras/tests/git_helper.py +9 -1
  108. nautobot/extras/tests/integration/__init__.py +29 -16
  109. nautobot/extras/tests/test_api.py +6 -0
  110. nautobot/extras/tests/test_customfields.py +49 -51
  111. nautobot/extras/tests/test_datasources.py +27 -0
  112. nautobot/extras/tests/test_dynamicgroups.py +14 -0
  113. nautobot/extras/tests/test_models.py +283 -0
  114. nautobot/extras/tests/test_utils.py +22 -1
  115. nautobot/extras/tests/test_views.py +197 -9
  116. nautobot/extras/utils.py +47 -8
  117. nautobot/extras/views.py +84 -26
  118. nautobot/ipam/api/views.py +3 -3
  119. nautobot/ipam/forms.py +2 -6
  120. nautobot/ipam/models.py +8 -2
  121. nautobot/ipam/tables.py +2 -2
  122. nautobot/ipam/templates/ipam/ipaddress.html +1 -1
  123. nautobot/ipam/templates/ipam/prefix.html +1 -1
  124. nautobot/ipam/templates/ipam/rir.html +1 -1
  125. nautobot/ipam/templates/ipam/routetarget.html +1 -1
  126. nautobot/ipam/templates/ipam/service.html +1 -1
  127. nautobot/ipam/templates/ipam/vlan.html +1 -1
  128. nautobot/ipam/templates/ipam/vlangroup.html +1 -1
  129. nautobot/ipam/templates/ipam/vrf.html +1 -1
  130. nautobot/ipam/tests/test_models.py +24 -0
  131. nautobot/ipam/tests/test_utils.py +41 -2
  132. nautobot/ipam/utils/__init__.py +18 -11
  133. nautobot/project-static/bootstrap-filestyle-1.2.3/bootstrap-filestyle.min.js +11 -0
  134. nautobot/project-static/docs/404.html +87 -12
  135. nautobot/project-static/docs/apps/index.html +88 -13
  136. nautobot/project-static/docs/apps/nautobot-apps.html +88 -13
  137. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.60a45f97.min.js} +1 -1
  138. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.60a45f97.min.js.map} +1 -1
  139. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
  140. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
  141. nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
  142. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +87 -12
  143. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +87 -12
  144. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +87 -12
  145. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +87 -12
  146. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +87 -12
  147. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +87 -12
  148. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +87 -12
  149. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +87 -12
  150. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +87 -12
  151. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +87 -12
  152. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +87 -12
  153. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +87 -12
  154. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +87 -12
  155. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +87 -12
  156. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +87 -12
  157. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +87 -12
  158. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +87 -12
  159. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +87 -12
  160. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +87 -12
  161. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +87 -12
  162. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +87 -12
  163. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +87 -12
  164. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +177 -20
  165. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +114 -17
  166. nautobot/project-static/docs/development/apps/api/configuration-view.html +87 -12
  167. nautobot/project-static/docs/development/apps/api/database-backend-config.html +87 -12
  168. nautobot/project-static/docs/development/apps/api/models/django-admin.html +87 -12
  169. nautobot/project-static/docs/development/apps/api/models/global-search.html +87 -12
  170. nautobot/project-static/docs/development/apps/api/models/graphql.html +96 -21
  171. nautobot/project-static/docs/development/apps/api/models/index.html +87 -12
  172. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +87 -12
  173. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +87 -12
  174. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +89 -14
  175. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +87 -12
  176. nautobot/project-static/docs/development/apps/api/platform-features/index.html +87 -12
  177. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +87 -12
  178. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +87 -12
  179. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +87 -12
  180. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +87 -12
  181. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +87 -12
  182. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +87 -12
  183. nautobot/project-static/docs/development/apps/api/prometheus.html +87 -12
  184. nautobot/project-static/docs/development/apps/api/setup.html +88 -13
  185. nautobot/project-static/docs/development/apps/api/testing.html +87 -12
  186. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +87 -12
  187. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +87 -12
  188. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +87 -12
  189. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +87 -12
  190. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +87 -12
  191. nautobot/project-static/docs/development/apps/api/views/base-template.html +87 -12
  192. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +87 -12
  193. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +87 -12
  194. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +87 -12
  195. nautobot/project-static/docs/development/apps/api/views/index.html +87 -12
  196. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +87 -12
  197. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +87 -12
  198. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +87 -12
  199. nautobot/project-static/docs/development/apps/api/views/notes.html +87 -12
  200. nautobot/project-static/docs/development/apps/api/views/rest-api.html +87 -12
  201. nautobot/project-static/docs/development/apps/api/views/urls.html +87 -12
  202. nautobot/project-static/docs/development/apps/index.html +87 -12
  203. nautobot/project-static/docs/development/apps/migration/code-updates.html +93 -17
  204. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +89 -14
  205. nautobot/project-static/docs/development/apps/migration/from-v1.html +90 -15
  206. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +87 -12
  207. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +87 -12
  208. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +87 -12
  209. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +87 -12
  210. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +87 -12
  211. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +87 -12
  212. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +88 -13
  213. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +87 -12
  214. nautobot/project-static/docs/development/apps/porting-from-netbox.html +87 -12
  215. nautobot/project-static/docs/development/core/application-registry.html +87 -12
  216. nautobot/project-static/docs/development/core/best-practices.html +88 -13
  217. nautobot/project-static/docs/development/core/bootstrap-ui.html +88 -13
  218. nautobot/project-static/docs/development/core/caching.html +87 -12
  219. nautobot/project-static/docs/development/core/controllers.html +87 -12
  220. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +94 -19
  221. nautobot/project-static/docs/development/core/generic-views.html +87 -12
  222. nautobot/project-static/docs/development/core/getting-started.html +89 -14
  223. nautobot/project-static/docs/development/core/homepage.html +87 -12
  224. nautobot/project-static/docs/development/core/index.html +88 -13
  225. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +90 -15
  226. nautobot/project-static/docs/development/core/model-checklist.html +88 -13
  227. nautobot/project-static/docs/development/core/model-features.html +87 -12
  228. nautobot/project-static/docs/development/core/natural-keys.html +87 -12
  229. nautobot/project-static/docs/development/core/navigation-menu.html +88 -13
  230. nautobot/project-static/docs/development/core/release-checklist.html +88 -13
  231. nautobot/project-static/docs/development/core/role-internals.html +87 -12
  232. nautobot/project-static/docs/development/core/settings.html +88 -13
  233. nautobot/project-static/docs/development/core/style-guide.html +91 -16
  234. nautobot/project-static/docs/development/core/templates.html +88 -13
  235. nautobot/project-static/docs/development/core/testing.html +87 -12
  236. nautobot/project-static/docs/development/core/ui-component-framework.html +87 -12
  237. nautobot/project-static/docs/development/core/user-preferences.html +87 -12
  238. nautobot/project-static/docs/development/index.html +87 -12
  239. nautobot/project-static/docs/development/jobs/index.html +95 -13
  240. nautobot/project-static/docs/development/jobs/migration/from-v1.html +90 -14
  241. nautobot/project-static/docs/index.html +90 -14
  242. nautobot/project-static/docs/objects.inv +0 -0
  243. nautobot/project-static/docs/overview/application_stack.html +89 -14
  244. nautobot/project-static/docs/overview/design_philosophy.html +87 -12
  245. nautobot/project-static/docs/release-notes/index.html +87 -12
  246. nautobot/project-static/docs/release-notes/version-1.0.html +89 -14
  247. nautobot/project-static/docs/release-notes/version-1.1.html +89 -14
  248. nautobot/project-static/docs/release-notes/version-1.2.html +90 -15
  249. nautobot/project-static/docs/release-notes/version-1.3.html +88 -13
  250. nautobot/project-static/docs/release-notes/version-1.4.html +104 -29
  251. nautobot/project-static/docs/release-notes/version-1.5.html +95 -20
  252. nautobot/project-static/docs/release-notes/version-1.6.html +91 -16
  253. nautobot/project-static/docs/release-notes/version-2.0.html +97 -22
  254. nautobot/project-static/docs/release-notes/version-2.1.html +94 -19
  255. nautobot/project-static/docs/release-notes/version-2.2.html +88 -13
  256. nautobot/project-static/docs/release-notes/version-2.3.html +91 -16
  257. nautobot/project-static/docs/release-notes/version-2.4.html +465 -12
  258. nautobot/project-static/docs/requirements.txt +1 -1
  259. nautobot/project-static/docs/search/search_index.json +1 -1
  260. nautobot/project-static/docs/sitemap.xml +296 -288
  261. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  262. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +90 -15
  263. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +87 -12
  264. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +91 -16
  265. nautobot/project-static/docs/user-guide/administration/configuration/index.html +87 -12
  266. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +88 -13
  267. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +90 -15
  268. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +87 -12
  269. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +95 -20
  270. nautobot/project-static/docs/user-guide/administration/guides/docker.html +90 -15
  271. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +88 -13
  272. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +87 -12
  273. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +91 -16
  274. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +87 -12
  275. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +102 -27
  276. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +89 -14
  277. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +87 -12
  278. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +88 -13
  279. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +87 -12
  280. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +87 -12
  281. nautobot/project-static/docs/user-guide/administration/installation/index.html +87 -12
  282. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +88 -13
  283. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +93 -18
  284. nautobot/project-static/docs/user-guide/administration/installation/services.html +88 -13
  285. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +87 -12
  286. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +87 -12
  287. nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
  288. nautobot/project-static/docs/user-guide/administration/security/notices.html +9844 -0
  289. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +87 -12
  290. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +88 -13
  291. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +87 -12
  292. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +87 -12
  293. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +87 -12
  294. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +87 -12
  295. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +87 -12
  296. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +87 -12
  297. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +87 -12
  298. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +98 -20
  299. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +87 -12
  300. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +87 -12
  301. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +87 -12
  302. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +87 -12
  303. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +87 -12
  304. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +87 -12
  305. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +87 -12
  306. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +87 -12
  307. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +87 -12
  308. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +87 -12
  309. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +87 -12
  310. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +87 -12
  311. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +87 -12
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +87 -12
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +87 -12
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +87 -12
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +87 -12
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +87 -12
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +87 -12
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +87 -12
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +87 -12
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +87 -12
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +87 -12
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +87 -12
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +87 -12
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +87 -12
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +87 -12
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +87 -12
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +87 -12
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +87 -12
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +87 -12
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +87 -12
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +87 -12
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +87 -12
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +87 -12
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +87 -12
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +87 -12
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +87 -12
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +87 -12
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +87 -12
  339. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +87 -12
  340. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +87 -12
  341. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +87 -12
  342. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +87 -12
  343. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +87 -12
  344. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +87 -12
  345. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +87 -12
  346. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +87 -12
  347. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +87 -12
  348. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +87 -12
  349. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +87 -12
  350. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +87 -12
  351. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +87 -12
  352. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +87 -12
  353. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +87 -12
  354. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +87 -12
  355. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +87 -12
  356. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +87 -12
  357. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +87 -12
  358. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +87 -12
  359. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +87 -12
  360. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +87 -12
  361. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +87 -12
  362. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +87 -12
  363. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +87 -12
  364. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +87 -12
  365. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +87 -12
  366. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +87 -12
  367. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +87 -12
  368. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +87 -12
  369. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +87 -12
  370. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +87 -12
  371. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +87 -12
  372. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +87 -12
  373. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +87 -12
  374. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +87 -12
  375. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +87 -12
  376. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +87 -12
  377. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +87 -12
  378. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +87 -12
  379. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +87 -12
  380. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +99 -24
  381. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +87 -12
  382. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +87 -12
  383. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +90 -15
  384. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +87 -12
  385. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +87 -12
  386. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +87 -12
  387. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +87 -12
  388. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +87 -12
  389. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +87 -12
  390. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +188 -30
  391. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +87 -12
  392. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +87 -12
  393. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +87 -12
  394. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +87 -12
  395. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +87 -12
  396. nautobot/project-static/docs/user-guide/index.html +87 -12
  397. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +87 -12
  398. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +87 -12
  399. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +87 -12
  400. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +87 -12
  401. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +88 -13
  402. nautobot/project-static/docs/user-guide/platform-functionality/events.html +87 -12
  403. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +87 -12
  404. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +87 -12
  405. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +407 -14
  406. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +90 -15
  407. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +87 -12
  408. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +87 -12
  409. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +87 -12
  410. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +87 -12
  411. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +87 -12
  412. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +87 -12
  413. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +89 -14
  414. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +93 -18
  415. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +87 -12
  416. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +87 -12
  417. nautobot/project-static/docs/user-guide/platform-functionality/note.html +87 -12
  418. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +87 -12
  419. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +87 -12
  420. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +87 -12
  421. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +87 -12
  422. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +87 -12
  423. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +87 -12
  424. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +87 -12
  425. nautobot/project-static/docs/user-guide/platform-functionality/role.html +87 -12
  426. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +87 -12
  427. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +87 -12
  428. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +87 -12
  429. nautobot/project-static/docs/user-guide/platform-functionality/status.html +87 -12
  430. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +87 -12
  431. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +87 -12
  432. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +87 -12
  433. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +87 -12
  434. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +87 -12
  435. nautobot/project-static/js/dropdown.js +28 -0
  436. nautobot/tenancy/forms.py +9 -0
  437. nautobot/tenancy/templates/tenancy/tenant.html +1 -2
  438. nautobot/tenancy/templates/tenancy/tenant_create.html +21 -0
  439. nautobot/tenancy/templates/tenancy/tenant_edit.html +2 -21
  440. nautobot/tenancy/templates/tenancy/tenantgroup.html +2 -44
  441. nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +1 -0
  442. nautobot/tenancy/tests/test_views.py +5 -1
  443. nautobot/tenancy/urls.py +7 -79
  444. nautobot/tenancy/views.py +51 -80
  445. nautobot/virtualization/templates/virtualization/cluster.html +1 -1
  446. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
  447. nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
  448. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  449. nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
  450. nautobot/wireless/api/serializers.py +6 -1
  451. nautobot/wireless/api/views.py +3 -3
  452. nautobot/wireless/tests/test_api.py +5 -0
  453. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/METADATA +12 -12
  454. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/RECORD +459 -443
  455. nautobot/dcim/tests/integration/test_device_bulk_delete.py +0 -189
  456. nautobot/dcim/tests/integration/test_device_bulk_edit.py +0 -181
  457. /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
  458. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/LICENSE.txt +0 -0
  459. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/NOTICE +0 -0
  460. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/WHEEL +0 -0
  461. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/entry_points.txt +0 -0
@@ -750,16 +750,48 @@ class BulkEditTestCase(TransactionTestCase):
750
750
  description="Example description for bulk edit",
751
751
  )
752
752
 
753
- # Assert Namespaces withing pk_list updated tags
753
+ # Assert Namespaces within pk_list get updated tags
754
754
  for namespace in namespaces[:3]:
755
755
  self.assertTrue(namespace.tags.filter(pk__in=[tag.pk for tag in self.tags[:3]]).exists())
756
756
  self.assertFalse(namespace.tags.filter(pk__in=[tag.pk for tag in self.tags[3:]]).exists())
757
757
 
758
- # Assert Namespaces not withing pk_list tags did not get updated
758
+ # Assert Namespaces not within pk_list did not get updated tags
759
759
  for namespace in namespaces[3:]:
760
760
  self.assertFalse(namespace.tags.filter(pk__in=[tag.pk for tag in self.tags[:3]]).exists())
761
761
  self.assertTrue(namespace.tags.filter(pk__in=[tag.pk for tag in self.tags[3:]]).exists())
762
762
 
763
+ job_result = create_job_result_and_run_job(
764
+ "nautobot.core.jobs.bulk_actions",
765
+ "BulkEditObjects",
766
+ content_type=self.namespace_ct.id,
767
+ edit_all=False,
768
+ filter_query_params={},
769
+ form_data={
770
+ "pk": pk_list,
771
+ "description": "Example description for bulk edit",
772
+ "add_tags": [str(self.tags[0].id)],
773
+ "remove_tags": [str(self.tags[-1].id)],
774
+ },
775
+ username=self.user.username,
776
+ )
777
+
778
+ self._common_no_error_test_assertion(
779
+ Namespace,
780
+ job_result,
781
+ 3,
782
+ description="Example description for bulk edit",
783
+ )
784
+
785
+ # Assert Namespaces within pk_list get updated tag
786
+ for namespace in namespaces[:3]:
787
+ self.assertTrue(namespace.tags.filter(pk__in=[self.tags[0].pk]).exists())
788
+ self.assertFalse(namespace.tags.filter(pk__in=[self.tags[-1].pk]).exists())
789
+
790
+ # Assert Namespaces not within pk_list did not get updated tag
791
+ for namespace in namespaces[3:]:
792
+ self.assertFalse(namespace.tags.filter(pk__in=[self.tags[0].pk]).exists())
793
+ self.assertTrue(namespace.tags.filter(pk__in=[self.tags[-1].pk]).exists())
794
+
763
795
  def test_bulk_edit_objects_filter_all(self):
764
796
  """
765
797
  Bulk edit all of the filtered Status instances.
@@ -273,10 +273,25 @@ class GetFooForModelTest(TestCase):
273
273
  """
274
274
  Test that `get_model_for_view_name` returns the appropriate Model, if the colon separated view name provided.
275
275
  """
276
- with self.subTest("Test core view."):
276
+ with self.subTest("Test core UI view."):
277
277
  self.assertEqual(lookup.get_model_for_view_name("dcim:device_list"), dcim_models.Device)
278
- with self.subTest("Test app view."):
278
+ self.assertEqual(lookup.get_model_for_view_name("dcim:device"), dcim_models.Device)
279
+ with self.subTest("Test app UI view."):
279
280
  self.assertEqual(lookup.get_model_for_view_name("plugins:example_app:examplemodel_list"), ExampleModel)
281
+ self.assertEqual(lookup.get_model_for_view_name("plugins:example_app:examplemodel"), ExampleModel)
282
+ with self.subTest("Test core API view."):
283
+ self.assertEqual(lookup.get_model_for_view_name("dcim-api:device-list"), dcim_models.Device)
284
+ self.assertEqual(lookup.get_model_for_view_name("dcim-api:device-detail"), dcim_models.Device)
285
+ with self.subTest("Test app API view."):
286
+ self.assertEqual(
287
+ lookup.get_model_for_view_name("plugins-api:example_app-api:examplemodel-detail"), ExampleModel
288
+ )
289
+ self.assertEqual(
290
+ lookup.get_model_for_view_name("plugins-api:example_app-api:examplemodel-list"), ExampleModel
291
+ )
292
+ with self.subTest("Test unconventional model views."):
293
+ self.assertEqual(lookup.get_model_for_view_name("extras-api:contenttype-detail"), ContentType)
294
+ self.assertEqual(lookup.get_model_for_view_name("users-api:group-detail"), Group)
280
295
  with self.subTest("Test unexpected view."):
281
296
  with self.assertRaises(ValueError) as err:
282
297
  lookup.get_model_for_view_name("unknown:plugins:example_app:examplemodel_list")
@@ -57,7 +57,7 @@ class BranchDoesNotExist(Exception):
57
57
 
58
58
 
59
59
  class GitRepo:
60
- def __init__(self, path, url, clone_initially=True):
60
+ def __init__(self, path, url, clone_initially=True, branch=None, depth=0):
61
61
  """
62
62
  Ensure that we have a clone of the given remote Git repository URL at the given local directory path.
63
63
 
@@ -65,6 +65,8 @@ class GitRepo:
65
65
  path (str): path to git repo
66
66
  url (str): git repo url
67
67
  clone_initially (bool): True if the repo needs to be cloned
68
+ branch (str): branch to checkout
69
+ depth (int): depth of the clone
68
70
  """
69
71
  self.url = url
70
72
  self.sanitized_url = sanitize(url)
@@ -73,7 +75,10 @@ class GitRepo:
73
75
  elif clone_initially:
74
76
  # Don't log `url` as it may include authentication details.
75
77
  logger.debug("Cloning git repository to %s...", path)
76
- self.repo = Repo.clone_from(url, to_path=path, env=GIT_ENVIRONMENT)
78
+ if not depth:
79
+ self.repo = Repo.clone_from(url, to_path=path, env=GIT_ENVIRONMENT, branch=branch)
80
+ else:
81
+ self.repo = Repo.clone_from(url, to_path=path, env=GIT_ENVIRONMENT, branch=branch, depth=depth)
77
82
  else:
78
83
  self.repo = Repo.init(path)
79
84
  self.repo.create_remote("origin", url=url)
@@ -250,6 +250,11 @@ def get_model_for_view_name(view_name):
250
250
  Return the model class associated with the given view_name e.g. "circuits:circuit_detail", "dcim:device_list" and etc.
251
251
  If the app_label or model_name contained by the given view_name is invalid, this will return `None`.
252
252
  """
253
+ if view_name == "users-api:group-detail":
254
+ return Group
255
+ if view_name == "extras-api:contenttype-detail":
256
+ return ContentType
257
+
253
258
  split_view_name = view_name.split(":")
254
259
  if len(split_view_name) == 2:
255
260
  app_label, model_name = split_view_name # dcim, device_list
@@ -257,7 +262,13 @@ def get_model_for_view_name(view_name):
257
262
  _, app_label, model_name = split_view_name # plugins, app_name, model_list
258
263
  else:
259
264
  raise ValueError(f"Unexpected View Name: {view_name}")
260
- model_name = model_name.split("_")[0] # device
265
+
266
+ delimiter = "_"
267
+ if app_label.endswith("-api"):
268
+ app_label = app_label.replace("-api", "")
269
+ delimiter = "-"
270
+
271
+ model_name = model_name.split(delimiter)[0] # device
261
272
 
262
273
  try:
263
274
  model = apps.get_model(app_label=app_label, model_name=model_name)
@@ -214,8 +214,16 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
214
214
  resolved_path = resolve(request.path)
215
215
  list_url = f"{resolved_path.app_name}:{resolved_path.url_name}"
216
216
 
217
+ skip_user_and_global_default_saved_view = False
218
+ if self.filterset is not None:
219
+ skip_user_and_global_default_saved_view = get_filterable_params_from_filter_params(
220
+ request.GET.copy(),
221
+ self.non_filter_params,
222
+ self.filterset(),
223
+ )
224
+
217
225
  # If the user clicks on the clear view button, we do not check for global or user defaults
218
- if not clear_view and not request.GET.get("saved_view"):
226
+ if not skip_user_and_global_default_saved_view and not clear_view and not request.GET.get("saved_view"):
219
227
  # Check if there is a default for this view for this specific user
220
228
  if not isinstance(user, AnonymousUser):
221
229
  try:
@@ -1051,7 +1059,7 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, BulkEditAnd
1051
1059
 
1052
1060
  if form.is_valid():
1053
1061
  logger.debug("Form validation was successful")
1054
- return self.send_bulk_edit_objects_to_job(request, form, model)
1062
+ return self.send_bulk_edit_objects_to_job(request, form.cleaned_data, model)
1055
1063
  else:
1056
1064
  logger.debug("Form validation failed")
1057
1065
 
@@ -723,8 +723,16 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
723
723
  if response is not None:
724
724
  return response
725
725
 
726
+ skip_user_and_global_default_saved_view = False
727
+ if self.filterset_class is not None:
728
+ skip_user_and_global_default_saved_view = get_filterable_params_from_filter_params(
729
+ request.GET.copy(),
730
+ self.non_filter_params,
731
+ self.filterset_class(),
732
+ )
733
+
726
734
  # If the user clicks on the clear view button, we do not check for global or user defaults
727
- if not clear_view and not request.GET.get("saved_view"):
735
+ if not skip_user_and_global_default_saved_view and not clear_view and not request.GET.get("saved_view"):
728
736
  # Check if there is a default for this view for this specific user
729
737
  app_label, model_name = queryset.model._meta.label.split(".")
730
738
  view_name = f"{app_label}:{model_name.lower()}_list"
@@ -992,10 +1000,9 @@ class BulkEditAndBulkDeleteModelMixin:
992
1000
  )
993
1001
  return redirect("extras:jobresult", pk=job_result.pk)
994
1002
 
995
- def send_bulk_edit_objects_to_job(self, request, form, model):
1003
+ def send_bulk_edit_objects_to_job(self, request, form_data, model):
996
1004
  """Prepare and enqueue a bulk edit job."""
997
1005
  job_model = Job.objects.get_for_class_path(BulkEditObjects.class_path)
998
- form_data = normalize_querydict(request.POST, form)
999
1006
  if filterset_class := lookup.get_filterset_for_model(model):
1000
1007
  filter_query_params = normalize_querydict(request.GET, filterset=filterset_class())
1001
1008
  else:
@@ -1288,7 +1295,7 @@ class ObjectBulkUpdateViewMixin(NautobotViewSetMixin, BulkUpdateModelMixin, Bulk
1288
1295
  form = form_class(queryset.model, request.POST, edit_all=edit_all)
1289
1296
  restrict_form_fields(form, request.user)
1290
1297
  if form.is_valid():
1291
- return self.send_bulk_edit_objects_to_job(self.request, form, queryset.model)
1298
+ return self.send_bulk_edit_objects_to_job(self.request, form.cleaned_data, queryset.model)
1292
1299
  else:
1293
1300
  return self.form_invalid(form)
1294
1301
  table = None
@@ -1312,10 +1319,14 @@ class ObjectBulkUpdateViewMixin(NautobotViewSetMixin, BulkUpdateModelMixin, Bulk
1312
1319
 
1313
1320
  class ObjectChangeLogViewMixin(NautobotViewSetMixin):
1314
1321
  """
1315
- UI mixin to list a model's changelog queryset
1322
+ UI mixin to list a model's changelog queryset.
1323
+
1324
+ base_template: Specify to explicitly identify the base object detail template to render.
1325
+ If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
1326
+ will be used, as per `get_base_template()`.
1316
1327
  """
1317
1328
 
1318
- base_template = None
1329
+ base_template: Optional[str] = None
1319
1330
 
1320
1331
  @drf_action(detail=True)
1321
1332
  def changelog(self, request, *args, **kwargs):
@@ -1330,9 +1341,13 @@ class ObjectChangeLogViewMixin(NautobotViewSetMixin):
1330
1341
  class ObjectNotesViewMixin(NautobotViewSetMixin):
1331
1342
  """
1332
1343
  UI Mixin for an Object's Notes.
1344
+
1345
+ base_template: Specify to explicitly identify the base object detail template to render.
1346
+ If not provided, "<app>/<model>.html", "<app>/<model>_retrieve.html", or "generic/object_retrieve.html"
1347
+ will be used, as per `get_base_template()`.
1333
1348
  """
1334
1349
 
1335
- base_template = None
1350
+ base_template: Optional[str] = None
1336
1351
 
1337
1352
  @drf_action(detail=True)
1338
1353
  def notes(self, request, *args, **kwargs):
@@ -137,10 +137,10 @@ def get_csv_form_fields_from_serializer_class(serializer_class):
137
137
  elif cf.type == CustomFieldTypeChoices.TYPE_DATE:
138
138
  field_info["format"] = mark_safe("<code>YYYY-MM-DD</code>") # noqa: S308
139
139
  elif cf.type == CustomFieldTypeChoices.TYPE_SELECT:
140
- field_info["choices"] = {cfc.value: cfc.value for cfc in cf.custom_field_choices.all()}
140
+ field_info["choices"] = {value: value for value in cf.choices}
141
141
  elif cf.type == CustomFieldTypeChoices.TYPE_MULTISELECT:
142
142
  field_info["format"] = mark_safe('<code>"value,value"</code>') # noqa: S308
143
- field_info["choices"] = {cfc.value: cfc.value for cfc in cf.custom_field_choices.all()}
143
+ field_info["choices"] = {value: value for value in cf.choices}
144
144
  fields.append(field_info)
145
145
  continue
146
146
 
@@ -74,6 +74,7 @@ from nautobot.dcim.models import (
74
74
  )
75
75
  from nautobot.extras.api.views import (
76
76
  ConfigContextQuerySetMixin,
77
+ CustomFieldModelViewSet,
77
78
  NautobotModelViewSet,
78
79
  )
79
80
  from nautobot.extras.choices import SecretsGroupAccessTypeChoices, SecretsGroupSecretTypeChoices
@@ -300,13 +301,13 @@ class DeviceTypeViewSet(NautobotModelViewSet):
300
301
  #
301
302
 
302
303
 
303
- class ConsolePortTemplateViewSet(NautobotModelViewSet):
304
+ class ConsolePortTemplateViewSet(CustomFieldModelViewSet):
304
305
  queryset = ConsolePortTemplate.objects.select_related("device_type__manufacturer", "module_type__manufacturer")
305
306
  serializer_class = serializers.ConsolePortTemplateSerializer
306
307
  filterset_class = filters.ConsolePortTemplateFilterSet
307
308
 
308
309
 
309
- class ConsoleServerPortTemplateViewSet(NautobotModelViewSet):
310
+ class ConsoleServerPortTemplateViewSet(CustomFieldModelViewSet):
310
311
  queryset = ConsoleServerPortTemplate.objects.select_related(
311
312
  "device_type__manufacturer", "module_type__manufacturer"
312
313
  )
@@ -314,43 +315,43 @@ class ConsoleServerPortTemplateViewSet(NautobotModelViewSet):
314
315
  filterset_class = filters.ConsoleServerPortTemplateFilterSet
315
316
 
316
317
 
317
- class PowerPortTemplateViewSet(NautobotModelViewSet):
318
+ class PowerPortTemplateViewSet(CustomFieldModelViewSet):
318
319
  queryset = PowerPortTemplate.objects.select_related("device_type__manufacturer", "module_type__manufacturer")
319
320
  serializer_class = serializers.PowerPortTemplateSerializer
320
321
  filterset_class = filters.PowerPortTemplateFilterSet
321
322
 
322
323
 
323
- class PowerOutletTemplateViewSet(NautobotModelViewSet):
324
+ class PowerOutletTemplateViewSet(CustomFieldModelViewSet):
324
325
  queryset = PowerOutletTemplate.objects.select_related("device_type__manufacturer", "module_type__manufacturer")
325
326
  serializer_class = serializers.PowerOutletTemplateSerializer
326
327
  filterset_class = filters.PowerOutletTemplateFilterSet
327
328
 
328
329
 
329
- class InterfaceTemplateViewSet(NautobotModelViewSet):
330
+ class InterfaceTemplateViewSet(CustomFieldModelViewSet):
330
331
  queryset = InterfaceTemplate.objects.select_related("device_type__manufacturer", "module_type__manufacturer")
331
332
  serializer_class = serializers.InterfaceTemplateSerializer
332
333
  filterset_class = filters.InterfaceTemplateFilterSet
333
334
 
334
335
 
335
- class FrontPortTemplateViewSet(NautobotModelViewSet):
336
+ class FrontPortTemplateViewSet(CustomFieldModelViewSet):
336
337
  queryset = FrontPortTemplate.objects.select_related("device_type__manufacturer", "module_type__manufacturer")
337
338
  serializer_class = serializers.FrontPortTemplateSerializer
338
339
  filterset_class = filters.FrontPortTemplateFilterSet
339
340
 
340
341
 
341
- class RearPortTemplateViewSet(NautobotModelViewSet):
342
+ class RearPortTemplateViewSet(CustomFieldModelViewSet):
342
343
  queryset = RearPortTemplate.objects.select_related("device_type__manufacturer", "module_type__manufacturer")
343
344
  serializer_class = serializers.RearPortTemplateSerializer
344
345
  filterset_class = filters.RearPortTemplateFilterSet
345
346
 
346
347
 
347
- class DeviceBayTemplateViewSet(NautobotModelViewSet):
348
+ class DeviceBayTemplateViewSet(CustomFieldModelViewSet):
348
349
  queryset = DeviceBayTemplate.objects.select_related("device_type__manufacturer")
349
350
  serializer_class = serializers.DeviceBayTemplateSerializer
350
351
  filterset_class = filters.DeviceBayTemplateFilterSet
351
352
 
352
353
 
353
- class ModuleBayTemplateViewSet(NautobotModelViewSet):
354
+ class ModuleBayTemplateViewSet(CustomFieldModelViewSet):
354
355
  queryset = ModuleBayTemplate.objects.select_related("device_type__manufacturer", "module_type__manufacturer")
355
356
  serializer_class = serializers.ModuleBayTemplateSerializer
356
357
  filterset_class = filters.ModuleBayTemplateFilterSet
@@ -831,7 +832,7 @@ class VirtualDeviceContextViewSet(NautobotModelViewSet):
831
832
  filterset_class = filters.VirtualDeviceContextFilterSet
832
833
 
833
834
 
834
- class InterfaceVDCAssignmentViewSet(NautobotModelViewSet):
835
+ class InterfaceVDCAssignmentViewSet(ModelViewSet):
835
836
  queryset = InterfaceVDCAssignment.objects.all()
836
837
  serializer_class = serializers.InterfaceVDCAssignmentSerializer
837
838
  filterset_class = filters.InterfaceVDCAssignmentFilterSet
nautobot/dcim/forms.py CHANGED
@@ -17,6 +17,7 @@ from nautobot.core.forms import (
17
17
  AutoPositionPatternField,
18
18
  BootstrapMixin,
19
19
  BulkEditNullBooleanSelect,
20
+ ClearableFileInput,
20
21
  ColorSelect,
21
22
  CommentField,
22
23
  DatePicker,
@@ -307,6 +308,17 @@ class LocationTypeFilterForm(NautobotFilterForm):
307
308
  content_types = MultipleContentTypeField(feature="locations", choices_as_strings=True, required=False)
308
309
 
309
310
 
311
+ class LocationTypeBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
312
+ pk = forms.ModelMultipleChoiceField(queryset=LocationType.objects.all(), widget=forms.MultipleHiddenInput())
313
+ description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
314
+ nestable = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
315
+ add_content_types = MultipleContentTypeField(feature="locations", required=False)
316
+ remove_content_types = MultipleContentTypeField(feature="locations", required=False)
317
+
318
+ class Meta:
319
+ nullable_fields = []
320
+
321
+
310
322
  #
311
323
  # Locations
312
324
  #
@@ -784,6 +796,7 @@ class DeviceFamilyForm(NautobotModelForm):
784
796
  fields = [
785
797
  "name",
786
798
  "description",
799
+ "tags",
787
800
  ]
788
801
 
789
802
 
@@ -838,12 +851,8 @@ class DeviceTypeForm(NautobotModelForm):
838
851
  widgets = {
839
852
  "subdevice_role": StaticSelect2(),
840
853
  # Exclude SVG images (unsupported by PIL)
841
- "front_image": forms.ClearableFileInput(
842
- attrs={"accept": "image/bmp,image/gif,image/jpeg,image/png,image/tiff"}
843
- ),
844
- "rear_image": forms.ClearableFileInput(
845
- attrs={"accept": "image/bmp,image/gif,image/jpeg,image/png,image/tiff"}
846
- ),
854
+ "front_image": ClearableFileInput(attrs={"accept": "image/bmp,image/gif,image/jpeg,image/png,image/tiff"}),
855
+ "rear_image": ClearableFileInput(attrs={"accept": "image/bmp,image/gif,image/jpeg,image/png,image/tiff"}),
847
856
  }
848
857
 
849
858
 
@@ -1419,11 +1419,10 @@ class Controller(PrimaryModel):
1419
1419
  "controller_device": ("Cannot assign both a device and a device redundancy group to a controller."),
1420
1420
  },
1421
1421
  )
1422
-
1423
1422
  if self.location:
1424
1423
  if ContentType.objects.get_for_model(self) not in self.location.location_type.content_types.all():
1425
1424
  raise ValidationError(
1426
- {"location": f'Devices may not associate to locations of type "{self.location.location_type}".'}
1425
+ {"location": f'Controllers may not associate to locations of type "{self.location.location_type}".'}
1427
1426
  )
1428
1427
 
1429
1428
  def get_capabilities_display(self):
@@ -106,7 +106,8 @@ __all__ = (
106
106
 
107
107
  class PlatformTable(BaseTable):
108
108
  pk = ToggleColumn()
109
- name = tables.LinkColumn()
109
+ name = tables.Column(linkify=True)
110
+ manufacturer = tables.Column(linkify=True)
110
111
  device_count = LinkedCountColumn(
111
112
  viewname="dcim:device_list",
112
113
  url_params={"platform": "pk"},
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block content_left_page %}
@@ -13,8 +13,8 @@
13
13
  {% for near_end, cable, far_end in traced_path %}
14
14
 
15
15
  {# Near end #}
16
- {% if near_end.device %}
17
- {% include 'dcim/trace/device.html' with device=near_end.device %}
16
+ {% if near_end.device or near_end.module %}
17
+ {% include 'dcim/trace/device.html' with device=near_end.parent %}
18
18
  {% include 'dcim/trace/termination.html' with termination=near_end %}
19
19
  {% elif near_end.power_panel %}
20
20
  {% include 'dcim/trace/powerpanel.html' with powerpanel=near_end.power_panel %}
@@ -30,10 +30,10 @@
30
30
  {% endif %}
31
31
 
32
32
  {# Far end #}
33
- {% if far_end.device %}
33
+ {% if far_end.device or far_end.module %}
34
34
  {% include 'dcim/trace/termination.html' with termination=far_end %}
35
35
  {% if forloop.last %}
36
- {% include 'dcim/trace/device.html' with device=far_end.device %}
36
+ {% include 'dcim/trace/device.html' with device=far_end.parent %}
37
37
  {% endif %}
38
38
  {% elif far_end.power_panel %}
39
39
  {% include 'dcim/trace/termination.html' with termination=far_end %}
@@ -8,8 +8,13 @@
8
8
  </div>
9
9
  <table class="table table-hover panel-body attr-table">
10
10
  <tr>
11
- <td>Device</td>
12
- <td>{{ object.device|hyperlinked_object }}</td>
11
+ {% if object.device %}
12
+ <td>Device</td>
13
+ <td>{{ object.device|hyperlinked_object }}</td>
14
+ {% else %}
15
+ <td>Module</td>
16
+ <td>{{ object.module|hyperlinked_object }}</td>
17
+ {% endif %}
13
18
  </tr>
14
19
  <tr>
15
20
  <td>Name</td>
@@ -49,8 +54,13 @@
49
54
  </tr>
50
55
  {% if object.connected_endpoint %}
51
56
  <tr>
52
- <td>Device</td>
53
- <td>{{ object.connected_endpoint.device|hyperlinked_object }}</td>
57
+ {% if object.connected_endpoint.device %}
58
+ <td>Device</td>
59
+ <td>{{ object.connected_endpoint.device|hyperlinked_object }}</td>
60
+ {% else %}
61
+ <td>Module</td>
62
+ <td>{{ object.connected_endpoint.module|hyperlinked_object }}</td>
63
+ {% endif %}
54
64
  </tr>
55
65
  <tr>
56
66
  <td>Console Server Port</td>
@@ -8,8 +8,13 @@
8
8
  </div>
9
9
  <table class="table table-hover panel-body attr-table">
10
10
  <tr>
11
- <td>Device</td>
12
- <td>{{ object.device|hyperlinked_object }}</td>
11
+ {% if object.device %}
12
+ <td>Device</td>
13
+ <td>{{ object.device|hyperlinked_object }}</td>
14
+ {% else %}
15
+ <td>Module</td>
16
+ <td>{{ object.module|hyperlinked_object }}</td>
17
+ {% endif %}
13
18
  </tr>
14
19
  <tr>
15
20
  <td>Name</td>
@@ -49,8 +54,13 @@
49
54
  </tr>
50
55
  {% if object.connected_endpoint %}
51
56
  <tr>
52
- <td>Device</td>
53
- <td>{{ object.connected_endpoint.device|hyperlinked_object }}</td>
57
+ {% if object.connected_endpoint.device %}
58
+ <td>Device</td>
59
+ <td>{{ object.connected_endpoint.device|hyperlinked_object }}</td>
60
+ {% else %}
61
+ <td>Module</td>
62
+ <td>{{ object.connected_endpoint.module|hyperlinked_object }}</td>
63
+ {% endif %}
54
64
  </tr>
55
65
  <tr>
56
66
  <td>Console Port</td>
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
  {% load static %}
4
4
 
@@ -24,9 +24,9 @@
24
24
  {% for iface in interfaces %}
25
25
  <tr data-interface-name="{{ iface.name }}">
26
26
  <td>{{ iface }}</td>
27
- {% if iface.connected_endpoint.device %}
28
- <td class="configured_device" data="{{ iface.connected_endpoint.device }}" data-chassis="{{ iface.connected_endpoint.device.virtual_chassis.name }}">
29
- {{ iface.connected_endpoint.device|hyperlinked_object }}
27
+ {% if iface.connected_endpoint.device or iface.connected_endpoint.module %}
28
+ <td class="configured_device" data="{{ iface.connected_endpoint.parent }}" data-chassis="{{ iface.connected_endpoint.parent.virtual_chassis.name }}">
29
+ {{ iface.connected_endpoint.parent|hyperlinked_object }}
30
30
  </td>
31
31
  <td class="configured_interface" data-interface-name="{{ iface.connected_endpoint }}">
32
32
  <span title="{{ iface.connected_endpoint.get_type_display }}">{{ iface.connected_endpoint }}</span>
@@ -214,8 +214,8 @@
214
214
  <tr>
215
215
  <td>Cluster</td>
216
216
  <td>
217
- {% if object.cluster.group %}
218
- {{ object.cluster.group|hyperlinked_object }} /
217
+ {% if object.cluster.cluster_group %}
218
+ {{ object.cluster.cluster_group|hyperlinked_object }} /
219
219
  {% endif %}
220
220
  {{ object.cluster|hyperlinked_object }}
221
221
  </td>
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block breadcrumbs %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load buttons %}
3
3
  {% load helpers %}
4
4
  {% load plugins %}
@@ -8,8 +8,13 @@
8
8
  </div>
9
9
  <table class="table table-hover panel-body attr-table">
10
10
  <tr>
11
- <td>Device</td>
12
- <td>{{ object.device|hyperlinked_object }}</td>
11
+ {% if object.device %}
12
+ <td>Device</td>
13
+ <td>{{ object.device|hyperlinked_object }}</td>
14
+ {% else %}
15
+ <td>Module</td>
16
+ <td>{{ object.module|hyperlinked_object }}</td>
17
+ {% endif %}
13
18
  </tr>
14
19
  <tr>
15
20
  <td>Name</td>
@@ -101,11 +101,16 @@
101
101
  </a>
102
102
  </td>
103
103
  </tr>
104
- {% if object.connected_endpoint.device %}
104
+ {% if object.connected_endpoint.device or object.connected_endpoint.module %}
105
105
  {% with iface=object.connected_endpoint %}
106
106
  <tr>
107
- <td>Device</td>
108
- <td>{{ iface.device|hyperlinked_object }}</td>
107
+ {% if iface.device %}
108
+ <td>Device</td>
109
+ <td>{{ iface.device|hyperlinked_object }}</td>
110
+ {% else %}
111
+ <td>Module</td>
112
+ <td>{{ iface.module|hyperlinked_object }}</td>
113
+ {% endif %}
109
114
  </tr>
110
115
  <tr>
111
116
  <td>Interface</td>
@@ -201,7 +206,7 @@
201
206
  <tbody>
202
207
  {% for member in object.member_interfaces.all %}
203
208
  <tr>
204
- <td>{{ member.device|hyperlinked_object }}</td>
209
+ <td>{{ member.parent|hyperlinked_object }}</td>
205
210
  <td>{{ member|hyperlinked_object }}</td>
206
211
  <td>
207
212
  {{ member.get_type_display }}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load buttons %}
3
3
  {% load plugins %}
4
4
  {% load helpers %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block extra_breadcrumbs %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block extra_breadcrumbs %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block content_left_page %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block content_left_page %}