nautobot 2.2.3__py3-none-any.whl → 2.2.5__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 (327) hide show
  1. nautobot/circuits/forms.py +15 -0
  2. nautobot/circuits/navigation.py +9 -1
  3. nautobot/circuits/views.py +2 -0
  4. nautobot/core/filters.py +11 -0
  5. nautobot/core/settings.py +6 -4
  6. nautobot/core/settings.yaml +54 -19
  7. nautobot/core/templates/admin/base.html +2 -2
  8. nautobot/core/templates/base_django.html +2 -2
  9. nautobot/core/templates/buttons/export.html +47 -47
  10. nautobot/core/templates/inc/javascript.html +3 -0
  11. nautobot/core/templates/inc/media.html +3 -0
  12. nautobot/core/templates/login.html +2 -2
  13. nautobot/core/templates/nautobot_config.py.j2 +2 -0
  14. nautobot/core/testing/filters.py +24 -1
  15. nautobot/core/testing/views.py +13 -1
  16. nautobot/core/tests/test_jobs.py +79 -2
  17. nautobot/core/tests/test_views.py +33 -0
  18. nautobot/core/views/mixins.py +4 -0
  19. nautobot/core/views/utils.py +18 -1
  20. nautobot/dcim/filters/__init__.py +1 -1
  21. nautobot/dcim/forms.py +23 -4
  22. nautobot/dcim/tables/devicetypes.py +15 -4
  23. nautobot/dcim/tests/test_views.py +323 -55
  24. nautobot/dcim/views.py +26 -20
  25. nautobot/extras/api/serializers.py +17 -6
  26. nautobot/extras/api/views.py +2 -2
  27. nautobot/extras/context_managers.py +3 -0
  28. nautobot/extras/filters/__init__.py +15 -1
  29. nautobot/extras/forms/forms.py +33 -0
  30. nautobot/extras/forms/mixins.py +0 -6
  31. nautobot/extras/signals.py +6 -1
  32. nautobot/extras/tests/test_api.py +24 -2
  33. nautobot/extras/tests/test_context_managers.py +51 -1
  34. nautobot/extras/tests/test_filters.py +69 -0
  35. nautobot/extras/tests/test_forms.py +0 -3
  36. nautobot/extras/tests/test_views.py +48 -4
  37. nautobot/extras/utils.py +2 -1
  38. nautobot/extras/views.py +47 -31
  39. nautobot/ipam/forms.py +18 -0
  40. nautobot/ipam/tests/test_views.py +9 -2
  41. nautobot/ipam/views.py +17 -6
  42. nautobot/project-static/docs/404.html +107 -51
  43. nautobot/project-static/docs/apps/index.html +107 -51
  44. nautobot/project-static/docs/apps/nautobot-apps.html +107 -51
  45. nautobot/project-static/docs/assets/_mkdocstrings.css +6 -1
  46. nautobot/project-static/docs/assets/extra.css +7 -0
  47. nautobot/project-static/docs/assets/javascripts/bundle.ebd0bdb7.min.js +29 -0
  48. nautobot/project-static/docs/assets/javascripts/bundle.ebd0bdb7.min.js.map +7 -0
  49. nautobot/project-static/docs/assets/stylesheets/main.6543a935.min.css +1 -0
  50. nautobot/project-static/docs/assets/stylesheets/main.6543a935.min.css.map +1 -0
  51. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +107 -51
  52. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +107 -51
  53. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +107 -51
  54. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +107 -51
  55. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +107 -51
  56. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +107 -51
  57. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +107 -51
  58. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +107 -51
  59. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +107 -51
  60. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +107 -51
  61. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +107 -51
  62. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +107 -51
  63. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +107 -51
  64. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +107 -51
  65. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +107 -51
  66. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +107 -51
  67. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +107 -51
  68. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +107 -51
  69. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +107 -51
  70. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +107 -51
  71. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +107 -51
  72. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +107 -51
  73. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +107 -51
  74. nautobot/project-static/docs/development/apps/api/configuration-view.html +110 -54
  75. nautobot/project-static/docs/development/apps/api/database-backend-config.html +110 -54
  76. nautobot/project-static/docs/development/apps/api/models/django-admin.html +107 -51
  77. nautobot/project-static/docs/development/apps/api/models/global-search.html +110 -54
  78. nautobot/project-static/docs/development/apps/api/models/graphql.html +113 -57
  79. nautobot/project-static/docs/development/apps/api/models/index.html +107 -51
  80. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +113 -57
  81. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +107 -51
  82. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +111 -55
  83. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +107 -51
  84. nautobot/project-static/docs/development/apps/api/platform-features/index.html +107 -51
  85. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +110 -54
  86. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +110 -54
  87. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +110 -54
  88. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +110 -54
  89. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +107 -51
  90. nautobot/project-static/docs/development/apps/api/prometheus.html +110 -54
  91. nautobot/project-static/docs/development/apps/api/setup.html +107 -51
  92. nautobot/project-static/docs/development/apps/api/testing.html +113 -57
  93. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +110 -54
  94. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +110 -54
  95. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +107 -51
  96. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +107 -51
  97. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +113 -57
  98. nautobot/project-static/docs/development/apps/api/views/base-template.html +107 -51
  99. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +110 -54
  100. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +107 -51
  101. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +110 -54
  102. nautobot/project-static/docs/development/apps/api/views/index.html +107 -51
  103. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +113 -57
  104. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +122 -66
  105. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +110 -54
  106. nautobot/project-static/docs/development/apps/api/views/notes.html +110 -54
  107. nautobot/project-static/docs/development/apps/api/views/rest-api.html +107 -51
  108. nautobot/project-static/docs/development/apps/api/views/urls.html +107 -51
  109. nautobot/project-static/docs/development/apps/index.html +128 -72
  110. nautobot/project-static/docs/development/apps/migration/code-updates.html +107 -51
  111. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +107 -51
  112. nautobot/project-static/docs/development/apps/migration/from-v1.html +109 -53
  113. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +107 -51
  114. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +107 -51
  115. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +107 -51
  116. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +107 -51
  117. nautobot/project-static/docs/development/apps/porting-from-netbox.html +110 -54
  118. nautobot/project-static/docs/development/core/application-registry.html +120 -64
  119. nautobot/project-static/docs/development/core/best-practices.html +122 -66
  120. nautobot/project-static/docs/development/core/bootstrap-ui.html +107 -51
  121. nautobot/project-static/docs/development/core/caching.html +107 -51
  122. nautobot/project-static/docs/development/core/controllers.html +107 -51
  123. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +113 -57
  124. nautobot/project-static/docs/development/core/generic-views.html +110 -54
  125. nautobot/project-static/docs/development/core/getting-started.html +137 -81
  126. nautobot/project-static/docs/development/core/homepage.html +110 -54
  127. nautobot/project-static/docs/development/core/index.html +107 -51
  128. nautobot/project-static/docs/development/core/model-checklist.html +107 -51
  129. nautobot/project-static/docs/development/core/model-features.html +107 -51
  130. nautobot/project-static/docs/development/core/natural-keys.html +110 -54
  131. nautobot/project-static/docs/development/core/navigation-menu.html +107 -51
  132. nautobot/project-static/docs/development/core/release-checklist.html +107 -51
  133. nautobot/project-static/docs/development/core/role-internals.html +107 -51
  134. nautobot/project-static/docs/development/core/settings.html +107 -51
  135. nautobot/project-static/docs/development/core/style-guide.html +110 -54
  136. nautobot/project-static/docs/development/core/templates.html +113 -57
  137. nautobot/project-static/docs/development/core/testing.html +126 -70
  138. nautobot/project-static/docs/development/core/user-preferences.html +107 -51
  139. nautobot/project-static/docs/development/index.html +107 -51
  140. nautobot/project-static/docs/development/jobs/index.html +173 -117
  141. nautobot/project-static/docs/development/jobs/migration/from-v1.html +110 -54
  142. nautobot/project-static/docs/docker/index.html +3 -3
  143. nautobot/project-static/docs/index.html +125 -69
  144. nautobot/project-static/docs/installation/selinux-troubleshooting.html +3 -3
  145. nautobot/project-static/docs/release-notes/index.html +107 -51
  146. nautobot/project-static/docs/release-notes/version-1.0.html +108 -52
  147. nautobot/project-static/docs/release-notes/version-1.1.html +107 -51
  148. nautobot/project-static/docs/release-notes/version-1.2.html +109 -53
  149. nautobot/project-static/docs/release-notes/version-1.3.html +108 -52
  150. nautobot/project-static/docs/release-notes/version-1.4.html +109 -53
  151. nautobot/project-static/docs/release-notes/version-1.5.html +118 -62
  152. nautobot/project-static/docs/release-notes/version-1.6.html +721 -285
  153. nautobot/project-static/docs/release-notes/version-2.0.html +113 -57
  154. nautobot/project-static/docs/release-notes/version-2.1.html +107 -51
  155. nautobot/project-static/docs/release-notes/version-2.2.html +503 -120
  156. nautobot/project-static/docs/requirements.txt +4 -4
  157. nautobot/project-static/docs/search/search_index.json +1 -1
  158. nautobot/project-static/docs/sitemap.xml +262 -262
  159. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  160. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +107 -51
  161. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +107 -51
  162. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +109 -53
  163. nautobot/project-static/docs/user-guide/administration/configuration/index.html +108 -52
  164. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +254 -167
  165. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +113 -57
  166. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +107 -51
  167. nautobot/project-static/docs/user-guide/administration/guides/caching.html +113 -57
  168. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +107 -51
  169. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +107 -51
  170. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +107 -51
  171. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +113 -57
  172. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +108 -52
  173. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +107 -51
  174. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +107 -51
  175. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +171 -112
  176. nautobot/project-static/docs/user-guide/administration/installation/docker.html +13 -8626
  177. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +117 -61
  178. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +13 -8614
  179. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +252 -165
  180. nautobot/project-static/docs/user-guide/administration/installation/index.html +165 -192
  181. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +411 -691
  182. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +249 -230
  183. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +13 -8118
  184. nautobot/project-static/docs/user-guide/administration/installation/services.html +351 -241
  185. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +8684 -0
  186. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +8672 -0
  187. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +8176 -0
  188. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +110 -54
  189. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +110 -54
  190. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +155 -99
  191. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +107 -51
  192. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +109 -53
  193. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +107 -51
  194. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +107 -51
  195. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +108 -52
  196. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +107 -51
  197. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +107 -51
  198. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +107 -70
  199. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +115 -59
  200. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +113 -57
  201. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +107 -51
  202. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +107 -51
  203. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +107 -51
  204. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +107 -51
  205. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +110 -54
  206. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +107 -51
  207. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +110 -54
  208. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +110 -54
  209. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +110 -54
  210. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +110 -54
  211. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +107 -51
  212. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +107 -51
  213. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +115 -59
  214. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +110 -54
  215. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +110 -54
  216. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +110 -54
  217. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +110 -54
  218. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +117 -61
  219. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +110 -54
  220. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +110 -54
  221. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +119 -63
  222. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +110 -54
  223. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +110 -54
  224. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +113 -57
  225. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +113 -57
  226. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +113 -57
  227. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +107 -51
  228. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +113 -57
  229. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +107 -51
  230. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +110 -54
  231. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +110 -54
  232. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +107 -51
  233. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +110 -54
  234. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +110 -54
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +107 -51
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +107 -51
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +107 -51
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +110 -54
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +110 -54
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +110 -54
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +110 -54
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +107 -51
  243. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +113 -57
  244. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +110 -54
  245. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +110 -54
  246. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +110 -54
  247. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +125 -69
  248. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +113 -57
  249. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +128 -72
  250. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +110 -54
  251. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +107 -51
  252. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +110 -54
  253. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +116 -60
  254. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +107 -51
  255. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +113 -57
  256. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +107 -51
  257. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +110 -54
  258. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +107 -51
  259. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +107 -51
  260. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +107 -51
  261. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +107 -51
  262. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +113 -57
  263. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +113 -57
  264. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +107 -51
  265. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +113 -57
  266. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +110 -54
  267. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +107 -51
  268. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +107 -51
  269. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +108 -52
  270. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +107 -51
  271. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +107 -51
  272. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +107 -51
  273. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +107 -51
  274. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +108 -52
  275. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +110 -54
  276. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +113 -57
  277. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +107 -51
  278. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +110 -54
  279. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +107 -51
  280. nautobot/project-static/docs/user-guide/index.html +109 -53
  281. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +107 -51
  282. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +113 -57
  283. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +128 -72
  284. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +107 -51
  285. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +125 -69
  286. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +107 -51
  287. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +110 -54
  288. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +125 -69
  289. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +110 -54
  290. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +107 -51
  291. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +107 -51
  292. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +128 -72
  293. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +114 -58
  294. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +113 -57
  295. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +110 -54
  296. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +121 -65
  297. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +109 -53
  298. nautobot/project-static/docs/user-guide/platform-functionality/note.html +110 -54
  299. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +116 -60
  300. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +110 -54
  301. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +131 -75
  302. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +149 -93
  303. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +110 -54
  304. nautobot/project-static/docs/user-guide/platform-functionality/role.html +108 -84
  305. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +116 -60
  306. nautobot/project-static/docs/user-guide/platform-functionality/status.html +119 -63
  307. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +110 -54
  308. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +137 -81
  309. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +110 -54
  310. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +107 -51
  311. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +110 -54
  312. nautobot/project-static/js/forms.js +2 -1
  313. nautobot/tenancy/forms.py +9 -0
  314. nautobot/tenancy/views.py +3 -6
  315. nautobot/virtualization/forms.py +18 -6
  316. nautobot/virtualization/templates/virtualization/clustertype.html +2 -2
  317. nautobot/virtualization/views.py +7 -9
  318. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/METADATA +2 -2
  319. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/RECORD +323 -320
  320. nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js +0 -29
  321. nautobot/project-static/docs/assets/javascripts/bundle.bd41221c.min.js.map +0 -7
  322. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css +0 -1
  323. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css.map +0 -1
  324. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/LICENSE.txt +0 -0
  325. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/NOTICE +0 -0
  326. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/WHEEL +0 -0
  327. {nautobot-2.2.3.dist-info → nautobot-2.2.5.dist-info}/entry_points.txt +0 -0
