nautobot 2.3.16__py3-none-any.whl → 2.4.0__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 (721) hide show
  1. nautobot/__init__.py +15 -0
  2. nautobot/apps/__init__.py +1 -1
  3. nautobot/apps/api.py +8 -10
  4. nautobot/apps/change_logging.py +2 -2
  5. nautobot/apps/choices.py +4 -4
  6. nautobot/apps/config.py +32 -3
  7. nautobot/apps/events.py +19 -0
  8. nautobot/apps/exceptions.py +0 -2
  9. nautobot/apps/factory.py +2 -2
  10. nautobot/apps/filters.py +1 -1
  11. nautobot/apps/forms.py +20 -20
  12. nautobot/apps/graphql.py +2 -2
  13. nautobot/apps/jobs.py +8 -8
  14. nautobot/apps/models.py +19 -19
  15. nautobot/apps/tables.py +1 -1
  16. nautobot/apps/testing.py +10 -10
  17. nautobot/apps/ui.py +44 -9
  18. nautobot/apps/utils.py +7 -15
  19. nautobot/apps/views.py +8 -6
  20. nautobot/circuits/api/serializers.py +1 -0
  21. nautobot/circuits/api/views.py +4 -8
  22. nautobot/circuits/navigation.py +0 -57
  23. nautobot/circuits/templates/circuits/circuit_create.html +1 -7
  24. nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -71
  25. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -64
  26. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +40 -0
  27. nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +26 -0
  28. nautobot/circuits/templates/circuits/provider_retrieve.html +0 -76
  29. nautobot/circuits/tests/integration/test_relationships.py +33 -24
  30. nautobot/circuits/tests/test_filters.py +4 -8
  31. nautobot/circuits/views.py +140 -23
  32. nautobot/cloud/api/views.py +6 -10
  33. nautobot/cloud/factory.py +4 -1
  34. nautobot/cloud/tests/test_filters.py +5 -4
  35. nautobot/cloud/views.py +0 -16
  36. nautobot/core/api/constants.py +11 -0
  37. nautobot/core/api/filter_backends.py +3 -9
  38. nautobot/core/api/metadata.py +28 -256
  39. nautobot/core/api/pagination.py +3 -2
  40. nautobot/core/api/renderers.py +3 -0
  41. nautobot/core/api/schema.py +13 -2
  42. nautobot/core/api/serializers.py +45 -259
  43. nautobot/core/api/urls.py +3 -4
  44. nautobot/core/api/utils.py +0 -62
  45. nautobot/core/api/views.py +99 -157
  46. nautobot/core/apps/__init__.py +22 -578
  47. nautobot/core/celery/__init__.py +13 -0
  48. nautobot/core/celery/schedulers.py +47 -2
  49. nautobot/core/choices.py +2 -2
  50. nautobot/core/cli/__init__.py +8 -0
  51. nautobot/core/constants.py +7 -0
  52. nautobot/core/events/__init__.py +116 -0
  53. nautobot/core/events/base.py +27 -0
  54. nautobot/core/events/exceptions.py +10 -0
  55. nautobot/core/events/redis_broker.py +48 -0
  56. nautobot/core/events/syslog_broker.py +19 -0
  57. nautobot/core/exceptions.py +0 -6
  58. nautobot/core/forms/__init__.py +19 -19
  59. nautobot/core/forms/fields.py +57 -9
  60. nautobot/core/forms/forms.py +33 -2
  61. nautobot/core/forms/utils.py +2 -1
  62. nautobot/core/graphql/schema.py +3 -1
  63. nautobot/core/jobs/__init__.py +24 -3
  64. nautobot/core/jobs/bulk_actions.py +248 -0
  65. nautobot/core/jobs/cleanup.py +1 -1
  66. nautobot/core/management/commands/generate_test_data.py +21 -0
  67. nautobot/core/middleware.py +16 -0
  68. nautobot/core/models/fields.py +11 -7
  69. nautobot/core/settings.py +68 -4
  70. nautobot/core/settings.yaml +99 -0
  71. nautobot/core/tables.py +10 -46
  72. nautobot/core/tasks.py +1 -1
  73. nautobot/core/templates/about.html +67 -0
  74. nautobot/core/templates/components/button/default.html +7 -0
  75. nautobot/core/templates/components/button/dropdown.html +20 -0
  76. nautobot/core/templates/components/layout/one_over_two.html +19 -0
  77. nautobot/core/templates/components/layout/two_over_one.html +19 -0
  78. nautobot/core/templates/components/panel/body_content_data_table.html +27 -0
  79. nautobot/core/templates/components/panel/body_content_objects_table.html +4 -0
  80. nautobot/core/templates/components/panel/body_content_tags.html +6 -0
  81. nautobot/core/templates/components/panel/body_content_text.html +12 -0
  82. nautobot/core/templates/components/panel/body_wrapper_generic.html +3 -0
  83. nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +3 -0
  84. nautobot/core/templates/components/panel/body_wrapper_table.html +3 -0
  85. nautobot/core/templates/components/panel/footer_contacts_table.html +20 -0
  86. nautobot/core/templates/components/panel/footer_content_table.html +14 -0
  87. nautobot/core/templates/components/panel/grouping_toggle.html +14 -0
  88. nautobot/core/templates/components/panel/header_extra_content_table.html +3 -0
  89. nautobot/core/templates/components/panel/panel.html +16 -0
  90. nautobot/core/templates/components/panel/stats_panel_body.html +8 -0
  91. nautobot/core/templates/components/tab/content_wrapper.html +3 -0
  92. nautobot/core/templates/components/tab/label_wrapper.html +5 -0
  93. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +3 -0
  94. nautobot/core/templates/generic/object_retrieve.html +28 -17
  95. nautobot/core/templates/inc/computed_fields/panel_data.html +4 -7
  96. nautobot/core/templates/inc/custom_fields/panel.html +2 -2
  97. nautobot/core/templates/inc/custom_fields/panel_data.html +4 -7
  98. nautobot/core/templates/inc/footer.html +1 -0
  99. nautobot/core/templates/inc/nav_menu.html +2 -1
  100. nautobot/core/templates/inc/relationships_panel.html +1 -1
  101. nautobot/core/templates/inc/tenancy_form_panel.html +9 -0
  102. nautobot/core/templates/inc/tenant_table_row.html +11 -0
  103. nautobot/core/templates/nautobot_config.py.j2 +13 -0
  104. nautobot/core/templates/panel_table.html +12 -0
  105. nautobot/core/templates/utilities/render_jinja2.html +117 -0
  106. nautobot/core/templates/utilities/templatetags/tag.html +1 -1
  107. nautobot/core/templates/utilities/theme_preview.html +7 -0
  108. nautobot/core/templatetags/helpers.py +104 -6
  109. nautobot/core/templatetags/ui_framework.py +40 -0
  110. nautobot/core/testing/__init__.py +8 -8
  111. nautobot/core/testing/api.py +187 -137
  112. nautobot/core/testing/context.py +18 -0
  113. nautobot/core/testing/filters.py +41 -35
  114. nautobot/core/testing/forms.py +2 -0
  115. nautobot/core/testing/views.py +65 -148
  116. nautobot/core/tests/integration/test_view_authentication.py +1 -1
  117. nautobot/core/tests/nautobot_config.py +198 -0
  118. nautobot/core/tests/runner.py +2 -2
  119. nautobot/core/tests/test_api.py +154 -176
  120. nautobot/core/tests/test_events.py +214 -0
  121. nautobot/core/tests/test_forms.py +1 -0
  122. nautobot/core/tests/test_jinja_filters.py +1 -0
  123. nautobot/core/tests/test_jobs.py +387 -14
  124. nautobot/core/tests/test_navigations.py +7 -241
  125. nautobot/core/tests/test_settings_schema.py +7 -0
  126. nautobot/core/tests/test_tables.py +100 -0
  127. nautobot/core/tests/test_templatetags_helpers.py +16 -0
  128. nautobot/core/tests/test_ui.py +150 -0
  129. nautobot/core/tests/test_utils.py +55 -18
  130. nautobot/core/tests/test_views.py +124 -5
  131. nautobot/core/ui/__init__.py +0 -0
  132. nautobot/core/ui/base.py +11 -0
  133. nautobot/core/ui/choices.py +44 -0
  134. nautobot/core/ui/homepage.py +167 -0
  135. nautobot/core/ui/nav.py +280 -0
  136. nautobot/core/ui/object_detail.py +1855 -0
  137. nautobot/core/ui/utils.py +36 -0
  138. nautobot/core/urls.py +6 -0
  139. nautobot/core/utils/config.py +30 -3
  140. nautobot/core/utils/lookup.py +12 -2
  141. nautobot/core/utils/querysets.py +64 -0
  142. nautobot/core/utils/requests.py +24 -9
  143. nautobot/core/views/__init__.py +48 -1
  144. nautobot/core/views/generic.py +37 -140
  145. nautobot/core/views/mixins.py +82 -32
  146. nautobot/core/views/paginator.py +8 -5
  147. nautobot/core/views/renderers.py +9 -9
  148. nautobot/core/views/utils.py +11 -0
  149. nautobot/core/wsgi.py +3 -3
  150. nautobot/dcim/api/serializers.py +34 -141
  151. nautobot/dcim/api/urls.py +5 -0
  152. nautobot/dcim/api/views.py +57 -110
  153. nautobot/dcim/apps.py +1 -0
  154. nautobot/dcim/choices.py +28 -0
  155. nautobot/dcim/factory.py +58 -0
  156. nautobot/dcim/filters/__init__.py +204 -2
  157. nautobot/dcim/forms.py +219 -9
  158. nautobot/dcim/migrations/0063_interfacevdcassignment_virtualdevicecontext_and_more.py +165 -0
  159. nautobot/dcim/migrations/0064_virtualdevicecontext_status_data_migration.py +28 -0
  160. nautobot/dcim/migrations/0065_controller_capabilities_and_more.py +29 -0
  161. nautobot/dcim/migrations/0066_controllermanageddevicegroup_radio_profiles_and_more.py +33 -0
  162. nautobot/dcim/migrations/0067_controllermanageddevicegroup_tenant.py +25 -0
  163. nautobot/dcim/models/__init__.py +5 -1
  164. nautobot/dcim/models/devices.py +180 -2
  165. nautobot/dcim/models/racks.py +2 -2
  166. nautobot/dcim/navigation.py +25 -224
  167. nautobot/dcim/signals.py +44 -0
  168. nautobot/dcim/tables/__init__.py +2 -0
  169. nautobot/dcim/tables/devices.py +103 -7
  170. nautobot/dcim/tables/racks.py +1 -1
  171. nautobot/dcim/templates/dcim/controller/base.html +10 -0
  172. nautobot/dcim/templates/dcim/controller_create.html +2 -7
  173. nautobot/dcim/templates/dcim/controller_retrieve.html +6 -10
  174. nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +25 -0
  175. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +68 -0
  176. nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +51 -0
  177. nautobot/dcim/templates/dcim/device/base.html +6 -42
  178. nautobot/dcim/templates/dcim/device/wireless.html +73 -0
  179. nautobot/dcim/templates/dcim/device.html +4 -10
  180. nautobot/dcim/templates/dcim/device_edit.html +36 -37
  181. nautobot/dcim/templates/dcim/interface.html +1 -0
  182. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  183. nautobot/dcim/templates/dcim/location.html +1 -9
  184. nautobot/dcim/templates/dcim/location_edit.html +1 -7
  185. nautobot/dcim/templates/dcim/locationtype.html +0 -107
  186. nautobot/dcim/templates/dcim/locationtype_retrieve.html +8 -0
  187. nautobot/dcim/templates/dcim/rack.html +1 -9
  188. nautobot/dcim/templates/dcim/rack_edit.html +1 -7
  189. nautobot/dcim/templates/dcim/rackreservation.html +1 -9
  190. nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +68 -0
  191. nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +28 -0
  192. nautobot/dcim/tests/integration/test_controller.py +62 -0
  193. nautobot/dcim/tests/integration/test_controller_managed_device_group.py +71 -0
  194. nautobot/dcim/tests/test_api.py +188 -64
  195. nautobot/dcim/tests/test_filters.py +171 -76
  196. nautobot/dcim/tests/test_jobs.py +118 -0
  197. nautobot/dcim/tests/test_models.py +157 -6
  198. nautobot/dcim/tests/test_signals.py +1 -0
  199. nautobot/dcim/tests/test_views.py +118 -88
  200. nautobot/dcim/urls.py +72 -27
  201. nautobot/dcim/utils.py +2 -2
  202. nautobot/dcim/views.py +356 -61
  203. nautobot/extras/api/serializers.py +39 -18
  204. nautobot/extras/api/urls.py +4 -0
  205. nautobot/extras/api/views.py +89 -31
  206. nautobot/extras/choices.py +13 -0
  207. nautobot/extras/constants.py +2 -1
  208. nautobot/extras/context_managers.py +23 -6
  209. nautobot/extras/datasources/git.py +4 -1
  210. nautobot/extras/factory.py +27 -0
  211. nautobot/extras/filters/__init__.py +66 -5
  212. nautobot/extras/forms/base.py +2 -2
  213. nautobot/extras/forms/forms.py +262 -59
  214. nautobot/extras/forms/mixins.py +2 -2
  215. nautobot/extras/graphql/types.py +25 -1
  216. nautobot/extras/jobs.py +109 -15
  217. nautobot/extras/management/__init__.py +1 -0
  218. nautobot/extras/management/commands/runjob.py +7 -79
  219. nautobot/extras/management/commands/runjob_with_job_result.py +46 -0
  220. nautobot/extras/management/utils.py +87 -0
  221. nautobot/extras/migrations/0117_create_job_queue_model.py +129 -0
  222. nautobot/extras/migrations/0118_task_queue_to_job_queue_migration.py +78 -0
  223. nautobot/extras/migrations/0119_remove_task_queues_from_job_and_queue_from_scheduled_job.py +28 -0
  224. nautobot/extras/migrations/0120_job_is_singleton_job_is_singleton_override.py +22 -0
  225. nautobot/extras/migrations/0121_alter_team_contacts.py +17 -0
  226. nautobot/extras/models/__init__.py +5 -1
  227. nautobot/extras/models/change_logging.py +7 -3
  228. nautobot/extras/models/contacts.py +1 -1
  229. nautobot/extras/models/groups.py +0 -2
  230. nautobot/extras/models/jobs.py +233 -33
  231. nautobot/extras/models/relationships.py +69 -1
  232. nautobot/extras/models/secrets.py +5 -0
  233. nautobot/extras/navigation.py +20 -262
  234. nautobot/extras/plugins/__init__.py +54 -19
  235. nautobot/extras/plugins/marketplace_manifest.yml +455 -0
  236. nautobot/extras/plugins/tables.py +16 -14
  237. nautobot/extras/plugins/urls.py +1 -0
  238. nautobot/extras/plugins/views.py +103 -60
  239. nautobot/extras/registry.py +1 -1
  240. nautobot/extras/secrets/__init__.py +2 -2
  241. nautobot/extras/signals.py +39 -1
  242. nautobot/extras/tables.py +37 -1
  243. nautobot/extras/templates/extras/dynamicgroup.html +1 -9
  244. nautobot/extras/templates/extras/externalintegration_retrieve.html +0 -47
  245. nautobot/extras/templates/extras/inc/tags_panel.html +1 -5
  246. nautobot/extras/templates/extras/job_bulk_edit.html +2 -1
  247. nautobot/extras/templates/extras/job_detail.html +52 -6
  248. nautobot/extras/templates/extras/job_edit.html +6 -2
  249. nautobot/extras/templates/extras/job_list.html +2 -7
  250. nautobot/extras/templates/extras/jobqueue_retrieve.html +36 -0
  251. nautobot/extras/templates/extras/marketplace.html +296 -0
  252. nautobot/extras/templates/extras/plugin_detail.html +32 -15
  253. nautobot/extras/templates/extras/plugins_list.html +35 -1
  254. nautobot/extras/templates/extras/plugins_tiles.html +90 -0
  255. nautobot/extras/templates/extras/role_retrieve.html +16 -0
  256. nautobot/extras/templates/extras/secret.html +0 -65
  257. nautobot/extras/templates/extras/secret_check.js +16 -0
  258. nautobot/extras/templates/extras/secret_create.html +114 -0
  259. nautobot/extras/templates/extras/secret_edit.html +1 -114
  260. nautobot/extras/templates/extras/secretsgroup_edit.html +1 -1
  261. nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -0
  262. nautobot/extras/templatetags/job_buttons.py +5 -4
  263. nautobot/extras/templatetags/plugins.py +69 -6
  264. nautobot/extras/test_jobs/singleton.py +16 -0
  265. nautobot/extras/tests/test_api.py +145 -43
  266. nautobot/extras/tests/test_context_managers.py +4 -1
  267. nautobot/extras/tests/test_filters.py +213 -529
  268. nautobot/extras/tests/test_job_variables.py +73 -152
  269. nautobot/extras/tests/test_jobs.py +181 -51
  270. nautobot/extras/tests/test_models.py +61 -6
  271. nautobot/extras/tests/test_plugins.py +62 -9
  272. nautobot/extras/tests/test_relationships.py +123 -9
  273. nautobot/extras/tests/test_utils.py +23 -2
  274. nautobot/extras/tests/test_views.py +146 -145
  275. nautobot/extras/tests/test_webhooks.py +2 -1
  276. nautobot/extras/urls.py +2 -20
  277. nautobot/extras/utils.py +119 -4
  278. nautobot/extras/views.py +168 -125
  279. nautobot/extras/webhooks.py +5 -2
  280. nautobot/ipam/api/serializers.py +10 -103
  281. nautobot/ipam/api/views.py +31 -49
  282. nautobot/ipam/factory.py +1 -1
  283. nautobot/ipam/filters.py +3 -2
  284. nautobot/ipam/models.py +10 -12
  285. nautobot/ipam/navigation.py +0 -90
  286. nautobot/ipam/tables.py +3 -1
  287. nautobot/ipam/templates/ipam/ipaddress.html +1 -9
  288. nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +1 -7
  289. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -7
  290. nautobot/ipam/templates/ipam/prefix.html +1 -9
  291. nautobot/ipam/templates/ipam/prefix_edit.html +1 -7
  292. nautobot/ipam/templates/ipam/routetarget.html +0 -28
  293. nautobot/ipam/templates/ipam/vlan.html +1 -9
  294. nautobot/ipam/templates/ipam/vlan_edit.html +1 -7
  295. nautobot/ipam/templates/ipam/vrf.html +0 -47
  296. nautobot/ipam/templates/ipam/vrf_edit.html +1 -7
  297. nautobot/ipam/tests/test_api.py +7 -5
  298. nautobot/ipam/tests/test_filters.py +39 -119
  299. nautobot/ipam/tests/test_forms.py +0 -2
  300. nautobot/ipam/tests/test_models.py +56 -36
  301. nautobot/ipam/tests/test_views.py +3 -0
  302. nautobot/ipam/urls.py +3 -69
  303. nautobot/ipam/utils/__init__.py +16 -10
  304. nautobot/ipam/views.py +91 -162
  305. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
  306. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
  307. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +40 -2
  308. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
  309. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
  310. nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
  311. nautobot/project-static/css/base.css +38 -3
  312. nautobot/project-static/docs/404.html +461 -17
  313. nautobot/project-static/docs/apps/index.html +461 -17
  314. nautobot/project-static/docs/apps/nautobot-apps.html +462 -19
  315. nautobot/project-static/docs/assets/_mkdocstrings.css +25 -1
  316. nautobot/project-static/docs/assets/extra.css +5 -1
  317. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +477 -23
  318. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +474 -20
  319. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +790 -289
  320. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +505 -31
  321. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +510 -34
  322. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +471 -20
  323. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +467 -18
  324. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +497 -33
  325. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9883 -0
  326. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +523 -75
  327. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +546 -51
  328. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +670 -94
  329. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1030 -177
  330. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +524 -49
  331. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +874 -188
  332. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +955 -235
  333. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +475 -21
  334. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +486 -28
  335. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +661 -99
  336. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +947 -479
  337. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +6425 -1234
  338. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +474 -20
  339. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +877 -344
  340. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +828 -171
  341. nautobot/project-static/docs/development/apps/api/configuration-view.html +461 -17
  342. nautobot/project-static/docs/development/apps/api/database-backend-config.html +461 -17
  343. nautobot/project-static/docs/development/apps/api/models/django-admin.html +461 -17
  344. nautobot/project-static/docs/development/apps/api/models/global-search.html +461 -17
  345. nautobot/project-static/docs/development/apps/api/models/graphql.html +461 -17
  346. nautobot/project-static/docs/development/apps/api/models/index.html +461 -17
  347. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +461 -17
  348. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +461 -17
  349. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +461 -17
  350. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +461 -17
  351. nautobot/project-static/docs/development/apps/api/platform-features/index.html +461 -17
  352. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +461 -17
  353. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +461 -17
  354. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +461 -17
  355. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +461 -17
  356. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +461 -17
  357. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +461 -17
  358. nautobot/project-static/docs/development/apps/api/prometheus.html +461 -17
  359. nautobot/project-static/docs/development/apps/api/setup.html +465 -153
  360. nautobot/project-static/docs/development/apps/api/testing.html +461 -17
  361. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +461 -17
  362. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +461 -17
  363. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +461 -17
  364. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +461 -17
  365. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +741 -128
  366. nautobot/project-static/docs/development/apps/api/views/base-template.html +461 -17
  367. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +461 -17
  368. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +461 -17
  369. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +461 -17
  370. nautobot/project-static/docs/development/apps/api/views/index.html +463 -18
  371. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +465 -17
  372. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +491 -17
  373. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +461 -17
  374. nautobot/project-static/docs/development/apps/api/views/notes.html +461 -17
  375. nautobot/project-static/docs/development/apps/api/views/rest-api.html +467 -19
  376. nautobot/project-static/docs/development/apps/api/views/urls.html +461 -17
  377. nautobot/project-static/docs/development/apps/index.html +461 -17
  378. nautobot/project-static/docs/development/apps/migration/code-updates.html +462 -50
  379. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +462 -18
  380. nautobot/project-static/docs/development/apps/migration/from-v1.html +461 -17
  381. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +461 -17
  382. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +461 -17
  383. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +461 -17
  384. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +464 -20
  385. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9261 -0
  386. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +9375 -0
  387. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +9671 -0
  388. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +9559 -0
  389. nautobot/project-static/docs/development/apps/porting-from-netbox.html +464 -20
  390. nautobot/project-static/docs/development/core/application-registry.html +461 -17
  391. nautobot/project-static/docs/development/core/best-practices.html +461 -17
  392. nautobot/project-static/docs/development/core/bootstrap-ui.html +461 -17
  393. nautobot/project-static/docs/development/core/caching.html +461 -17
  394. nautobot/project-static/docs/development/core/controllers.html +463 -17
  395. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +464 -20
  396. nautobot/project-static/docs/development/core/generic-views.html +461 -17
  397. nautobot/project-static/docs/development/core/getting-started.html +539 -127
  398. nautobot/project-static/docs/development/core/homepage.html +472 -28
  399. nautobot/project-static/docs/development/core/index.html +461 -17
  400. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9754 -0
  401. nautobot/project-static/docs/development/core/model-checklist.html +471 -25
  402. nautobot/project-static/docs/development/core/model-features.html +461 -17
  403. nautobot/project-static/docs/development/core/natural-keys.html +461 -17
  404. nautobot/project-static/docs/development/core/navigation-menu.html +478 -24
  405. nautobot/project-static/docs/development/core/release-checklist.html +478 -46
  406. nautobot/project-static/docs/development/core/role-internals.html +461 -17
  407. nautobot/project-static/docs/development/core/settings.html +461 -17
  408. nautobot/project-static/docs/development/core/style-guide.html +464 -20
  409. nautobot/project-static/docs/development/core/templates.html +471 -20
  410. nautobot/project-static/docs/development/core/testing.html +461 -17
  411. nautobot/project-static/docs/development/core/ui-component-framework.html +11116 -0
  412. nautobot/project-static/docs/development/core/user-preferences.html +464 -20
  413. nautobot/project-static/docs/development/index.html +461 -17
  414. nautobot/project-static/docs/development/jobs/index.html +499 -19
  415. nautobot/project-static/docs/development/jobs/migration/from-v1.html +461 -17
  416. nautobot/project-static/docs/index.html +469 -36
  417. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit.png +0 -0
  418. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_edit_button.png +0 -0
  419. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_nav.png +0 -0
  420. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_list_view.png +0 -0
  421. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue.png +0 -0
  422. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_add.png +0 -0
  423. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_queue_config.png +0 -0
  424. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_completed.png +0 -0
  425. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_nav.png +0 -0
  426. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_result_pending.png +0 -0
  427. nautobot/project-static/docs/media/development/core/kubernetes/k8s_job_run_form.png +0 -0
  428. nautobot/project-static/docs/media/development/core/kubernetes/k8s_nautobot_login.png +0 -0
  429. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_job.png +0 -0
  430. nautobot/project-static/docs/media/development/core/kubernetes/k8s_run_scheduled_job_form.png +0 -0
  431. nautobot/project-static/docs/media/development/core/kubernetes/k8s_scheduled_job_result.png +0 -0
  432. nautobot/project-static/docs/media/development/core/ui-component-framework/basic-panel-layout.png +0 -0
  433. nautobot/project-static/docs/media/development/core/ui-component-framework/button-example.png +0 -0
  434. nautobot/project-static/docs/media/development/core/ui-component-framework/buttons-example.png +0 -0
  435. nautobot/project-static/docs/media/development/core/ui-component-framework/cluster-type-before-after-example.png +0 -0
  436. nautobot/project-static/docs/media/development/core/ui-component-framework/dropdown-button-example.png +0 -0
  437. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-1.png +0 -0
  438. nautobot/project-static/docs/media/development/core/ui-component-framework/grouped-key-value-table-panel-example-2.png +0 -0
  439. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example.png +0 -0
  440. nautobot/project-static/docs/media/development/core/ui-component-framework/object-fields-panel-example_2.png +0 -0
  441. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example-code.png +0 -0
  442. nautobot/project-static/docs/media/development/core/ui-component-framework/stats-panel-example.png +0 -0
  443. nautobot/project-static/docs/media/development/core/ui-component-framework/table-panels-family.png +0 -0
  444. nautobot/project-static/docs/media/development/core/ui-component-framework/text-panels-family.png +0 -0
  445. nautobot/project-static/docs/media/development/core/ui-component-framework/ui-framework-example.png +0 -0
  446. nautobot/project-static/docs/media/models/virtual_device_context_overview.drawio +73 -0
  447. nautobot/project-static/docs/media/models/virtual_device_context_overview.png +0 -0
  448. nautobot/project-static/docs/models/dcim/virtualdevicecontext.html +14 -0
  449. nautobot/project-static/docs/models/extras/jobqueue.html +14 -0
  450. nautobot/project-static/docs/models/wireless/radioprofile.html +14 -0
  451. nautobot/project-static/docs/models/wireless/supporteddatarate.html +14 -0
  452. nautobot/project-static/docs/models/wireless/wirelessnetwork.html +14 -0
  453. nautobot/project-static/docs/objects.inv +0 -0
  454. nautobot/project-static/docs/overview/application_stack.html +467 -21
  455. nautobot/project-static/docs/overview/design_philosophy.html +461 -17
  456. nautobot/project-static/docs/release-notes/index.html +483 -20
  457. nautobot/project-static/docs/release-notes/version-1.0.html +649 -206
  458. nautobot/project-static/docs/release-notes/version-1.1.html +646 -203
  459. nautobot/project-static/docs/release-notes/version-1.2.html +721 -278
  460. nautobot/project-static/docs/release-notes/version-1.3.html +747 -304
  461. nautobot/project-static/docs/release-notes/version-1.4.html +832 -390
  462. nautobot/project-static/docs/release-notes/version-1.5.html +1020 -579
  463. nautobot/project-static/docs/release-notes/version-1.6.html +940 -516
  464. nautobot/project-static/docs/release-notes/version-2.0.html +943 -502
  465. nautobot/project-static/docs/release-notes/version-2.1.html +778 -337
  466. nautobot/project-static/docs/release-notes/version-2.2.html +771 -330
  467. nautobot/project-static/docs/release-notes/version-2.3.html +914 -471
  468. nautobot/project-static/docs/release-notes/version-2.4.html +10323 -0
  469. nautobot/project-static/docs/search/search_index.json +1 -1
  470. nautobot/project-static/docs/sitemap.xml +342 -270
  471. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  472. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +461 -17
  473. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +461 -17
  474. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +461 -17
  475. nautobot/project-static/docs/user-guide/administration/configuration/index.html +473 -30
  476. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +461 -17
  477. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +842 -155
  478. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +461 -17
  479. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +461 -17
  480. nautobot/project-static/docs/user-guide/administration/guides/docker.html +474 -27
  481. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +461 -17
  482. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +461 -17
  483. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +461 -17
  484. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +461 -17
  485. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +461 -17
  486. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +463 -19
  487. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +461 -17
  488. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +461 -17
  489. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +461 -17
  490. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +481 -21
  491. nautobot/project-static/docs/user-guide/administration/installation/index.html +466 -18
  492. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +462 -18
  493. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +461 -17
  494. nautobot/project-static/docs/user-guide/administration/installation/services.html +461 -17
  495. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +461 -17
  496. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +482 -39
  497. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +475 -64
  498. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +475 -64
  499. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +461 -17
  500. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +461 -17
  501. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +461 -17
  502. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +461 -17
  503. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +461 -17
  504. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +464 -21
  505. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +461 -17
  506. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-nautobot-app-location.yaml +0 -16
  507. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +461 -17
  508. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +467 -19
  509. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +461 -17
  510. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +461 -17
  511. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +461 -17
  512. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +461 -17
  513. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +461 -17
  514. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +461 -17
  515. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +461 -17
  516. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +461 -17
  517. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +461 -17
  518. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +461 -17
  519. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +461 -17
  520. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +461 -17
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +461 -17
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +461 -17
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +461 -17
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +461 -17
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +461 -17
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +497 -18
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +487 -20
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +461 -17
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +461 -17
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +461 -17
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +461 -17
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +461 -17
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +461 -17
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +461 -17
  535. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +461 -17
  536. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +461 -17
  537. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +461 -17
  538. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +461 -17
  539. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +461 -17
  540. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +461 -17
  541. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +461 -17
  542. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +461 -17
  543. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +461 -17
  544. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +461 -17
  545. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +461 -17
  546. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +461 -17
  547. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +461 -17
  548. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +461 -17
  549. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +461 -17
  550. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +461 -17
  551. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +461 -17
  552. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +461 -17
  553. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +461 -17
  554. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +461 -17
  555. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +461 -17
  556. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +461 -17
  557. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +461 -17
  558. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +461 -17
  559. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +461 -17
  560. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +461 -17
  561. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +461 -17
  562. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9375 -0
  563. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +468 -28
  564. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +461 -17
  565. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +461 -17
  566. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +461 -17
  567. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +461 -17
  568. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +461 -17
  569. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +461 -17
  570. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +461 -17
  571. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +461 -17
  572. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +461 -17
  573. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +461 -17
  574. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +461 -17
  575. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +461 -17
  576. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +464 -20
  577. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +461 -17
  578. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +461 -17
  579. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +461 -17
  580. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +461 -17
  581. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +461 -17
  582. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +461 -17
  583. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +464 -20
  584. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9313 -0
  585. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9217 -0
  586. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9211 -0
  587. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9277 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +461 -17
  589. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +461 -17
  590. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +461 -17
  591. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +461 -17
  592. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +461 -17
  593. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +461 -17
  594. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +461 -17
  595. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +461 -17
  596. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +461 -17
  597. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +461 -17
  598. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +461 -17
  599. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +466 -20
  600. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +461 -17
  601. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/central-mode.png +0 -0
  602. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-add.png +0 -0
  603. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-1.png +0 -0
  604. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/device-group-create-2.png +0 -0
  605. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-add.png +0 -0
  606. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/radio-profile-create.png +0 -0
  607. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-add.png +0 -0
  608. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/supported-data-rate-create.png +0 -0
  609. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-add.png +0 -0
  610. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-1.png +0 -0
  611. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-controller-create-2.png +0 -0
  612. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-add.png +0 -0
  613. nautobot/project-static/docs/user-guide/feature-guides/images/wireless/wireless-network-create.png +0 -0
  614. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +461 -17
  615. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +461 -17
  616. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +464 -20
  617. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9444 -0
  618. nautobot/project-static/docs/user-guide/index.html +461 -17
  619. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +464 -20
  620. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +465 -21
  621. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +461 -17
  622. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +461 -17
  623. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +461 -17
  624. nautobot/project-static/docs/user-guide/platform-functionality/events.html +9617 -0
  625. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +464 -20
  626. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +461 -17
  627. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +461 -17
  628. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +461 -17
  629. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +461 -17
  630. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +461 -17
  631. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +470 -21
  632. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +464 -20
  633. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +464 -20
  634. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +461 -17
  635. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9224 -0
  636. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9722 -0
  637. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +464 -20
  638. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +461 -17
  639. nautobot/project-static/docs/user-guide/platform-functionality/note.html +461 -17
  640. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +461 -17
  641. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +465 -21
  642. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9292 -0
  643. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +461 -17
  644. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +509 -38
  645. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +492 -21
  646. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +461 -17
  647. nautobot/project-static/docs/user-guide/platform-functionality/role.html +461 -17
  648. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +461 -17
  649. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +461 -17
  650. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +464 -20
  651. nautobot/project-static/docs/user-guide/platform-functionality/status.html +461 -17
  652. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +461 -17
  653. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +529 -54
  654. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +461 -17
  655. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +461 -17
  656. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +461 -17
  657. nautobot/project-static/img/jinja_logo.svg +97 -0
  658. nautobot/project-static/js/forms.js +6 -1
  659. nautobot/project-static/js/nav_menu.js +2 -1
  660. nautobot/tenancy/api/serializers.py +0 -2
  661. nautobot/tenancy/api/views.py +9 -13
  662. nautobot/tenancy/factory.py +1 -1
  663. nautobot/tenancy/navigation.py +0 -29
  664. nautobot/tenancy/templates/tenancy/tenant.html +4 -91
  665. nautobot/tenancy/tests/test_filters.py +29 -134
  666. nautobot/tenancy/views.py +35 -24
  667. nautobot/users/admin.py +2 -0
  668. nautobot/users/api/views.py +2 -2
  669. nautobot/users/forms.py +19 -0
  670. nautobot/users/templates/users/preferences.html +22 -0
  671. nautobot/users/tests/test_filters.py +1 -19
  672. nautobot/users/tests/test_views.py +57 -0
  673. nautobot/users/utils.py +8 -0
  674. nautobot/users/views.py +48 -11
  675. nautobot/virtualization/api/views.py +5 -24
  676. nautobot/virtualization/filters.py +1 -2
  677. nautobot/virtualization/models.py +1 -1
  678. nautobot/virtualization/navigation.py +0 -48
  679. nautobot/virtualization/tables.py +2 -2
  680. nautobot/virtualization/templates/virtualization/cluster_edit.html +1 -7
  681. nautobot/virtualization/templates/virtualization/clustertype.html +0 -39
  682. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -9
  683. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +2 -8
  684. nautobot/virtualization/tests/test_filters.py +57 -166
  685. nautobot/virtualization/views.py +18 -15
  686. nautobot/wireless/__init__.py +0 -0
  687. nautobot/wireless/api/__init__.py +0 -0
  688. nautobot/wireless/api/serializers.py +44 -0
  689. nautobot/wireless/api/urls.py +20 -0
  690. nautobot/wireless/api/views.py +34 -0
  691. nautobot/wireless/apps.py +8 -0
  692. nautobot/wireless/choices.py +345 -0
  693. nautobot/wireless/factory.py +138 -0
  694. nautobot/wireless/filters.py +167 -0
  695. nautobot/wireless/forms.py +283 -0
  696. nautobot/wireless/homepage.py +19 -0
  697. nautobot/wireless/migrations/0001_initial.py +223 -0
  698. nautobot/wireless/migrations/__init__.py +0 -0
  699. nautobot/wireless/models.py +207 -0
  700. nautobot/wireless/navigation.py +105 -0
  701. nautobot/wireless/tables.py +244 -0
  702. nautobot/wireless/templates/wireless/radioprofile_retrieve.html +81 -0
  703. nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +26 -0
  704. nautobot/wireless/templates/wireless/wirelessnetwork_create.html +88 -0
  705. nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +56 -0
  706. nautobot/wireless/tests/__init__.py +0 -0
  707. nautobot/wireless/tests/integration/__init__.py +0 -0
  708. nautobot/wireless/tests/integration/test_radio_profile.py +42 -0
  709. nautobot/wireless/tests/test_api.py +247 -0
  710. nautobot/wireless/tests/test_filters.py +82 -0
  711. nautobot/wireless/tests/test_models.py +22 -0
  712. nautobot/wireless/tests/test_views.py +378 -0
  713. nautobot/wireless/urls.py +13 -0
  714. nautobot/wireless/views.py +119 -0
  715. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/METADATA +9 -12
  716. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/RECORD +720 -549
  717. nautobot/core/utils/navigation.py +0 -54
  718. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/LICENSE.txt +0 -0
  719. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/NOTICE +0 -0
  720. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/WHEEL +0 -0
  721. {nautobot-2.3.16.dist-info → nautobot-2.4.0.dist-info}/entry_points.txt +0 -0
