nautobot 2.4.0__py3-none-any.whl → 2.4.0b1__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 (592) hide show
  1. nautobot/apps/__init__.py +1 -1
  2. nautobot/apps/api.py +8 -8
  3. nautobot/apps/change_logging.py +2 -2
  4. nautobot/apps/choices.py +4 -4
  5. nautobot/apps/events.py +3 -3
  6. nautobot/apps/factory.py +2 -2
  7. nautobot/apps/filters.py +1 -1
  8. nautobot/apps/forms.py +20 -20
  9. nautobot/apps/graphql.py +2 -2
  10. nautobot/apps/jobs.py +8 -8
  11. nautobot/apps/models.py +19 -19
  12. nautobot/apps/tables.py +1 -1
  13. nautobot/apps/testing.py +10 -10
  14. nautobot/apps/ui.py +2 -2
  15. nautobot/apps/utils.py +7 -7
  16. nautobot/apps/views.py +7 -7
  17. nautobot/circuits/api/serializers.py +0 -1
  18. nautobot/circuits/api/views.py +8 -4
  19. nautobot/circuits/tables.py +1 -2
  20. nautobot/circuits/templates/circuits/circuit_create.html +7 -1
  21. nautobot/circuits/views.py +3 -3
  22. nautobot/cloud/api/views.py +10 -6
  23. nautobot/cloud/models.py +1 -1
  24. nautobot/cloud/views.py +16 -0
  25. nautobot/core/api/fields.py +5 -5
  26. nautobot/core/api/filter_backends.py +9 -3
  27. nautobot/core/api/schema.py +2 -13
  28. nautobot/core/api/serializers.py +34 -40
  29. nautobot/core/api/views.py +4 -56
  30. nautobot/core/celery/log.py +4 -4
  31. nautobot/core/celery/schedulers.py +1 -1
  32. nautobot/core/choices.py +2 -2
  33. nautobot/core/events/__init__.py +3 -3
  34. nautobot/core/filters.py +16 -21
  35. nautobot/core/fixtures/user-data.json +59 -0
  36. nautobot/core/forms/__init__.py +19 -19
  37. nautobot/core/forms/fields.py +11 -14
  38. nautobot/core/forms/forms.py +2 -33
  39. nautobot/core/graphql/types.py +1 -1
  40. nautobot/core/jobs/__init__.py +7 -28
  41. nautobot/core/jobs/cleanup.py +12 -48
  42. nautobot/core/jobs/groups.py +1 -1
  43. nautobot/core/management/commands/validate_models.py +1 -1
  44. nautobot/core/models/__init__.py +1 -1
  45. nautobot/core/models/query_functions.py +2 -2
  46. nautobot/core/models/tree_queries.py +3 -6
  47. nautobot/core/settings.py +2 -29
  48. nautobot/core/settings.yaml +0 -21
  49. nautobot/core/tables.py +60 -74
  50. nautobot/core/templates/inc/media.html +0 -3
  51. nautobot/core/templates/inc/nav_menu.html +0 -1
  52. nautobot/core/templates/nautobot_config.py.j2 +0 -13
  53. nautobot/core/templates/search.html +0 -7
  54. nautobot/core/templates/utilities/render_jinja2.html +1 -1
  55. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  56. nautobot/core/templates/utilities/theme_preview.html +0 -7
  57. nautobot/core/templatetags/helpers.py +2 -11
  58. nautobot/core/testing/__init__.py +8 -8
  59. nautobot/core/testing/api.py +15 -170
  60. nautobot/core/testing/filters.py +2 -25
  61. nautobot/core/testing/forms.py +0 -2
  62. nautobot/core/testing/mixins.py +2 -7
  63. nautobot/core/testing/views.py +29 -44
  64. nautobot/core/tests/integration/test_app_home.py +1 -0
  65. nautobot/core/tests/integration/test_app_navbar.py +1 -0
  66. nautobot/core/tests/integration/test_filters.py +2 -0
  67. nautobot/core/tests/integration/test_home.py +1 -0
  68. nautobot/core/tests/integration/test_navbar.py +1 -0
  69. nautobot/core/tests/integration/test_view_authentication.py +0 -1
  70. nautobot/core/tests/runner.py +1 -1
  71. nautobot/core/tests/test_api.py +1 -98
  72. nautobot/core/tests/test_csv.py +3 -25
  73. nautobot/core/tests/test_forms.py +0 -1
  74. nautobot/core/tests/test_jobs.py +1 -303
  75. nautobot/core/tests/test_settings_schema.py +0 -7
  76. nautobot/core/tests/test_tables.py +0 -100
  77. nautobot/core/tests/test_utils.py +1 -63
  78. nautobot/core/tests/test_views.py +3 -30
  79. nautobot/core/ui/nav.py +0 -1
  80. nautobot/core/ui/object_detail.py +1 -15
  81. nautobot/core/urls.py +0 -11
  82. nautobot/core/utils/lookup.py +8 -11
  83. nautobot/core/utils/requests.py +9 -24
  84. nautobot/core/views/__init__.py +0 -42
  85. nautobot/core/views/generic.py +176 -78
  86. nautobot/core/views/mixins.py +34 -94
  87. nautobot/core/views/renderers.py +6 -6
  88. nautobot/dcim/api/serializers.py +62 -54
  89. nautobot/dcim/api/views.py +113 -47
  90. nautobot/dcim/filters/__init__.py +2 -31
  91. nautobot/dcim/forms.py +17 -36
  92. nautobot/dcim/graphql/types.py +2 -2
  93. nautobot/dcim/models/__init__.py +1 -1
  94. nautobot/dcim/models/device_component_templates.py +2 -2
  95. nautobot/dcim/models/device_components.py +20 -22
  96. nautobot/dcim/models/devices.py +1 -10
  97. nautobot/dcim/models/locations.py +3 -3
  98. nautobot/dcim/models/power.py +5 -6
  99. nautobot/dcim/models/racks.py +4 -4
  100. nautobot/dcim/tables/__init__.py +3 -3
  101. nautobot/dcim/tables/devices.py +5 -7
  102. nautobot/dcim/tables/devicetypes.py +2 -2
  103. nautobot/dcim/tables/racks.py +1 -1
  104. nautobot/dcim/templates/dcim/controller_create.html +7 -1
  105. nautobot/dcim/templates/dcim/controller_retrieve.html +9 -1
  106. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +0 -2
  107. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -5
  108. nautobot/dcim/templates/dcim/device.html +9 -1
  109. nautobot/dcim/templates/dcim/device_edit.html +37 -36
  110. nautobot/dcim/templates/dcim/location.html +9 -1
  111. nautobot/dcim/templates/dcim/location_edit.html +7 -1
  112. nautobot/dcim/templates/dcim/rack.html +9 -1
  113. nautobot/dcim/templates/dcim/rack_edit.html +7 -1
  114. nautobot/dcim/templates/dcim/rackreservation.html +9 -1
  115. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +9 -1
  116. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +7 -1
  117. nautobot/dcim/tests/test_api.py +3 -16
  118. nautobot/dcim/tests/test_filters.py +0 -33
  119. nautobot/dcim/tests/test_forms.py +2 -51
  120. nautobot/dcim/tests/test_graphql.py +0 -52
  121. nautobot/dcim/tests/test_models.py +5 -34
  122. nautobot/dcim/tests/test_views.py +83 -21
  123. nautobot/dcim/views.py +13 -1
  124. nautobot/extras/api/customfields.py +2 -2
  125. nautobot/extras/api/serializers.py +85 -90
  126. nautobot/extras/api/views.py +27 -22
  127. nautobot/extras/constants.py +0 -2
  128. nautobot/extras/filters/__init__.py +6 -8
  129. nautobot/extras/forms/base.py +2 -2
  130. nautobot/extras/forms/forms.py +31 -139
  131. nautobot/extras/forms/mixins.py +5 -13
  132. nautobot/extras/group_sync.py +3 -3
  133. nautobot/extras/health_checks.py +2 -1
  134. nautobot/extras/jobs.py +12 -70
  135. nautobot/extras/managers.py +1 -3
  136. nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
  137. nautobot/extras/models/__init__.py +1 -1
  138. nautobot/extras/models/contacts.py +1 -1
  139. nautobot/extras/models/customfields.py +11 -12
  140. nautobot/extras/models/groups.py +9 -11
  141. nautobot/extras/models/jobs.py +4 -23
  142. nautobot/extras/models/models.py +2 -2
  143. nautobot/extras/plugins/__init__.py +2 -13
  144. nautobot/extras/plugins/marketplace_manifest.yml +79 -84
  145. nautobot/extras/plugins/tables.py +14 -16
  146. nautobot/extras/plugins/views.py +69 -65
  147. nautobot/extras/registry.py +1 -1
  148. nautobot/extras/secrets/__init__.py +2 -2
  149. nautobot/extras/tables.py +5 -7
  150. nautobot/extras/templates/extras/dynamicgroup.html +9 -1
  151. nautobot/extras/templates/extras/job_detail.html +0 -16
  152. nautobot/extras/templates/extras/job_edit.html +0 -1
  153. nautobot/extras/templates/extras/jobqueue_retrieve.html +9 -1
  154. nautobot/extras/templates/extras/marketplace.html +11 -29
  155. nautobot/extras/templates/extras/plugin_detail.html +15 -32
  156. nautobot/extras/templates/extras/plugins_tiles.html +10 -21
  157. nautobot/extras/test_jobs/api_test_job.py +1 -1
  158. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  159. nautobot/extras/test_jobs/dry_run.py +1 -1
  160. nautobot/extras/test_jobs/fail.py +5 -5
  161. nautobot/extras/test_jobs/file_output.py +1 -1
  162. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  163. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  164. nautobot/extras/test_jobs/ipaddress_vars.py +1 -3
  165. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  166. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  167. nautobot/extras/test_jobs/log_redaction.py +1 -1
  168. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  169. nautobot/extras/test_jobs/modify_db.py +1 -1
  170. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  171. nautobot/extras/test_jobs/object_var_required.py +1 -1
  172. nautobot/extras/test_jobs/object_vars.py +1 -1
  173. nautobot/extras/test_jobs/pass.py +3 -3
  174. nautobot/extras/test_jobs/profiling.py +1 -1
  175. nautobot/extras/test_jobs/relative_import.py +3 -3
  176. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  177. nautobot/extras/test_jobs/task_queues.py +1 -1
  178. nautobot/extras/tests/integration/test_plugin_banner.py +2 -0
  179. nautobot/extras/tests/test_api.py +13 -13
  180. nautobot/extras/tests/test_customfields.py +1 -1
  181. nautobot/extras/tests/test_datasources.py +1 -2
  182. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  183. nautobot/extras/tests/test_filters.py +6 -6
  184. nautobot/extras/tests/test_forms.py +1 -20
  185. nautobot/extras/tests/test_jobs.py +19 -160
  186. nautobot/extras/tests/test_models.py +10 -10
  187. nautobot/extras/tests/test_plugins.py +9 -62
  188. nautobot/extras/tests/test_relationships.py +9 -120
  189. nautobot/extras/tests/test_views.py +191 -52
  190. nautobot/extras/utils.py +2 -3
  191. nautobot/extras/views.py +98 -30
  192. nautobot/ipam/api/fields.py +3 -3
  193. nautobot/ipam/api/serializers.py +33 -41
  194. nautobot/ipam/api/views.py +117 -68
  195. nautobot/ipam/factory.py +1 -1
  196. nautobot/ipam/filters.py +2 -3
  197. nautobot/ipam/lookups.py +62 -101
  198. nautobot/ipam/models.py +16 -66
  199. nautobot/ipam/querysets.py +2 -2
  200. nautobot/ipam/tables.py +7 -23
  201. nautobot/ipam/templates/ipam/ipaddress.html +9 -1
  202. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +7 -1
  203. nautobot/ipam/templates/ipam/ipaddress_edit.html +7 -1
  204. nautobot/ipam/templates/ipam/prefix.html +9 -1
  205. nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
  206. nautobot/ipam/templates/ipam/vlan.html +9 -1
  207. nautobot/ipam/templates/ipam/vlan_edit.html +7 -1
  208. nautobot/ipam/templates/ipam/vrf_edit.html +7 -1
  209. nautobot/ipam/tests/test_api.py +3 -416
  210. nautobot/ipam/tests/test_forms.py +47 -49
  211. nautobot/ipam/tests/test_migrations.py +30 -30
  212. nautobot/ipam/tests/test_models.py +34 -95
  213. nautobot/ipam/tests/test_querysets.py +1 -63
  214. nautobot/ipam/tests/test_views.py +0 -3
  215. nautobot/ipam/utils/__init__.py +6 -36
  216. nautobot/ipam/views.py +87 -61
  217. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  218. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  219. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +2 -40
  220. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  221. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  222. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  223. nautobot/project-static/docs/404.html +4 -46
  224. nautobot/project-static/docs/apps/index.html +4 -46
  225. nautobot/project-static/docs/apps/nautobot-apps.html +6 -47
  226. nautobot/project-static/docs/assets/_mkdocstrings.css +1 -25
  227. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.83f73b43.min.js} +2 -2
  228. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.83f73b43.min.js.map} +2 -2
  229. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +10 -62
  230. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +7 -59
  231. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +122 -374
  232. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +18 -90
  233. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +21 -95
  234. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +6 -53
  235. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +5 -52
  236. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +17 -79
  237. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +28 -102
  238. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +21 -108
  239. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +38 -131
  240. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +65 -239
  241. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +165 -581
  242. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +36 -109
  243. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +167 -453
  244. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +211 -493
  245. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +8 -60
  246. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +15 -71
  247. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +55 -407
  248. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +205 -585
  249. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +412 -858
  250. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +7 -59
  251. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +186 -448
  252. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +147 -365
  253. nautobot/project-static/docs/development/apps/api/configuration-view.html +4 -46
  254. nautobot/project-static/docs/development/apps/api/database-backend-config.html +4 -46
  255. nautobot/project-static/docs/development/apps/api/models/django-admin.html +4 -46
  256. nautobot/project-static/docs/development/apps/api/models/global-search.html +4 -46
  257. nautobot/project-static/docs/development/apps/api/models/graphql.html +4 -46
  258. nautobot/project-static/docs/development/apps/api/models/index.html +4 -46
  259. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +4 -46
  260. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +4 -46
  261. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +4 -46
  262. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +4 -46
  263. nautobot/project-static/docs/development/apps/api/platform-features/index.html +4 -46
  264. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +4 -46
  265. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +4 -46
  266. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +4 -46
  267. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +4 -46
  268. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +7 -68
  269. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +4 -46
  270. nautobot/project-static/docs/development/apps/api/prometheus.html +4 -46
  271. nautobot/project-static/docs/development/apps/api/setup.html +4 -46
  272. nautobot/project-static/docs/development/apps/api/testing.html +4 -46
  273. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +4 -46
  274. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +4 -46
  275. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +4 -46
  276. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +4 -46
  277. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +4 -46
  278. nautobot/project-static/docs/development/apps/api/views/base-template.html +4 -46
  279. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +4 -46
  280. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +4 -46
  281. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +4 -46
  282. nautobot/project-static/docs/development/apps/api/views/index.html +4 -46
  283. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +4 -46
  284. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +4 -46
  285. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +4 -46
  286. nautobot/project-static/docs/development/apps/api/views/notes.html +4 -46
  287. nautobot/project-static/docs/development/apps/api/views/rest-api.html +6 -52
  288. nautobot/project-static/docs/development/apps/api/views/urls.html +4 -46
  289. nautobot/project-static/docs/development/apps/index.html +4 -46
  290. nautobot/project-static/docs/development/apps/migration/code-updates.html +4 -46
  291. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +4 -46
  292. nautobot/project-static/docs/development/apps/migration/from-v1.html +4 -46
  293. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +4 -46
  294. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +4 -46
  295. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +4 -46
  296. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +4 -46
  297. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +8 -50
  298. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +4 -46
  299. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +14 -211
  300. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +4 -46
  301. nautobot/project-static/docs/development/apps/porting-from-netbox.html +4 -46
  302. nautobot/project-static/docs/development/core/application-registry.html +4 -46
  303. nautobot/project-static/docs/development/core/best-practices.html +4 -46
  304. nautobot/project-static/docs/development/core/bootstrap-ui.html +4 -46
  305. nautobot/project-static/docs/development/core/caching.html +4 -46
  306. nautobot/project-static/docs/development/core/controllers.html +4 -46
  307. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +74 -73
  308. nautobot/project-static/docs/development/core/generic-views.html +4 -46
  309. nautobot/project-static/docs/development/core/getting-started.html +224 -249
  310. nautobot/project-static/docs/development/core/homepage.html +7 -49
  311. nautobot/project-static/docs/development/core/index.html +4 -46
  312. nautobot/project-static/docs/development/core/{minikube-dev-environment-for-k8s-jobs.html → local-k8s.html} +168 -469
  313. nautobot/project-static/docs/development/core/model-checklist.html +12 -56
  314. nautobot/project-static/docs/development/core/model-features.html +4 -46
  315. nautobot/project-static/docs/development/core/natural-keys.html +4 -46
  316. nautobot/project-static/docs/development/core/navigation-menu.html +4 -46
  317. nautobot/project-static/docs/development/core/release-checklist.html +7 -49
  318. nautobot/project-static/docs/development/core/role-internals.html +4 -46
  319. nautobot/project-static/docs/development/core/settings.html +4 -46
  320. nautobot/project-static/docs/development/core/style-guide.html +7 -49
  321. nautobot/project-static/docs/development/core/templates.html +4 -46
  322. nautobot/project-static/docs/development/core/testing.html +4 -46
  323. nautobot/project-static/docs/development/core/ui-component-framework.html +273 -369
  324. nautobot/project-static/docs/development/core/user-preferences.html +4 -46
  325. nautobot/project-static/docs/development/index.html +4 -46
  326. nautobot/project-static/docs/development/jobs/index.html +122 -216
  327. nautobot/project-static/docs/development/jobs/migration/from-v1.html +4 -46
  328. nautobot/project-static/docs/index.html +23 -54
  329. nautobot/project-static/docs/objects.inv +0 -0
  330. nautobot/project-static/docs/overview/application_stack.html +7 -47
  331. nautobot/project-static/docs/overview/design_philosophy.html +4 -46
  332. nautobot/project-static/docs/release-notes/index.html +12 -52
  333. nautobot/project-static/docs/release-notes/version-1.0.html +193 -234
  334. nautobot/project-static/docs/release-notes/version-1.1.html +190 -231
  335. nautobot/project-static/docs/release-notes/version-1.2.html +265 -306
  336. nautobot/project-static/docs/release-notes/version-1.3.html +291 -332
  337. nautobot/project-static/docs/release-notes/version-1.4.html +377 -417
  338. nautobot/project-static/docs/release-notes/version-1.5.html +566 -605
  339. nautobot/project-static/docs/release-notes/version-1.6.html +447 -904
  340. nautobot/project-static/docs/release-notes/version-2.0.html +489 -528
  341. nautobot/project-static/docs/release-notes/version-2.1.html +324 -363
  342. nautobot/project-static/docs/release-notes/version-2.2.html +317 -356
  343. nautobot/project-static/docs/release-notes/version-2.3.html +352 -997
  344. nautobot/project-static/docs/release-notes/version-2.4.html +101 -417
  345. nautobot/project-static/docs/requirements.txt +2 -2
  346. nautobot/project-static/docs/search/search_index.json +1 -1
  347. nautobot/project-static/docs/sitemap.xml +287 -295
  348. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  349. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +4 -46
  350. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +4 -46
  351. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +6 -48
  352. nautobot/project-static/docs/user-guide/administration/configuration/index.html +4 -46
  353. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +4 -46
  354. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +8 -110
  355. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +4 -46
  356. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +4 -46
  357. nautobot/project-static/docs/user-guide/administration/guides/docker.html +4 -46
  358. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +4 -46
  359. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +4 -46
  360. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +4 -46
  361. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +4 -46
  362. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +4 -46
  363. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +6 -48
  364. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +4 -46
  365. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +4 -46
  366. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +4 -46
  367. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +8 -66
  368. nautobot/project-static/docs/user-guide/administration/installation/index.html +4 -46
  369. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +5 -47
  370. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +4 -46
  371. nautobot/project-static/docs/user-guide/administration/installation/services.html +4 -46
  372. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +4 -46
  373. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +4 -46
  374. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +4 -46
  375. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +4 -46
  376. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +4 -46
  377. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +4 -46
  378. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +4 -46
  379. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +4 -46
  380. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +4 -46
  381. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +8 -49
  382. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +4 -46
  383. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +4 -46
  384. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +4 -46
  385. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +4 -46
  386. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +4 -46
  387. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +4 -46
  388. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +4 -46
  389. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +4 -46
  390. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +4 -46
  391. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +4 -46
  392. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +4 -46
  393. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +4 -46
  394. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +4 -46
  395. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +4 -46
  396. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +4 -46
  397. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +4 -46
  398. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +4 -46
  399. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +4 -46
  400. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +4 -46
  401. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +4 -46
  402. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +4 -46
  403. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +4 -46
  404. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +4 -46
  405. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +4 -46
  406. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +4 -46
  407. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +4 -46
  408. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +4 -46
  409. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +4 -46
  410. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +4 -46
  411. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +4 -46
  412. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +4 -46
  413. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +4 -46
  414. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +4 -46
  415. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +4 -46
  416. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +4 -46
  417. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +4 -46
  418. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +4 -46
  419. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +4 -46
  420. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +4 -46
  421. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +4 -46
  422. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +4 -46
  423. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +4 -46
  424. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +4 -46
  425. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +4 -46
  426. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +4 -46
  427. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +4 -46
  428. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +4 -46
  429. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +4 -46
  430. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +4 -46
  431. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +4 -46
  432. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +4 -46
  433. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +4 -46
  434. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +4 -46
  435. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -46
  436. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +4 -46
  437. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +4 -46
  438. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +4 -46
  439. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +12 -50
  440. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +4 -46
  441. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +4 -46
  442. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +4 -46
  443. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +4 -46
  444. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +4 -46
  445. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +4 -46
  446. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +4 -46
  447. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +4 -46
  448. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +4 -46
  449. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +4 -46
  450. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +4 -46
  451. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +4 -46
  452. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +7 -49
  453. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +4 -46
  454. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +4 -46
  455. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +4 -46
  456. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +4 -46
  457. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +4 -46
  458. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +4 -46
  459. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +4 -46
  460. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +4 -46
  461. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +4 -46
  462. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +4 -46
  463. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +4 -46
  464. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +4 -46
  465. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +4 -46
  466. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +4 -46
  467. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +4 -46
  468. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +4 -46
  469. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +4 -46
  470. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -46
  471. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +4 -46
  472. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +4 -46
  473. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +4 -46
  474. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +4 -46
  475. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +7 -51
  476. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +4 -46
  477. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +4 -46
  478. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +4 -46
  479. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +7 -49
  480. nautobot/project-static/docs/user-guide/index.html +4 -46
  481. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +4 -46
  482. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +8 -50
  483. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +4 -46
  484. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +4 -46
  485. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +4 -46
  486. nautobot/project-static/docs/user-guide/platform-functionality/events.html +4 -46
  487. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +4 -46
  488. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +4 -46
  489. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +4 -46
  490. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +4 -46
  491. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +4 -46
  492. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +4 -46
  493. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +7 -50
  494. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +4 -46
  495. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +7 -49
  496. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +4 -46
  497. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +4 -46
  498. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +4 -46
  499. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +4 -46
  500. nautobot/project-static/docs/user-guide/platform-functionality/note.html +4 -46
  501. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +4 -46
  502. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +5 -47
  503. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +4 -46
  504. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +4 -46
  505. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +25 -94
  506. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +5 -74
  507. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +4 -46
  508. nautobot/project-static/docs/user-guide/platform-functionality/role.html +4 -46
  509. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +4 -46
  510. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +4 -46
  511. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +4 -46
  512. nautobot/project-static/docs/user-guide/platform-functionality/status.html +4 -46
  513. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +4 -46
  514. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +4 -46
  515. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +4 -46
  516. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +4 -46
  517. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +4 -46
  518. nautobot/project-static/js/forms.js +1 -1
  519. nautobot/tenancy/api/views.py +13 -9
  520. nautobot/tenancy/views.py +2 -4
  521. nautobot/users/admin.py +1 -1
  522. nautobot/users/api/serializers.py +4 -5
  523. nautobot/users/api/views.py +3 -3
  524. nautobot/virtualization/api/serializers.py +4 -4
  525. nautobot/virtualization/api/views.py +24 -5
  526. nautobot/virtualization/filters.py +3 -20
  527. nautobot/virtualization/models.py +1 -1
  528. nautobot/virtualization/tables.py +2 -2
  529. nautobot/virtualization/templates/virtualization/cluster_edit.html +7 -1
  530. nautobot/virtualization/templates/virtualization/virtualmachine.html +9 -1
  531. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +8 -2
  532. nautobot/virtualization/tests/test_filters.py +0 -17
  533. nautobot/wireless/filters.py +2 -2
  534. nautobot/wireless/forms.py +1 -1
  535. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +9 -1
  536. nautobot/wireless/tests/test_filters.py +1 -29
  537. nautobot/wireless/views.py +10 -0
  538. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +6 -6
  539. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +543 -591
  540. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
  541. nautobot/core/api/constants.py +0 -11
  542. nautobot/core/jobs/bulk_actions.py +0 -248
  543. nautobot/core/templates/about.html +0 -67
  544. nautobot/core/templates/inc/tenancy_form_panel.html +0 -9
  545. nautobot/core/templates/inc/tenant_table_row.html +0 -11
  546. nautobot/core/utils/querysets.py +0 -64
  547. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +0 -25
  548. nautobot/dcim/tests/integration/test_controller.py +0 -62
  549. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +0 -71
  550. nautobot/dcim/tests/test_jobs.py +0 -118
  551. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +0 -22
  552. nautobot/extras/migrations/0121_alter_team_contacts.py +0 -17
  553. nautobot/extras/test_jobs/singleton.py +0 -16
  554. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  555. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  556. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  557. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  558. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  559. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  560. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  561. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  562. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  563. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  564. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  565. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  566. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  567. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  568. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  569. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  570. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  571. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  572. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  573. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  574. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  575. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  576. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +0 -9444
  587. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +0 -9722
  588. nautobot/wireless/tests/integration/__init__.py +0 -0
  589. nautobot/wireless/tests/integration/test_radio_profile.py +0 -42
  590. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
  591. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
  592. {nautobot-2.4.0.dist-info → nautobot-2.4.0b1.dist-info}/entry_points.txt +0 -0
