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
@@ -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
@@ -1,5 +1,6 @@
1
1
  from rest_framework.pagination import LimitOffsetPagination
2
2
 
3
+ from nautobot.core.constants import MAX_PAGE_SIZE_DEFAULT, PAGINATE_COUNT_DEFAULT
3
4
  from nautobot.core.utils.config import get_settings_or_config
4
5
 
5
6
 
@@ -38,7 +39,7 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
38
39
  if limit < 0:
39
40
  raise ValueError()
40
41
  # Enforce maximum page size, if defined
41
- max_page_size = get_settings_or_config("MAX_PAGE_SIZE")
42
+ max_page_size = get_settings_or_config("MAX_PAGE_SIZE", fallback=MAX_PAGE_SIZE_DEFAULT)
42
43
  if max_page_size:
43
44
  if limit == 0:
44
45
  return max_page_size
@@ -48,7 +49,7 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
48
49
  except (KeyError, ValueError):
49
50
  pass
50
51
 
51
- return get_settings_or_config("PAGINATE_COUNT")
52
+ return get_settings_or_config("PAGINATE_COUNT", fallback=PAGINATE_COUNT_DEFAULT)
52
53
 
53
54
  def get_next_link(self):
54
55
  # Pagination has been disabled
@@ -156,6 +156,9 @@ class NautobotCSVRenderer(BaseRenderer):
156
156
  # value = [v["composite_key"] for v in value]
157
157
  else:
158
158
  value = [v["id"] for v in value]
159
+ if value and isinstance(value[0], dict) and ("value" in value[0]) and ("label" in value[0]):
160
+ # Multiple enum types
161
+ value = [v["value"] for v in value]
159
162
  # The below makes for better UX than `json.dump()` for most current cases.
160
163
  value = ",".join([str(v) if v is not None else "" for v in value])
161
164
  elif not isinstance(value, (str, int)):
@@ -1,5 +1,4 @@
1
1
  import contextlib
2
- from copy import deepcopy
3
2
  import logging
4
3
  import uuid
5
4
 
@@ -18,7 +17,7 @@ from django.db.models.functions import Cast
18
17
  from django.urls import NoReverseMatch
19
18
  from drf_spectacular.types import OpenApiTypes
20
19
  from drf_spectacular.utils import extend_schema_field, PolymorphicProxySerializer as _PolymorphicProxySerializer
21
- from rest_framework import relations as drf_relations, serializers
20
+ from rest_framework import serializers
22
21
  from rest_framework.exceptions import PermissionDenied, ValidationError
23
22
  from rest_framework.fields import CreateOnlyDefault
24
23
  from rest_framework.reverse import reverse
@@ -31,11 +30,9 @@ from nautobot.core.api.utils import (
31
30
  dict_to_filter_params,
32
31
  nested_serializer_factory,
33
32
  )
34
- from nautobot.core.exceptions import ViewConfigException
35
33
  from nautobot.core.models.fields import LaxURLField as LaxURLModelField
36
34
  from nautobot.core.models.managers import TagsManager
37
35
  from nautobot.core.models.utils import construct_composite_key, construct_natural_slug
38
- from nautobot.core.templatetags.helpers import bettertitle
39
36
  from nautobot.core.utils.lookup import get_route_for_model
40
37
  from nautobot.core.utils.requests import normalize_querydict
41
38
  from nautobot.extras.api.customfields import CustomFieldDefaultValues, CustomFieldsDataField
@@ -134,7 +131,6 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
134
131
 
135
132
  serializer_related_field = NautobotHyperlinkedRelatedField
136
133
 
137
- id = serializers.UUIDField(read_only=False, default=serializers.CreateOnlyDefault(uuid.uuid4))
138
134
  display = serializers.SerializerMethodField(read_only=True, help_text="Human friendly display value")
139
135
  object_type = ObjectTypeField()
140
136
  # composite_key = serializers.SerializerMethodField() # TODO: Revisit if we reintroduce composite keys