@@ -2,9 +2,18 @@ from django.contrib import messages
2
2
  from django.db import transaction
3
3
  from django.db.models import Q
4
4
  from django.shortcuts import get_object_or_404, redirect, render
5
+ from django.utils.html import format_html
5
6
  from django_tables2 import RequestConfig
6
7
 
7
8
  from nautobot.core.forms import ConfirmationForm
9
+ from nautobot.core.templatetags.helpers import bettertitle, humanize_speed, placeholder
10
+ from nautobot.core.ui.choices import SectionChoices
11
+ from nautobot.core.ui.object_detail import (
12
+ ObjectDetailContent,
13
+ ObjectFieldsPanel,
14
+ ObjectsTablePanel,
15
+ )
16
+ from nautobot.core.ui.utils import render_component_template
8
17
  from nautobot.core.views import generic, mixins as view_mixins
9
18
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
10
19
  from nautobot.core.views.viewsets import NautobotUIViewSet
@@ -94,27 +103,22 @@ class ProviderUIViewSet(NautobotUIViewSet):
94
103
  queryset = Provider.objects.all()
95
104
  serializer_class = serializers.ProviderSerializer
96
105
  table_class = tables.ProviderTable
97
-
98
- def get_extra_context(self, request, instance):
99
- context = super().get_extra_context(request, instance)
100
- if self.action == "retrieve":
101
- circuits = (
102
- Circuit.objects.restrict(request.user, "view")
103
- .filter(provider=instance)
104
- .select_related("circuit_type", "tenant")
105
- .prefetch_related("circuit_terminations__location")
106
- )
107
- circuits_table = tables.CircuitTable(circuits)
108
- circuits_table.columns.hide("provider")
109
-
110
- paginate = {
111
- "paginator_class": EnhancedPaginator,
112
- "per_page": get_paginate_count(request),
113
- }
114
- RequestConfig(request, paginate).configure(circuits_table)
115
-
116
- context["circuits_table"] = circuits_table
117
- return context
106
+ object_detail_content = ObjectDetailContent(
107
+ panels=(
108
+ ObjectFieldsPanel(
109
+ section=SectionChoices.LEFT_HALF,
110
+ weight=100,
111
+ fields="__all__",
112
+ ),
113
+ ObjectsTablePanel(
114
+ weight=200,
115
+ table_class=tables.CircuitTable,
116
+ table_filter="provider",
117
+ section=SectionChoices.FULL_WIDTH,
118
+ exclude_columns=["provider"],
119
+ ),
120
+ ),
121
+ )
118
122
 