@@ -5,13 +5,11 @@ from typing import Optional, Sequence, Union
5
5
  from django.conf import settings
6
6
  from django.contrib.contenttypes.fields import GenericForeignKey
7
7
  from django.contrib.contenttypes.models import ContentType
8
- from django.db import connections, DEFAULT_DB_ALIAS
9
8
  from django.db.models import ForeignKey, ManyToManyField, QuerySet
10
9
  from django.test import override_settings, tag
11
- from django.test.utils import CaptureQueriesContext
12
10
  from django.urls import reverse
13
11
  from django.utils.text import slugify
14
- from rest_framework import serializers, status
12
+ from rest_framework import status
15
13
  from rest_framework.relations import ManyRelatedField
16
14
  from rest_framework.test import APITransactionTestCase as _APITransactionTestCase
17
15
 
@@ -96,15 +94,6 @@ class APITestCase(views.ModelTestCase):
96
94
  for verboten in self.VERBOTEN_STRINGS:
97
95
  self.assertNotIn(verboten, response_raw_content)
98
96
 
99
- def get_m2m_fields(self):
100
- """Get serializer field names that are many-to-many or one-to-many and thus affected by ?exclude_m2m=true."""
101
- serializer_class = get_serializer_for_model(self.model)
102
- m2m_fields = []
103
- for field_name, field_instance in serializer_class().fields.items():
104
- if isinstance(field_instance, (serializers.ManyRelatedField, serializers.ListSerializer)):
105
- m2m_fields.append(field_name)
106
- return m2m_fields
107
-
108
97
 
