nautobot 2.3.16__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 (675) hide show
  1. nautobot/__init__.py +15 -0
  2. nautobot/apps/api.py +0 -2
  3. nautobot/apps/config.py +32 -3
  4. nautobot/apps/events.py +19 -0
  5. nautobot/apps/exceptions.py +0 -2
  6. nautobot/apps/ui.py +44 -9
  7. nautobot/apps/utils.py +0 -8
  8. nautobot/apps/views.py +2 -0
  9. nautobot/circuits/navigation.py +0 -57
  10. nautobot/circuits/tables.py +1 -2
  11. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -71
  12. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -64
  13. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +40 -0
  14. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +26 -0
  15. nautobot/circuits/templates/circuits/provider_retrieve.html +0 -76
  16. nautobot/circuits/tests/integration/test_relationships.py +33 -24
  17. nautobot/circuits/tests/test_filters.py +4 -8
  18. nautobot/circuits/views.py +143 -26
  19. nautobot/cloud/factory.py +4 -1
  20. nautobot/cloud/models.py +1 -1
  21. nautobot/cloud/tests/test_filters.py +5 -4
  22. nautobot/core/api/fields.py +5 -5
  23. nautobot/core/api/metadata.py +28 -256
  24. nautobot/core/api/pagination.py +3 -2
  25. nautobot/core/api/renderers.py +3 -0
  26. nautobot/core/api/serializers.py +24 -244
  27. nautobot/core/api/urls.py +3 -4
  28. nautobot/core/api/utils.py +0 -62
  29. nautobot/core/api/views.py +48 -158
  30. nautobot/core/apps/__init__.py +22 -578
  31. nautobot/core/celery/__init__.py +13 -0
  32. nautobot/core/celery/log.py +4 -4
  33. nautobot/core/celery/schedulers.py +48 -3
  34. nautobot/core/cli/__init__.py +8 -0
  35. nautobot/core/constants.py +7 -0
  36. nautobot/core/events/__init__.py +116 -0
  37. nautobot/core/events/base.py +27 -0
  38. nautobot/core/events/exceptions.py +10 -0
  39. nautobot/core/events/redis_broker.py +48 -0
  40. nautobot/core/events/syslog_broker.py +19 -0
  41. nautobot/core/exceptions.py +0 -6
  42. nautobot/core/filters.py +16 -21
  43. nautobot/core/fixtures/user-data.json +59 -0
  44. nautobot/core/forms/fields.py +53 -8
  45. nautobot/core/forms/utils.py +2 -1
  46. nautobot/core/graphql/schema.py +3 -1
  47. nautobot/core/graphql/types.py +1 -1
  48. nautobot/core/jobs/__init__.py +4 -4
  49. nautobot/core/jobs/cleanup.py +13 -49
  50. nautobot/core/jobs/groups.py +1 -1
  51. nautobot/core/management/commands/generate_test_data.py +21 -0
  52. nautobot/core/management/commands/validate_models.py +1 -1
  53. nautobot/core/middleware.py +16 -0
  54. nautobot/core/models/__init__.py +1 -1
  55. nautobot/core/models/fields.py +11 -7
  56. nautobot/core/models/query_functions.py +2 -2
  57. nautobot/core/models/tree_queries.py +3 -6
  58. nautobot/core/settings.py +44 -7
  59. nautobot/core/settings.yaml +86 -8
  60. nautobot/core/tables.py +15 -65
  61. nautobot/core/tasks.py +1 -1
  62. nautobot/core/templates/components/button/default.html +7 -0
  63. nautobot/core/templates/components/button/dropdown.html +20 -0
  64. nautobot/core/templates/components/layout/one_over_two.html +19 -0
  65. nautobot/core/templates/components/layout/two_over_one.html +19 -0
  66. nautobot/core/templates/components/panel/body_content_data_table.html +27 -0
  67. nautobot/core/templates/components/panel/body_content_objects_table.html +4 -0
  68. nautobot/core/templates/components/panel/body_content_tags.html +6 -0
  69. nautobot/core/templates/components/panel/body_content_text.html +12 -0
  70. nautobot/core/templates/components/panel/body_wrapper_generic.html +3 -0
  71. nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +3 -0
  72. nautobot/core/templates/components/panel/body_wrapper_table.html +3 -0
  73. nautobot/core/templates/components/panel/footer_contacts_table.html +20 -0
  74. nautobot/core/templates/components/panel/footer_content_table.html +14 -0
  75. nautobot/core/templates/components/panel/grouping_toggle.html +14 -0
  76. nautobot/core/templates/components/panel/header_extra_content_table.html +3 -0
  77. nautobot/core/templates/components/panel/panel.html +16 -0
  78. nautobot/core/templates/components/panel/stats_panel_body.html +8 -0
  79. nautobot/core/templates/components/tab/content_wrapper.html +3 -0
  80. nautobot/core/templates/components/tab/label_wrapper.html +5 -0
  81. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +3 -0
  82. nautobot/core/templates/generic/object_retrieve.html +28 -17
  83. nautobot/core/templates/inc/computed_fields/panel_data.html +4 -7
  84. nautobot/core/templates/inc/custom_fields/panel.html +2 -2
  85. nautobot/core/templates/inc/custom_fields/panel_data.html +4 -7
  86. nautobot/core/templates/inc/footer.html +1 -0
  87. nautobot/core/templates/inc/media.html +0 -3
  88. nautobot/core/templates/inc/nav_menu.html +1 -1
  89. nautobot/core/templates/inc/relationships_panel.html +1 -1
  90. nautobot/core/templates/nautobot_config.py.j2 +3 -3
  91. nautobot/core/templates/panel_table.html +12 -0
  92. nautobot/core/templates/search.html +0 -7
  93. nautobot/core/templates/utilities/render_jinja2.html +117 -0
  94. nautobot/core/templatetags/helpers.py +101 -12
  95. nautobot/core/templatetags/ui_framework.py +40 -0
  96. nautobot/core/testing/api.py +23 -128
  97. nautobot/core/testing/context.py +18 -0
  98. nautobot/core/testing/filters.py +41 -58
  99. nautobot/core/testing/mixins.py +2 -7
  100. nautobot/core/testing/views.py +25 -123
  101. nautobot/core/tests/integration/test_app_home.py +1 -0
  102. nautobot/core/tests/integration/test_app_navbar.py +1 -0
  103. nautobot/core/tests/integration/test_filters.py +2 -0
  104. nautobot/core/tests/integration/test_home.py +1 -0
  105. nautobot/core/tests/integration/test_navbar.py +1 -0
  106. nautobot/core/tests/integration/test_view_authentication.py +1 -2
  107. nautobot/core/tests/nautobot_config.py +198 -0
  108. nautobot/core/tests/runner.py +3 -3
  109. nautobot/core/tests/test_api.py +82 -201
  110. nautobot/core/tests/test_csv.py +3 -25
  111. nautobot/core/tests/test_events.py +214 -0
  112. nautobot/core/tests/test_jinja_filters.py +1 -0
  113. nautobot/core/tests/test_jobs.py +84 -13
  114. nautobot/core/tests/test_navigations.py +7 -241
  115. nautobot/core/tests/test_templatetags_helpers.py +16 -0
  116. nautobot/core/tests/test_ui.py +150 -0
  117. nautobot/core/tests/test_utils.py +0 -25
  118. nautobot/core/tests/test_views.py +123 -31
  119. nautobot/core/ui/__init__.py +0 -0
  120. nautobot/core/ui/base.py +11 -0
  121. nautobot/core/ui/choices.py +44 -0
  122. nautobot/core/ui/homepage.py +167 -0
  123. nautobot/core/ui/nav.py +279 -0
  124. nautobot/core/ui/object_detail.py +1841 -0
  125. nautobot/core/ui/utils.py +36 -0
  126. nautobot/core/urls.py +4 -9
  127. nautobot/core/utils/config.py +30 -3
  128. nautobot/core/utils/lookup.py +20 -13
  129. nautobot/core/views/__init__.py +6 -1
  130. nautobot/core/views/generic.py +47 -52
  131. nautobot/core/views/mixins.py +15 -25
  132. nautobot/core/views/paginator.py +8 -5
  133. nautobot/core/views/renderers.py +3 -3
  134. nautobot/core/views/utils.py +11 -0
  135. nautobot/core/wsgi.py +3 -3
  136. nautobot/dcim/api/serializers.py +80 -179
  137. nautobot/dcim/api/urls.py +5 -0
  138. nautobot/dcim/api/views.py +17 -4
  139. nautobot/dcim/apps.py +1 -0
  140. nautobot/dcim/choices.py +28 -0
  141. nautobot/dcim/factory.py +58 -0
  142. nautobot/dcim/filters/__init__.py +197 -24
  143. nautobot/dcim/forms.py +203 -12
  144. nautobot/dcim/graphql/types.py +2 -2
  145. nautobot/dcim/migrations/0063_interfacevdcassignment_virtualdevicecontext_and_more.py +165 -0
  146. nautobot/dcim/migrations/0064_virtualdevicecontext_status_data_migration.py +28 -0
  147. nautobot/dcim/migrations/0065_controller_capabilities_and_more.py +29 -0
  148. nautobot/dcim/migrations/0066_controllermanageddevicegroup_radio_profiles_and_more.py +33 -0
  149. nautobot/dcim/models/__init__.py +4 -0
  150. nautobot/dcim/models/device_component_templates.py +2 -2
  151. nautobot/dcim/models/device_components.py +20 -22
  152. nautobot/dcim/models/devices.py +173 -4
  153. nautobot/dcim/models/locations.py +3 -3
  154. nautobot/dcim/models/power.py +5 -6
  155. nautobot/dcim/models/racks.py +6 -6
  156. nautobot/dcim/navigation.py +25 -224
  157. nautobot/dcim/signals.py +44 -0
  158. nautobot/dcim/tables/__init__.py +5 -3
  159. nautobot/dcim/tables/devices.py +96 -2
  160. nautobot/dcim/tables/devicetypes.py +2 -2
  161. nautobot/dcim/templates/dcim/controller/base.html +10 -0
  162. nautobot/dcim/templates/dcim/controller_create.html +1 -0
  163. nautobot/dcim/templates/dcim/controller_retrieve.html +5 -1
  164. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
  165. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +66 -0
  166. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +46 -0
  167. nautobot/dcim/templates/dcim/device/base.html +6 -42
  168. nautobot/dcim/templates/dcim/device/wireless.html +73 -0
  169. nautobot/dcim/templates/dcim/device.html +3 -1
  170. nautobot/dcim/templates/dcim/interface.html +1 -0
  171. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  172. nautobot/dcim/templates/dcim/locationtype.html +0 -107
  173. nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
  174. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +76 -0
  175. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +34 -0
  176. nautobot/dcim/tests/test_api.py +172 -61
  177. nautobot/dcim/tests/test_filters.py +171 -109
  178. nautobot/dcim/tests/test_forms.py +2 -51
  179. nautobot/dcim/tests/test_graphql.py +0 -52
  180. nautobot/dcim/tests/test_models.py +126 -4
  181. nautobot/dcim/tests/test_signals.py +1 -0
  182. nautobot/dcim/tests/test_views.py +103 -11
  183. nautobot/dcim/urls.py +72 -27
  184. nautobot/dcim/utils.py +2 -2
  185. nautobot/dcim/views.py +369 -62
  186. nautobot/extras/api/customfields.py +2 -2
  187. nautobot/extras/api/serializers.py +91 -75
  188. nautobot/extras/api/urls.py +4 -0
  189. nautobot/extras/api/views.py +78 -15
  190. nautobot/extras/choices.py +13 -0
  191. nautobot/extras/constants.py +0 -1
  192. nautobot/extras/context_managers.py +23 -6
  193. nautobot/extras/datasources/git.py +4 -1
  194. nautobot/extras/factory.py +27 -0
  195. nautobot/extras/filters/__init__.py +59 -0
  196. nautobot/extras/forms/forms.py +125 -30
  197. nautobot/extras/forms/mixins.py +3 -11
  198. nautobot/extras/graphql/types.py +25 -1
  199. nautobot/extras/group_sync.py +3 -3
  200. nautobot/extras/health_checks.py +2 -1
  201. nautobot/extras/jobs.py +62 -26
  202. nautobot/extras/management/__init__.py +1 -0
  203. nautobot/extras/management/commands/runjob.py +7 -79
  204. nautobot/extras/management/commands/runjob_with_job_result.py +46 -0
  205. nautobot/extras/management/utils.py +87 -0
  206. nautobot/extras/managers.py +1 -3
  207. nautobot/extras/migrations/0018_joblog_data_migration.py +9 -7
  208. nautobot/extras/migrations/0117_create_job_queue_model.py +129 -0
  209. nautobot/extras/migrations/0118_task_queue_to_job_queue_migration.py +78 -0
  210. nautobot/extras/migrations/0119_remove_task_queues_from_job_and_queue_from_scheduled_job.py +28 -0
  211. nautobot/extras/models/__init__.py +4 -0
  212. nautobot/extras/models/change_logging.py +7 -3
  213. nautobot/extras/models/customfields.py +11 -12
  214. nautobot/extras/models/groups.py +9 -13
  215. nautobot/extras/models/jobs.py +218 -37
  216. nautobot/extras/models/models.py +2 -2
  217. nautobot/extras/models/relationships.py +69 -1
  218. nautobot/extras/models/secrets.py +5 -0
  219. nautobot/extras/navigation.py +20 -262
  220. nautobot/extras/plugins/__init__.py +56 -32
  221. nautobot/extras/plugins/marketplace_manifest.yml +450 -0
  222. nautobot/extras/plugins/urls.py +1 -0
  223. nautobot/extras/plugins/views.py +48 -1
  224. nautobot/extras/signals.py +39 -1
  225. nautobot/extras/tables.py +40 -6
  226. nautobot/extras/templates/extras/externalintegration_retrieve.html +0 -47
  227. nautobot/extras/templates/extras/inc/tags_panel.html +1 -5
  228. nautobot/extras/templates/extras/job_bulk_edit.html +2 -1
  229. nautobot/extras/templates/extras/job_detail.html +36 -6
  230. nautobot/extras/templates/extras/job_edit.html +5 -2
  231. nautobot/extras/templates/extras/job_list.html +2 -7
  232. nautobot/extras/templates/extras/jobqueue_retrieve.html +44 -0
  233. nautobot/extras/templates/extras/marketplace.html +278 -0
  234. nautobot/extras/templates/extras/plugins_list.html +35 -1
  235. nautobot/extras/templates/extras/plugins_tiles.html +79 -0
  236. nautobot/extras/templates/extras/role_retrieve.html +16 -0
  237. nautobot/extras/templates/extras/secret.html +0 -65
  238. nautobot/extras/templates/extras/secret_check.js +16 -0
  239. nautobot/extras/templates/extras/secret_create.html +114 -0
  240. nautobot/extras/templates/extras/secret_edit.html +1 -114
  241. nautobot/extras/templates/extras/secretsgroup_edit.html +1 -1
  242. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -0
  243. nautobot/extras/templatetags/job_buttons.py +5 -4
  244. nautobot/extras/templatetags/plugins.py +69 -6
  245. nautobot/extras/test_jobs/api_test_job.py +1 -1
  246. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  247. nautobot/extras/test_jobs/dry_run.py +1 -1
  248. nautobot/extras/test_jobs/fail.py +5 -5
  249. nautobot/extras/test_jobs/file_output.py +1 -1
  250. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  251. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  252. nautobot/extras/test_jobs/ipaddress_vars.py +1 -3
  253. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  254. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  255. nautobot/extras/test_jobs/log_redaction.py +1 -1
  256. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  257. nautobot/extras/test_jobs/modify_db.py +1 -1
  258. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  259. nautobot/extras/test_jobs/object_var_required.py +1 -1
  260. nautobot/extras/test_jobs/object_vars.py +1 -1
  261. nautobot/extras/test_jobs/pass.py +3 -3
  262. nautobot/extras/test_jobs/profiling.py +1 -1
  263. nautobot/extras/test_jobs/relative_import.py +3 -3
  264. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  265. nautobot/extras/test_jobs/task_queues.py +1 -1
  266. nautobot/extras/tests/integration/test_plugin_banner.py +2 -0
  267. nautobot/extras/tests/test_api.py +157 -55
  268. nautobot/extras/tests/test_context_managers.py +4 -1
  269. nautobot/extras/tests/test_customfields.py +1 -1
  270. nautobot/extras/tests/test_datasources.py +1 -2
  271. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  272. nautobot/extras/tests/test_filters.py +219 -535
  273. nautobot/extras/tests/test_forms.py +1 -20
  274. nautobot/extras/tests/test_job_variables.py +73 -152
  275. nautobot/extras/tests/test_jobs.py +43 -54
  276. nautobot/extras/tests/test_models.py +71 -16
  277. nautobot/extras/tests/test_relationships.py +5 -2
  278. nautobot/extras/tests/test_utils.py +23 -2
  279. nautobot/extras/tests/test_views.py +183 -43
  280. nautobot/extras/tests/test_webhooks.py +2 -1
  281. nautobot/extras/urls.py +2 -20
  282. nautobot/extras/utils.py +118 -4
  283. nautobot/extras/views.py +203 -92
  284. nautobot/extras/webhooks.py +5 -2
  285. nautobot/ipam/api/fields.py +3 -3
  286. nautobot/ipam/api/serializers.py +36 -137
  287. nautobot/ipam/api/views.py +93 -62
  288. nautobot/ipam/lookups.py +62 -101
  289. nautobot/ipam/models.py +11 -63
  290. nautobot/ipam/navigation.py +0 -90
  291. nautobot/ipam/querysets.py +2 -2
  292. nautobot/ipam/tables.py +6 -20
  293. nautobot/ipam/templates/ipam/routetarget.html +0 -28
  294. nautobot/ipam/templates/ipam/vrf.html +0 -47
  295. nautobot/ipam/tests/test_api.py +8 -419
  296. nautobot/ipam/tests/test_filters.py +39 -119
  297. nautobot/ipam/tests/test_forms.py +47 -51
  298. nautobot/ipam/tests/test_migrations.py +30 -30
  299. nautobot/ipam/tests/test_models.py +0 -41
  300. nautobot/ipam/tests/test_querysets.py +1 -63
  301. nautobot/ipam/urls.py +3 -69
  302. nautobot/ipam/utils/__init__.py +0 -24
  303. nautobot/ipam/views.py +153 -198
  304. nautobot/project-static/css/base.css +38 -3
  305. nautobot/project-static/docs/404.html +421 -19
  306. nautobot/project-static/docs/apps/index.html +421 -19
  307. nautobot/project-static/docs/apps/nautobot-apps.html +421 -19
  308. nautobot/project-static/docs/assets/extra.css +5 -1
  309. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.83f73b43.min.js} +2 -2
  310. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.83f73b43.min.js.map} +2 -2
  311. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +421 -19
  312. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +421 -19
  313. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +421 -172
  314. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +421 -19
  315. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +421 -19
  316. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +425 -21
  317. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +421 -19
  318. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +424 -22
  319. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9809 -0
  320. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +424 -63
  321. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +421 -19
  322. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +421 -19
  323. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +457 -20
  324. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +421 -19
  325. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +425 -25
  326. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +457 -19
  327. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +421 -19
  328. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +421 -19
  329. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +425 -215
  330. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +430 -342
  331. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +5799 -1054
  332. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +421 -19
  333. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +447 -176
  334. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +460 -21
  335. nautobot/project-static/docs/development/apps/api/configuration-view.html +421 -19
  336. nautobot/project-static/docs/development/apps/api/database-backend-config.html +421 -19
  337. nautobot/project-static/docs/development/apps/api/models/django-admin.html +421 -19
  338. nautobot/project-static/docs/development/apps/api/models/global-search.html +421 -19
  339. nautobot/project-static/docs/development/apps/api/models/graphql.html +421 -19
  340. nautobot/project-static/docs/development/apps/api/models/index.html +421 -19
  341. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +421 -19
  342. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +421 -19
  343. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +421 -19
  344. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +421 -19
  345. nautobot/project-static/docs/development/apps/api/platform-features/index.html +421 -19
  346. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +421 -19
  347. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +421 -19
  348. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +421 -19
  349. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +421 -19
  350. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +424 -41
  351. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +421 -19
  352. nautobot/project-static/docs/development/apps/api/prometheus.html +421 -19
  353. nautobot/project-static/docs/development/apps/api/setup.html +425 -155
  354. nautobot/project-static/docs/development/apps/api/testing.html +421 -19
  355. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +421 -19
  356. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +421 -19
  357. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +421 -19
  358. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +421 -19
  359. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +701 -130
  360. nautobot/project-static/docs/development/apps/api/views/base-template.html +421 -19
  361. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +421 -19
  362. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +421 -19
  363. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +421 -19
  364. nautobot/project-static/docs/development/apps/api/views/index.html +423 -20
  365. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +425 -19
  366. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +451 -19
  367. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +421 -19
  368. nautobot/project-static/docs/development/apps/api/views/notes.html +421 -19
  369. nautobot/project-static/docs/development/apps/api/views/rest-api.html +421 -19
  370. nautobot/project-static/docs/development/apps/api/views/urls.html +421 -19
  371. nautobot/project-static/docs/development/apps/index.html +421 -19
  372. nautobot/project-static/docs/development/apps/migration/code-updates.html +422 -52
  373. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +422 -20
  374. nautobot/project-static/docs/development/apps/migration/from-v1.html +421 -19
  375. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +421 -19
  376. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +421 -19
  377. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +421 -19
  378. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +424 -22
  379. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9219 -0
  380. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9333 -0
  381. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9474 -0
  382. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9517 -0
  383. nautobot/project-static/docs/development/apps/porting-from-netbox.html +424 -22
  384. nautobot/project-static/docs/development/core/application-registry.html +421 -19
  385. nautobot/project-static/docs/development/core/best-practices.html +421 -19
  386. nautobot/project-static/docs/development/core/bootstrap-ui.html +421 -19
  387. nautobot/project-static/docs/development/core/caching.html +421 -19
  388. nautobot/project-static/docs/development/core/controllers.html +423 -19
  389. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +490 -45
  390. nautobot/project-static/docs/development/core/generic-views.html +421 -19
  391. nautobot/project-static/docs/development/core/getting-started.html +566 -179
  392. nautobot/project-static/docs/development/core/homepage.html +432 -30
  393. nautobot/project-static/docs/development/core/index.html +421 -19
  394. nautobot/project-static/docs/development/core/local-k8s.html +9453 -0
  395. nautobot/project-static/docs/development/core/model-checklist.html +424 -22
  396. nautobot/project-static/docs/development/core/model-features.html +421 -19
  397. nautobot/project-static/docs/development/core/natural-keys.html +421 -19
  398. nautobot/project-static/docs/development/core/navigation-menu.html +438 -26
  399. nautobot/project-static/docs/development/core/release-checklist.html +435 -45
  400. nautobot/project-static/docs/development/core/role-internals.html +421 -19
  401. nautobot/project-static/docs/development/core/settings.html +421 -19
  402. nautobot/project-static/docs/development/core/style-guide.html +421 -19
  403. nautobot/project-static/docs/development/core/templates.html +431 -22
  404. nautobot/project-static/docs/development/core/testing.html +421 -19
  405. nautobot/project-static/docs/development/core/ui-component-framework.html +11020 -0
  406. nautobot/project-static/docs/development/core/user-preferences.html +424 -22
  407. nautobot/project-static/docs/development/index.html +421 -19
  408. nautobot/project-static/docs/development/jobs/index.html +546 -160
  409. nautobot/project-static/docs/development/jobs/migration/from-v1.html +421 -19
  410. nautobot/project-static/docs/index.html +421 -19
  411. nautobot/project-static/docs/media/development/core/ui-component-framework/basic-panel-layout.png +0 -0
  412. nautobot/project-static/docs/media/development/core/ui-component-framework/button-example.png +0 -0
  413. nautobot/project-static/docs/media/development/core/ui-component-framework/dropdown-button-example.png +0 -0
  414. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-1.png +0 -0
  415. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-2.png +0 -0
  416. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example.png +0 -0
  417. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example.png +0 -0
  418. nautobot/project-static/docs/media/development/core/ui-component-framework/table-panels-family.png +0 -0
  419. nautobot/project-static/docs/media/development/core/ui-component-framework/text-panels-family.png +0 -0
  420. nautobot/project-static/docs/media/development/core/ui-component-framework/ui-framework-example.png +0 -0
  421. nautobot/project-static/docs/media/models/virtual_device_context_overview.drawio +73 -0
  422. nautobot/project-static/docs/media/models/virtual_device_context_overview.png +0 -0
  423. nautobot/project-static/docs/models/dcim/virtualdevicecontext.html +14 -0
  424. nautobot/project-static/docs/models/extras/jobqueue.html +14 -0
  425. nautobot/project-static/docs/models/wireless/radioprofile.html +14 -0
  426. nautobot/project-static/docs/models/wireless/supporteddatarate.html +14 -0
  427. nautobot/project-static/docs/models/wireless/wirelessnetwork.html +14 -0
  428. nautobot/project-static/docs/objects.inv +0 -0
  429. nautobot/project-static/docs/overview/application_stack.html +426 -20
  430. nautobot/project-static/docs/overview/design_philosophy.html +421 -19
  431. nautobot/project-static/docs/release-notes/index.html +445 -22
  432. nautobot/project-static/docs/release-notes/version-1.0.html +421 -19
  433. nautobot/project-static/docs/release-notes/version-1.1.html +421 -19
  434. nautobot/project-static/docs/release-notes/version-1.2.html +421 -19
  435. nautobot/project-static/docs/release-notes/version-1.3.html +421 -19
  436. nautobot/project-static/docs/release-notes/version-1.4.html +421 -19
  437. nautobot/project-static/docs/release-notes/version-1.5.html +421 -19
  438. nautobot/project-static/docs/release-notes/version-1.6.html +634 -667
  439. nautobot/project-static/docs/release-notes/version-2.0.html +421 -19
  440. nautobot/project-static/docs/release-notes/version-2.1.html +421 -19
  441. nautobot/project-static/docs/release-notes/version-2.2.html +421 -19
  442. nautobot/project-static/docs/release-notes/version-2.3.html +684 -886
  443. nautobot/project-static/docs/release-notes/version-2.4.html +10007 -0
  444. nautobot/project-static/docs/requirements.txt +2 -2
  445. nautobot/project-static/docs/search/search_index.json +1 -1
  446. nautobot/project-static/docs/sitemap.xml +334 -270
  447. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  448. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +421 -19
  449. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +421 -19
  450. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +423 -21
  451. nautobot/project-static/docs/user-guide/administration/configuration/index.html +433 -32
  452. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +421 -19
  453. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +765 -180
  454. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +421 -19
  455. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +421 -19
  456. nautobot/project-static/docs/user-guide/administration/guides/docker.html +434 -29
  457. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +421 -19
  458. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +421 -19
  459. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +421 -19
  460. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +421 -19
  461. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +421 -19
  462. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +421 -19
  463. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +421 -19
  464. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +421 -19
  465. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +421 -19
  466. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +421 -19
  467. nautobot/project-static/docs/user-guide/administration/installation/index.html +426 -20
  468. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +421 -19
  469. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +421 -19
  470. nautobot/project-static/docs/user-guide/administration/installation/services.html +421 -19
  471. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +421 -19
  472. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +442 -41
  473. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +435 -66
  474. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +435 -66
  475. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +421 -19
  476. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +421 -19
  477. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +421 -19
  478. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +421 -19
  479. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +421 -19
  480. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +421 -19
  481. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +421 -19
  482. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-nautobot-app-location.yaml +0 -16
  483. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +421 -19
  484. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +427 -21
  485. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +421 -19
  486. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +421 -19
  487. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +421 -19
  488. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +421 -19
  489. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +421 -19
  490. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +421 -19
  491. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +421 -19
  492. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +421 -19
  493. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +421 -19
  494. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +421 -19
  495. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +421 -19
  496. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +421 -19
  497. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +421 -19
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +421 -19
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +421 -19
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +421 -19
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +421 -19
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +457 -20
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +447 -22
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +421 -19
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +421 -19
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +421 -19
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +421 -19
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +421 -19
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +421 -19
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +421 -19
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +421 -19
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +421 -19
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +421 -19
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +421 -19
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +421 -19
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +421 -19
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +421 -19
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +421 -19
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +421 -19
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +421 -19
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +421 -19
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +421 -19
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +421 -19
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +421 -19
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +421 -19
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +421 -19
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +421 -19
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +421 -19
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +421 -19
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +421 -19
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +421 -19
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +421 -19
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +421 -19
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +421 -19
  535. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +421 -19
  536. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +421 -19
  537. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +421 -19
  538. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9333 -0
  539. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +424 -22
  540. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +421 -19
  541. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +421 -19
  542. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +421 -19
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +421 -19
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +421 -19
  545. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +421 -19
  546. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +421 -19
  547. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +421 -19
  548. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +421 -19
  549. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +421 -19
  550. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +421 -19
  551. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +421 -19
  552. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +421 -19
  553. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +421 -19
  554. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +421 -19
  555. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +421 -19
  556. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +421 -19
  557. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +421 -19
  558. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +421 -19
  559. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +424 -22
  560. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9271 -0
  561. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9175 -0
  562. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9169 -0
  563. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9235 -0
  564. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +421 -19
  565. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +421 -19
  566. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +421 -19
  567. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +421 -19
  568. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +421 -19
  569. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +421 -19
  570. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +421 -19
  571. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +421 -19
  572. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +421 -19
  573. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +421 -19
  574. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +421 -19
  575. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +421 -19
  576. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +421 -19
  577. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +421 -19
  578. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +421 -19
  579. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +421 -19
  580. nautobot/project-static/docs/user-guide/index.html +421 -19
  581. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +424 -22
  582. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +421 -19
  583. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +421 -19
  584. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +421 -19
  585. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +421 -19
  586. nautobot/project-static/docs/user-guide/platform-functionality/events.html +9575 -0
  587. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +424 -22
  588. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +421 -19
  589. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +421 -19
  590. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +421 -19
  591. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +421 -19
  592. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +421 -19
  593. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +426 -20
  594. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +424 -22
  595. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +421 -19
  596. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +421 -19
  597. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9182 -0
  598. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +424 -22
  599. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +421 -19
  600. nautobot/project-static/docs/user-guide/platform-functionality/note.html +421 -19
  601. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +421 -19
  602. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +424 -22
  603. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9250 -0
  604. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +421 -19
  605. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +421 -19
  606. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +424 -22
  607. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +421 -19
  608. nautobot/project-static/docs/user-guide/platform-functionality/role.html +421 -19
  609. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +421 -19
  610. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +421 -19
  611. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +424 -22
  612. nautobot/project-static/docs/user-guide/platform-functionality/status.html +421 -19
  613. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +421 -19
  614. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +489 -56
  615. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +421 -19
  616. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +421 -19
  617. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +421 -19
  618. nautobot/project-static/img/jinja_logo.svg +97 -0
  619. nautobot/project-static/js/forms.js +5 -0
  620. nautobot/project-static/js/nav_menu.js +2 -1
  621. nautobot/tenancy/api/serializers.py +0 -2
  622. nautobot/tenancy/factory.py +1 -1
  623. nautobot/tenancy/navigation.py +0 -29
  624. nautobot/tenancy/templates/tenancy/tenant.html +4 -91
  625. nautobot/tenancy/tests/test_filters.py +29 -134
  626. nautobot/tenancy/views.py +32 -23
  627. nautobot/users/admin.py +3 -1
  628. nautobot/users/api/serializers.py +4 -5
  629. nautobot/users/api/views.py +1 -1
  630. nautobot/users/forms.py +19 -0
  631. nautobot/users/templates/users/preferences.html +22 -0
  632. nautobot/users/tests/test_filters.py +1 -19
  633. nautobot/users/tests/test_views.py +57 -0
  634. nautobot/users/utils.py +8 -0
  635. nautobot/users/views.py +48 -11
  636. nautobot/virtualization/api/serializers.py +4 -4
  637. nautobot/virtualization/filters.py +2 -20
  638. nautobot/virtualization/navigation.py +0 -48
  639. nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
  640. nautobot/virtualization/tests/test_filters.py +57 -183
  641. nautobot/virtualization/views.py +18 -15
  642. nautobot/wireless/__init__.py +0 -0
  643. nautobot/wireless/api/__init__.py +0 -0
  644. nautobot/wireless/api/serializers.py +44 -0
  645. nautobot/wireless/api/urls.py +20 -0
  646. nautobot/wireless/api/views.py +34 -0
  647. nautobot/wireless/apps.py +8 -0
  648. nautobot/wireless/choices.py +345 -0
  649. nautobot/wireless/factory.py +138 -0
  650. nautobot/wireless/filters.py +167 -0
  651. nautobot/wireless/forms.py +283 -0
  652. nautobot/wireless/homepage.py +19 -0
  653. nautobot/wireless/migrations/0001_initial.py +223 -0
  654. nautobot/wireless/migrations/__init__.py +0 -0
  655. nautobot/wireless/models.py +207 -0
  656. nautobot/wireless/navigation.py +105 -0
  657. nautobot/wireless/tables.py +244 -0
  658. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +81 -0
  659. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +26 -0
  660. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +88 -0
  661. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +64 -0
  662. nautobot/wireless/tests/__init__.py +0 -0
  663. nautobot/wireless/tests/test_api.py +247 -0
  664. nautobot/wireless/tests/test_filters.py +54 -0
  665. nautobot/wireless/tests/test_models.py +22 -0
  666. nautobot/wireless/tests/test_views.py +378 -0
  667. nautobot/wireless/urls.py +13 -0
  668. nautobot/wireless/views.py +129 -0
  669. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/METADATA +11 -14
  670. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/RECORD +674 -551
  671. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/WHEEL +1 -1
  672. nautobot/core/utils/navigation.py +0 -54
  673. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/LICENSE.txt +0 -0
  674. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/NOTICE +0 -0
  675. {nautobot-2.3.16.dist-info → nautobot-2.4.0b1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,18 @@
1
+ from django.test.utils import TestContextDecorator
2
+
3
+ from nautobot.core.events import _EVENT_BROKERS, deregister_event_broker, load_event_brokers
4
+
5
+
6
+ class load_event_broker_override_settings(TestContextDecorator):
7
+ def __init__(self, **kwargs) -> None:
8
+ self.options = kwargs
9
+ super().__init__()
10
+
11
+ def enable(self):
12
+ """Registered event brokers"""
13
+ load_event_brokers(self.options["EVENT_BROKERS"])
14
+
15
+ def disable(self):
16
+ """Clear all registered event brokers"""
17
+ for event_broker in _EVENT_BROKERS:
18
+ deregister_event_broker(event_broker)
@@ -1,7 +1,6 @@
1
- from __future__ import annotations # python 3.8
2
-
3
1
  import random
4
2
  import string
3
+ from typing import ClassVar, Iterable
5
4
 
6
5
  from django.contrib.contenttypes.models import ContentType
7
6
  from django.db.models import Count, Q, QuerySet
@@ -21,6 +20,7 @@ from nautobot.core.filters import (
21
20
  )
22
21
  from nautobot.core.models.generics import PrimaryModel
23
22
  from nautobot.core.testing import views
23
+ from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
24
24
  from nautobot.extras.models import Contact, ContactAssociation, Role, Status, Tag, Team
25
25
  from nautobot.tenancy import models
26
26
 
@@ -30,8 +30,6 @@ class FilterTestCases:
30
30
  class BaseFilterTestCase(views.TestCase):
31
31
  """Base class for testing of FilterSets."""
32
32
 
33
- queryset: 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: 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 = []
@@ -85,34 +84,18 @@ class FilterTestCases:
85
84
  # ["filter1"],
86
85
  # ["filter2", "field2__name"],
87
86
  # ]
88
- generic_filter_tests = []
87
+ generic_filter_tests: ClassVar[Iterable]
88
+
89
+ def setUp(self):
90
+ for attr in ["queryset", "filterset", "generic_filter_tests"]:
91
+ if not hasattr(self, attr):
92
+ raise NotImplementedError(f'{self} is missing a value for required attribute "{attr}"')
93
+ super().setUp()
89
94
 
90
95
  def get_q_filter(self):
91
96
  """Helper method to return q filter."""
92
97
  return self.filterset.declared_filters["q"].filter_predicates
93
98
 
94
- def test_id(self):
95
- """Verify that the filterset supports filtering by id with only lookup `__n`."""
96
- with self.subTest("Assert `id`"):
97
- params = {"id": list(self.queryset.values_list("pk", flat=True)[:2])}
98
- expected_queryset = self.queryset.filter(id__in=params["id"])
99
- filterset = self.filterset(params, self.queryset)
100
- self.assertTrue(filterset.is_valid())
101
- self.assertQuerysetEqualAndNotEmpty(filterset.qs.order_by("id"), expected_queryset.order_by("id"))
102
-
103
- with self.subTest("Assert negate lookup"):
104
- params = {"id__n": list(self.queryset.values_list("pk", flat=True)[:2])}
105
- expected_queryset = self.queryset.exclude(id__in=params["id__n"])
106
- filterset = self.filterset(params, self.queryset)
107
- self.assertTrue(filterset.is_valid())
108
- self.assertQuerysetEqualAndNotEmpty(filterset.qs.order_by("id"), expected_queryset.order_by("id"))
109
-
110
- with self.subTest("Assert invalid lookup"):
111
- params = {"id__in": list(self.queryset.values_list("pk", flat=True)[:2])}
112
- filterset = self.filterset(params, self.queryset)
113
- self.assertFalse(filterset.is_valid())
114
- self.assertIn("Unknown filter field", filterset.errors.as_text())
115
-
116
99
  def test_invalid_filter(self):
117
100
  """Verify that the filterset reports as invalid when initialized with an unsupported filter parameter."""
118
101
  params = {"ice_cream_flavor": ["chocolate"]}
@@ -141,15 +124,22 @@ class FilterTestCases:
141
124
  )