@@ -17,6 +17,7 @@ from nautobot.core.choices import ColorChoices
17
17
  from nautobot.core.models.fields import slugify_dashes_to_underscores
18
18
  from nautobot.core.testing import extract_form_failures, extract_page_body, TestCase, ViewTestCases
19
19
  from nautobot.core.testing.utils import disable_warnings, post_data
20
+ from nautobot.core.utils.permissions import get_permission_for_model
20
21
  from nautobot.dcim.models import (
21
22
  ConsolePort,
22
23
  Controller,
@@ -777,9 +778,11 @@ class DynamicGroupTestCase(
777
778
  content_type = ContentType.objects.get_for_model(Device)
778
779
 
779
780
  # DynamicGroup objects to test.
780
- DynamicGroup.objects.create(name="DG 1", content_type=content_type)
781
- DynamicGroup.objects.create(name="DG 2", content_type=content_type)
782
- DynamicGroup.objects.create(name="DG 3", content_type=content_type)
781
+ cls.dynamic_groups = [
782
+ DynamicGroup.objects.create(name="DG 1", content_type=content_type),
783
+ DynamicGroup.objects.create(name="DG 2", content_type=content_type),
784
+ DynamicGroup.objects.create(name="DG 3", content_type=content_type),
785
+ ]
783
786
 
784
787
  cls.form_data = {
785
788
  "name": "new_dynamic_group",
@@ -792,6 +795,38 @@ class DynamicGroupTestCase(
792
795
  "dynamic_group_memberships-MAX_NUM_FORMS": "1000",
793
796
  }
794
797
 
798
+ def test_get_object_with_permission(self):
799
+ instance = self._get_queryset().first()
800
+ # Add view permissions for the group's members:
801
+ self.add_permissions(get_permission_for_model(instance.content_type.model_class(), "view"))
802
+
803
+ response = super().test_get_object_with_permission()
804
+
805
+ response_body = extract_page_body(response.content.decode(response.charset))
806
+ # Check that the "members" table in the detail view includes all appropriate member objects
807
+ for member in instance.members:
808
+ self.assertIn(str(member.pk), response_body)
809
+
810
+ def test_get_object_with_constrained_permission(self):
811
+ instance = self._get_queryset().first()
812
+ # Add view permission for one of the group's members but not the others:
813
+ member1, member2 = instance.members[:2]
814
+ obj_perm = ObjectPermission(
815
+ name="Members permission",
816
+ constraints={"pk": member1.pk},
817
+ actions=["view"],
818
+ )
819
+ obj_perm.save()
820
+ obj_perm.users.add(self.user)
821
+ obj_perm.object_types.add(instance.content_type)
822
+
823
+ response = super().test_get_object_with_constrained_permission()
824
+
825
+ response_body = extract_page_body(response.content.decode(response.charset))
826
+ # Check that the "members" table in the detail view includes all permitted member objects
827
+ self.assertIn(str(member1.pk), response_body)
828
+ self.assertNotIn(str(member2.pk), response_body)
829
+
795
830
  def test_get_object_dynamic_groups_anonymous(self):
796
831
  url = reverse("dcim:device_dynamicgroups", kwargs={"pk": Device.objects.first().pk})
797
832
  self.client.logout()
@@ -815,7 +850,6 @@ class DynamicGroupTestCase(
815
850
  self.assertIn("DG 3", response_body, msg=response_body)
816
851
 
817
852
  def test_get_object_dynamic_groups_with_constrained_permission(self):
818
- self.add_permissions("extras.view_dynamicgroup")
819
853
  obj_perm = ObjectPermission(
820
854
  name="View a device",
821
855
  constraints={"pk": Device.objects.first().pk},
@@ -824,12 +858,22 @@ class DynamicGroupTestCase(
824
858
  obj_perm.save()
825
859
  obj_perm.users.add(self.user)
826
860
  obj_perm.object_types.add(ContentType.objects.get_for_model(Device))
861
+ obj_perm_2 = ObjectPermission(
862
+ name="View a Dynamic Group",
863
+ constraints={"pk": self.dynamic_groups[0].pk},
864
+ actions=["view"],
865
+ )
866
+ obj_perm_2.save()
867
+ obj_perm_2.users.add(self.user)
868
+ obj_perm_2.object_types.add(ContentType.objects.get_for_model(DynamicGroup))
827
869
 
828
870
  url = reverse("dcim:device_dynamicgroups", kwargs={"pk": Device.objects.first().pk})
829
871
  response = self.client.get(url)
830
872
  self.assertHttpStatus(response, 200)
831
873
  response_body = response.content.decode(response.charset)
832
874
  self.assertIn("DG 1", response_body, msg=response_body)
875
+ self.assertNotIn("DG 2", response_body, msg=response_body)
876
+ self.assertNotIn("DG 3", response_body, msg=response_body)
833
877
 
834
878
  url = reverse("dcim:device_dynamicgroups", kwargs={"pk": Device.objects.last().pk})
835
879
  response = self.client.get(url)
nautobot/extras/utils.py CHANGED
@@ -637,7 +637,8 @@ def bulk_delete_with_bulk_change_logging(qs, batch_size=1000):
637
637
  ObjectChange.objects.bulk_create(queued_object_changes)
638
638
  queued_object_changes = []
639
639
  oc = obj.to_objectchange(ObjectChangeActionChoices.ACTION_DELETE)
640
- oc.user = change_context.user
640
+ oc.user = change_context.get_user()
641
+ oc.user_name = oc.user.username
641
642
  oc.request_id = change_context.change_id
642
643
  oc.change_context = change_context.context
643
644
  oc.change_context_detail = change_context.context_detail[:CHANGELOG_MAX_CHANGE_CONTEXT_DETAIL]
nautobot/extras/views.py CHANGED
@@ -56,8 +56,6 @@ from .datasources import (
56
56
  enqueue_pull_git_repository_and_refresh_data,
57
57
  get_datasource_contents,
58
58
  )
59
- from .filters import RoleFilterSet
60
- from .forms import RoleBulkEditForm, RoleForm
61
59
  from .jobs import get_job
62
60
  from .models import (
63
61
  ComputedField,
@@ -94,7 +92,6 @@ from .models import (
94
92
  Webhook,
95
93
  )
96
94
  from .registry import registry
97
- from .tables import AssociatedContactsTable, RoleTable
98
95
 
99
96
  logger = logging.getLogger(__name__)
100
97
 
@@ -152,19 +149,18 @@ class ConfigContextView(generic.ObjectView):
152
149
  queryset = ConfigContext.objects.all()
153
150
 
154
151
  def get_extra_context(self, request, instance):
152
+ context = super().get_extra_context(request, instance)
155
153
  # Determine user's preferred output format
156
154
  if request.GET.get("format") in ["json", "yaml"]:
157
- format_ = request.GET.get("format")
155
+ context["format"] = request.GET.get("format")
158
156
  if request.user.is_authenticated:
159
- request.user.set_config("extras.configcontext.format", format_, commit=True)
157
+ request.user.set_config("extras.configcontext.format", context["format"], commit=True)
160
158
  elif request.user.is_authenticated:
161
- format_ = request.user.get_config("extras.configcontext.format", "json")
159
+ context["format"] = request.user.get_config("extras.configcontext.format", "json")
162
160
  else:
163
- format_ = "json"
161
+ context["format"] = "json"
164
162
 
165
- return {
166
- "format": format_,
167
- }
163
+ return context
168
164
 
169
165
 
170
166
  class ConfigContextEditView(generic.ObjectEditView):
@@ -236,19 +232,18 @@ class ConfigContextSchemaView(generic.ObjectView):
236
232
  queryset = ConfigContextSchema.objects.all()
237
233
 
238
234
  def get_extra_context(self, request, instance):
235
+ context = super().get_extra_context(request, instance)
239
236
  # Determine user's preferred output format
240
237
  if request.GET.get("format") in ["json", "yaml"]:
241
- format_ = request.GET.get("format")
238
+ context["format"] = request.GET.get("format")
242
239
  if request.user.is_authenticated:
243
- request.user.set_config("extras.configcontextschema.format", format_, commit=True)
240
+ request.user.set_config("extras.configcontextschema.format", context["format"], commit=True)
244
241
  elif request.user.is_authenticated:
245
- format_ = request.user.get_config("extras.configcontextschema.format", "json")
242
+ context["format"] = request.user.get_config("extras.configcontextschema.format", "json")
246
243
  else:
247
- format_ = "json"
244
+ context["format"] = "json"
248
245
 
249
- return {
250
- "format": format_,
251
- }
246
+ return context
252
247
 
253
248
 
254
249
  class ConfigContextSchemaObjectValidationView(generic.ObjectView):
@@ -400,7 +395,7 @@ class ContactAssociationUIViewSet(
400
395
  filterset_class = filters.ContactAssociationFilterSet
401
396
  queryset = ContactAssociation.objects.all()
402
397
  serializer_class = serializers.ContactAssociationSerializer
403
- table_class = AssociatedContactsTable
398
+ table_class = tables.AssociatedContactsTable
404
399
  non_filter_params = ("export", "page", "per_page", "sort")
405
400
 
406
401
 
@@ -508,6 +503,7 @@ class CustomFieldListView(generic.ObjectListView):
508
503
  queryset = CustomField.objects.all()
509
504
  table = tables.CustomFieldTable
510
505
  filterset = filters.CustomFieldFilterSet
506
+ filterset_form = forms.CustomFieldFilterForm
511
507
  action_buttons = ("add",)
512
508
 
513
509
 
@@ -708,7 +704,7 @@ class DynamicGroupView(generic.ObjectView):
708
704
 
709
705
  if table_class is not None:
710
706
  # Members table (for display on Members nav tab)
711
- members_table = table_class(instance.members, orderable=False)
707
+ members_table = table_class(instance.members.restrict(request.user, "view"), orderable=False)
712
708
  paginate = {
713
709
  "paginator_class": EnhancedPaginator,
714
710
  "per_page": get_paginate_count(request),
@@ -888,7 +884,9 @@ class ObjectDynamicGroupsView(generic.GenericView):
888
884
  obj = get_object_or_404(model, **kwargs)
889
885
 
890
886
  # Gather all dynamic groups for this object (and its related objects)
891
- dynamicsgroups_table = tables.DynamicGroupTable(data=obj.dynamic_groups_cached, orderable=False)
887
+ dynamicsgroups_table = tables.DynamicGroupTable(
888
+ data=obj.dynamic_groups_cached.restrict(request.user, "view"), orderable=False
889
+ )
892
890
 
893
891
  # Apply the request context
894
892
  paginate = {
@@ -952,6 +950,7 @@ class ExportTemplateBulkDeleteView(generic.BulkDeleteView):
952
950
  class ExternalIntegrationUIViewSet(NautobotUIViewSet):
953
951
  bulk_update_form_class = forms.ExternalIntegrationBulkEditForm
954
952
  filterset_class = filters.ExternalIntegrationFilterSet
953
+ filterset_form_class = forms.ExternalIntegrationFilterForm
955
954
  form_class = forms.ExternalIntegrationForm
956
955
  queryset = ExternalIntegration.objects.select_related("secrets_group")
957
956
  serializer_class = serializers.ExternalIntegrationSerializer
@@ -994,6 +993,7 @@ class GitRepositoryView(generic.ObjectView):
994
993
  def get_extra_context(self, request, instance):
995
994
  return {
996
995
  "datasource_contents": get_datasource_contents("extras.gitrepository"),
996
+ **super().get_extra_context(request, instance),
997
997
  }
998
998
 
999
999
 
@@ -1520,9 +1520,7 @@ class JobApprovalRequestView(generic.ObjectView):
1520
1520
  else:
1521
1521
  job_form = None
1522
1522
 
1523
- return {
1524
- "job_form": job_form,
1525
- }
1523
+ return {"job_form": job_form, **super().get_extra_context(request, instance)}
1526
1524
 
1527
1525
  def post(self, request, pk):
1528
1526
  """
@@ -1652,7 +1650,11 @@ class ScheduledJobView(generic.ObjectView):
1652
1650
  labels[name] = field.label
1653
1651
  else:
1654
1652
  labels[name] = pretty_name(name)
1655
- return {"labels": labels, "job_class_found": (job_class is not None)}
1653
+ return {
1654
+ "labels": labels,
1655
+ "job_class_found": (job_class is not None),
1656
+ **super().get_extra_context(request, instance),
1657
+ }
1656
1658
 
1657
1659
 
1658
1660
  class ScheduledJobDeleteView(generic.ObjectDeleteView):
@@ -1676,7 +1678,10 @@ class JobHookView(generic.ObjectView):
1676
1678
  queryset = JobHook.objects.all()
1677
1679
 
1678
1680
  def get_extra_context(self, request, instance):
1679
- return {"content_types": instance.content_types.order_by("app_label", "model")}
1681
+ return {
1682
+ "content_types": instance.content_types.order_by("app_label", "model"),
1683
+ **super().get_extra_context(request, instance),
1684
+ }
1680
1685
 
1681
1686
 
1682
1687
  class JobHookEditView(generic.ObjectEditView):
@@ -1761,6 +1766,7 @@ class JobResultView(generic.ObjectView):
1761
1766
  "job": job_class,
1762
1767
  "associated_record": associated_record,
1763
1768
  "result": instance,
1769
+ **super().get_extra_context(request, instance),
1764
1770
  }
1765
1771
 
1766
1772
 
@@ -1848,6 +1854,7 @@ class ObjectChangeView(generic.ObjectView):
1848
1854
  "prev_change": instance.get_prev_change(request.user),
1849
1855
  "related_changes_table": related_changes_table,
1850
1856
  "related_changes_count": related_changes.count(),
1857
+ **super().get_extra_context(request, instance),
1851
1858
  }
1852
1859
 
1853
1860
 
@@ -2042,11 +2049,12 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
2042
2049
  """`Roles` UIViewSet."""
2043
2050
 
2044
2051
  queryset = Role.objects.all()
2045
- bulk_update_form_class = RoleBulkEditForm
2046
- filterset_class = RoleFilterSet
2047
- form_class = RoleForm
2052
+ bulk_update_form_class = forms.RoleBulkEditForm
2053
+ filterset_class = filters.RoleFilterSet
2054
+ filterset_form_class = forms.RoleFilterForm
2055
+ form_class = forms.RoleForm
2048
2056
  serializer_class = serializers.RoleSerializer
2049
- table_class = RoleTable
2057
+ table_class = tables.RoleTable
2050
2058
 
2051
2059
  def get_extra_context(self, request, instance):
2052
2060
  context = super().get_extra_context(request, instance)
@@ -2192,6 +2200,7 @@ class SecretView(generic.ObjectView):
2192
2200
  "format": format_,
2193
2201
  "provider_name": provider.name if provider else instance.provider,
2194
2202
  "groups_table": groups_table,
2203
+ **super().get_extra_context(request, instance),
2195
2204
  }
2196
2205
 
2197
2206
 
@@ -2407,7 +2416,10 @@ class StatusView(generic.ObjectView):
2407
2416
 
2408
2417
  def get_extra_context(self, request, instance):
2409
2418
  """Return ordered content types."""
2410
- return {"content_types": instance.content_types.order_by("app_label", "model")}
2419
+ return {
2420
+ "content_types": instance.content_types.order_by("app_label", "model"),
2421
+ **super().get_extra_context(request, instance),
2422
+ }
2411
2423
 
2412
2424
 
2413
2425
  #
@@ -2442,6 +2454,7 @@ class TagView(generic.ObjectView):
2442
2454
  "items_count": tagged_items.count(),
2443
2455
  "items_table": items_table,
2444
2456
  "content_types": instance.content_types.order_by("app_label", "model"),
2457
+ **super().get_extra_context(request, instance),
2445
2458
  }
2446
2459
 
2447
2460
 
@@ -2522,7 +2535,10 @@ class WebhookView(generic.ObjectView):
2522
2535
  queryset = Webhook.objects.all()
2523
2536
 
2524
2537
  def get_extra_context(self, request, instance):
2525
- return {"content_types": instance.content_types.order_by("app_label", "model")}
2538
+ return {
2539
+ "content_types": instance.content_types.order_by("app_label", "model"),
2540
+ **super().get_extra_context(request, instance),
2541
+ }
2526
2542
 
2527
2543
 
2528
2544
  class WebhookEditView(generic.ObjectEditView):
nautobot/ipam/forms.py CHANGED
@@ -94,6 +94,12 @@ class NamespaceBulkEditForm(
94
94
  ]
95
95
 
96
96
 
97
+ class NamespaceFilterForm(LocatableModelFilterFormMixin, NautobotFilterForm):
98
+ model = Namespace
99
+ q = forms.CharField(required=False, label="Search")
100
+ name = forms.CharField(required=False)
101
+
102
+
97
103
  #
98
104
  # VRFs
99
105
  #
@@ -142,6 +148,12 @@ class VRFBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm):
142
148
  namespace = DynamicModelChoiceField(queryset=Namespace.objects.all(), required=False)
143
149
  tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
144
150
  description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
151
+ add_prefixes = DynamicModelMultipleChoiceField(
152
+ queryset=Prefix.objects.all(), required=False, query_params={"namespace": "$namespace"}
153
+ )
154
+ remove_prefixes = DynamicModelMultipleChoiceField(
155
+ queryset=Prefix.objects.all(), required=False, query_params={"namespace": "$namespace"}
156
+ )
145
157
 
146
158
  class Meta:
147
159
  nullable_fields = [
@@ -358,6 +370,12 @@ class PrefixBulkEditForm(
358
370
  remove_locations = DynamicModelMultipleChoiceField(
359
371
  queryset=Location.objects.all(), required=False, query_params={"content_type": Prefix._meta.label_lower}
360
372
  )
373
+ add_vrfs = DynamicModelMultipleChoiceField(
374
+ queryset=VRF.objects.all(), required=False, query_params={"namespace": "$namespace"}
375
+ )
376
+ remove_vrfs = DynamicModelMultipleChoiceField(
377
+ queryset=VRF.objects.all(), required=False, query_params={"namespace": "$namespace"}
378
+ )
361
379
  tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
362
380
  rir = DynamicModelChoiceField(queryset=RIR.objects.all(), required=False, label="RIR")
363
381
  date_allocated = forms.DateTimeField(required=False, widget=DateTimePicker)
@@ -71,7 +71,8 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
71
71
  @classmethod
72
72
  def setUpTestData(cls):
73
73
  tenants = Tenant.objects.all()[:2]
74
- namespace = Namespace.objects.create(name="ipam_test_views_vrf_test")
74
+ namespace = Prefix.objects.first().namespace
75
+ prefixes = Prefix.objects.filter(namespace=namespace)
75
76
 
76
77
  cls.form_data = {
77
78
  "name": "VRF X",
@@ -79,12 +80,16 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
79
80
  "rd": "65000:999",
80
81
  "tenant": tenants[0].pk,
81
82
  "description": "A new VRF",
83
+ "prefixes": [prefixes[1].id],
82
84
  "tags": [t.pk for t in Tag.objects.get_for_model(VRF)],
83
85
  }
84
86
 
85
87
  cls.bulk_edit_data = {
86
88
  "tenant": tenants[1].pk,
87
89
  "description": "New description",
90
+ "namespace": prefixes[0].namespace.id,
91
+ "add_prefixes": [prefixes[0].id],
92
+ "remove_prefixes": [prefixes[1].id],
88
93
  }
89
94
 
90
95
 
@@ -158,7 +163,6 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase, ViewTestCases.List
158
163
 
159
164
  cls.bulk_edit_data = {
160
165
  "tenant": None,
161
- # TODO "vrf": vrfs[1].pk,
162
166
  "status": cls.statuses[1].pk,
163
167
  "role": cls.roles[1].pk,
164
168
  "rir": RIR.objects.last().pk,
@@ -166,6 +170,9 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase, ViewTestCases.List
166
170
  "description": "New description",
167
171
  "add_locations": [cls.locations[0].pk],
168
172
  "remove_locations": [cls.locations[1].pk],
173
+ "namespace": vrfs[0].namespace.pk,
174
+ "add_vrfs": [vrfs[0].pk],
175
+ "remove_vrfs": [vrfs[1].pk],
169
176
  }
170
177
 
171
178
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
nautobot/ipam/views.py CHANGED
@@ -80,6 +80,7 @@ class NamespaceUIViewSet(
80
80
  form_class = forms.NamespaceForm
81
81
  bulk_update_form_class = forms.NamespaceBulkEditForm
82
82
  filterset_class = filters.NamespaceFilterSet
83
+ filterset_form_class = forms.NamespaceFilterForm
83
84
  queryset = Namespace.objects.all()
84
85
  serializer_class = serializers.NamespaceSerializer
85
86
  table_class = tables.NamespaceTable
@@ -303,6 +304,12 @@ class VRFBulkEditView(generic.BulkEditView):
303
304
  table = tables.VRFTable
304
305
  form = forms.VRFBulkEditForm
305
306
 
307
+ def extra_post_save_action(self, obj, form):
308
+ if form.cleaned_data.get("add_prefixes", None):
309
+ obj.prefixes.add(*form.cleaned_data["add_prefixes"])
310
+ if form.cleaned_data.get("remove_prefixes", None):
311
+ obj.prefixes.remove(*form.cleaned_data["remove_prefixes"])
312
+
306
313
 
307
314
  class VRFBulkDeleteView(generic.BulkDeleteView):
308
315
  queryset = VRF.objects.select_related("tenant")
@@ -332,6 +339,7 @@ class RouteTargetView(generic.ObjectView):
332
339
  return {
333
340
  "importing_vrfs_table": importing_vrfs_table,
334
341
  "exporting_vrfs_table": exporting_vrfs_table,
342
+ **super().get_extra_context(request, instance),
335
343
  }
336
344
 
337
345
 
@@ -389,9 +397,7 @@ class RIRView(generic.ObjectView):
389
397
  }
390
398
  RequestConfig(request, paginate).configure(assigned_prefix_table)
391
399
 
392
- return {
393
- "assigned_prefix_table": assigned_prefix_table,
394
- }
400
+ return {"assigned_prefix_table": assigned_prefix_table, **super().get_extra_context(request, instance)}
395
401
 
396
402
 
397
403
  class RIREditView(generic.ObjectEditView):
@@ -457,6 +463,7 @@ class PrefixView(generic.ObjectView):
457
463
  return {
458
464
  "vrf_table": vrf_table,
459
465
  "parent_prefix_table": parent_prefix_table,
466
+ **super().get_extra_context(request, instance),
460
467
  }
461
468
 
462
469
 
@@ -713,6 +720,10 @@ class PrefixBulkEditView(generic.BulkEditView):
713
720
  obj.locations.add(*form.cleaned_data["add_locations"])
714
721
  if form.cleaned_data.get("remove_locations", None):
715
722
  obj.locations.remove(*form.cleaned_data["remove_locations"])
723
+ if form.cleaned_data.get("add_vrfs", None):
724
+ obj.vrfs.add(*form.cleaned_data["add_vrfs"])
725
+ if form.cleaned_data.get("remove_vrfs", None):
726
+ obj.vrfs.remove(*form.cleaned_data["remove_vrfs"])
716
727
 
717
728
 
718
729
  class PrefixBulkDeleteView(generic.BulkDeleteView):
@@ -780,6 +791,7 @@ class IPAddressView(generic.ObjectView):
780
791
  return {
781
792
  "parent_prefixes_table": parent_prefixes_table,
782
793
  "related_ips_table": related_ips_table,
794
+ **super().get_extra_context(request, instance),
783
795
  }
784
796
 
785
797
 
@@ -1261,6 +1273,7 @@ class VLANGroupView(generic.ObjectView):
1261
1273
  "vlan_table": vlan_table,
1262
1274
  "permissions": permissions,
1263
1275
  "vlans_count": vlans_count,
1276
+ **super().get_extra_context(request, instance),
1264
1277
  }
1265
1278
 
1266
1279
 
@@ -1317,9 +1330,7 @@ class VLANView(generic.ObjectView):
1317
1330
  prefix_table = tables.PrefixTable(list(prefixes))
1318
1331
  prefix_table.exclude = ("vlan",)
1319
1332
 
1320
- return {
1321
- "prefix_table": prefix_table,
1322
- }
1333
+ return {"prefix_table": prefix_table, **super().get_extra_context(request, instance)}
1323
1334
 
1324
1335
 
1325
1336
  class VLANInterfacesView(generic.ObjectView):