109
98
  @tag("unit")
110
99
  class APIViewTestCases:
@@ -242,7 +231,7 @@ class APIViewTestCases:
242
231
  def get_depth_fields(self):
243
232
  """Get a list of model fields that could be tested with the ?depth query parameter"""
244
233
  depth_fields = []
245
- for field in self.model._meta.get_fields():
234
+ for field in self.model._meta.fields:
246
235
  if not field.name.startswith("_"):
247
236
  if isinstance(field, (ForeignKey, GenericForeignKey, ManyToManyField, core_fields.TagsField)) and (
248
237
  # we represent content-types as "app_label.modelname" rather than as FKs
@@ -251,9 +240,6 @@ class APIViewTestCases:
251
240
  and not (field.name == "user" and self.model == users_models.Token)
252
241
  ):
253
242
  depth_fields.append(field.name)
254
- serializer_class = get_serializer_for_model(self.model)
255
- serializer = serializer_class()
256
- depth_fields = [field_name for field_name in depth_fields if field_name in serializer.fields]
257
243
  return depth_fields
258
244
 
259
245
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
@@ -283,12 +269,9 @@ class APIViewTestCases:
283
269
  GET a list of objects using the "?depth=0" parameter.
284
270
  """
285
271
  depth_fields = self.get_depth_fields()
286
- m2m_fields = self.get_m2m_fields()
287
272
  self.add_permissions(f"{self.model._meta.app_label}.view_{self.model._meta.model_name}")
288
- list_url = f"{self._get_list_url()}?depth=0"
289
- with CaptureQueriesContext(connections[DEFAULT_DB_ALIAS]) as cqc:
290
- response = self.client.get(list_url, **self.header)
291
- base_num_queries = len(cqc)
273
+ url = f"{self._get_list_url()}?depth=0"
274
+ response = self.client.get(url, **self.header)
292
275
 
293
276
  self.assertHttpStatus(response, status.HTTP_200_OK)
294
277
  self.assertIsInstance(response.data, dict)
@@ -297,23 +280,15 @@ class APIViewTestCases:
297
280
  self.assert_no_verboten_content(response)
298
281
 
299
282
  for response_data in response.data["results"]:
300
- for field in m2m_fields:
301
- self.assertIn(field, response_data)
302
- self.assertIsInstance(response_data[field], list)
303
283
  for field in depth_fields:
304
284
  self.assertIn(field, response_data)
305
285
  if isinstance(response_data[field], list):
306
286
  for entry in response_data[field]:
307
287
  self.assertIsInstance(entry, dict)
308
- if entry["object_type"] in ["auth.group"]:
309
- self.assertIsInstance(entry["id"], int)
310
- else:
311
- self.assertTrue(is_uuid(entry["id"]))
312
- self.assertEqual(len(entry.keys()), 3) # just id/object_type/url
288
+ self.assertTrue(is_uuid(entry["id"]))
313
289
  else:
314
290
  if response_data[field] is not None:
315
291
  self.assertIsInstance(response_data[field], dict)
316
- self.assertEqual(len(response_data[field].keys()), 3) # just id/object_type/url
317
292
  url = response_data[field]["url"]
318
293
  pk = response_data[field]["id"]
319
294
  object_type = response_data[field]["object_type"]
@@ -321,67 +296,22 @@ class APIViewTestCases:
321
296
  # URL ending in the UUID of the relevant object:
322
297
  # http://nautobot.example.com/api/circuits/providers/<uuid>/
323
298
  # ^^^^^^
324
- if object_type in ["auth.group"]:
325
- self.assertIsInstance(url.split("/")[-2], int)
326
- self.assertIsInstance(pk, int)
327
- else:
328
- self.assertTrue(is_uuid(url.split("/")[-2]))
329
- self.assertTrue(is_uuid(pk))
299
+ self.assertTrue(is_uuid(url.split("/")[-2]))
300
+ self.assertTrue(is_uuid(pk))
330
301
 
331
302
  with self.subTest(f"Assert object_type {object_type} is valid"):
332
303
  app_label, model_name = object_type.split(".")
333
304
  ContentType.objects.get(app_label=app_label, model=model_name)
334
305
 
335
- list_url += "&exclude_m2m=true"
336
- with CaptureQueriesContext(connections[DEFAULT_DB_ALIAS]) as cqc:
337
- response = self.client.get(list_url, **self.header)
338
-
339
- self.assertHttpStatus(response, status.HTTP_200_OK)
340
- self.assertIsInstance(response.data, dict)
341
- self.assertIn("results", response.data)
342
- self.assert_no_verboten_content(response)
343
-
344
- if m2m_fields:
345
- if self.model._meta.app_label in [
346
- "circuits",
347
- "cloud",
348
- "dcim",
349
- "extras",
350
- "ipam",
351
- "tenancy",
352
- "users",
353
- "virtualization",
354
- "wireless",
355
- ]:
356
- self.assertLess(
357
- len(cqc), base_num_queries, "Number of queries did not decrease with ?exclude_m2m=true"
358
- )
359
- else:
360
- # Less strict check for non-core APIs
361
- self.assertLessEqual(
362
- len(cqc), base_num_queries, "Number of queries increased with ?exclude_m2m=true"
363
- )
364
- else:
365
- # No M2M fields to exclude
366
- self.assertLessEqual(len(cqc), base_num_queries, "Number of queries increased with ?exclude_m2m=true")
367
-
368
- for response_data in response.data["results"]:
369
- for field in m2m_fields:
370
- self.assertNotIn(field, response_data)
371
- # TODO: we should assert that all other fields are still present, but there's a few corner cases...
372
-
373
306
  @override_settings(EXEMPT_VIEW_PERMISSIONS=[])
374
307
  def test_list_objects_depth_1(self):
375
308
  """