142
125
  This expects a field named `devices` on the model and a filter named `devices` on the filterset.
143
126
  """
127
+ if not any(test[0] == "id" for test in self.generic_filter_tests):
128
+ self.generic_filter_tests = (["id"], *self.generic_filter_tests)
129
+
144
130
  if getattr(self.queryset.model, "is_contact_associable_model", False):
145
- for generic_filter_test in (
146
- ["contacts", "associated_contacts__contact__name"],
147
- ["contacts", "associated_contacts__contact__id"],
148
- ["teams", "associated_contacts__team__name"],
149
- ["teams", "associated_contacts__team__id"],
150
- ):
151
- if generic_filter_test not in self.generic_filter_tests:
152
- self.generic_filter_tests = (*self.generic_filter_tests, generic_filter_test)
131
+ if not any(test[0] == "contacts" for test in self.generic_filter_tests):
132
+ self.generic_filter_tests = (
133
+ *self.generic_filter_tests,
134
+ ["contacts", "associated_contacts__contact__name"],
135
+ ["contacts", "associated_contacts__contact__id"],
136
+ )
137
+ if not any(test[0] == "teams" for test in self.generic_filter_tests):
138
+ self.generic_filter_tests = (
139
+ *self.generic_filter_tests,
140
+ ["teams", "associated_contacts__team__name"],
141
+ ["teams", "associated_contacts__team__id"],
142
+ )
153
143
 
154
144
  # Make sure we have at least 3 contacts and 3 teams in the database
155
145
  if Contact.objects.count() < 3:
@@ -177,9 +167,6 @@ class FilterTestCases:
177
167
  status=Status.objects.get_for_model(ContactAssociation).last(),
178
168
  )
179
169
 
180
- if not self.generic_filter_tests:
181
- self.skipTest("No generic_filter_tests defined?")
182
-
183
170
  for test in self.generic_filter_tests:
184
171
  filter_name = test[0]
185
172
  field_name = test[-1] # default to filter_name if a second list item was not supplied
@@ -238,10 +225,6 @@ class FilterTestCases:
238
225
  qs_result = self.queryset.filter(tags=tags[0]).filter(tags=tags[1]).distinct()
239
226
  self.assertQuerysetEqualAndNotEmpty(filterset_result, qs_result)
240
227
 
241
- def test_q_filter_exists(self):
242
- """Test the `q` filter exists on a filterset, does not validate the filter works as expected."""
243
- self.assertIn("q", self.filterset.declared_filters)
244
-
245
228
  def _assert_valid_filter_predicates(self, obj, field_name):
246
229
  self.assertTrue(
247
230
  hasattr(obj, field_name),
@@ -377,27 +360,27 @@ class FilterTestCases:
377
360
  filter_field, (ContentTypeFilter, ContentTypeMultipleChoiceFilter, ContentTypeChoiceFilter)
378
361
  )
379
362
 
363
+ # Test cases should just explicitly include `name` as a generic_filter_tests entry
364
+ @class_deprecated_in_favor_of(FilterTestCase) # pylint: disable=undefined-variable
380
365
  class NameOnlyFilterTestCase(FilterTestCase):
381
366
  """Add simple tests for filtering by name."""
382
367
 
383
- def test_name(self):
384
- """Verify that the filterset supports filtering by name."""
385
- params = {"name": list(self.queryset.values_list("name", flat=True)[:2])}
386
- filterset = self.filterset(params, self.queryset)
387
- self.assertTrue(filterset.is_valid())
388
- self.assertQuerysetEqualAndNotEmpty(
389
- filterset.qs.order_by("name"), self.queryset.filter(name__in=params["name"]).order_by("name")
390
- )
368
+ def test_filters_generic(self):
369
+ if not any(test[0] == "name" for test in self.generic_filter_tests):
370
+ self.generic_filter_tests = (["name"], *self.generic_filter_tests)
371
+ super().test_filters_generic()
391
372
 
392
- class NameSlugFilterTestCase(NameOnlyFilterTestCase):
373
+ # Test cases should just explicitly include `name` and `slug` as generic_filter_tests entries
374
+ @class_deprecated_in_favor_of(FilterTestCase) # pylint: disable=undefined-variable
375
+ class NameSlugFilterTestCase(FilterTestCase):
393
376
  """Add simple tests for filtering by name and by slug."""
394
377
 
395
- def test_slug(self):
396
- """Verify that the filterset supports filtering by slug."""
397
- params = {"slug": self.queryset.values_list("slug", flat=True)[:2]}
398
- filterset = self.filterset(params, self.queryset)
399
- self.assertTrue(filterset.is_valid())
400
- self.assertEqual(filterset.qs.count(), 2)
378
+ def test_filters_generic(self):
379
+ if not any(test[0] == "slug" for test in self.generic_filter_tests):
380
+ self.generic_filter_tests = (["slug"], *self.generic_filter_tests)
381
+ if not any(test[0] == "name" for test in self.generic_filter_tests):
382
+ self.generic_filter_tests = (["name"], *self.generic_filter_tests)
383
+ super().test_filters_generic()
401
384
 
402
385
  class TenancyFilterTestCaseMixin(views.TestCase):
403
386
  """Add test cases for tenant and tenant-group filters."""
@@ -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)
@@ -1,5 +1,3 @@
1
- from __future__ import annotations # python 3.8
2
-
3
1
  import contextlib
4
2
  import re
5
3
  from typing import Optional, Sequence
@@ -23,7 +21,6 @@ from nautobot.core.models.tree_queries import TreeModel
23
21
  from nautobot.core.templatetags import helpers
24
22
  from nautobot.core.testing import mixins, utils
25
23
  from nautobot.core.utils import lookup
26
- from nautobot.dcim.models.device_components import ComponentModel
27
24
  from nautobot.extras import choices as extras_choices, models as extras_models, querysets as extras_querysets
28
25
  from nautobot.extras.forms import CustomFieldModelFormMixin, RelationshipModelFormMixin
29
26
  from nautobot.extras.models import CustomFieldModel, RelationshipModel
@@ -174,10 +171,7 @@ class ViewTestCases:
174
171
  instance = self._get_queryset().first()
175
172
 
176
173
  # Add model-level permission
177
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["view"])
178
- obj_perm.save()
179
- obj_perm.users.add(self.user)
180
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
174
+ self.add_permissions(f"{self.model._meta.app_label}.view_{self.model._meta.model_name}")
181
175
 
182
176
  # Try GET with model-level permission
183
177
  response = self.client.get(instance.get_absolute_url())
@@ -326,10 +320,7 @@ class ViewTestCases:
326
320
  initial_count = self._get_queryset().count()
327
321
 
328
322
  # Assign unconstrained permission
329
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["add"])
330
- obj_perm.save()
331
- obj_perm.users.add(self.user)
332
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
323
+ self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
333
324
 
334
325
  # Try GET with model-level permission
335
326
  self.assertHttpStatus(self.client.get(self._get_url("add")), 200)
@@ -436,32 +427,6 @@ class ViewTestCases:
436
427
  if isinstance(model_class, PrimaryModel):
437
428
  self.assertIsNotNone(fields.get("tags"))
438
429
 
439
- def test_slug_autocreation(self):
440
- """Test that slug is autocreated through ORM."""
441
- # This really should go on a models test page, but we don't have test structures for models.
442
- if getattr(self.model, "slug_source", None) is not None:
443
- obj = self.model.objects.get(**{self.slug_source: self.slug_test_object})
444
- expected_slug = self.slugify_function(getattr(obj, self.slug_source))
445
- self.assertEqual(obj.slug, expected_slug)
446
-
447
- def test_slug_not_modified(self):
448
- """Ensure save method does not modify slug that is passed in."""
449
- # This really should go on a models test page, but we don't have test structures for models.
450
- if getattr(self.model, "slug_source", None) is not None:
451
- new_slug_source_value = "kwyjibo"
452
-
453
- obj = self.model.objects.get(**{self.slug_source: self.slug_test_object})
454
- expected_slug = self.slugify_function(getattr(obj, self.slug_source))
455
- # Update slug source field str
456
- filter_ = self.slug_source + "__exact"
457
- self.model.objects.filter(**{filter_: self.slug_test_object}).update(
458
- **{self.slug_source: new_slug_source_value}
459
- )
460
-
461
- obj.refresh_from_db()
462
- self.assertEqual(getattr(obj, self.slug_source), new_slug_source_value)
463
- self.assertEqual(obj.slug, expected_slug)
464
-
465
430
  class EditObjectViewTestCase(ModelViewTestCase):
466
431
  """