119
123
 
120
124
  class CircuitUIViewSet(NautobotUIViewSet):
@@ -127,8 +131,121 @@ class CircuitUIViewSet(NautobotUIViewSet):
127
131
  queryset = Circuit.objects.all()
128
132
  serializer_class = serializers.CircuitSerializer
129
133
  table_class = tables.CircuitTable
130
- # NOTE: This is how `NautobotUIViewSet` would define use_new_ui attr
131
- # use_new_ui = ["list", "retrieve"]
134
+
135
+ class CircuitTerminationPanel(ObjectFieldsPanel):
136
+ def __init__(self, **kwargs):
137
+ super().__init__(
138
+ fields=(
139
+ "location", # TODO: render location hierarchy
140
+ "cable",
141
+ "provider_network",
142
+ "cloud_network",
143
+ "port_speed",
144
+ "upstream_speed",
145
+ "ip_addresses",
146
+ "xconnect_id",
147
+ "pp_info",
148
+ "description",
149
+ ),
150
+ value_transforms={
151
+ "port_speed": [humanize_speed, placeholder],
152
+ "upstream_speed": [humanize_speed],
153
+ },
154
+ hide_if_unset=("location", "provider_network", "cloud_network", "upstream_speed"),
155
+ ignore_nonexistent_fields=True, # ip_addresses may be undefined
156
+ header_extra_content_template_path="circuits/inc/circuit_termination_header_extra_content.html",
157
+ **kwargs,
158
+ )
159
+
160
+ def should_render(self, context):
161
+ return True
162
+
163
+ def get_extra_context(self, context):
164
+ return {"termination": context[self.context_object_key]}
165
+
166
+ def get_data(self, context):
167
+ """
168
+ Extend the panel data to include custom relationships on the termination.
169
+
170
+ This is done for feature parity with the existing UI, and is not a pattern we *generally* should emulate.
171
+ """
172
+ data = super().get_data(context)
173
+ termination = context["termination"]
174
+ if termination is not None:
175
+ for side, relationships in termination.get_relationships_with_related_objects(
176
+ include_hidden=False
177
+ ).items():
178
+ for relationship, value in relationships.items():
179
+ key = (relationship, side)
180
+ data[key] = value
181
+
182
+ return data
183
+
184
+ def render_key(self, key, value, context):
185
+ """
186
+ Extend the panel rendering to render custom relationship information.
187
+
188
+ This is done for feature parity with the existing UI, and is not a pattern we *generally* should emulate.
189
+ """
190
+ if isinstance(key, tuple):
191
+ # Copied from _ObjectRelationshipsPanel.render_key()
192
+ relationship, side = key
193
+ return format_html(
194
+ '<span title="{} ({})">{}</span>',
195
+ relationship.label,
196
+ relationship.key,
197
+ bettertitle(relationship.get_label(side)),
198
+ )
199
+ return super().render_key(key, value, context)
200
+
201
+ def render_value(self, key, value, context):
202
+ """
203
+ Add custom rendering of connected cables.
204
+
205
+ TODO: this might make sense to move into the base class to handle Cable objects in general?
206
+ """
207
+ if key == "cable":
208
+ if not context["termination"].location:
209
+ return ""
210
+ return render_component_template("circuits/inc/circuit_termination_cable_fragment.html", context)
211
+ return super().render_value(key, value, context)
212
+
213
+ def queryset_list_url_filter(self, key, value, context):
214
+ """
215
+ Extend the panel rendering to render custom relationship information.
216
+
217
+ This is done for feature parity with the existing UI, and is not a pattern we *generally* should emulate.
218
+ """
219
+ if isinstance(key, tuple):
220
+ # Copied from _ObjectRelationshipsPanel.queryset_list_url_filter()
221
+ relationship, side = key
222
+ termination = context["termination"]
223
+ return f"cr_{relationship.key}__{side}={termination.pk}"
224
+ return super().queryset_list_url_filter(key, value, context)
225
+
226
+ object_detail_content = ObjectDetailContent(
227
+ panels=(
228
+ ObjectFieldsPanel(
229
+ section=SectionChoices.LEFT_HALF,
230
+ weight=100,
231
+ fields="__all__",
232
+ exclude_fields=["comments", "circuit_termination_a", "circuit_termination_z"],
233
+ value_transforms={"commit_rate": [humanize_speed, placeholder]},
234
+ ),
235
+ CircuitTerminationPanel(
236
+ label="Termination - A Side",
237
+ section=SectionChoices.RIGHT_HALF,
238
+ weight=100,
239
+ context_object_key="circuit_termination_a",
240
+ ),
241
+ CircuitTerminationPanel(
242
+ label="Termination - Z Side",
243
+ section=SectionChoices.RIGHT_HALF,
244
+ weight=200,
245
+ context_object_key="circuit_termination_z",
246
+ ),
247
+ ),
248
+ )
132
249
 