376
309
  GET a list of objects using the "?depth=1" parameter.
377
310
  """
378
311
  depth_fields = self.get_depth_fields()
379
- m2m_fields = self.get_m2m_fields()
380
312
  self.add_permissions(f"{self.model._meta.app_label}.view_{self.model._meta.model_name}")
381
- list_url = f"{self._get_list_url()}?depth=1"
382
- with CaptureQueriesContext(connections[DEFAULT_DB_ALIAS]) as cqc:
383
- response = self.client.get(list_url, **self.header)
384
- base_num_queries = len(cqc)
313
+ url = f"{self._get_list_url()}?depth=1"
314
+ response = self.client.get(url, **self.header)
385
315
 
386
316
  self.assertHttpStatus(response, status.HTTP_200_OK)
387
317
  self.assertIsInstance(response.data, dict)
@@ -390,65 +320,16 @@ class APIViewTestCases:
390
320
  self.assert_no_verboten_content(response)
391
321
 
392
322
  for response_data in response.data["results"]:
393
- for field in m2m_fields:
394
- self.assertIn(field, response_data)
395
- self.assertIsInstance(response_data[field], list)
396
323
  for field in depth_fields:
397
324
  self.assertIn(field, response_data)
398
325
  if isinstance(response_data[field], list):
399
326
  for entry in response_data[field]:
400
327
  self.assertIsInstance(entry, dict)
401
- if entry["object_type"] in ["auth.group"]:
402
- self.assertIsInstance(entry["id"], int)
403
- else:
404
- self.assertTrue(is_uuid(entry["id"]))
405
- self.assertGreater(len(entry.keys()), 3, entry) # not just id/object_type/url!
328
+ self.assertTrue(is_uuid(entry["id"]))
406
329
  else:
407
330
  if response_data[field] is not None:
408
331
  self.assertIsInstance(response_data[field], dict)
409
- if response_data[field]["object_type"] in ["auth.group"]:
410
- self.assertIsInstance(response_data[field]["id"], int)
411
- else:
412
- self.assertTrue(is_uuid(response_data[field]["id"]))
413
- self.assertGreater(len(response_data[field].keys()), 3, response_data[field])
414
-
415
- list_url += "&exclude_m2m=true"
416
- with CaptureQueriesContext(connections[DEFAULT_DB_ALIAS]) as cqc:
417
- response = self.client.get(list_url, **self.header)
418
-
419
- self.assertHttpStatus(response, status.HTTP_200_OK)
420
- self.assertIsInstance(response.data, dict)
421
- self.assertIn("results", response.data)
422
- self.assert_no_verboten_content(response)
423
-
424
- if m2m_fields:
425
- if self.model._meta.app_label in [
426
- "circuits",
427
- "cloud",
428
- "dcim",
429
- "extras",
430
- "ipam",
431
- "tenancy",
432
- "users",
433
- "virtualization",
434
- "wireless",
435
- ]:
436
- self.assertLess(
437
- len(cqc), base_num_queries, "Number of queries did not decrease with ?exclude_m2m=true"
438
- )
439
- else:
440
- # Less strict check for non-core APIs
441
- self.assertLessEqual(
442
- len(cqc), base_num_queries, "Number of queries increased with ?exclude_m2m=true"
443
- )
444
- else:
445
- # No M2M fields to exclude
446
- self.assertLessEqual(len(cqc), base_num_queries, "Number of queries increased with ?exclude_m2m=true")
447
-
448
- for response_data in response.data["results"]:
449
- for field in m2m_fields:
450
- self.assertNotIn(field, response_data)
451
- # TODO: we should assert that all other fields are still present, but there's a few corner cases...
332
+ self.assertTrue(is_uuid(response_data[field]["id"]))
452
333
 
453
334
  @override_settings(EXEMPT_VIEW_PERMISSIONS=[])
454
335
  def test_list_objects_without_permission(self):
@@ -774,11 +655,7 @@ class APIViewTestCases:
774
655
  self.assertHttpStatus(response, status.HTTP_201_CREATED, csv_data)
775
656
  # Note that create via CSV is always treated as a bulk-create, and so the response is always a list of dicts
776
657
  new_instance = self._get_queryset().get(pk=response.data[0]["id"])
777
- if isinstance(orig_pk, int):
778
- self.assertNotEqual(new_instance.pk, orig_pk)
779
- else:
780
- # for our non-integer PKs, we're expecting the creation to respect the requested PK
781
- self.assertEqual(new_instance.pk, orig_pk)
658
+ self.assertNotEqual(new_instance.pk, orig_pk)
782
659
 
783
660
  new_serializer = serializer_class(new_instance, context={"request": None})
784
661
  new_data = new_serializer.data
@@ -860,7 +737,7 @@ class APIViewTestCases:
860
737
 
861
738
  def strip_serialized_object(this_object):
862
739
  """