467
432
  Edit a single existing instance.
@@ -494,10 +459,7 @@ class ViewTestCases:
494
459
  instance = self._get_queryset().first()
495
460
 
496
461
  # Assign model-level permission
497
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["change"])
498
- obj_perm.save()
499
- obj_perm.users.add(self.user)
500
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
462
+ self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
501
463
 
502
464
  # Try GET with model-level permission
503
465
  self.assertHttpStatus(self.client.get(self._get_url("edit", instance)), 200)
@@ -618,10 +580,7 @@ class ViewTestCases:
618
580
  instance_note_pk_list.append(note.pk)
619
581
 
620
582
  # Assign model-level permission
621
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["delete"])
622
- obj_perm.save()
623
- obj_perm.users.add(self.user)
624
- obj_perm.object_types.add(assigned_object_type)
583
+ self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
625
584
 
626
585
  # Try GET with model-level permission
627
586
  self.assertHttpStatus(self.client.get(self._get_url("delete", instance)), 200)
@@ -657,10 +616,7 @@ class ViewTestCases:
657
616
  instance = self.get_deletable_object()
658
617
 
659
618
  # Assign model-level permission
660
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["delete"])
661
- obj_perm.save()
662
- obj_perm.users.add(self.user)
663
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
619
+ self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
664
620
 