133
250
  def get_extra_context(self, request, instance):
134
251
  context = super().get_extra_context(request, instance)
@@ -9,40 +9,36 @@ from . import serializers
9
9
 
10
10
 
11
11
  class CloudAccountViewSet(NautobotModelViewSet):
12
- queryset = models.CloudAccount.objects.select_related("provider", "secrets_group")
12
+ queryset = models.CloudAccount.objects.all()
13
13
  serializer_class = serializers.CloudAccountSerializer
14
14
  filterset_class = filters.CloudAccountFilterSet
15
15
 
16
16
 
17
17
  class CloudResourceTypeViewSet(NautobotModelViewSet):
18
- queryset = models.CloudResourceType.objects.select_related("provider")
18
+ queryset = models.CloudResourceType.objects.all()
19
19
  serializer_class = serializers.CloudResourceTypeSerializer
20
20
  filterset_class = filters.CloudResourceTypeFilterSet
21
21
 
22
22
 
23
23
  class CloudNetworkViewSet(NautobotModelViewSet):
24
- queryset = models.CloudNetwork.objects.select_related(
25
- "cloud_resource_type", "cloud_account", "parent"
26
- ).prefetch_related("prefixes")
24
+ queryset = models.CloudNetwork.objects.all()
27
25
  serializer_class = serializers.CloudNetworkSerializer