863
- Work around acceptable differences in PATCH response vs GET response which are known behaviors.
740
+ Only here to work around acceptable differences in PATCH response vs GET response which are known bugs.
864
741
  """
865
742
  # Work around for https://github.com/nautobot/nautobot/issues/3321
866
743
  this_object.pop("last_updated", None)
@@ -869,12 +746,6 @@ class APIViewTestCases:
869
746
  this_object.pop("config_context", None)
870
747
  this_object.pop("relationships", None)
871
748
 
872
- serializer = get_serializer_for_model(self.model)()
873
- for field_name, field_instance in serializer.fields.items():
874
- if field_instance.read_only:
875
- # Likely a derived field, might change as a consequence of other data updates
876
- this_object.pop(field_name, None)
877
-
878
749
  for value in this_object.values():
879
750
  if isinstance(value, dict):
880
751
  strip_serialized_object(value)
@@ -918,7 +789,7 @@ class APIViewTestCases:
918
789
  self.assertEqual(initial_serialized_object, serialized_object)
919
790
 
920
791
  # Verify ObjectChange creation -- yes, even though nothing actually changed
921
- # TODO: This may change (hah) at some point -- see https://github.com/nautobot/nautobot/issues/3321
792
+ # This may change (hah) at some point -- see https://github.com/nautobot/nautobot/issues/3321
922
793
  if hasattr(self.model, "to_objectchange"):
923
794
  objectchanges = lookup.get_changes_for_model(instance)
924
795
  self.assertEqual(objectchanges[0].action, extras_choices.ObjectChangeActionChoices.ACTION_UPDATE)
@@ -927,16 +798,10 @@ class APIViewTestCases:
927
798
  # Verify that a PATCH with some data updates that data correctly.
928
799
  response = self.client.patch(url, update_data, format="json", **self.header)
929
800
  self.assertHttpStatus(response, status.HTTP_200_OK)
930
- serialized_object = response.json()
931
- strip_serialized_object(serialized_object)
932
801
  # Check for unexpected side effects on fields we DIDN'T intend to update
933
802
  for field in initial_serialized_object:
934
803
  if field not in update_data:
935
- self.assertEqual(
936
- initial_serialized_object[field],
937
- serialized_object[field],
938
- f"data changed unexpectedly for field '{field}'",
939
- )
804
+ self.assertEqual(initial_serialized_object[field], serialized_object[field])
940
805
  instance.refresh_from_db()
941
806
  self.assertInstanceEqual(instance, update_data, exclude=self.validation_excluded_fields, api=True)
942
807
 
@@ -945,26 +810,6 @@ class APIViewTestCases:
945
810
  objectchanges = lookup.get_changes_for_model(instance)
946
811
  self.assertEqual(objectchanges[0].action, extras_choices.ObjectChangeActionChoices.ACTION_UPDATE)
947
812
 
948
- # Verify that a PATCH with ?exclude_m2m=true correctly excludes many-to-many fields from the response
949
- # This also doubles as a test for idempotence of the PATCH request.
950
- response = self.client.patch(url + "?exclude_m2m=true", update_data, format="json", **self.header)
951
- self.assertHttpStatus(response, status.HTTP_200_OK)
952
- m2m_fields = self.get_m2m_fields()
953
- serialized_object = response.json()
954
- strip_serialized_object(serialized_object)
955
- for field in m2m_fields:
956
- self.assertNotIn(field, serialized_object)
957
- # Check for unexpected side effects on fields we DIDN'T intend to update
958
- for field in initial_serialized_object:
959
- if field not in update_data and field not in m2m_fields:
960
- self.assertEqual(
961
- initial_serialized_object[field],
962
- serialized_object[field],
963
- f"data changed unexpectedly for field '{field}'",
964
- )
965
- instance.refresh_from_db()
966
- self.assertInstanceEqual(instance, update_data, exclude=self.validation_excluded_fields, api=True)
967
-
968
813
  def test_get_put_round_trip(self):
969
814
  """GET and then PUT an object and verify that it's accepted and unchanged."""