@@ -297,21 +293,6 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
297
293
  """Return whether this is a nested serializer."""
298
294
  return getattr(self.Meta, "is_nested", False)
299
295
 
300
- @property
301
- def list_display_fields(self):
302
- return list(getattr(self.Meta, "list_display_fields", []))
303
-
304
- @property
305
- def advanced_tab_fields(self):
306
- advanced_fields = list(
307
- getattr(
308
- self.Meta,
309
- "advanced_tab_fields",
310
- ["id", "url", "object_type", "created", "last_updated", "natural_slug"],
311
- )
312
- )
313
- return [field for field in advanced_fields if field in self.fields]
314
-
315
296
  @extend_schema_field(serializers.CharField)
316
297
  def get_display(self, instance):
317
298
  """
@@ -405,221 +386,6 @@ class BaseModelSerializer(OptInFieldsMixin, serializers.HyperlinkedModelSerializ
405
386
  fields = [field for field in fields if filter_field(field)]
406
387
  return fields
407
388
 
408
- def determine_view_options(self, request=None):
409
- """
410
- Determine view options to use for rendering the list and detail views associated with this serializer.
411
- """
412
- list_display = []
413
- fields = []
414
-
415
- from nautobot.core.api.metadata import NautobotColumnProcessor # avoid circular import
416
-
417
- processor = NautobotColumnProcessor(self, request.parser_context if request else {})
418
- field_map = dict(self.fields)
419
- all_fields = list(field_map)
420
-
421
- # Explicitly order the "big ugly" fields to the bottom
422
- processor.order_fields(all_fields)
423
- list_display_fields = self.list_display_fields
424
-
425
- # Process the list_display_fields first.
426
- for field_name in list_display_fields:
427
- try:
428
- field = field_map[field_name]
429
- except KeyError:
430
- continue # Ignore unknown fields.
431
- column_data = processor._get_column_properties(field, field_name)
432
- list_display.append(column_data)
433
- fields.append(column_data)
434
-
435
- # Process the rest of the fields second.
436
- for field_name in all_fields:
437
- # Don't process list display fields twice.
438
- if field_name in list_display_fields:
439
- continue
440
- try:
441
- field = field_map[field_name]
442
- except KeyError:
443
- continue # Ignore unknown fields.
444
- column_data = processor._get_column_properties(field, field_name)
445
- fields.append(column_data)
446
-
447
- return {
448
- "retrieve": {
449
- "tabs": self._determine_detail_view_tabs(),
450
- },
451
- "list": {
452
- "default_fields": list_display,
453
- "all_fields": fields,
454
- },
455
- }
456
-
457
- def _determine_detail_view_tabs(self):
458
- """Determine the layout for the detail view tabs that are intrinsic to this serializer."""
459
- tabs = self.get_additional_detail_view_tabs()
460
-
461
- if hasattr(self.Meta, "detail_view_config"):
462
- detail_view_config = self._validate_view_config(self.Meta.detail_view_config)
463
- else:
464
- detail_view_config = self._get_default_detail_view_config()
465
- detail_view_config = self._refine_detail_view_config(detail_view_config, tabs)
466
-
467
- return {
468
- bettertitle(self.Meta.model._meta.verbose_name): detail_view_config,
469
- **tabs,
470
- }
471
-
472
- def get_additional_detail_view_tabs(self):
473
- """
474
- Retrieve definitions of non-default detail view tabs.
475
-
476
- By default provides an "Advanced" tab containing `self.advanced_tab_fields`, but subclasses
477
- can override this to move additional serializer fields to this or other tabs.
478
-
479
- Returns:
480
- (dict): `{<tab label>: [{<panel label>: {"fields": [<list of fields>]}, ...}, ...], ...}`
481
- """
482
- return {
483
- "Advanced": [{"Object Details": {"fields": self.advanced_tab_fields}}],
484
- }
485
-
486
- def _get_default_detail_view_config(self):
487
- """
488
- Generate detail view config for the view based on the serializer's fields.
489
-
490
- Examples:
491
- >>> DeviceSerializer._get_default_detail_view_config().
492
- {
493
- "layout":[
494
- {
495
- Device: {
496
- "fields": ["name", "subdevice_role", "height", "comments"...]
497
- }
498
- },
499
- {
500
- Tags: {
501
- "fields": ["tags"]
502
- }
503
- }
504
- ]
505
- }
506
-
507
- Returns:
508
- (list): A list representing the view config.
509
- """
510
- m2m_fields, other_fields = self._get_m2m_and_non_m2m_fields()
511
- # TODO(timizuo): How do we get verbose_name of not model serializers?
512
- model_verbose_name = self.Meta.model._meta.verbose_name
513
- return {
514
- "layout": [
515
- {
516
- bettertitle(model_verbose_name): {
517
- "fields": [field["name"] for field in other_fields],
518
- }
519
- },
520
- {field["label"]: {"fields": [field["name"]]} for field in m2m_fields},
521
- ]
522
- }
523
-
524
- def _get_m2m_and_non_m2m_fields(self):
525
- """
526
- Retrieve the many-to-many (m2m) fields and other non-m2m fields from the serializer.
527
-
528
- Returns:
529
- A tuple containing two lists: m2m_fields and non m2m fields.
530
- - m2m_fields: A list of dictionaries, each containing the name and label of an m2m field.
531
- - non_m2m_fields: A list of dictionaries, each containing the name and label of a non m2m field.
532
- """
533
- m2m_fields = []
534
- non_m2m_fields = []
535
-
536
- for field_name, field in self.fields.items():
537
- if isinstance(field, drf_relations.ManyRelatedField):
538
- m2m_fields.append({"name": field_name, "label": field.label or field_name})
539
- else:
540
- non_m2m_fields.append({"name": field_name, "label": field.label or field_name})
541
-
542
- return m2m_fields, non_m2m_fields
543
-
544
- def _refine_detail_view_config(self, detail_view_config, other_tabs):
545
- """
546
- Refine the detail view config for the default tab (auto-generated, or as defined by Meta.detail_view_config).
547
-
548
- - Remove fields that should never be present in the detail view config (e.g. `notes_url`).
549
- - Ensure that fields that are already present in `other_tabs` aren't included in the detail view config.
550
- - Ensure that certain fields such as `tags` are always included in the detail view config if applicable.
551
-
552
- Args:
553
- detail_view_config (dict): `{"layout": [{<left-column>}, {<right-column}], "include_others": False}`
554
-
555
- Returns:
556
- (list): `[{"Panel 1 Name": {"fields": ["field1", "field2", ...]}, "Panel 2 Name": ...}, {...}]`
557
- """
558
- fields_to_always_move_to_right_column = [
559
- "comments",
560
- "tags",
561
- ]
562
- fields_to_always_remove = [
563
- # always handled explicitly by the UI
564
- "display",
565
- "status",
566
- # not yet supported in the UI
567
- "custom_fields",
568
- "relationships",
569
- "computed_fields",
570
- # irrelevant to the UI
571
- "notes_url",
572
- ]
573
- fields_to_remove = fields_to_always_remove + fields_to_always_move_to_right_column
574
- # Any field that's already present in another tab
575
- for tab_layout in other_tabs.values():
576
- for column in tab_layout:
577
- for grouping in column.values():
578
- fields_to_remove += grouping["fields"]
579
-
580
- # Make a deepcopy to avoid altering view_config
581
- view_config_layout = deepcopy(detail_view_config.get("layout"))
582
-
583
- # Remove fields_to_remove from the view_config_layout
584
- for column in view_config_layout:
585
- for section in column.values():
586
- for field in fields_to_remove:
587
- if field in section["fields"]:
588
- section["fields"].remove(field)
589
-
590
- serializer_fields = list(self.fields)
591
-
592
- # Add special-cased fields to right column
593
- for field in fields_to_always_move_to_right_column:
594
- if field in serializer_fields:
595
- if len(view_config_layout) < 2:
596
- view_config_layout.append({})
597
- view_config_layout[1].setdefault(bettertitle(field), {}).setdefault("fields", []).append(field)
598
-
599
- # Add fields not otherwise included in another tab, only if include_others is set to True.
600
- if detail_view_config.get("include_others", False):
601
- view_config_fields = [
602
- field for column in view_config_layout for section in column.values() for field in section["fields"]
603
- ]
604
- missing_fields = sorted(set(serializer_fields) - set(view_config_fields) - set(fields_to_remove))
605
- view_config_layout[0]["Other Fields"] = {"fields": missing_fields}
606
-
607
- return view_config_layout
608
-
609
- def _validate_view_config(self, view_config):
610
- """Validate view config."""
611
- # 1. Validate key `layout` is in view_config; as this is a required key is creating a view config
612
- if not view_config["layout"]:
613
- raise ViewConfigException("`layout` is a required key in creating a custom view_config")
614
-
615
- # 2. Validate `Other Fields` is not part of a layout group name, as this is a reserved keyword for group names
616
- for col in view_config["layout"]:
617
- for group_name in col.keys():
618
- if group_name in constants.RESERVED_NAMES_FOR_OBJECT_DETAIL_VIEW_SCHEMA:
619
- raise ViewConfigException(f"`{group_name}` is a reserved group name keyword.")
620
-
621
- return view_config
622
-
623
389
  def build_field(self, field_name, info, model_class, nested_depth):
624
390
  """