28
26
  filterset_class = filters.CloudNetworkFilterSet
29
27
 
30
28
 
31
29
  class CloudNetworkPrefixAssignmentViewSet(NautobotModelViewSet):
32
- queryset = models.CloudNetworkPrefixAssignment.objects.select_related("cloud_network", "prefix")
30
+ queryset = models.CloudNetworkPrefixAssignment.objects.all()
33
31
  serializer_class = serializers.CloudNetworkPrefixAssignmentSerializer
34
32
  filterset_class = filters.CloudNetworkPrefixAssignmentFilterSet
35
33
 
36
34
 
37
35
  class CloudServiceViewSet(NautobotModelViewSet):
38
- queryset = models.CloudService.objects.select_related("cloud_account", "cloud_resource_type").prefetch_related(
39
- "cloud_networks"
40
- )
36
+ queryset = models.CloudService.objects.all()
41
37
  serializer_class = serializers.CloudServiceSerializer
42
38
  filterset_class = filters.CloudServiceFilterSet
43
39
 
44
40
 
45
41
  class CloudServiceNetworkAssignmentViewSet(NautobotModelViewSet):
46
- queryset = models.CloudServiceNetworkAssignment.objects.select_related("cloud_network", "cloud_service")
42
+ queryset = models.CloudServiceNetworkAssignment.objects.all()
47
43
  serializer_class = serializers.CloudServiceNetworkAssignmentSerializer