970
815
  self.maxDiff = None
@@ -30,8 +30,6 @@ class FilterTestCases:
30
30
  class BaseFilterTestCase(views.TestCase):
31
31
  """Base class for testing of FilterSets."""
32
32
 
33
- queryset: ClassVar[QuerySet]
34
-
35
33
  def get_filterset_test_values(self, field_name, queryset=None):
36
34
  """Returns a list of distinct values from the requested queryset field to use in filterset tests.
37
35
 
@@ -73,7 +71,8 @@ class FilterTestCases:
73
71
  class FilterTestCase(BaseFilterTestCase):
74
72
  """Add common tests for all FilterSets."""
75
73
 
76
- filterset: ClassVar[type[FilterSet]]
74
+ queryset: ClassVar[QuerySet]
75
+ filterset: ClassVar[FilterSet]
77
76
 
78
77
  # filter predicate fields that should be excluded from q test case
79
78
  exclude_q_filter_predicates = []
@@ -97,28 +96,6 @@ class FilterTestCases:
97
96
  """Helper method to return q filter."""
98
97
  return self.filterset.declared_filters["q"].filter_predicates
99
98
 
100
- def test_id(self):
101
- """Verify that the filterset supports filtering by id with only lookup `__n`."""
102
- with self.subTest("Assert `id`"):
103
- params = {"id": list(self.queryset.values_list("pk", flat=True)[:2])}
104
- expected_queryset = self.queryset.filter(id__in=params["id"])
105
- filterset = self.filterset(params, self.queryset)
106
- self.assertTrue(filterset.is_valid())
107
- self.assertQuerysetEqualAndNotEmpty(filterset.qs.order_by("id"), expected_queryset.order_by("id"))
108
-
109
- with self.subTest("Assert negate lookup"):
110
- params = {"id__n": list(self.queryset.values_list("pk", flat=True)[:2])}
111
- expected_queryset = self.queryset.exclude(id__in=params["id__n"])
112
- filterset = self.filterset(params, self.queryset)
113
- self.assertTrue(filterset.is_valid())
114
- self.assertQuerysetEqualAndNotEmpty(filterset.qs.order_by("id"), expected_queryset.order_by("id"))
115
-
116
- with self.subTest("Assert invalid lookup"):
117
- params = {"id__in": list(self.queryset.values_list("pk", flat=True)[:2])}
118
- filterset = self.filterset(params, self.queryset)
119
- self.assertFalse(filterset.is_valid())
120
- self.assertIn("Unknown filter field", filterset.errors.as_text())
121
-
122
99
  def test_invalid_filter(self):
123
100
  """Verify that the filterset reports as invalid when initialized with an unsupported filter parameter."""
124
101
  params = {"ice_cream_flavor": ["chocolate"]}
@@ -1,6 +1,5 @@
1
1
  from django.test import tag, TestCase
2
2
 
3
- from nautobot.core.api.constants import NON_FILTER_QUERY_PARAMS
4
3
  from nautobot.core.forms.fields import DynamicModelChoiceMixin
5
4
  from nautobot.core.utils.lookup import get_filterset_for_model
6
5
 
@@ -19,7 +18,6 @@ class FormTestCases:
19
18
  continue
20
19
  with self.subTest(f"Assert {self.form_class.__name__}.{field_name} query_params are valid."):
21
20
  query_params_fields = set(field_class.query_params.keys())
22
- query_params_fields = query_params_fields - set(NON_FILTER_QUERY_PARAMS)
23
21
  if not query_params_fields:
24
22
  self.skipTest(f"{self.form_class.__name__}.{field_name} has no query_params")
25
23
  field_model = field_class.queryset.model
@@ -144,18 +144,13 @@ class NautobotTestCaseMixin:
144
144
  # Permissions management
145
145
  #
146
146
 
147
- def add_permissions(self, *names, **kwargs):
147
+ def add_permissions(self, *names):
148
148
  """
