nautobot 2.2.4__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 (303) 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.yaml +3 -3
  6. nautobot/core/testing/filters.py +24 -1
  7. nautobot/core/testing/views.py +13 -1
  8. nautobot/core/views/utils.py +18 -1
  9. nautobot/dcim/filters/__init__.py +1 -1
  10. nautobot/dcim/forms.py +23 -4
  11. nautobot/dcim/tables/devicetypes.py +15 -4
  12. nautobot/dcim/tests/test_views.py +84 -0
  13. nautobot/dcim/views.py +3 -0
  14. nautobot/extras/api/views.py +2 -2
  15. nautobot/extras/context_managers.py +3 -0
  16. nautobot/extras/filters/__init__.py +15 -1
  17. nautobot/extras/forms/forms.py +33 -0
  18. nautobot/extras/forms/mixins.py +0 -6
  19. nautobot/extras/signals.py +6 -1
  20. nautobot/extras/tests/test_api.py +24 -2
  21. nautobot/extras/tests/test_context_managers.py +33 -1
  22. nautobot/extras/tests/test_filters.py +69 -0
  23. nautobot/extras/tests/test_forms.py +0 -3
  24. nautobot/extras/tests/test_views.py +48 -4
  25. nautobot/extras/views.py +12 -10
  26. nautobot/ipam/forms.py +18 -0
  27. nautobot/ipam/tests/test_views.py +9 -2
  28. nautobot/ipam/views.py +11 -0
  29. nautobot/project-static/docs/404.html +3 -3
  30. nautobot/project-static/docs/apps/index.html +3 -3
  31. nautobot/project-static/docs/apps/nautobot-apps.html +3 -3
  32. nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js → bundle.ebd0bdb7.min.js} +5 -5
  33. nautobot/project-static/docs/assets/javascripts/{bundle.3220b9d7.min.js.map → bundle.ebd0bdb7.min.js.map} +3 -3
  34. nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css → main.6543a935.min.css} +1 -1
  35. nautobot/project-static/docs/assets/stylesheets/{main.66ac8b77.min.css.map → main.6543a935.min.css.map} +1 -1
  36. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +3 -3
  37. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +3 -3
  38. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3 -3
  39. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +3 -3
  40. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +3 -3
  41. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +3 -3
  42. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +3 -3
  43. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +3 -3
  44. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +3 -3
  45. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +3 -3
  46. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +3 -3
  47. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +3 -3
  48. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +3 -3
  49. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +3 -3
  50. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +3 -3
  51. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +3 -3
  52. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +3 -3
  53. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +3 -3
  54. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +3 -3
  55. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +3 -3
  56. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +3 -3
  57. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +3 -3
  58. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +3 -3
  59. nautobot/project-static/docs/development/apps/api/configuration-view.html +3 -3
  60. nautobot/project-static/docs/development/apps/api/database-backend-config.html +3 -3
  61. nautobot/project-static/docs/development/apps/api/models/django-admin.html +3 -3
  62. nautobot/project-static/docs/development/apps/api/models/global-search.html +3 -3
  63. nautobot/project-static/docs/development/apps/api/models/graphql.html +3 -3
  64. nautobot/project-static/docs/development/apps/api/models/index.html +3 -3
  65. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +3 -3
  66. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +3 -3
  67. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +4 -4
  68. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +3 -3
  69. nautobot/project-static/docs/development/apps/api/platform-features/index.html +3 -3
  70. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +3 -3
  71. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +3 -3
  72. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +3 -3
  73. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +3 -3
  74. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +3 -3
  75. nautobot/project-static/docs/development/apps/api/prometheus.html +3 -3
  76. nautobot/project-static/docs/development/apps/api/setup.html +3 -3
  77. nautobot/project-static/docs/development/apps/api/testing.html +3 -3
  78. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +3 -3
  79. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +3 -3
  80. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +3 -3
  81. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +3 -3
  82. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +3 -3
  83. nautobot/project-static/docs/development/apps/api/views/base-template.html +3 -3
  84. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +3 -3
  85. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +3 -3
  86. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +3 -3
  87. nautobot/project-static/docs/development/apps/api/views/index.html +3 -3
  88. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +3 -3
  89. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +3 -3
  90. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +3 -3
  91. nautobot/project-static/docs/development/apps/api/views/notes.html +3 -3
  92. nautobot/project-static/docs/development/apps/api/views/rest-api.html +3 -3
  93. nautobot/project-static/docs/development/apps/api/views/urls.html +3 -3
  94. nautobot/project-static/docs/development/apps/index.html +3 -3
  95. nautobot/project-static/docs/development/apps/migration/code-updates.html +3 -3
  96. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +3 -3
  97. nautobot/project-static/docs/development/apps/migration/from-v1.html +5 -5
  98. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +3 -3
  99. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +3 -3
  100. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +3 -3
  101. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +3 -3
  102. nautobot/project-static/docs/development/apps/porting-from-netbox.html +3 -3
  103. nautobot/project-static/docs/development/core/application-registry.html +4 -4
  104. nautobot/project-static/docs/development/core/best-practices.html +3 -3
  105. nautobot/project-static/docs/development/core/bootstrap-ui.html +3 -3
  106. nautobot/project-static/docs/development/core/caching.html +3 -3
  107. nautobot/project-static/docs/development/core/controllers.html +3 -3
  108. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +3 -3
  109. nautobot/project-static/docs/development/core/generic-views.html +3 -3
  110. nautobot/project-static/docs/development/core/getting-started.html +5 -5
  111. nautobot/project-static/docs/development/core/homepage.html +3 -3
  112. nautobot/project-static/docs/development/core/index.html +3 -3
  113. nautobot/project-static/docs/development/core/model-checklist.html +3 -3
  114. nautobot/project-static/docs/development/core/model-features.html +3 -3
  115. nautobot/project-static/docs/development/core/natural-keys.html +3 -3
  116. nautobot/project-static/docs/development/core/navigation-menu.html +3 -3
  117. nautobot/project-static/docs/development/core/release-checklist.html +3 -3
  118. nautobot/project-static/docs/development/core/role-internals.html +3 -3
  119. nautobot/project-static/docs/development/core/settings.html +3 -3
  120. nautobot/project-static/docs/development/core/style-guide.html +3 -3
  121. nautobot/project-static/docs/development/core/templates.html +3 -3
  122. nautobot/project-static/docs/development/core/testing.html +4 -4
  123. nautobot/project-static/docs/development/core/user-preferences.html +3 -3
  124. nautobot/project-static/docs/development/index.html +3 -3
  125. nautobot/project-static/docs/development/jobs/index.html +3 -3
  126. nautobot/project-static/docs/development/jobs/migration/from-v1.html +3 -3
  127. nautobot/project-static/docs/index.html +3 -3
  128. nautobot/project-static/docs/release-notes/index.html +3 -3
  129. nautobot/project-static/docs/release-notes/version-1.0.html +4 -4
  130. nautobot/project-static/docs/release-notes/version-1.1.html +3 -3
  131. nautobot/project-static/docs/release-notes/version-1.2.html +5 -5
  132. nautobot/project-static/docs/release-notes/version-1.3.html +4 -4
  133. nautobot/project-static/docs/release-notes/version-1.4.html +5 -5
  134. nautobot/project-static/docs/release-notes/version-1.5.html +5 -5
  135. nautobot/project-static/docs/release-notes/version-1.6.html +543 -163
  136. nautobot/project-static/docs/release-notes/version-2.0.html +6 -6
  137. nautobot/project-static/docs/release-notes/version-2.1.html +3 -3
  138. nautobot/project-static/docs/release-notes/version-2.2.html +263 -84
  139. nautobot/project-static/docs/requirements.txt +2 -2
  140. nautobot/project-static/docs/search/search_index.json +1 -1
  141. nautobot/project-static/docs/sitemap.xml +254 -254
  142. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  143. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +3 -3
  144. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +3 -3
  145. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +5 -5
  146. nautobot/project-static/docs/user-guide/administration/configuration/index.html +4 -4
  147. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +6 -6
  148. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +3 -3
  149. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +3 -3
  150. nautobot/project-static/docs/user-guide/administration/guides/caching.html +3 -3
  151. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +3 -3
  152. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +3 -3
  153. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +3 -3
  154. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +3 -3
  155. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +4 -4
  156. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +3 -3
  157. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +3 -3
  158. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +3 -3
  159. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +3 -3
  160. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +3 -3
  161. nautobot/project-static/docs/user-guide/administration/installation/index.html +3 -3
  162. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +3 -3
  163. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +7 -7
  164. nautobot/project-static/docs/user-guide/administration/installation/services.html +4 -4
  165. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +3 -3
  166. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +3 -3
  167. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +3 -3
  168. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +3 -3
  169. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +3 -3
  170. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +3 -3
  171. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +3 -3
  172. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +3 -3
  173. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +3 -3
  174. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +3 -3
  175. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +4 -4
  176. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +3 -3
  177. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +3 -3
  178. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +3 -22
  179. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +5 -5
  180. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +3 -3
  181. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +3 -3
  182. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +3 -3
  183. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +3 -3
  184. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +3 -3
  185. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +3 -3
  186. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +3 -3
  187. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +3 -3
  188. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +3 -3
  189. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +3 -3
  190. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +3 -3
  191. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +3 -3
  192. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +3 -3
  193. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +5 -5
  194. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +3 -3
  195. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +3 -3
  196. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +3 -3
  197. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +3 -3
  198. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +4 -4
  199. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +3 -3
  200. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +3 -3
  201. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +3 -3
  202. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +3 -3
  203. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +3 -3
  204. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +3 -3
  205. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +3 -3
  206. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +3 -3
  207. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +3 -3
  208. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +3 -3
  209. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +3 -3
  210. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +3 -3
  211. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +3 -3
  212. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +3 -3
  213. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +3 -3
  214. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +3 -3
  215. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +3 -3
  216. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +3 -3
  217. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +3 -3
  218. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +3 -3
  219. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +3 -3
  220. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +3 -3
  221. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +3 -3
  222. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +3 -3
  223. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +3 -3
  224. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +3 -3
  225. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +3 -3
  226. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +3 -3
  227. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +3 -3
  228. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +3 -3
  229. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +3 -3
  230. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +3 -3
  231. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +3 -3
  232. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +3 -3
  233. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +3 -3
  234. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +3 -3
  235. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +3 -3
  236. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +3 -3
  237. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +3 -3
  238. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +3 -3
  239. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +3 -3
  240. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +3 -3
  241. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +3 -3
  242. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +3 -3
  243. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +3 -3
  244. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +3 -3
  245. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +3 -3
  246. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +6 -6
  247. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +3 -3
  248. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +3 -3
  249. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +4 -4
  250. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +3 -3
  251. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +3 -3
  252. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +3 -3
  253. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +3 -3
  254. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +4 -4
  255. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +3 -3
  256. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +3 -3
  257. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +3 -3
  258. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +3 -3
  259. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +3 -3
  260. nautobot/project-static/docs/user-guide/index.html +3 -3
  261. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +3 -3
  262. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +3 -3
  263. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +3 -3
  264. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +3 -3
  265. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +3 -3
  266. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +3 -3
  267. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +3 -3
  268. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +3 -3
  269. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +3 -3
  270. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +3 -3
  271. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +3 -3
  272. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +3 -3
  273. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +4 -4
  274. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +3 -3
  275. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +3 -3
  276. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +5 -5
  277. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +5 -5
  278. nautobot/project-static/docs/user-guide/platform-functionality/note.html +3 -3
  279. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +3 -3
  280. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +3 -3
  281. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +3 -3
  282. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +3 -3
  283. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +3 -3
  284. nautobot/project-static/docs/user-guide/platform-functionality/role.html +4 -36
  285. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +3 -3
  286. nautobot/project-static/docs/user-guide/platform-functionality/status.html +3 -3
  287. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +3 -3
  288. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +3 -3
  289. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +3 -3
  290. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +3 -3
  291. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +3 -3
  292. nautobot/project-static/js/forms.js +2 -1
  293. nautobot/tenancy/forms.py +9 -0
  294. nautobot/tenancy/views.py +1 -0
  295. nautobot/virtualization/forms.py +18 -6
  296. nautobot/virtualization/templates/virtualization/clustertype.html +2 -2
  297. nautobot/virtualization/views.py +2 -0
  298. {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/METADATA +1 -1
  299. {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/RECORD +303 -303
  300. {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/LICENSE.txt +0 -0
  301. {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/NOTICE +0 -0
  302. {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/WHEEL +0 -0
  303. {nautobot-2.2.4.dist-info → nautobot-2.2.5.dist-info}/entry_points.txt +0 -0
@@ -17,6 +17,7 @@ from nautobot.core.models.fields import slugify_dashes_to_underscores
17
17
  from nautobot.core.testing import APITestCase, APIViewTestCases
18
18
  from nautobot.core.testing.utils import disable_warnings
19
19
  from nautobot.core.utils.lookup import get_route_for_model
20
+ from nautobot.core.utils.permissions import get_permission_for_model
20
21
  from nautobot.dcim.models import (
21
22
  Controller,
22
23
  Device,
@@ -768,7 +769,7 @@ class DynamicGroupTestMixin:
768
769
 
769
770
  # Then the DynamicGroups.
770
771
  cls.content_type = ContentType.objects.get_for_model(Device)
771
- cls.groups = cls.groups = [
772
+ cls.groups = [
772
773
  DynamicGroup.objects.create(
773
774
  name="API DynamicGroup 1",
774
775
  content_type=cls.content_type,
@@ -811,13 +812,34 @@ class DynamicGroupTest(DynamicGroupTestMixin, APIViewTestCases.APIViewTestCase):
811
812
  def test_get_members(self):
812
813
  """Test that the `/members/` API endpoint returns what is expected."""
813
814
  self.add_permissions("extras.view_dynamicgroup")
814
- instance = DynamicGroup.objects.first()
815
+ instance = self.groups[0]
816
+ self.add_permissions(get_permission_for_model(instance.content_type.model_class(), "view"))
815
817
  member_count = instance.members.count()
816
818
  url = reverse("extras-api:dynamicgroup-members", kwargs={"pk": instance.pk})
817
819
  response = self.client.get(url, **self.header)
818
820
  self.assertHttpStatus(response, status.HTTP_200_OK)
819
821
  self.assertEqual(member_count, len(response.json()["results"]))
820
822
 
823
+ def test_get_members_with_constrained_permission(self):
824
+ """Test that the `/members/` API endpoint enforces permissions on the member model."""
825
+ self.add_permissions("extras.view_dynamicgroup")
826
+ instance = self.groups[0]
827
+ obj1 = instance.members.first()
828
+ obj_perm = ObjectPermission(
829
+ name="Test permission",
830
+ constraints={"pk__in": [obj1.pk]},
831
+ actions=["view"],
832
+ )
833
+ obj_perm.save()
834
+ obj_perm.users.add(self.user)
835
+ obj_perm.object_types.add(instance.content_type)
836
+
837
+ url = reverse("extras-api:dynamicgroup-members", kwargs={"pk": instance.pk})
838
+ response = self.client.get(url, **self.header)
839
+ self.assertHttpStatus(response, status.HTTP_200_OK)
840
+ self.assertEqual(len(response.json()["results"]), 1)
841
+ self.assertEqual(response.json()["results"][0]["id"], str(obj1.pk))
842
+
821
843
 
822
844
  class DynamicGroupMembershipTest(DynamicGroupTestMixin, APIViewTestCases.APIViewTestCase):
823
845
  model = DynamicGroupMembership
@@ -5,7 +5,16 @@ from django.test import TestCase
5
5
  from nautobot.core.celery import app
6
6
  from nautobot.core.testing import TransactionTestCase
7
7
  from nautobot.core.utils.lookup import get_changes_for_model
8
- from nautobot.dcim.models import Location, LocationType
8
+ from nautobot.dcim.models import (
9
+ DeviceType,
10
+ DeviceTypeToSoftwareImageFile,
11
+ Location,
12
+ LocationType,
13
+ Manufacturer,
14
+ Platform,
15
+ SoftwareImageFile,
16
+ SoftwareVersion,
17
+ )
9
18
  from nautobot.extras.choices import ObjectChangeActionChoices, ObjectChangeEventContextChoices
10
19
  from nautobot.extras.context_managers import (
11
20
  deferred_change_logging_for_bulk_operation,
@@ -294,6 +303,29 @@ class BulkEditDeleteChangeLogging(TestCase):
294
303
  self.assertIsNone(snapshots["differences"]["removed"])
295
304
  self.assertEqual(snapshots["differences"]["added"]["description"], "changed")
296
305
 
306
+ def test_bulk_edit_device_type_software_image_file(self):
307
+ """Test that bulk edits to null does not cause integrity error"""
308
+ manufacturer = Manufacturer.objects.create(name="Test")
309
+ platform = Platform.objects.create(name="Test")
310
+ software_status = Status.objects.get_for_model(SoftwareVersion).first()
311
+ software_version = SoftwareVersion.objects.create(version="1.0.0", platform=platform, status=software_status)
312
+ software_image_file = SoftwareImageFile.objects.create(
313
+ image_file_name="test.iso", software_version=software_version, status=software_status
314
+ )
315
+ device_type = DeviceType.objects.create(manufacturer=manufacturer, model="test123")
316
+ device_type.software_image_files.set([software_image_file])
317
+ with web_request_context(self.user):
318
+ with deferred_change_logging_for_bulk_operation():
319
+ device_type.software_image_files.set([])
320
+ device_type.save()
321
+
322
+ oc_list = get_changes_for_model(DeviceTypeToSoftwareImageFile)
323
+ self.assertEqual(len(oc_list), 1)
324
+ self.assertEqual(oc_list[0].action, ObjectChangeActionChoices.ACTION_DELETE)
325
+ self.assertIsNotNone(oc_list[0].changed_object_id)
326
+ self.assertEqual(oc_list[0].user, self.user)
327
+ self.assertEqual(oc_list[0].user_name, self.user.username)
328
+
297
329
  def test_change_log_context(self):
298
330
  location_type = LocationType.objects.get(name="Campus")
299
331
  location_status = Status.objects.get_for_model(Location).first()
@@ -30,6 +30,7 @@ from nautobot.extras.constants import HTTP_CONTENT_TYPE_JSON
30
30
  from nautobot.extras.filters import (
31
31
  ComputedFieldFilterSet,
32
32
  ConfigContextFilterSet,
33
+ ContactAssociationFilterSet,
33
34
  ContactFilterSet,
34
35
  ContentTypeFilterSet,
35
36
  CustomFieldChoiceFilterSet,
@@ -555,6 +556,74 @@ class ContactFilterSetTestCase(ContactAndTeamFilterSetTestCaseMixin, FilterTestC
555
556
  )
556
557
 
557
558
 
559
+ class ContactAssociationFilterSetTestCase(FilterTestCases.FilterTestCase):
560
+ queryset = ContactAssociation.objects.all()
561
+ filterset = ContactAssociationFilterSet
562
+
563
+ generic_filter_tests = (
564
+ ["status", "status__id"],
565
+ ["status", "status__name"],
566
+ ["contact", "contact__id"],
567
+ ["contact", "contact__name"],
568
+ ["team", "team__id"],
569
+ ["team", "team__name"],
570
+ ["role", "role__id"],
571
+ ["role", "role__name"],
572
+ )
573
+
574
+ @classmethod
575
+ def setUpTestData(cls):
576
+ roles = Role.objects.get_for_model(ContactAssociation)
577
+ statuses = Status.objects.get_for_model(ContactAssociation)
578
+ ip_addresses = IPAddress.objects.all()
579
+ locations = Location.objects.all()
580
+
581
+ cls.location_ct = ContentType.objects.get_for_model(Location)
582
+ ipaddress_ct = ContentType.objects.get_for_model(IPAddress)
583
+
584
+ ContactAssociation.objects.create(
585
+ contact=Contact.objects.first(),
586
+ associated_object_type=ipaddress_ct,
587
+ associated_object_id=ip_addresses[0].pk,
588
+ role=roles[2],
589
+ status=statuses[1],
590
+ )
591
+ ContactAssociation.objects.create(
592
+ contact=Contact.objects.last(),
593
+ associated_object_type=ipaddress_ct,
594
+ associated_object_id=ip_addresses[1].pk,
595
+ role=roles[1],
596
+ status=statuses[2],
597
+ )
598
+ ContactAssociation.objects.create(
599
+ team=Team.objects.first(),
600
+ associated_object_type=cls.location_ct,
601
+ associated_object_id=locations[0].pk,
602
+ role=roles[3],
603
+ status=statuses[0],
604
+ )
605
+ ContactAssociation.objects.create(
606
+ team=Team.objects.last(),
607
+ associated_object_type=cls.location_ct,
608
+ associated_object_id=locations[1].pk,
609
+ role=roles[0],
610
+ status=statuses[1],
611
+ )
612
+
613
+ def test_associated_object_type(self):
614
+ params = {"associated_object_type": "dcim.location"}
615
+ self.assertEqual(
616
+ self.filterset(params, self.queryset).qs.count(),
617
+ ContactAssociation.objects.filter(associated_object_type=self.location_ct).count(),
618
+ )
619
+
620
+ params = {"associated_object_type": self.location_ct.pk}
621
+ self.assertEqual(
622
+ self.filterset(params, self.queryset).qs.count(),
623
+ ContactAssociation.objects.filter(associated_object_type=self.location_ct).count(),
624
+ )
625
+
626
+
558
627
  class CustomFieldChoiceFilterSetTestCase(FilterTestCases.FilterTestCase):
559
628
  queryset = CustomFieldChoice.objects.all()
560
629
  filterset = CustomFieldChoiceFilterSet
@@ -14,7 +14,6 @@ from nautobot.extras.forms import (
14
14
  ConfigContextFilterForm,
15
15
  ConfigContextForm,
16
16
  CustomFieldModelBulkEditFormMixin,
17
- CustomFieldModelFilterFormMixin,
18
17
  CustomFieldModelFormMixin,
19
18
  JobButtonForm,
20
19
  JobEditForm,
@@ -1089,7 +1088,6 @@ class DeprecatedAliasesTestCase(TestCase):
1089
1088
  AddRemoveTagsForm,
1090
1089
  CustomFieldBulkCreateForm,
1091
1090
  CustomFieldBulkEditForm,
1092
- CustomFieldFilterForm,
1093
1091
  CustomFieldModelForm,
1094
1092
  RelationshipModelForm,
1095
1093
  StatusBulkEditFormMixin,
@@ -1100,7 +1098,6 @@ class DeprecatedAliasesTestCase(TestCase):
1100
1098
  (AddRemoveTagsForm, TagsBulkEditFormMixin),
1101
1099
  (CustomFieldBulkEditForm, CustomFieldModelBulkEditFormMixin),
1102
1100
  (CustomFieldBulkCreateForm, CustomFieldModelBulkEditFormMixin),
1103
- (CustomFieldFilterForm, CustomFieldModelFilterFormMixin),
1104
1101
  (CustomFieldModelForm, CustomFieldModelFormMixin),
1105
1102
  (RelationshipModelForm, RelationshipModelFormMixin),
1106
1103
  (StatusBulkEditFormMixin, StatusModelBulkEditFormMixin),
@@ -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/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
 
@@ -398,7 +395,7 @@ class ContactAssociationUIViewSet(
398
395
  filterset_class = filters.ContactAssociationFilterSet
399
396
  queryset = ContactAssociation.objects.all()
400
397
  serializer_class = serializers.ContactAssociationSerializer
401
- table_class = AssociatedContactsTable
398
+ table_class = tables.AssociatedContactsTable
402
399
  non_filter_params = ("export", "page", "per_page", "sort")
403
400
 
404
401
 
@@ -506,6 +503,7 @@ class CustomFieldListView(generic.ObjectListView):
506
503
  queryset = CustomField.objects.all()
507
504
  table = tables.CustomFieldTable
508
505
  filterset = filters.CustomFieldFilterSet
506
+ filterset_form = forms.CustomFieldFilterForm
509
507
  action_buttons = ("add",)
510
508
 
511
509
 
@@ -706,7 +704,7 @@ class DynamicGroupView(generic.ObjectView):
706
704
 
707
705
  if table_class is not None:
708
706
  # Members table (for display on Members nav tab)
709
- members_table = table_class(instance.members, orderable=False)
707
+ members_table = table_class(instance.members.restrict(request.user, "view"), orderable=False)
710
708
  paginate = {
711
709
  "paginator_class": EnhancedPaginator,
712
710
  "per_page": get_paginate_count(request),
@@ -886,7 +884,9 @@ class ObjectDynamicGroupsView(generic.GenericView):
886
884
  obj = get_object_or_404(model, **kwargs)
887
885
 
888
886
  # Gather all dynamic groups for this object (and its related objects)
889
- 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
+ )
890
890
 
891
891
  # Apply the request context
892
892
  paginate = {
@@ -950,6 +950,7 @@ class ExportTemplateBulkDeleteView(generic.BulkDeleteView):
950
950
  class ExternalIntegrationUIViewSet(NautobotUIViewSet):
951
951
  bulk_update_form_class = forms.ExternalIntegrationBulkEditForm
952
952
  filterset_class = filters.ExternalIntegrationFilterSet
953
+ filterset_form_class = forms.ExternalIntegrationFilterForm
953
954
  form_class = forms.ExternalIntegrationForm
954
955
  queryset = ExternalIntegration.objects.select_related("secrets_group")
955
956
  serializer_class = serializers.ExternalIntegrationSerializer
@@ -2048,11 +2049,12 @@ class RoleUIViewSet(viewsets.NautobotUIViewSet):
2048
2049
  """`Roles` UIViewSet."""
2049
2050
 
2050
2051
  queryset = Role.objects.all()
2051
- bulk_update_form_class = RoleBulkEditForm
2052
- filterset_class = RoleFilterSet
2053
- 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
2054
2056
  serializer_class = serializers.RoleSerializer
2055
- table_class = RoleTable
2057
+ table_class = tables.RoleTable
2056
2058
 
2057
2059
  def get_extra_context(self, request, instance):
2058
2060
  context = super().get_extra_context(request, instance)
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")
@@ -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):
@@ -12,7 +12,7 @@
12
12
 
13
13
 
14
14
  <link rel="icon" href="/projects/core/en/stable/assets/favicon.ico">
15
- <meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.18">
15
+ <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.23">
16
16
 
17
17
 
18
18
 
@@ -20,7 +20,7 @@
20
20
 
21
21
 
22
22
 
23
- <link rel="stylesheet" href="/projects/core/en/stable/assets/stylesheets/main.66ac8b77.min.css">
23
+ <link rel="stylesheet" href="/projects/core/en/stable/assets/stylesheets/main.6543a935.min.css">
24
24
 
25
25
 
26
26
  <link rel="stylesheet" href="/projects/core/en/stable/assets/stylesheets/palette.06af60db.min.css">
@@ -7838,7 +7838,7 @@
7838
7838
  <script id="__config" type="application/json">{"base": "/projects/core/en/stable/", "features": ["content.code.copy", "content.tabs.link", "navigation.footer", "navigation.tabs", "navigation.tabs.sticky", "navigation.tracking", "search.highlight", "search.share", "search.suggest"], "search": "/projects/core/en/stable/assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
7839
7839
 
7840
7840
 
7841
- <script src="/projects/core/en/stable/assets/javascripts/bundle.3220b9d7.min.js"></script>
7841
+ <script src="/projects/core/en/stable/assets/javascripts/bundle.ebd0bdb7.min.js"></script>
7842
7842
 
7843
7843
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
7844
7844
 
@@ -18,7 +18,7 @@
18
18
 
19
19
 
20
20
  <link rel="icon" href="../assets/favicon.ico">
21
- <meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.18">
21
+ <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.23">
22
22
 
23
23
 
24
24
 
@@ -26,7 +26,7 @@
26
26
 
27
27
 
28
28
 
29
- <link rel="stylesheet" href="../assets/stylesheets/main.66ac8b77.min.css">
29
+ <link rel="stylesheet" href="../assets/stylesheets/main.6543a935.min.css">
30
30
 
31
31
 
32
32
  <link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
@@ -7996,7 +7996,7 @@
7996
7996
  <script id="__config" type="application/json">{"base": "..", "features": ["content.code.copy", "content.tabs.link", "navigation.footer", "navigation.tabs", "navigation.tabs.sticky", "navigation.tracking", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
7997
7997
 
7998
7998
 
7999
- <script src="../assets/javascripts/bundle.3220b9d7.min.js"></script>
7999
+ <script src="../assets/javascripts/bundle.ebd0bdb7.min.js"></script>
8000
8000
 
8001
8001
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
8002
8002
 
@@ -16,7 +16,7 @@
16
16
 
17
17
 
18
18
  <link rel="icon" href="../assets/favicon.ico">
19
- <meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.18">
19
+ <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.23">
20
20
 
21
21
 
22
22
 
@@ -24,7 +24,7 @@
24
24
 
25
25
 
26
26
 
27
- <link rel="stylesheet" href="../assets/stylesheets/main.66ac8b77.min.css">
27
+ <link rel="stylesheet" href="../assets/stylesheets/main.6543a935.min.css">
28
28
 
29
29
 
30
30
  <link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
@@ -7930,7 +7930,7 @@
7930
7930
  <script id="__config" type="application/json">{"base": "..", "features": ["content.code.copy", "content.tabs.link", "navigation.footer", "navigation.tabs", "navigation.tabs.sticky", "navigation.tracking", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
7931
7931
 
7932
7932
 
7933
- <script src="../assets/javascripts/bundle.3220b9d7.min.js"></script>
7933
+ <script src="../assets/javascripts/bundle.ebd0bdb7.min.js"></script>
7934
7934
 
7935
7935
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
7936
7936