48
44
  filterset_class = filters.CloudServiceNetworkAssignmentFilterSet
nautobot/cloud/factory.py CHANGED
@@ -86,7 +86,10 @@ class CloudNetworkFactory(PrimaryModelFactory):
86
86
  if extracted:
87
87
  self.prefixes.set(extracted)
88
88
  else:
89
- self.prefixes.set(get_random_instances(Prefix))
89
+ # TODO Investigate https://github.com/nautobot/nautobot/actions/runs/11019738391/job/30603271529
90
+ # to uncomment the line below.
91
+ # self.prefixes.set(get_random_instances(Prefix))
92
+ self.prefixes.set(get_random_instances(model_or_queryset_or_lambda=Prefix, maximum=1))
90
93
 
91
94
 
92
95
  class CloudServiceFactory(PrimaryModelFactory):
@@ -5,12 +5,13 @@ from nautobot.core.testing import FilterTestCases
5
5
  from nautobot.extras.models import SecretsGroup
6
6
 
7
7
 
8
- class CloudAccountTestCase(FilterTestCases.NameOnlyFilterTestCase):
8
+ class CloudAccountTestCase(FilterTestCases.FilterTestCase):
9
9
  queryset = models.CloudAccount.objects.all()
10
10
  filterset = filters.CloudAccountFilterSet