149
149
  Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>.
150
- Additional keyword arguments will be passed to the ObjectPermission constructor to allow creating more detailed permissions.
151
-
152
- Examples:
153
- >>> add_permissions("ipam.add_vlangroup", "ipam.view_vlangroup")
154
- >>> add_permissions("ipam.add_vlangroup", "ipam.view_vlangroup", constraints={"pk": "uuid-1234"})
155
150
  """
156
151
  for name in names:
157
152
  ct, action = permissions.resolve_permission_ct(name)
158
- obj_perm = users_models.ObjectPermission(name=name, actions=[action], **kwargs)
153
+ obj_perm = users_models.ObjectPermission(name=name, actions=[action])
159
154
  obj_perm.save()
160
155
  obj_perm.users.add(self.user)
161
156
  obj_perm.object_types.add(ct)
@@ -21,19 +21,17 @@ from nautobot.core.models.tree_queries import TreeModel
21
21
  from nautobot.core.templatetags import helpers
22
22
  from nautobot.core.testing import mixins, utils
23
23
  from nautobot.core.utils import lookup
24
- from nautobot.dcim.models.device_components import ComponentModel
25
24
  from nautobot.extras import choices as extras_choices, models as extras_models, querysets as extras_querysets
26
25
  from nautobot.extras.forms import CustomFieldModelFormMixin, RelationshipModelFormMixin
27
26
  from nautobot.extras.models import CustomFieldModel, RelationshipModel
28
- from nautobot.extras.models.jobs import JobResult
29
27
  from nautobot.extras.models.mixins import NotesMixin
30
28
  from nautobot.ipam.models import Prefix
31
29
  from nautobot.users import models as users_models
32
30
 
33
31
  __all__ = (
32
+ "TestCase",
34
33
  "ModelTestCase",
35
34
  "ModelViewTestCase",
36
- "TestCase",
37
35
  "ViewTestCases",
38
36
  )
39
37
 
@@ -1021,17 +1019,6 @@ class ViewTestCases:
1021
1019
  for instance in self._get_queryset().filter(pk__in=pk_list):
1022
1020
  self.assertInstanceEqual(instance, self.bulk_edit_data)
1023
1021
 
1024
- def validate_redirect_to_job_result(self, response):
1025
- # Get the last Bulk Edit Objects JobResult created
1026
- job_result = JobResult.objects.filter(name="Bulk Edit Objects").first()
1027
- # Assert redirect to Job Results
1028
- self.assertRedirects(
1029
- response,
1030
- reverse("extras:jobresult", args=[job_result.pk]),
1031
- status_code=302,
1032
- target_status_code=200,
1033
- )
1034
-
1035
1022
  def test_bulk_edit_objects_without_permission(self):
1036
1023
  pk_list = list(self._get_queryset().values_list("pk", flat=True)[:3])
1037
1024
  data = {
@@ -1057,8 +1044,9 @@ class ViewTestCases:
1057
1044
  # Assign model-level permission
1058
1045
  self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
1059
1046
 
1060
- response = self.client.post(self._get_url("bulk_edit"), data)
1061
- self.validate_redirect_to_job_result(response)
1047
+ # Try POST with model-level permission
1048
+ self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), 302)
1049
+ self.validate_object_data_after_bulk_edit(pk_list)
1062
1050
 
1063
1051
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
1064
1052
  def test_bulk_edit_form_contains_all_pks(self):
@@ -1083,7 +1071,7 @@ class ViewTestCases:
1083
1071
  for pk in pk_list:
1084
1072
  self.assertNotIn(str(pk), response_body)
1085
1073
  self.assertInHTML(
1086
- '<input type="hidden" name="_all" value="True" class="form-control" placeholder="None" id="id__all">',
1074
+ '<input type="hidden" name="_all" value="True" class="form-control" required="required" placeholder="None" id="id__all">',
1087
1075
  response_body,
1088
1076
  )
1089
1077
 
@@ -1119,7 +1107,7 @@ class ViewTestCases:
1119
1107
  self.assertNotIn(str(third_pk), response_body)
1120
1108
  self.assertIn("Editing 2 ", response_body)
1121
1109
  self.assertInHTML(
1122
- '<input type="hidden" name="_all" value="True" class="form-control" placeholder="None" id="id__all">',
1110
+ '<input type="hidden" name="_all" value="True" class="form-control" required="required" placeholder="None" id="id__all">',
1123
1111
  response_body,
1124
1112
  )
1125
1113
 
@@ -1159,9 +1147,21 @@ class ViewTestCases:
1159
1147
 
1160
1148
  # Attempt to bulk edit permitted objects into a non-permitted state
1161
1149
  response = self.client.post(self._get_url("bulk_edit"), data)
1162
- # NOTE: There is no way of testing constrained failure as bulk edit is a system Job;
1163
- # and can only be tested by checking JobLogs.
1164
- self.validate_redirect_to_job_result(response)
1150
+ # 200 because we're sent back to the edit form to try again; if the update were successful it'd be a 302
1151
+ self.assertHttpStatus(response, 200)
1152
+ # Assert that the objects are NOT updated
1153
+ for instance in self._get_queryset().filter(pk__in=pk_list):
1154
+ self.assertIn(field.value_from_object(instance), values)
1155
+ self.assertNotEqual(field.value_from_object(instance), self.bulk_edit_data[attr_name])
1156
+
1157
+ # Update permission constraints to permit all objects
1158
+ obj_perm.constraints = {"pk__gt": 0}
1159
+ obj_perm.save()
1160
+
1161
+ # Bulk edit permitted objects and expect a redirect back to the list view
1162
+ self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), 302)
1163
+ # Assert that the objects were all updated correctly
1164
+ self.validate_object_data_after_bulk_edit(pk_list)
1165
1165
 
1166
1166
  class BulkDeleteObjectsViewTestCase(ModelViewTestCase):
1167
1167
  """