665
621
  # Try GET with model-level permission
666
622
  self.assertHttpStatus(self.client.get(self._get_url("delete", instance)), 200)
@@ -865,10 +821,7 @@ class ViewTestCases:
865
821
  @override_settings(EXEMPT_VIEW_PERMISSIONS=[])
866
822
  def test_list_objects_with_permission(self):
867
823
  # Add model-level permission
868
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["view"])
869
- obj_perm.save()
870
- obj_perm.users.add(self.user)
871
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
824
+ self.add_permissions(f"{self.model._meta.app_label}.view_{self.model._meta.model_name}")
872
825
 
873
826
  # Try GET with model-level permission
874
827
  response = self.client.get(self._get_url("list"))
@@ -881,11 +834,17 @@ class ViewTestCases:
881
834
  # Check if breadcrumb is rendered correctly
882
835
  self.assertBodyContains(response, f'<a href="{list_url}">{title}</a>', html=True)
883
836
 
884
- # Check if import button is absent due to user permissions
885
- self.assertNotIn(
886
- reverse("extras:job_run_by_class_path", kwargs={"class_path": "nautobot.core.jobs.ImportObjects"}),
887
- response_body,
888
- )
837
+ with self.subTest("Assert import-objects URL is absent due to user permissions"):
838
+ self.assertNotIn(
839
+ reverse("extras:job_run_by_class_path", kwargs={"class_path": "nautobot.core.jobs.ImportObjects"}),
840
+ response_body,
841
+ )
842
+
843
+ if "example_app" in settings.PLUGINS:
844
+ with self.subTest("Assert example-app banner is present"):
845
+ self.assertIn(
846
+ f"<div>You are viewing a table of {self.model._meta.verbose_name_plural}</div>", response_body
847
+ )
889
848
 