11
11
  generic_filter_tests = [
12
12
  ("account_number",),
13
13
  ("description",),
14
+ ("name",),
14
15
  ("provider", "provider__id"),
15
16
  ("provider", "provider__name"),
16
17
  ("secrets_group", "secrets_group__id"),
@@ -31,7 +32,7 @@ class CloudAccountTestCase(FilterTestCases.NameOnlyFilterTestCase):
31
32
  cls.cloud_accounts[i].validated_save()
32
33
 
33
34
 
34
- class CloudResourceTypeTestCase(FilterTestCases.NameOnlyFilterTestCase):
35
+ class CloudResourceTypeTestCase(FilterTestCases.FilterTestCase):
35
36
  queryset = models.CloudResourceType.objects.all()
36
37
  filterset = filters.CloudResourceTypeFilterSet
37
38
  generic_filter_tests = [
@@ -50,7 +51,7 @@ class CloudResourceTypeTestCase(FilterTestCases.NameOnlyFilterTestCase):
50
51
  )
51
52
 
52
53
 
53
- class CloudNetworkTestCase(FilterTestCases.NameOnlyFilterTestCase):
54
+ class CloudNetworkTestCase(FilterTestCases.FilterTestCase):
54
55
  queryset = models.CloudNetwork.objects.all()
55
56
  filterset = filters.CloudNetworkFilterSet
56
57
  generic_filter_tests = [
@@ -110,7 +111,7 @@ class CloudServiceNetworkAssignmentTestCase(FilterTestCases.FilterTestCase):
110
111
  ]
111
112
 
112
113
 
113
- class CloudServiceTestCase(FilterTestCases.NameOnlyFilterTestCase):
114
+ class CloudServiceTestCase(FilterTestCases.FilterTestCase):
114
115
  queryset = models.CloudService.objects.all()
115
116
  filterset = filters.CloudServiceFilterSet
116
117
  generic_filter_tests = [
nautobot/cloud/views.py CHANGED
@@ -100,16 +100,6 @@ class CloudNetworkUIViewSet(NautobotUIViewSet):
100
100
 
101
101
  return context
102
102
 
103
- def extra_post_save_action(self, obj, form):
104
- if form.cleaned_data.get("add_prefixes", None):
105
- obj.prefixes.add(*form.cleaned_data["add_prefixes"])
106
- if form.cleaned_data.get("remove_prefixes", None):
107
- obj.prefixes.remove(*form.cleaned_data["remove_prefixes"])
108
- if form.cleaned_data.get("add_cloud_services", None):
109
- obj.cloud_services.add(*form.cleaned_data["add_cloud_services"])
110
- if form.cleaned_data.get("remove_cloud_services", None):
111
- obj.cloud_services.remove(*form.cleaned_data["remove_cloud_services"])
112
-
113
103
 
114
104
  class CloudResourceTypeUIViewSet(NautobotUIViewSet):
115
105
  queryset = CloudResourceType.objects.all()
@@ -173,9 +163,3 @@ class CloudServiceUIViewSet(NautobotUIViewSet):
173
163
  )
174
164
 
175
165
  return context
176
-
177
- def extra_post_save_action(self, obj, form):
178
- if form.cleaned_data.get("add_cloud_networks", None):
179
- obj.cloud_networks.add(*form.cleaned_data["add_cloud_networks"])
180
- if form.cleaned_data.get("remove_cloud_networks", None):
181
- obj.cloud_networks.remove(*form.cleaned_data["remove_cloud_networks"])
@@ -0,0 +1,11 @@
1
+ # Query parameters that are *not* filterset filters
2
+ NON_FILTER_QUERY_PARAMS = (
3
+ "api_version", # used to select the Nautobot API version
4
+ "depth", # nested levels of the serializers default to depth=0
5
+ "exclude_m2m", # used to exclude many-to-many fields from the REST API
6
+ "format", # "json" or "api", used in the interactive HTML REST API views
7
+ "include", # used to include computed fields, relationships, config-contexts, etc. (excluded by default)
8
+ "limit", # pagination
9
+ "offset", # pagination
10
+ "sort", # sorting of results
11
+ )
@@ -2,6 +2,8 @@ from django_filters.rest_framework.backends import DjangoFilterBackend
2
2
  from rest_framework.filters import OrderingFilter
3
3
  from tree_queries.models import TreeNode
4
4
 
5
+ from nautobot.core.api.constants import NON_FILTER_QUERY_PARAMS
6
+
5
7
 
6
8
  class NautobotFilterBackend(DjangoFilterBackend):
7
9
  """Custom filtering backend for use with django-rest-framework and django-filters."""
@@ -17,15 +19,7 @@ class NautobotFilterBackend(DjangoFilterBackend):
17
19
  # The default 'data' is a reference to request.GET, which is immutable; copies of this data are mutable
18
20
  data = kwargs["data"].copy()
19
21
 
20
- for non_filter_param in (
21
- "api_version", # used to select the Nautobot API version
22
- "depth", # nested levels of the serializers default to depth=0
23
- "format", # "json" or "api", used in the interactive HTML REST API views
24
- "include", # used to include computed fields, relationships, config-contexts, etc. (excluded by default)
25
- "limit", # pagination
26
- "offset", # pagination
27
- "sort", # sorting of results
28
- ):
22
+ for non_filter_param in NON_FILTER_QUERY_PARAMS:
29
23
  data.pop(non_filter_param, None)
30
24
 
31
25
  kwargs["data"] = data
@@ -1,202 +1,25 @@
1
- import contextlib
2
- from typing import Any, Dict, List
3
-
4
1
  from django.core.exceptions import PermissionDenied
5
2
  from django.http import Http404
6
- from django.urls import reverse
7
- from django.urls.exceptions import NoReverseMatch
8
- import django_filters
9
- import drf_react_template.schema_form_encoder as schema
10
- from rest_framework import exceptions, fields as drf_fields, serializers as drf_serializers
3
+ from django.utils.encoding import force_str
4
+ from rest_framework import exceptions
11
5
  from rest_framework.metadata import SimpleMetadata
12
6
  from rest_framework.request import clone_request
13
7
 
14
- from nautobot.core.utils.filtering import build_lookup_label, get_filter_field_label
15
- from nautobot.core.utils.lookup import get_route_for_model
16
-
17
- # FIXME(jathan): I hate this pattern that these fields are hard-coded here. But for the moment, this
18
- # works reliably.
19
- BOTTOM_FIELDS = ["computed_fields", "custom_fields", "relationships"]
20
-
21
-
22
- class NautobotProcessingMixin(schema.ProcessingMixin):
23
- """Processing mixin to account for custom field types and behaviors for Nautobot."""
24
-
25
- def _get_type_map_value(self, field: schema.SerializerType):
26
- """Overload default to add "required" as a default mapping."""
27
- # This adds "required" as a default type mapping compared to drf_react_template core.
28
- result = {
29
- "type": field.style.get("schema:type"),
30
- "enum": field.style.get("schema:enum"),
31
- "format": field.style.get("schema:format"),
32
- "widget": field.style.get("ui:widget"),
33
- "required": field.style.get("schema:required"),
34
- "readOnly": field.style.get("schema:readOnly"),
35
- }
36
- result_default = self.TYPE_MAP.get(type(field).__name__, {})
37
- for k in result_default:
38
- if result[k] is None:
39
- result[k] = result_default[k]
40
- return result
41
-
42
- def order_fields(self, fields):
43
- """Explicitly order the "big ugly" fields to the bottom."""
44
- # FIXME(jathan): Correct the behavior introduced in #3500 by switching to `__all__` to
45
- # assert these get added at the end.
46
- for field_name in BOTTOM_FIELDS:
47
- if field_name in fields:
48
- fields.remove(field_name)
49
- fields.append(field_name)
50
- return fields
51
-
52
-
53
- class NautobotSchemaProcessor(NautobotProcessingMixin, schema.SchemaProcessor):
54
- """SchemaProcessor to account for custom field types and behaviors for Nautobot."""
55
-
56
- def _get_field_properties(self, field: schema.SerializerType, name: str) -> Dict[str, Any]:
57
- """
58
- This method is used to generate the proper schema based on serializer field mappings or
59
- per-field attribute markup.
60
-
61
- This has been overloaded with an `elif` to account for `ManyRelatedField`.
62
- """
63
- type_map_obj = self._get_type_map_value(field)
64
- result = {
65
- "type": type_map_obj["type"] or "string",
66
- "title": self._get_title(field, name),
67
- }
68
-
69
- if isinstance(field, drf_serializers.ListField):
70
- if field.allow_empty:
71
- result["required"] = not getattr(field, "allow_empty", True)
72
- result["items"] = self._get_field_properties(field.child, "")
73
- result["uniqueItems"] = True
74
- elif isinstance(field, drf_serializers.ManyRelatedField):
75
- if field.allow_empty:
76
- result["required"] = type_map_obj.get("required", [])
77
- result["items"] = self._get_field_properties(field.child_relation, "")
78
- result["uniqueItems"] = True
79
- else:
80
- if isinstance(field, drf_serializers.RelatedField):
81
- result["uniqueItems"] = True
82
- if hasattr(field, "queryset") and hasattr(field.queryset, "model"):
83
- # We construct the correct list view url here so we can just append the object id to the end of the url in our frontend.
84
- # e.g. /dcim/locations/
85
- try:
86
- model_url = reverse(get_route_for_model(model=field.queryset.model, action="list"))
87
- except NoReverseMatch:
88
- model_url = None
89
- model_options = field.queryset.model._meta
90
- result["required"] = field.required
91
- # Custom Keyword: modelName, modelNamePlural and appLabel
92
- # modelName represents the model name of the uuid model
93
- # modelUrl represents the UI URL to list instances of this model
94
- # and appLabel represents the app_name of the model
95
- result["modelName"] = model_options.model_name
96
- result["appLabel"] = model_options.app_label
97
- result["modelUrl"] = model_url
98
- if field.allow_null:
99
- result["type"] = [result["type"], "null"]
100
- if enum := type_map_obj.get("enum"):
101
- if enum == "choices":
102
- choices = field.choices
103
- result["enum"] = list(choices.keys())
104
- result["enumNames"] = list(choices.values())
105
- if isinstance(enum, (list, tuple)):
106
- result["enum"] = [item[0] for item in enum]
107
- result["enumNames"] = [item[1] for item in enum]
108
- if format_ := type_map_obj["format"]:
109
- result["format"] = format_
110
-
111
- if read_only := field.read_only:
112
- result["readOnly"] = read_only
113
-
114
- with contextlib.suppress(drf_fields.SkipField):
115
- result["default"] = field.get_default()
116
-
117
- result = self._set_validation_properties(field, result)
118
-
119
- return result
120
-
121
- @staticmethod
122
- def _filter_fields(all_fields):
123
- """
124
- Override super._filter_fields to return all fields, including read-only fields,
125
- as read-only fields have to be displayed in Detail View.
126
- """
127
- return tuple((name, field) for name, field in all_fields)
128
-
129
-
130
- class NautobotUiSchemaProcessor(NautobotProcessingMixin, schema.UiSchemaProcessor):
131
- """UiSchemaProcessor to account for custom field types and behaviors for Nautobot."""
132
-
133
- def _field_order(self) -> List[str]:
134
- """
135
- Overload the base which just returns `Meta.fields` and doesn't play nicely with "__all__".
136
-
137
- This instead calls `get_fields()` and returns the keys.
138
- """
139
- if self._is_list_serializer(self.serializer):
140
- fields = self.serializer.child.get_fields()
141
- else:
142
- fields = self.serializer.get_fields()
143
-
144
- field_names = self.order_fields(list(fields))
145
-
146
- return field_names
147
-
148
- def _get_ui_field_properties(self, field: schema.SerializerType, name: str) -> Dict[str, Any]:
149
- """
150
- We had to overload this here to make it so that array types with children validate properly
151
- and to also use `NautobotUiSchemaProcessor` over the default.
152
- """
153
- data_index = self._generate_data_index(name)
154
- result = {}
155
- is_int = False
156
- is_list = False
157
- if self._is_field_serializer(field):
158
- return NautobotUiSchemaProcessor(field, self.renderer_context, prefix=data_index).get_ui_schema()
159
- elif isinstance(field, drf_serializers.ListField):
160
- is_list = True
161
- child = field.child
162
- is_int = isinstance(child, drf_serializers.IntegerField)
163
- widget = self._get_type_map_value(field=child).get("widget")
164
- if not widget and isinstance(child, drf_serializers.ChoiceField):
165
- widget = "checkbox"
166
- else:
167
- widget = self._get_type_map_value(field=field).get("widget")
168
- help_text = field.help_text
169
- if widget:
170
- if is_list and is_int:
171
- if "items" not in result:
172
- result["items"] = {}
173
- result["items"]["ui:widget"] = widget
174
- else:
175
- result["ui:widget"] = widget
176
- if help_text:
177
- result["ui:help"] = help_text
178
- result.update(self._get_style_dict(field))
179
- result = self._set_validation_properties(field, result)
180
- return result
181
-
182
-
183
- class NautobotColumnProcessor(NautobotProcessingMixin, schema.ColumnProcessor):
184
- """ColumnProcessor to account for custom field types and behaviors for Nautobot."""
8
+ from nautobot.core.api import ContentTypeField
185
9
 
186
10
 
187
11
  class NautobotMetadata(SimpleMetadata):
188
12
  """
189
- Metadata class that emits JSON schema. It contains `schema` and `uiSchema` keys where:
190
-
191
- - schema: The object JSON schema
192
- - uiSchema: The object UI schema which describes the form layout in the UI
13
+ Metadata class that emits JSON schema.
193
14
  """
194
15
 
195
16
  def determine_actions(self, request, view):
196
17
  """Generate the actions and return the names of the allowed methods."""
197
- actions = []
18
+ actions = {}
198
19
  for method in {"PUT", "POST"} & set(view.allowed_methods):
199
20
  view.request = clone_request(request, method)
21
+ view.request.major_version = request.major_version
22
+ view.request.minor_version = request.minor_version
200
23
  try:
201
24
  # Test global permissions
202
25
  if hasattr(view, "check_permissions"):
@@ -207,82 +30,31 @@ class NautobotMetadata(SimpleMetadata):
207
30
  except (exceptions.APIException, PermissionDenied, Http404):
208
31
  pass
209
32
  else:
210
- actions.append(method)
33
+ # If user has appropriate permissions for the view, include
34
+ # appropriate metadata about the fields that should be supplied.
35
+ serializer = view.get_serializer()
36
+ actions[method] = self.get_serializer_info(serializer)
211
37
  finally:
212
38
  view.request = request
213
39
  return actions
214
40
 
215
- def get_list_display_fields(self, serializer):
216
- """Try to get the list display fields or default to an empty list."""
217
- serializer_meta = getattr(serializer, "Meta", None)
218
- return list(getattr(serializer_meta, "list_display_fields", []))
219
-
220
- def determine_metadata(self, request, view):
221
- """This is the metadata that gets returned on an `OPTIONS` request."""
222
- metadata = super().determine_metadata(request, view)
223
-
224
- # Include the object type label for this model.
225
- object_type = view.queryset.model._meta.label_lower if getattr(view, "queryset", None) else "unknown"
226
- metadata["object_type"] = object_type
41
+ def get_field_info(self, field):
42
+ """
43
+ Fixup field information:
227
44
 
228
- # If there's a serializer, do the needful to bind the schema/uiSchema.
229
- if hasattr(view, "get_serializer"):
230
- serializer = view.get_serializer()
231
- # TODO(jathan): Bit of a WIP here. Will likely refactor. There might be cases where we
232
- # want to explicitly override the UI field ordering, but that's not yet accounted for
233
- # here. For now the assertion is always put the `list_display_fields` first, and then
234
- # include the rest in whatever order.
235
- # See: https://rjsf-team.github.io/react-jsonschema-form/docs/usage/objects#specifying-property-order
236
- ui_schema = NautobotUiSchemaProcessor(serializer, request.parser_context).get_ui_schema()
237
- ui_schema["ui:order"] = [*self.get_list_display_fields(serializer), "*"]
238
- metadata.update(
45
+ - Set choices for ContentTypeField
46
+ - Replace DRF choices `display_name` to `display` to match new pattern.
47
+ """
48
+ field_info = super().get_field_info(field)
49
+ for choice in field_info.get("choices", []):
50
+ choice["display"] = choice.pop("display_name")
51
+ if hasattr(field, "queryset") and not field_info.get("read_only") and isinstance(field, ContentTypeField):
52
+ field_info["choices"] = [
239
53
  {
240
- "schema": NautobotSchemaProcessor(serializer, request.parser_context).get_schema(),
241
- "uiSchema": ui_schema,
54
+ "value": choice_value,
55
+ "display": force_str(choice_name, strings_only=True),
242
56
  }
243
- )
244
-
245
- metadata["filters"] = self.get_filter_info(view)
246
-
247
- if hasattr(serializer, "determine_view_options"):
248
- metadata["view_options"] = serializer.determine_view_options(request)
249
-
250
- return metadata
251
-
252
- def get_filter_info(self, view):
253
- """Enumerate filterset information for the view. Returns a dictionary with the following format:
254
-
255
- {
256
- "filter_name": {
257
- "label": "Filter Label",
258
- "lookup_types": [
259
- {"value": "filter_name__n", "label": "not exact (n)"},
260
- {"value": "filter_name__re", "label": "matches regex (re)"},
261
- ...
262
- ]
263
- }
264
- }
265
-
266
- """
267
-
268
- if not getattr(view, "filterset_class", None):
269
- return {}
270
- filterset = view.filterset_class
271
- filters = {}
272
- for filter_name, filter_instance in sorted(
273
- filterset.base_filters.items(),
274
- key=lambda x: get_filter_field_label(x[1]),
275
- ):
276
- filter_key = filter_name.rsplit("__", 1)[0]
277
- label = get_filter_field_label(filter_instance)
278
- lookup_label = self._filter_lookup_label(filter_name, filter_instance)
279
- filters.setdefault(filter_key, {"label": label})
280
- filters[filter_key].setdefault("lookup_types", []).append({"value": filter_name, "label": lookup_label})
281
- return filters
282
-
283
- # TODO: move this into `build_lookup_label` when the legacy UI is removed
284
- def _filter_lookup_label(self, filter_name, filter_instance):
285
- """Fix confusing lookup labels for boolean filters."""
286
- if isinstance(filter_instance, django_filters.BooleanFilter):
287
- return "exact"
288
- return build_lookup_label(filter_name, filter_instance.lookup_expr)
57
+ for choice_value, choice_name in field.choices.items()
58
+ ]
59
+ field_info["choices"].sort(key=lambda item: item["display"])
60
+ return field_info