@@ -1193,6 +1193,7 @@ class ViewTestCases:
1193
1193
  @override_settings(EXEMPT_VIEW_PERMISSIONS=[])
1194
1194
  def test_bulk_delete_objects_with_permission(self):
1195
1195
  pk_list = self.get_deletable_object_pks()
1196
+ initial_count = self._get_queryset().count()
1196
1197
  data = {
1197
1198
  "pk": pk_list,
1198
1199
  "confirm": True,
@@ -1200,18 +1201,11 @@ class ViewTestCases:
1200
1201
  }
1201
1202
 
1202
1203
  # Assign unconstrained permission
1203
- self.add_permissions(
1204
- f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}", "extras.view_jobresult"
1205
- )
1204
+ self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
1206
1205
 
1207
- response = self.client.post(self._get_url("bulk_delete"), data)
1208
- job_result = JobResult.objects.filter(name="Bulk Delete Objects").first()
1209
- self.assertRedirects(
1210
- response,
1211
- reverse("extras:jobresult", args=[job_result.pk]),
1212
- status_code=302,
1213
- target_status_code=200,
1214
- )
1206
+ # Try POST with model-level permission
1207
+ self.assertHttpStatus(self.client.post(self._get_url("bulk_delete"), data), 302)
1208
+ self.assertEqual(self._get_queryset().count(), initial_count - len(pk_list))
1215
1209
 
1216
1210
  @override_settings(EXEMPT_VIEW_PERMISSIONS=[])
1217
1211
  def test_bulk_delete_form_contains_all_objects(self):
@@ -1295,16 +1289,9 @@ class ViewTestCases:
1295
1289
  obj_perm.constraints = {"pk__isnull": False} # Match a non-existent pk (i.e., allow all)
1296
1290
  obj_perm.save()
1297
1291
 
1298
- # User would be redirected to Job Result therefore user needs to have permission to view Job Result
1299
- self.add_permissions("extras.view_jobresult")
1300
- response = self.client.post(self._get_url("bulk_delete"), data)
1301
- job_result = JobResult.objects.filter(name="Bulk Delete Objects").first()
1302
- self.assertRedirects(
1303
- response,
1304
- reverse("extras:jobresult", args=[job_result.pk]),
1305
- status_code=302,
1306
- target_status_code=200,
1307
- )
1292
+ # Bulk delete permitted objects
1293
+ self.assertHttpStatus(self.client.post(self._get_url("bulk_delete"), data), 302)
1294
+ self.assertEqual(self._get_queryset().count(), initial_count - len(pk_list))
1308
1295
 
1309
1296
  class BulkRenameObjectsViewTestCase(ModelViewTestCase):
1310
1297
  """
@@ -1454,8 +1441,6 @@ class ViewTestCases:
1454
1441
  maxDiff = None
1455
1442
  bulk_add_data = None
1456
1443
  """Used for bulk-add (distinct from bulk-create) view testing; self.bulk_create_data will be used if unset."""
1457
- selected_objects: list[ComponentModel]
1458
- selected_objects_parent_name: str
1459
1444
 
1460
1445
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
1461
1446
  def test_bulk_add_component(self):
@@ -9,6 +9,7 @@ from example_app.models import ExampleModel
9
9
  class AppHomeTestCase(SeleniumTestCase):
10
10
  """Integration test the Example App homepage extensions."""
11
11
 
12
+ fixtures = ["user-data.json"] # bob/bob
12
13
  layout = {
13
14
  "Organization": {
14
15
  "Locations": {"model": Location, "permission": "dcim.view_location"},
@@ -5,6 +5,7 @@ from nautobot.core.testing.integration import SeleniumTestCase
5
5
  class AppNavBarTestCase(SeleniumTestCase):
6
6
  """Integration test the navigation menu."""
7
7
 
8
+ fixtures = ["user-data.json"]
8
9
  navbar = {
9
10
  "Circuits": {
10
11
  "Circuits": {
@@ -11,6 +11,8 @@ from nautobot.extras.models import CustomField, CustomFieldChoice, Status
11
11
  class ListViewFilterTestCase(SeleniumTestCase):
12
12
  """Integration test for the list view filter ui."""
13
13
 
14
+ fixtures = ["user-data.json"]
15
+
14
16
  def setUp(self):
15
17
  super().setUp()
16
18
  self.login(self.user.username, self.password)
@@ -7,6 +7,7 @@ from nautobot.tenancy.models import Tenant
7
7
  class HomeTestCase(SeleniumTestCase):
8
8
  """Integration tests against the home page."""
9
9
 
10
+ fixtures = ["user-data.json"] # bob/bob
10
11
  layout = {
11
12
  "Organization": {
12
13
  "Locations": {"model": Location, "permission": "dcim.view_location"},
@@ -4,6 +4,7 @@ from nautobot.core.testing.integration import SeleniumTestCase
4
4
  class NavBarTestCase(SeleniumTestCase):
5
5
  """Integration test the navigation menu."""
6
6
 
7
+ fixtures = ["user-data.json"]
7
8
  navbar = {
8
9
  "Organization": {
9
10
  "Locations": {
@@ -31,7 +31,6 @@ class AuthenticationEnforcedTestCase(TestCase):
31
31
  "/health/",
32
32
  "/login/",
33
33
  "/media-failure/",
34
- "/robots.txt",
35
34
  "/template.css",
36
35
  ]:
37
36
  self.assertHttpStatus(response, 200, msg=url)
@@ -142,7 +142,7 @@ class NautobotTestRunner(DiscoverRunner):
142
142
  # branches/releases of Nautobot in separate files.
143
143
  hexdigest = hashlib.shake_128(
144
144
  ",".join(
145
- sorted(f"{m.app}.{m.name}" for m in MigrationRecorder.Migration.objects.all()) # pylint: disable=no-member
145
+ sorted(f"{m.app}.{m.name}" for m in MigrationRecorder.Migration.objects.all())
146
146
  ).encode("utf-8")
147
147
  ).hexdigest(10)
148
148
  command += ["--fixture-file", f"development/factory_dump.{hexdigest}.json"]