625
391
  Return a two tuple of (cls, kwargs) to build a serializer field with.
@@ -759,27 +525,27 @@ class ValidatedModelSerializer(BaseModelSerializer):
759
525
  validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)
760
526
  """
761
527
 
762
- def validate(self, attrs):
528
+ def validate(self, data):
763
529
  # Remove custom fields data and tags (if any) prior to model validation
764
- local_attrs = attrs.copy()
765
- local_attrs.pop("custom_fields", None)
766
- local_attrs.pop("relationships", None)
767
- local_attrs.pop("tags", None)
530
+ attrs = data.copy()
531
+ attrs.pop("custom_fields", None)
532
+ attrs.pop("relationships", None)
533
+ attrs.pop("tags", None)
768
534
 
769
535
  # Skip ManyToManyFields
770
536
  for field in self.Meta.model._meta.get_fields():
771
537
  if isinstance(field, models.ManyToManyField):
772
- local_attrs.pop(field.name, None)
538
+ attrs.pop(field.name, None)
773
539
 
774
540
  # Run clean() on an instance of the model
775
541
  if self.instance is None:
776
- instance = self.Meta.model(**local_attrs)
542
+ instance = self.Meta.model(**attrs)
777
543
  else:
778
544
  instance = self.instance
779
- for k, v in local_attrs.items():
545
+ for k, v in attrs.items():
780
546
  setattr(instance, k, v)
781
547
  instance.full_clean()
782
- return attrs
548
+ return data
783
549
 
784
550
 
785
551
  class WritableNestedSerializer(BaseModelSerializer):
@@ -1106,3 +872,17 @@ class NautobotModelSerializer(
1106
872
 
1107
873
  Can also be used for models derived from BaseModel, so long as they support custom fields, notes, and relationships.
1108
874
  """
875
+
876
+
877
+ #
878
+ # Tools
879
+ #
880
+
881
+
882
+ class RenderJinjaSerializer(serializers.Serializer): # pylint: disable=abstract-method
883
+ """Serializer for RenderJinjaView."""
884
+
885
+ template_code = serializers.CharField(required=True)
886
+ context = serializers.DictField(default=dict)
887
+ rendered_template = serializers.CharField(read_only=True)
888
+ rendered_template_lines = serializers.ListField(read_only=True, child=serializers.CharField())