890
849
  return response
891
850
 
@@ -992,13 +951,7 @@ class ViewTestCases:
992
951
  }
993
952
 
994
953
  # Assign non-constrained permission
995
- obj_perm = users_models.ObjectPermission(
996
- name="Test permission",
997
- actions=["add"],
998
- )
999
- obj_perm.save()
1000
- obj_perm.users.add(self.user)
1001
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
954
+ self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
1002
955
 
1003
956
  # Bulk create objects
1004
957
  response = self.client.post(**request)
@@ -1052,34 +1005,6 @@ class ViewTestCases:
1052
1005
  pass
1053
1006
  self.assertEqual(matching_count, self.bulk_create_count)
1054
1007
 
1055
- class BulkImportObjectsViewTestCase(ModelViewTestCase): # 3.0 TODO: remove this test mixin, no longer relevant.
1056
- """
1057
- Vestigial test case, to be removed in 3.0.
1058
-
1059
- This is vestigial since the introduction of the ImportObjects system Job to handle bulk-import of all
1060
- content-types via REST API serializers. The parsing of CSV data by the serializer is exercised by
1061
- APIViewTestCases.CreateObjectViewTestCase.test_recreate_object_csv(), and the basic operation of the Job is
1062
- exercised by nautobot.core.tests.test_jobs.
1063
- """
1064
-
1065
- csv_data = ()
1066
-
1067
- def _get_csv_data(self):
1068
- return "\n".join(self.csv_data)
1069
-
1070
- # Just in case Apps are extending any of these tests and calling super() in them.
1071
- def test_bulk_import_objects_without_permission(self):
1072
- pass
1073
-
1074
- def test_bulk_import_objects_with_permission(self):
1075
- pass
1076
-
1077
- def test_bulk_import_objects_with_permission_csv_file(self):
1078
- pass
1079
-
1080
- def test_bulk_import_objects_with_constrained_permission(self):
1081
- pass
1082
-
1083
1008
  class BulkEditObjectsViewTestCase(ModelViewTestCase):
1084
1009
  """
1085
1010
  Edit multiple instances.
@@ -1117,10 +1042,7 @@ class ViewTestCases:
1117
1042
  data.update(utils.post_data(self.bulk_edit_data))
1118
1043
 
1119
1044
  # Assign model-level permission
1120
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["change"])
1121
- obj_perm.save()
1122
- obj_perm.users.add(self.user)
1123
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
1045
+ self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
1124
1046
 
1125
1047
  # Try POST with model-level permission
1126
1048
  self.assertHttpStatus(self.client.post(self._get_url("bulk_edit"), data), 302)
@@ -1135,10 +1057,7 @@ class ViewTestCases:
1135
1057
  "_all": "on",
1136
1058
  }
1137
1059
  # Assign model-level permission
1138
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["change"])
1139
- obj_perm.save()
1140
- obj_perm.users.add(self.user)
1141
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
1060
+ self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
1142
1061
 
1143
1062
  # Try POST with model-level permission
1144
1063
  response = self.client.post(self._get_url("bulk_edit"), selected_data)
@@ -1282,10 +1201,7 @@ class ViewTestCases:
1282
1201
  }
1283
1202
 
1284
1203
  # Assign unconstrained permission
1285
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["delete"])
1286
- obj_perm.save()
1287
- obj_perm.users.add(self.user)
1288
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
1204
+ self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
1289
1205
 
1290
1206
  # Try POST with model-level permission
1291
1207
  self.assertHttpStatus(self.client.post(self._get_url("bulk_delete"), data), 302)
@@ -1301,10 +1217,7 @@ class ViewTestCases:
1301
1217
  }
1302
1218
 
1303
1219
  # Assign unconstrained permission
1304
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["delete"])
1305
- obj_perm.save()
1306
- obj_perm.users.add(self.user)
1307
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
1220
+ self.add_permissions(f"{self.model._meta.app_label}.delete_{self.model._meta.model_name}")
1308
1221
 
1309
1222
  # Try POST with the selected data first. Emulating selecting all -> pressing Delete Selected button.
1310
1223
  response = self.client.post(self._get_url("bulk_delete"), selected_data)
@@ -1418,10 +1331,7 @@ class ViewTestCases:
1418
1331
  data.update(self.rename_data)
1419
1332
 
1420
1333
  # Assign model-level permission
1421
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["change"])
1422
- obj_perm.save()
1423
- obj_perm.users.add(self.user)
1424
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
1334
+ self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
1425
1335
 
1426
1336
  # Try POST with model-level permission
1427
1337
  self.assertHttpStatus(self.client.post(self._get_url("bulk_rename"), data), 302)
@@ -1531,16 +1441,11 @@ class ViewTestCases:
1531
1441
  maxDiff = None
1532
1442
  bulk_add_data = None
1533
1443
  """Used for bulk-add (distinct from bulk-create) view testing; self.bulk_create_data will be used if unset."""
1534
- selected_objects: list[ComponentModel]
1535
- selected_objects_parent_name: str
1536
1444
 
1537
1445
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
1538
1446
  def test_bulk_add_component(self):
1539
1447
  """Test bulk-adding this component to devices/virtual-machines."""
1540
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["add"])
1541
- obj_perm.save()
1542
- obj_perm.users.add(self.user)
1543
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
1448
+ self.add_permissions(f"{self.model._meta.app_label}.add_{self.model._meta.model_name}")
1544
1449
 
1545
1450
  initial_count = self._get_queryset().count()
1546
1451
 
@@ -1584,10 +1489,7 @@ class ViewTestCases:
1584
1489
 
1585
1490
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
1586
1491
  def test_bulk_rename(self):
1587
- obj_perm = users_models.ObjectPermission(name="Test permission", actions=["change"])
1588
- obj_perm.save()
1589
- obj_perm.users.add(self.user)
1590
- obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
1492
+ self.add_permissions(f"{self.model._meta.app_label}.change_{self.model._meta.model_name}")
1591
1493
 
1592
1494
  objects = self.selected_objects
1593
1495
  pk_list = [obj.pk for obj in objects]
@@ -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": {
@@ -14,7 +14,7 @@ class AuthenticationEnforcedTestCase(TestCase):
14
14
 
15
15
  def test_all_views_require_authentication(self):
16
16
  self.client.logout()
17
- url_patterns = get_url_patterns()
17
+ url_patterns = get_url_patterns(ignore_redirects=True)
18
18
 
19
19
  for url_pattern in url_patterns:
20
20
  with self.subTest(url_pattern=url_pattern):
@@ -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)