nautobot 2.0.0a3__py3-none-any.whl → 2.0.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.
Files changed (780) hide show
  1. nautobot/apps/api.py +6 -8
  2. nautobot/apps/forms.py +0 -2
  3. nautobot/apps/ui.py +0 -8
  4. nautobot/circuits/api/serializers.py +9 -117
  5. nautobot/circuits/api/urls.py +1 -1
  6. nautobot/circuits/api/views.py +0 -1
  7. nautobot/circuits/forms.py +0 -65
  8. nautobot/circuits/migrations/0014_related_name_changes.py +1 -1
  9. nautobot/circuits/migrations/0016_tagsfield.py +34 -0
  10. nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
  11. nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
  12. nautobot/circuits/models.py +3 -87
  13. nautobot/circuits/navigation.py +14 -69
  14. nautobot/circuits/signals.py +0 -2
  15. nautobot/circuits/tables.py +39 -1
  16. nautobot/circuits/tests/integration/test_relationships.py +9 -9
  17. nautobot/circuits/tests/test_api.py +4 -8
  18. nautobot/circuits/tests/test_filters.py +10 -4
  19. nautobot/circuits/tests/test_models.py +5 -1
  20. nautobot/circuits/tests/test_views.py +27 -5
  21. nautobot/circuits/views.py +18 -10
  22. nautobot/core/api/__init__.py +8 -2
  23. nautobot/core/api/fields.py +15 -6
  24. nautobot/core/api/filter_backends.py +3 -2
  25. nautobot/core/api/metadata.py +237 -30
  26. nautobot/core/api/mixins.py +94 -0
  27. nautobot/core/api/pagination.py +4 -0
  28. nautobot/core/api/parsers.py +154 -0
  29. nautobot/core/api/renderers.py +153 -2
  30. nautobot/core/api/schema.py +46 -2
  31. nautobot/core/api/serializers.py +377 -35
  32. nautobot/core/api/urls.py +11 -3
  33. nautobot/core/api/utils.py +174 -2
  34. nautobot/core/api/versioning.py +32 -10
  35. nautobot/core/api/views.py +266 -72
  36. nautobot/core/apps/__init__.py +138 -220
  37. nautobot/core/celery/__init__.py +112 -41
  38. nautobot/core/celery/backends.py +19 -12
  39. nautobot/core/celery/control.py +46 -0
  40. nautobot/core/celery/encoders.py +53 -0
  41. nautobot/core/celery/log.py +38 -0
  42. nautobot/core/celery/schedulers.py +23 -4
  43. nautobot/core/celery/task.py +1 -16
  44. nautobot/core/checks.py +0 -27
  45. nautobot/core/choices.py +0 -113
  46. nautobot/core/{cli.py → cli/__init__.py} +1 -1
  47. nautobot/core/cli/__main__.py +3 -0
  48. nautobot/core/constants.py +0 -24
  49. nautobot/core/context_processors.py +12 -0
  50. nautobot/core/filters.py +2 -2
  51. nautobot/core/forms/__init__.py +0 -4
  52. nautobot/core/forms/fields.py +38 -65
  53. nautobot/core/forms/forms.py +4 -1
  54. nautobot/core/forms/utils.py +0 -52
  55. nautobot/core/graphql/schema.py +4 -27
  56. nautobot/core/jobs/__init__.py +75 -0
  57. nautobot/core/management/commands/build_ui.py +255 -0
  58. nautobot/core/management/commands/generate_test_data.py +3 -2
  59. nautobot/core/management/commands/post_upgrade.py +24 -24
  60. nautobot/core/models/__init__.py +26 -1
  61. nautobot/core/models/fields.py +24 -5
  62. nautobot/core/models/generics.py +2 -42
  63. nautobot/core/models/managers.py +5 -0
  64. nautobot/core/models/name_color_content_types.py +0 -14
  65. nautobot/core/models/tree_queries.py +14 -4
  66. nautobot/core/models/utils.py +5 -6
  67. nautobot/core/models/validators.py +17 -8
  68. nautobot/core/releases.py +8 -10
  69. nautobot/core/settings.py +80 -42
  70. nautobot/core/tables.py +5 -5
  71. nautobot/core/tasks.py +4 -7
  72. nautobot/core/templates/base.html +1 -49
  73. nautobot/core/templates/base_django.html +49 -0
  74. nautobot/core/templates/base_react.html +55 -0
  75. nautobot/core/templates/buttons/export.html +6 -4
  76. nautobot/core/templates/generic/object_bulk_create.html +10 -21
  77. nautobot/core/templates/generic/object_list.html +3 -1
  78. nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
  79. nautobot/core/templates/inc/footer.html +1 -0
  80. nautobot/core/templates/inc/javascript.html +0 -14
  81. nautobot/core/templates/inc/nav_menu.html +28 -33
  82. nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
  83. nautobot/core/templates/inc/relationships_table_rows.html +2 -2
  84. nautobot/core/templates/nautobot_config.py.j2 +8 -20
  85. nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
  86. nautobot/core/templates/rest_framework/api.html +8 -0
  87. nautobot/core/templatetags/buttons.py +32 -28
  88. nautobot/core/testing/__init__.py +47 -44
  89. nautobot/core/testing/api.py +362 -47
  90. nautobot/core/testing/filters.py +1 -1
  91. nautobot/core/testing/migrations.py +2 -0
  92. nautobot/core/testing/mixins.py +22 -9
  93. nautobot/core/testing/schema.py +2 -1
  94. nautobot/core/testing/views.py +21 -46
  95. nautobot/core/tests/integration/test_filters.py +17 -8
  96. nautobot/core/tests/integration/test_navbar.py +11 -34
  97. nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
  98. nautobot/core/tests/nautobot_config.py +2 -3
  99. nautobot/core/tests/test_api.py +290 -21
  100. nautobot/core/tests/test_checks.py +0 -7
  101. nautobot/core/tests/test_filters.py +107 -59
  102. nautobot/core/tests/test_forms.py +26 -92
  103. nautobot/core/tests/test_graphql.py +110 -77
  104. nautobot/core/tests/test_logging.py +4 -0
  105. nautobot/core/tests/test_managers.py +3 -1
  106. nautobot/core/tests/test_models.py +2 -0
  107. nautobot/core/tests/test_paginator.py +3 -1
  108. nautobot/core/tests/test_releases.py +12 -12
  109. nautobot/core/tests/test_templatetags_helpers.py +4 -4
  110. nautobot/core/tests/test_utils.py +32 -68
  111. nautobot/core/tests/test_views.py +12 -15
  112. nautobot/core/utils/data.py +17 -0
  113. nautobot/core/utils/deprecation.py +9 -6
  114. nautobot/core/utils/filtering.py +8 -3
  115. nautobot/core/utils/git.py +12 -4
  116. nautobot/core/utils/lookup.py +3 -1
  117. nautobot/core/utils/requests.py +1 -104
  118. nautobot/core/views/__init__.py +1 -0
  119. nautobot/core/views/generic.py +75 -110
  120. nautobot/core/views/mixins.py +52 -61
  121. nautobot/core/views/renderers.py +6 -7
  122. nautobot/core/views/utils.py +80 -0
  123. nautobot/dcim/api/serializers.py +160 -667
  124. nautobot/dcim/api/urls.py +1 -1
  125. nautobot/dcim/api/views.py +7 -44
  126. nautobot/dcim/choices.py +2 -0
  127. nautobot/dcim/filters/__init__.py +21 -0
  128. nautobot/dcim/form_mixins.py +1 -27
  129. nautobot/dcim/forms.py +19 -765
  130. nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -1
  131. nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -13
  132. nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -1
  133. nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -1
  134. nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
  135. nautobot/dcim/migrations/0035_related_name_changes.py +1 -1
  136. nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -1
  137. nautobot/dcim/migrations/0040_tagsfield.py +109 -0
  138. nautobot/dcim/migrations/{0040_ipam__namespaces.py → 0041_ipam__namespaces.py} +1 -1
  139. nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
  140. nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
  141. nautobot/dcim/models/cables.py +3 -33
  142. nautobot/dcim/models/device_component_templates.py +6 -0
  143. nautobot/dcim/models/device_components.py +12 -198
  144. nautobot/dcim/models/devices.py +30 -143
  145. nautobot/dcim/models/locations.py +3 -64
  146. nautobot/dcim/models/power.py +3 -50
  147. nautobot/dcim/models/racks.py +7 -84
  148. nautobot/dcim/navigation.py +141 -467
  149. nautobot/dcim/signals.py +0 -2
  150. nautobot/dcim/tables/locations.py +2 -2
  151. nautobot/dcim/tables/power.py +1 -2
  152. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
  153. nautobot/dcim/templates/dcim/devicetype.html +2 -2
  154. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
  155. nautobot/dcim/templates/dcim/location.html +16 -1
  156. nautobot/dcim/templates/dcim/locationtype.html +15 -0
  157. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
  158. nautobot/dcim/templates/dcim/rackgroup.html +0 -12
  159. nautobot/dcim/tests/test_api.py +166 -81
  160. nautobot/dcim/tests/test_cablepaths.py +41 -35
  161. nautobot/dcim/tests/test_filters.py +67 -23
  162. nautobot/dcim/tests/test_forms.py +5 -205
  163. nautobot/dcim/tests/test_graphql.py +7 -2
  164. nautobot/dcim/tests/test_migrations.py +6 -11
  165. nautobot/dcim/tests/test_models.py +182 -110
  166. nautobot/dcim/tests/test_natural_ordering.py +11 -8
  167. nautobot/dcim/tests/test_signals.py +6 -3
  168. nautobot/dcim/tests/test_views.py +197 -175
  169. nautobot/dcim/urls.py +11 -16
  170. nautobot/dcim/views.py +7 -134
  171. nautobot/docs/additional-features/caching.md +6 -87
  172. nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
  173. nautobot/docs/additional-features/jobs.md +177 -195
  174. nautobot/docs/administration/nautobot-server.md +6 -21
  175. nautobot/docs/administration/replicating-nautobot.md +0 -10
  176. nautobot/docs/configuration/optional-settings.md +32 -41
  177. nautobot/docs/configuration/required-settings.md +11 -52
  178. nautobot/docs/development/application-registry.md +2 -13
  179. nautobot/docs/development/extending-models.md +15 -17
  180. nautobot/docs/development/generic-views.md +0 -2
  181. nautobot/docs/development/getting-started.md +55 -5
  182. nautobot/docs/development/navigation-menu.md +22 -93
  183. nautobot/docs/development/react-ui.md +105 -0
  184. nautobot/docs/development/role-internals.md +1 -3
  185. nautobot/docs/development/style-guide.md +6 -4
  186. nautobot/docs/index.md +3 -2
  187. nautobot/docs/installation/migrating-from-netbox.md +11 -42
  188. nautobot/docs/installation/nautobot.md +1 -1
  189. nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  190. nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  191. nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  192. nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  193. nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
  194. nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  195. nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  196. nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  197. nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  198. nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  199. nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  200. nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  201. nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  202. nautobot/docs/installation/upgrading-from-nautobot-v1.md +170 -747
  203. nautobot/docs/models/dcim/device.md +3 -0
  204. nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
  205. nautobot/docs/models/extras/computedfield.md +4 -4
  206. nautobot/docs/models/extras/gitrepository.md +3 -0
  207. nautobot/docs/models/extras/job.md +1 -0
  208. nautobot/docs/models/extras/jobbutton.md +18 -13
  209. nautobot/docs/models/extras/jobhook.md +7 -4
  210. nautobot/docs/models/extras/jobresult.md +6 -2
  211. nautobot/docs/models/extras/relationship.md +2 -2
  212. nautobot/docs/models/extras/status.md +6 -19
  213. nautobot/docs/models/ipam/ipaddress.md +3 -0
  214. nautobot/docs/models/virtualization/virtualmachine.md +3 -0
  215. nautobot/docs/plugins/development.md +83 -21
  216. nautobot/docs/release-notes/version-1.5.md +53 -0
  217. nautobot/docs/release-notes/version-2.0.md +180 -0
  218. nautobot/docs/requirements.txt +1 -0
  219. nautobot/docs/rest-api/overview.md +384 -215
  220. nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
  221. nautobot/extras/admin.py +3 -5
  222. nautobot/extras/api/customfields.py +15 -39
  223. nautobot/extras/api/fields.py +0 -11
  224. nautobot/extras/api/mixins.py +45 -0
  225. nautobot/extras/api/relationships.py +63 -158
  226. nautobot/extras/api/serializers.py +165 -700
  227. nautobot/extras/api/urls.py +1 -1
  228. nautobot/extras/api/views.py +294 -280
  229. nautobot/extras/apps.py +4 -7
  230. nautobot/extras/choices.py +11 -9
  231. nautobot/extras/constants.py +9 -3
  232. nautobot/extras/datasources/__init__.py +2 -0
  233. nautobot/extras/datasources/git.py +135 -186
  234. nautobot/extras/datasources/registry.py +25 -35
  235. nautobot/extras/filters/__init__.py +20 -19
  236. nautobot/extras/filters/mixins.py +4 -4
  237. nautobot/extras/forms/forms.py +63 -127
  238. nautobot/extras/forms/mixins.py +23 -51
  239. nautobot/extras/health_checks.py +0 -33
  240. nautobot/extras/jobs.py +387 -565
  241. nautobot/extras/management/commands/runjob.py +24 -62
  242. nautobot/extras/managers.py +30 -7
  243. nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
  244. nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -1
  245. nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -1
  246. nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -7
  247. nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -32
  248. nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -1
  249. nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -1
  250. nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
  251. nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -1
  252. nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -2
  253. nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -1
  254. nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -1
  255. nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
  256. nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -1
  257. nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
  258. nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -9
  259. nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -1
  260. nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
  261. nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
  262. nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
  263. nautobot/extras/migrations/{0077_remove_slug.py → 0078_remove_slug.py} +1 -5
  264. nautobot/extras/migrations/0079_tagsfield.py +28 -0
  265. nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
  266. nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
  267. nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
  268. nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
  269. nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
  270. nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
  271. nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
  272. nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
  273. nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
  274. nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
  275. nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
  276. nautobot/extras/models/__init__.py +2 -3
  277. nautobot/extras/models/change_logging.py +0 -36
  278. nautobot/extras/models/customfields.py +39 -33
  279. nautobot/extras/models/datasources.py +48 -50
  280. nautobot/extras/models/groups.py +5 -6
  281. nautobot/extras/models/jobs.py +189 -321
  282. nautobot/extras/models/mixins.py +0 -71
  283. nautobot/extras/models/models.py +0 -19
  284. nautobot/extras/models/relationships.py +19 -13
  285. nautobot/extras/models/roles.py +0 -34
  286. nautobot/extras/models/secrets.py +2 -26
  287. nautobot/extras/models/statuses.py +6 -5
  288. nautobot/extras/models/tags.py +2 -17
  289. nautobot/extras/navigation.py +89 -307
  290. nautobot/extras/plugins/__init__.py +3 -120
  291. nautobot/extras/plugins/utils.py +0 -3
  292. nautobot/extras/plugins/validators.py +5 -4
  293. nautobot/extras/plugins/views.py +16 -3
  294. nautobot/extras/querysets.py +1 -7
  295. nautobot/extras/registry.py +3 -0
  296. nautobot/extras/signals.py +26 -60
  297. nautobot/extras/tables.py +34 -40
  298. nautobot/extras/tasks.py +0 -12
  299. nautobot/extras/templates/extras/configcontext.html +1 -1
  300. nautobot/extras/templates/extras/configcontextschema.html +16 -1
  301. nautobot/extras/templates/extras/customfield.html +0 -13
  302. nautobot/extras/templates/extras/gitrepository.html +3 -3
  303. nautobot/extras/templates/extras/inc/jobresult.html +10 -0
  304. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  305. nautobot/extras/templates/extras/job.html +35 -25
  306. nautobot/extras/templates/extras/job_approval_request.html +15 -30
  307. nautobot/extras/templates/extras/job_detail.html +13 -31
  308. nautobot/extras/templates/extras/job_edit.html +15 -17
  309. nautobot/extras/templates/extras/jobresult.html +24 -6
  310. nautobot/extras/templates/extras/scheduledjob.html +2 -2
  311. nautobot/extras/templates/extras/secret.html +28 -0
  312. nautobot/extras/templatetags/job_buttons.py +1 -0
  313. nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
  314. nautobot/extras/test_jobs/atomic_transaction.py +53 -0
  315. nautobot/extras/test_jobs/dry_run.py +29 -0
  316. nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
  317. nautobot/extras/test_jobs/duplicate_name2.py +9 -0
  318. nautobot/extras/test_jobs/fail.py +23 -0
  319. nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
  320. nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
  321. nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
  322. nautobot/extras/test_jobs/file_upload_pass.py +25 -0
  323. nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
  324. nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
  325. nautobot/extras/test_jobs/job_button_receiver.py +28 -0
  326. nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
  327. nautobot/extras/test_jobs/job_variables.py +88 -0
  328. nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
  329. nautobot/extras/test_jobs/log_redaction.py +20 -0
  330. nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
  331. nautobot/extras/test_jobs/modify_db.py +25 -0
  332. nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
  333. nautobot/extras/test_jobs/object_var_optional.py +21 -0
  334. nautobot/extras/test_jobs/object_var_required.py +21 -0
  335. nautobot/extras/test_jobs/object_vars.py +26 -0
  336. nautobot/extras/test_jobs/pass.py +25 -0
  337. nautobot/extras/test_jobs/profiling.py +32 -0
  338. nautobot/extras/test_jobs/read_only_job.py +15 -0
  339. nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
  340. nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
  341. nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
  342. nautobot/extras/tests/integration/test_computedfields.py +1 -1
  343. nautobot/extras/tests/integration/test_configcontextschema.py +5 -3
  344. nautobot/extras/tests/integration/test_customfields.py +4 -2
  345. nautobot/extras/tests/integration/test_dynamicgroups.py +1 -1
  346. nautobot/extras/tests/integration/test_jobs.py +25 -27
  347. nautobot/extras/tests/integration/test_notes.py +8 -4
  348. nautobot/extras/tests/integration/test_relationships.py +2 -2
  349. nautobot/extras/tests/test_api.py +649 -642
  350. nautobot/extras/tests/test_changelog.py +3 -3
  351. nautobot/extras/tests/test_context_managers.py +5 -3
  352. nautobot/extras/tests/test_customfields.py +92 -50
  353. nautobot/extras/tests/test_datasources.py +189 -112
  354. nautobot/extras/tests/test_dynamicgroups.py +7 -8
  355. nautobot/extras/tests/test_filters.py +137 -89
  356. nautobot/extras/tests/test_forms.py +73 -75
  357. nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
  358. nautobot/extras/tests/test_jobs.py +262 -263
  359. nautobot/extras/tests/test_migrations.py +4 -3
  360. nautobot/extras/tests/test_models.py +116 -161
  361. nautobot/extras/tests/test_plugins.py +38 -60
  362. nautobot/extras/tests/test_relationships.py +167 -120
  363. nautobot/extras/tests/test_tags.py +6 -11
  364. nautobot/extras/tests/test_utils.py +31 -1
  365. nautobot/extras/tests/test_views.py +201 -145
  366. nautobot/extras/tests/test_webhooks.py +6 -2
  367. nautobot/extras/urls.py +42 -42
  368. nautobot/extras/utils.py +137 -163
  369. nautobot/extras/views.py +78 -152
  370. nautobot/ipam/api/fields.py +17 -0
  371. nautobot/ipam/api/serializers.py +58 -164
  372. nautobot/ipam/api/urls.py +1 -1
  373. nautobot/ipam/api/views.py +3 -2
  374. nautobot/ipam/apps.py +1 -2
  375. nautobot/ipam/filters.py +1 -10
  376. nautobot/ipam/forms.py +4 -177
  377. nautobot/ipam/lookups.py +1 -0
  378. nautobot/ipam/management/commands/__init__.py +0 -0
  379. nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
  380. nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -1
  381. nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -38
  382. nautobot/ipam/migrations/0020_related_name_changes.py +1 -1
  383. nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +2 -2
  384. nautobot/ipam/migrations/0028_tagsfield.py +44 -0
  385. nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
  386. nautobot/ipam/migrations/{0028_ipam__namespaces.py → 0030_ipam__namespaces.py} +77 -28
  387. nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
  388. nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
  389. nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
  390. nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
  391. nautobot/ipam/models.py +100 -236
  392. nautobot/ipam/navigation.py +36 -181
  393. nautobot/ipam/querysets.py +20 -25
  394. nautobot/ipam/signals.py +49 -6
  395. nautobot/ipam/tables.py +10 -3
  396. nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
  397. nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
  398. nautobot/ipam/templates/ipam/namespace_retrieve.html +17 -4
  399. nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
  400. nautobot/ipam/templates/ipam/prefix.html +1 -1
  401. nautobot/ipam/templates/ipam/vlangroup.html +0 -13
  402. nautobot/ipam/templates/ipam/vrf_edit.html +6 -0
  403. nautobot/ipam/tests/integration/test_prefixes.py +3 -26
  404. nautobot/ipam/tests/test_api.py +22 -19
  405. nautobot/ipam/tests/test_filters.py +59 -23
  406. nautobot/ipam/tests/test_migrations.py +6 -10
  407. nautobot/ipam/tests/test_models.py +323 -198
  408. nautobot/ipam/tests/test_ordering.py +2 -2
  409. nautobot/ipam/tests/test_querysets.py +44 -24
  410. nautobot/ipam/tests/test_views.py +73 -26
  411. nautobot/ipam/urls.py +16 -0
  412. nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
  413. nautobot/ipam/utils/migrations.py +713 -0
  414. nautobot/ipam/views.py +137 -20
  415. nautobot/project-static/docs/404.html +1178 -10
  416. nautobot/project-static/docs/additional-features/caching.html +1224 -159
  417. nautobot/project-static/docs/additional-features/change-logging.html +1180 -12
  418. nautobot/project-static/docs/additional-features/config-contexts.html +1180 -12
  419. nautobot/project-static/docs/additional-features/graphql.html +1179 -11
  420. nautobot/project-static/docs/additional-features/healthcheck.html +1180 -12
  421. nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1184 -12
  422. nautobot/project-static/docs/additional-features/jobs.html +1514 -328
  423. nautobot/project-static/docs/additional-features/napalm.html +1180 -12
  424. nautobot/project-static/docs/additional-features/prometheus-metrics.html +1180 -12
  425. nautobot/project-static/docs/additional-features/template-filters.html +1180 -12
  426. nautobot/project-static/docs/administration/celery-queues.html +1178 -10
  427. nautobot/project-static/docs/administration/nautobot-server.html +1451 -304
  428. nautobot/project-static/docs/administration/nautobot-shell.html +1178 -10
  429. nautobot/project-static/docs/administration/permissions.html +1178 -10
  430. nautobot/project-static/docs/administration/replicating-nautobot.html +1262 -113
  431. nautobot/project-static/docs/apps/index.html +1178 -10
  432. nautobot/project-static/docs/apps/nautobot-apps.html +1178 -10
  433. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1580 -426
  434. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1178 -10
  435. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3481 -1838
  436. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1178 -10
  437. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1178 -10
  438. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1185 -11
  439. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1719 -551
  440. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2062 -930
  441. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +1946 -659
  442. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1180 -12
  443. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1189 -21
  444. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9283 -6218
  445. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2734 -2122
  446. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1178 -10
  447. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2337 -1300
  448. nautobot/project-static/docs/configuration/authentication/ldap.html +1178 -10
  449. nautobot/project-static/docs/configuration/authentication/remote.html +1178 -10
  450. nautobot/project-static/docs/configuration/authentication/sso.html +1178 -10
  451. nautobot/project-static/docs/configuration/index.html +1178 -10
  452. nautobot/project-static/docs/configuration/optional-settings.html +1311 -160
  453. nautobot/project-static/docs/configuration/required-settings.html +1312 -211
  454. nautobot/project-static/docs/core-functionality/circuits.html +1178 -10
  455. nautobot/project-static/docs/core-functionality/device-types.html +1178 -10
  456. nautobot/project-static/docs/core-functionality/devices.html +1182 -10
  457. nautobot/project-static/docs/core-functionality/ipam.html +1182 -10
  458. nautobot/project-static/docs/core-functionality/power.html +1178 -10
  459. nautobot/project-static/docs/core-functionality/secrets.html +1178 -10
  460. nautobot/project-static/docs/core-functionality/services.html +1178 -10
  461. nautobot/project-static/docs/core-functionality/sites-and-racks.html +1178 -10
  462. nautobot/project-static/docs/core-functionality/tenancy.html +1178 -10
  463. nautobot/project-static/docs/core-functionality/virtualization.html +1182 -10
  464. nautobot/project-static/docs/core-functionality/vlans.html +1179 -11
  465. nautobot/project-static/docs/development/application-registry.html +1190 -42
  466. nautobot/project-static/docs/development/best-practices.html +1178 -10
  467. nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1178 -10
  468. nautobot/project-static/docs/development/extending-models.html +1238 -83
  469. nautobot/project-static/docs/development/generic-views.html +1180 -14
  470. nautobot/project-static/docs/development/getting-started.html +1365 -90
  471. nautobot/project-static/docs/development/homepage.html +1178 -10
  472. nautobot/project-static/docs/development/index.html +1178 -10
  473. nautobot/project-static/docs/development/model-features.html +1178 -10
  474. nautobot/project-static/docs/development/natural-keys.html +1178 -10
  475. nautobot/project-static/docs/development/navigation-menu.html +1215 -125
  476. nautobot/project-static/docs/development/react-ui.html +4199 -0
  477. nautobot/project-static/docs/development/release-checklist.html +1178 -10
  478. nautobot/project-static/docs/development/role-internals.html +1179 -12
  479. nautobot/project-static/docs/development/style-guide.html +1188 -19
  480. nautobot/project-static/docs/development/templates.html +1178 -10
  481. nautobot/project-static/docs/development/testing.html +1178 -10
  482. nautobot/project-static/docs/development/user-preferences.html +1178 -10
  483. nautobot/project-static/docs/docker/index.html +1178 -10
  484. nautobot/project-static/docs/index.html +1183 -12
  485. nautobot/project-static/docs/installation/centos.html +1178 -10
  486. nautobot/project-static/docs/installation/external-authentication.html +1178 -10
  487. nautobot/project-static/docs/installation/http-server.html +1178 -10
  488. nautobot/project-static/docs/installation/index.html +1178 -10
  489. nautobot/project-static/docs/installation/migrating-from-netbox.html +1305 -189
  490. nautobot/project-static/docs/installation/migrating-from-postgresql.html +1178 -10
  491. nautobot/project-static/docs/installation/nautobot.html +1179 -11
  492. nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1178 -10
  493. nautobot/project-static/docs/installation/selinux-troubleshooting.html +1178 -10
  494. nautobot/project-static/docs/installation/services.html +1178 -10
  495. nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  496. nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  497. nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  498. nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  499. nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
  500. nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  501. nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  502. nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  503. nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  504. nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  505. nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  506. nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  507. nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  508. nautobot/project-static/docs/installation/ubuntu.html +1178 -10
  509. nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +3823 -2152
  510. nautobot/project-static/docs/installation/upgrading.html +1178 -10
  511. nautobot/project-static/docs/models/circuits/circuit.html +1293 -103
  512. nautobot/project-static/docs/models/circuits/circuittermination.html +1293 -103
  513. nautobot/project-static/docs/models/circuits/circuittype.html +1293 -103
  514. nautobot/project-static/docs/models/circuits/provider.html +1293 -103
  515. nautobot/project-static/docs/models/circuits/providernetwork.html +1293 -103
  516. nautobot/project-static/docs/models/dcim/cable.html +1324 -103
  517. nautobot/project-static/docs/models/dcim/consoleport.html +1293 -103
  518. nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1293 -103
  519. nautobot/project-static/docs/models/dcim/consoleserverport.html +1293 -103
  520. nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1293 -103
  521. nautobot/project-static/docs/models/dcim/device.html +1326 -132
  522. nautobot/project-static/docs/models/dcim/devicebay.html +1293 -103
  523. nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1293 -103
  524. nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1379 -97
  525. nautobot/project-static/docs/models/dcim/devicetype.html +1293 -103
  526. nautobot/project-static/docs/models/dcim/frontport.html +1293 -103
  527. nautobot/project-static/docs/models/dcim/frontporttemplate.html +1293 -103
  528. nautobot/project-static/docs/models/dcim/interface.html +1293 -103
  529. nautobot/project-static/docs/models/dcim/interfacetemplate.html +1293 -103
  530. nautobot/project-static/docs/models/dcim/inventoryitem.html +1293 -103
  531. nautobot/project-static/docs/models/dcim/location.html +1293 -103
  532. nautobot/project-static/docs/models/dcim/locationtype.html +1293 -103
  533. nautobot/project-static/docs/models/dcim/manufacturer.html +1292 -102
  534. nautobot/project-static/docs/models/dcim/platform.html +1272 -82
  535. nautobot/project-static/docs/models/dcim/powerfeed.html +1270 -80
  536. nautobot/project-static/docs/models/dcim/poweroutlet.html +1272 -82
  537. nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1272 -82
  538. nautobot/project-static/docs/models/dcim/powerpanel.html +1270 -80
  539. nautobot/project-static/docs/models/dcim/powerport.html +1272 -82
  540. nautobot/project-static/docs/models/dcim/powerporttemplate.html +1272 -82
  541. nautobot/project-static/docs/models/dcim/rack.html +1272 -82
  542. nautobot/project-static/docs/models/dcim/rackgroup.html +1272 -82
  543. nautobot/project-static/docs/models/dcim/rackreservation.html +1272 -82
  544. nautobot/project-static/docs/models/dcim/rearport.html +1286 -96
  545. nautobot/project-static/docs/models/dcim/rearporttemplate.html +1286 -96
  546. nautobot/project-static/docs/models/dcim/region.html +1178 -10
  547. nautobot/project-static/docs/models/dcim/site.html +1178 -10
  548. nautobot/project-static/docs/models/dcim/virtualchassis.html +1284 -94
  549. nautobot/project-static/docs/models/extras/computedfield.html +1184 -16
  550. nautobot/project-static/docs/models/extras/configcontext.html +1314 -86
  551. nautobot/project-static/docs/models/extras/configcontextschema.html +1276 -86
  552. nautobot/project-static/docs/models/extras/customfield.html +1180 -12
  553. nautobot/project-static/docs/models/extras/customlink.html +1180 -12
  554. nautobot/project-static/docs/models/extras/dynamicgroup.html +1180 -12
  555. nautobot/project-static/docs/models/extras/exporttemplate.html +1180 -12
  556. nautobot/project-static/docs/models/extras/gitrepository.html +1184 -12
  557. nautobot/project-static/docs/models/extras/graphqlquery.html +1321 -86
  558. nautobot/project-static/docs/models/extras/imageattachment.html +1276 -86
  559. nautobot/project-static/docs/models/extras/job.html +1277 -86
  560. nautobot/project-static/docs/models/extras/jobbutton.html +1201 -29
  561. nautobot/project-static/docs/models/extras/jobhook.html +1188 -16
  562. nautobot/project-static/docs/models/extras/joblogentry.html +1274 -84
  563. nautobot/project-static/docs/models/extras/jobresult.html +1364 -169
  564. nautobot/project-static/docs/models/extras/note.html +1180 -12
  565. nautobot/project-static/docs/models/extras/relationship.html +1182 -14
  566. nautobot/project-static/docs/models/extras/role.html +1320 -86
  567. nautobot/project-static/docs/models/extras/secret.html +1314 -86
  568. nautobot/project-static/docs/models/extras/secretsgroup.html +1276 -86
  569. nautobot/project-static/docs/models/extras/status.html +1188 -59
  570. nautobot/project-static/docs/models/extras/tag.html +1180 -12
  571. nautobot/project-static/docs/models/extras/webhook.html +1180 -12
  572. nautobot/project-static/docs/models/ipam/ipaddress.html +1327 -102
  573. nautobot/project-static/docs/models/ipam/prefix.html +1276 -86
  574. nautobot/project-static/docs/models/ipam/rir.html +1276 -86
  575. nautobot/project-static/docs/models/ipam/routetarget.html +1276 -86
  576. nautobot/project-static/docs/models/ipam/service.html +1276 -86
  577. nautobot/project-static/docs/models/ipam/vlan.html +1276 -86
  578. nautobot/project-static/docs/models/ipam/vlangroup.html +1276 -86
  579. nautobot/project-static/docs/models/ipam/vrf.html +1276 -86
  580. nautobot/project-static/docs/models/tenancy/tenant.html +1276 -86
  581. nautobot/project-static/docs/models/tenancy/tenantgroup.html +1276 -86
  582. nautobot/project-static/docs/models/users/objectpermission.html +1314 -86
  583. nautobot/project-static/docs/models/users/token.html +1276 -86
  584. nautobot/project-static/docs/models/virtualization/cluster.html +1276 -86
  585. nautobot/project-static/docs/models/virtualization/clustergroup.html +1276 -86
  586. nautobot/project-static/docs/models/virtualization/clustertype.html +1276 -86
  587. nautobot/project-static/docs/models/virtualization/virtualmachine.html +1321 -127
  588. nautobot/project-static/docs/models/virtualization/vminterface.html +1276 -86
  589. nautobot/project-static/docs/objects.inv +0 -0
  590. nautobot/project-static/docs/plugins/development.html +1726 -495
  591. nautobot/project-static/docs/plugins/index.html +1178 -10
  592. nautobot/project-static/docs/plugins/porting-from-netbox.html +1178 -10
  593. nautobot/project-static/docs/release-notes/index.html +1178 -10
  594. nautobot/project-static/docs/release-notes/version-1.0.html +1178 -10
  595. nautobot/project-static/docs/release-notes/version-1.1.html +1178 -10
  596. nautobot/project-static/docs/release-notes/version-1.2.html +1178 -10
  597. nautobot/project-static/docs/release-notes/version-1.3.html +1178 -10
  598. nautobot/project-static/docs/release-notes/version-1.4.html +1178 -10
  599. nautobot/project-static/docs/release-notes/version-1.5.html +1608 -225
  600. nautobot/project-static/docs/release-notes/version-2.0.html +1547 -47
  601. nautobot/project-static/docs/requirements.txt +1 -0
  602. nautobot/project-static/docs/rest-api/authentication.html +1179 -11
  603. nautobot/project-static/docs/rest-api/filtering.html +1178 -10
  604. nautobot/project-static/docs/rest-api/overview.html +1841 -446
  605. nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
  606. nautobot/project-static/docs/search/search_index.json +1 -1
  607. nautobot/project-static/docs/sitemap.xml +197 -187
  608. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  609. nautobot/project-static/docs/user-guides/custom-fields.html +1178 -10
  610. nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1178 -10
  611. nautobot/project-static/docs/user-guides/getting-started/index.html +1178 -10
  612. nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1178 -10
  613. nautobot/project-static/docs/user-guides/getting-started/ipam.html +1178 -10
  614. nautobot/project-static/docs/user-guides/getting-started/platforms.html +1178 -10
  615. nautobot/project-static/docs/user-guides/getting-started/regions.html +1178 -10
  616. nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1178 -10
  617. nautobot/project-static/docs/user-guides/getting-started/tenants.html +1178 -10
  618. nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1178 -10
  619. nautobot/project-static/docs/user-guides/git-data-source.html +1178 -10
  620. nautobot/project-static/docs/user-guides/graphql.html +1178 -10
  621. nautobot/project-static/docs/user-guides/relationships.html +1178 -10
  622. nautobot/project-static/docs/user-guides/s3-django-storage.html +1178 -10
  623. nautobot/project-static/js/forms.js +16 -9
  624. nautobot/project-static/js/theme.js +5 -0
  625. nautobot/tenancy/api/serializers.py +4 -32
  626. nautobot/tenancy/api/urls.py +1 -1
  627. nautobot/tenancy/forms.py +0 -28
  628. nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
  629. nautobot/tenancy/models.py +0 -25
  630. nautobot/tenancy/navigation.py +6 -39
  631. nautobot/tenancy/templates/tenancy/tenant.html +12 -12
  632. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  633. nautobot/tenancy/tests/test_api.py +1 -3
  634. nautobot/tenancy/tests/test_filters.py +10 -5
  635. nautobot/tenancy/views.py +0 -2
  636. nautobot/ui/.eslintignore +6 -0
  637. nautobot/ui/.gitignore +10 -0
  638. nautobot/ui/.prettierignore +9 -0
  639. nautobot/ui/.prettierrc +4 -0
  640. nautobot/ui/README.md +33 -0
  641. nautobot/ui/app_imports.js.j2 +7 -0
  642. nautobot/ui/craco.config.js +46 -0
  643. nautobot/ui/jsconfig-base.json +11 -0
  644. nautobot/ui/jsconfig.json +5 -0
  645. nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
  646. nautobot/ui/package-lock.json +21451 -0
  647. nautobot/ui/package.json +70 -0
  648. nautobot/ui/public/index.html +47 -0
  649. nautobot/ui/public/logo192.png +0 -0
  650. nautobot/ui/public/logo512.png +0 -0
  651. nautobot/ui/public/manifest.json +25 -0
  652. nautobot/ui/public/nautobot_logo.svg +131 -0
  653. nautobot/ui/public/robots.txt +3 -0
  654. nautobot/ui/src/App.js +71 -0
  655. nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
  656. nautobot/ui/src/components/AppTab.js +40 -0
  657. nautobot/ui/src/components/Apps.js +60 -0
  658. nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
  659. nautobot/ui/src/components/HomePanel.js +58 -0
  660. nautobot/ui/src/components/JobHistoryTable.js +78 -0
  661. nautobot/ui/src/components/Layout.js +53 -0
  662. nautobot/ui/src/components/LoadingWidget.js +25 -0
  663. nautobot/ui/src/components/Navbar.js +116 -0
  664. nautobot/ui/src/components/NotificationPopover.js +27 -0
  665. nautobot/ui/src/components/ObjectListTable.js +209 -0
  666. nautobot/ui/src/components/ReferenceDataTag.js +35 -0
  667. nautobot/ui/src/components/RouterButton.js +10 -0
  668. nautobot/ui/src/components/RouterLink.js +10 -0
  669. nautobot/ui/src/components/SidebarNav.js +147 -0
  670. nautobot/ui/src/components/Table.js +48 -0
  671. nautobot/ui/src/components/TableItem.js +71 -0
  672. nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
  673. nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
  674. nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
  675. nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
  676. nautobot/ui/src/components/__tests__/Table.test.js +36 -0
  677. nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
  678. nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
  679. nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
  680. nautobot/ui/src/components/pagination.js +93 -0
  681. nautobot/ui/src/components/paginator.js +79 -0
  682. nautobot/ui/src/components/paginator_form.js +43 -0
  683. nautobot/ui/src/components/usePagination.js +57 -0
  684. nautobot/ui/src/constants/apiPath.js +10 -0
  685. nautobot/ui/src/constants/icons.js +15 -0
  686. nautobot/ui/src/constants/size.js +15 -0
  687. nautobot/ui/src/index.js +65 -0
  688. nautobot/ui/src/reportWebVitals.js +15 -0
  689. nautobot/ui/src/router.js +77 -0
  690. nautobot/ui/src/utils/api.js +131 -0
  691. nautobot/ui/src/utils/app-import.js +15 -0
  692. nautobot/ui/src/utils/color.js +15 -0
  693. nautobot/ui/src/utils/date.js +14 -0
  694. nautobot/ui/src/utils/index.js +15 -0
  695. nautobot/ui/src/utils/navigation.js +32 -0
  696. nautobot/ui/src/utils/session.js +64 -0
  697. nautobot/ui/src/utils/store.js +242 -0
  698. nautobot/ui/src/utils/string.js +6 -0
  699. nautobot/ui/src/utils/url.js +4 -0
  700. nautobot/ui/src/views/Home.js +138 -0
  701. nautobot/ui/src/views/InstalledApps.js +80 -0
  702. nautobot/ui/src/views/Login.js +48 -0
  703. nautobot/ui/src/views/Logout.js +20 -0
  704. nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
  705. nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
  706. nautobot/ui/src/views/__tests__/Login.test.js +15 -0
  707. nautobot/ui/src/views/generic/GenericView.js +142 -0
  708. nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
  709. nautobot/ui/src/views/generic/ObjectList.js +127 -0
  710. nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
  711. nautobot/users/admin.py +1 -1
  712. nautobot/users/api/serializers.py +51 -61
  713. nautobot/users/api/urls.py +1 -1
  714. nautobot/users/api/views.py +53 -2
  715. nautobot/users/tests/test_api.py +110 -25
  716. nautobot/virtualization/api/serializers.py +18 -130
  717. nautobot/virtualization/api/urls.py +1 -1
  718. nautobot/virtualization/api/views.py +1 -22
  719. nautobot/virtualization/forms.py +13 -99
  720. nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -1
  721. nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -11
  722. nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -1
  723. nautobot/virtualization/migrations/0018_related_name_changes.py +1 -1
  724. nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
  725. nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
  726. nautobot/virtualization/migrations/{0021_ipam__namespaces.py → 0023_ipam__namespaces.py} +2 -2
  727. nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
  728. nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
  729. nautobot/virtualization/models.py +31 -123
  730. nautobot/virtualization/navigation.py +18 -99
  731. nautobot/virtualization/templates/virtualization/virtualmachine.html +2 -1
  732. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
  733. nautobot/virtualization/tests/test_api.py +25 -26
  734. nautobot/virtualization/tests/test_filters.py +41 -15
  735. nautobot/virtualization/tests/test_models.py +31 -7
  736. nautobot/virtualization/tests/test_views.py +42 -25
  737. nautobot/virtualization/views.py +7 -6
  738. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +3 -7
  739. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +744 -602
  740. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
  741. nautobot/circuits/api/nested_serializers.py +0 -69
  742. nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
  743. nautobot/dcim/api/nested_serializers.py +0 -356
  744. nautobot/dcim/templates/dcim/device_import.html +0 -5
  745. nautobot/dcim/templates/dcim/device_import_child.html +0 -5
  746. nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
  747. nautobot/extras/api/nested_serializers.py +0 -353
  748. nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -41
  749. nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -46
  750. nautobot/extras/reports.py +0 -60
  751. nautobot/extras/scripts.py +0 -72
  752. nautobot/extras/tests/example_jobs/script_variables.py +0 -67
  753. nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
  754. nautobot/extras/tests/example_jobs/test_fail.py +0 -16
  755. nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
  756. nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
  757. nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
  758. nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
  759. nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
  760. nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
  761. nautobot/extras/tests/example_jobs/test_modify_db.py +0 -18
  762. nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
  763. nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
  764. nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
  765. nautobot/extras/tests/example_jobs/test_pass.py +0 -19
  766. nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
  767. nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
  768. nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
  769. nautobot/ipam/api/nested_serializers.py +0 -159
  770. nautobot/ipam/migrations/0029_ipam__prefix__add_parent.py +0 -31
  771. nautobot/ipam/migrations/0030_ipam__prefix__data_migration.py +0 -13
  772. nautobot/ipam/migrations/0031_ipam__ipaddress__add_parent.py +0 -41
  773. nautobot/ipam/migrations/0032_ipam__ipaddress__data_migration.py +0 -11
  774. nautobot/tenancy/api/nested_serializers.py +0 -31
  775. nautobot/users/api/nested_serializers.py +0 -67
  776. nautobot/virtualization/api/nested_serializers.py +0 -65
  777. /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
  778. /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
  779. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
  780. {nautobot-2.0.0a3.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
@@ -6,19 +6,24 @@ from rest_framework.validators import UniqueTogetherValidator
6
6
  from nautobot.core.api import (
7
7
  ChoiceField,
8
8
  ContentTypeField,
9
- SerializedPKRelatedField,
9
+ NautobotModelSerializer,
10
10
  TimeZoneSerializerField,
11
11
  TreeModelSerializerMixin,
12
12
  ValidatedModelSerializer,
13
- WritableNestedSerializer,
14
13
  )
15
14
  from nautobot.core.api.serializers import PolymorphicProxySerializer
16
- from nautobot.core.api.utils import get_serializer_for_model, get_serializers_for_models
15
+ from nautobot.core.api.utils import (
16
+ get_nested_serializer_depth,
17
+ get_serializer_for_model,
18
+ get_serializers_for_models,
19
+ return_nested_serializer_data_based_on_depth,
20
+ )
17
21
  from nautobot.core.models.utils import get_all_concrete_models
18
22
  from nautobot.core.utils.config import get_settings_or_config
19
23
  from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
20
24
  from nautobot.dcim.choices import (
21
25
  CableLengthUnitChoices,
26
+ CableTypeChoices,
22
27
  ConsolePortTypeChoices,
23
28
  DeviceFaceChoices,
24
29
  DeviceRedundancyGroupFailoverStrategyChoices,
@@ -74,59 +79,10 @@ from nautobot.dcim.models import (
74
79
  RearPortTemplate,
75
80
  VirtualChassis,
76
81
  )
77
- from nautobot.extras.api.serializers import (
78
- NautobotModelSerializer,
79
- RoleRequiredRoleModelSerializerMixin,
80
- RoleModelSerializerMixin,
81
- StatusModelSerializerMixin,
82
+ from nautobot.extras.api.mixins import (
82
83
  TaggedModelSerializerMixin,
83
84
  )
84
- from nautobot.extras.api.nested_serializers import NestedConfigContextSchemaSerializer, NestedSecretsGroupSerializer
85
85
  from nautobot.extras.utils import FeatureQuery
86
- from nautobot.ipam.api.nested_serializers import (
87
- NestedIPAddressSerializer,
88
- NestedVLANSerializer,
89
- )
90
- from nautobot.ipam.models import IPAddress, VLAN
91
- from nautobot.tenancy.api.nested_serializers import NestedTenantSerializer
92
- from nautobot.users.api.nested_serializers import NestedUserSerializer
93
- from nautobot.virtualization.api.nested_serializers import NestedClusterSerializer
94
-
95
- # Not all of these variable(s) are not actually used anywhere in this file, but required for the
96
- # automagically replacing a Serializer with its corresponding NestedSerializer.
97
- from .nested_serializers import ( # noqa: F401
98
- NestedCableSerializer,
99
- NestedConsolePortSerializer,
100
- NestedConsolePortTemplateSerializer,
101
- NestedConsoleServerPortSerializer,
102
- NestedConsoleServerPortTemplateSerializer,
103
- NestedDeviceBaySerializer,
104
- NestedDeviceBayTemplateSerializer,
105
- NestedDeviceRedundancyGroupSerializer,
106
- NestedDeviceSerializer,
107
- NestedDeviceTypeSerializer,
108
- NestedFrontPortSerializer,
109
- NestedFrontPortTemplateSerializer,
110
- NestedInterfaceSerializer,
111
- NestedInterfaceTemplateSerializer,
112
- NestedInventoryItemSerializer,
113
- NestedLocationSerializer,
114
- NestedLocationTypeSerializer,
115
- NestedManufacturerSerializer,
116
- NestedPlatformSerializer,
117
- NestedPowerFeedSerializer,
118
- NestedPowerOutletSerializer,
119
- NestedPowerOutletTemplateSerializer,
120
- NestedPowerPanelSerializer,
121
- NestedPowerPortSerializer,
122
- NestedPowerPortTemplateSerializer,
123
- NestedRackGroupSerializer,
124
- NestedRackReservationSerializer,
125
- NestedRackSerializer,
126
- NestedRearPortSerializer,
127
- NestedRearPortTemplateSerializer,
128
- NestedVirtualChassisSerializer,
129
- )
130
86
 
131
87
 
132
88
  class CableTerminationModelSerializerMixin(serializers.ModelSerializer):
@@ -143,7 +99,7 @@ class CableTerminationModelSerializerMixin(serializers.ModelSerializer):
143
99
  PolymorphicProxySerializer(
144
100
  component_name="CableTermination",
145
101
  resource_type_field_name="object_type",
146
- serializers=lambda: get_serializers_for_models(get_all_concrete_models(CableTermination), prefix="Nested"),
102
+ serializers=lambda: get_serializers_for_models(get_all_concrete_models(CableTermination)),
147
103
  allow_null=True,
148
104
  )
149
105
  )
@@ -152,9 +108,8 @@ class CableTerminationModelSerializerMixin(serializers.ModelSerializer):
152
108
  Return the appropriate serializer for the cable termination model.
153
109
  """
154
110
  if obj._cable_peer is not None:
155
- serializer = get_serializer_for_model(obj._cable_peer, prefix="Nested")
156
- context = {"request": self.context["request"]}
157
- return serializer(obj._cable_peer, context=context).data
111
+ depth = get_nested_serializer_depth(self)
112
+ return return_nested_serializer_data_based_on_depth(self, depth, obj, obj._cable_peer, "_cable_peer")
158
113
  return None
159
114
 
160
115
 
@@ -179,7 +134,7 @@ class PathEndpointModelSerializerMixin(ValidatedModelSerializer):
179
134
  PolymorphicProxySerializer(
180
135
  component_name="PathEndpoint",
181
136
  resource_type_field_name="object_type",
182
- serializers=lambda: get_serializers_for_models(get_all_concrete_models(PathEndpoint), prefix="Nested"),
137
+ serializers=lambda: get_serializers_for_models(get_all_concrete_models(PathEndpoint)),
183
138
  allow_null=True,
184
139
  )
185
140
  )
@@ -188,9 +143,10 @@ class PathEndpointModelSerializerMixin(ValidatedModelSerializer):
188
143
  Return the appropriate serializer for the type of connected object.
189
144
  """
190
145
  if obj._path is not None and obj._path.destination is not None:
191
- serializer = get_serializer_for_model(obj._path.destination, prefix="Nested")
192
- context = {"request": self.context["request"]}
193
- return serializer(obj._path.destination, context=context).data
146
+ depth = get_nested_serializer_depth(self)
147
+ return return_nested_serializer_data_based_on_depth(
148
+ self, depth, obj, obj._path.destination, "connected_endpoint"
149
+ )
194
150
  return None
195
151
 
196
152
  @extend_schema_field(serializers.BooleanField(allow_null=True))
@@ -212,8 +168,6 @@ class ConnectedEndpointSerializer(PathEndpointModelSerializerMixin):
212
168
 
213
169
 
214
170
  class LocationTypeSerializer(NautobotModelSerializer, TreeModelSerializerMixin):
215
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:locationtype-detail")
216
- parent = NestedLocationTypeSerializer(required=False, allow_null=True, default=None)
217
171
  content_types = ContentTypeField(
218
172
  queryset=ContentType.objects.filter(FeatureQuery("locations").get_query()),
219
173
  required=False,
@@ -222,25 +176,15 @@ class LocationTypeSerializer(NautobotModelSerializer, TreeModelSerializerMixin):
222
176
 
223
177
  class Meta:
224
178
  model = LocationType
225
- fields = [
226
- "url",
227
- "name",
228
- "slug",
229
- "parent",
230
- "nestable",
231
- "content_types",
232
- "description",
233
- "tree_depth",
234
- ]
179
+ fields = "__all__"
180
+ list_display_fields = ["name", "nestable", "content_types", "description"]
235
181
 
236
182
 
237
183
  class LocationSerializer(
238
- NautobotModelSerializer, TaggedModelSerializerMixin, StatusModelSerializerMixin, TreeModelSerializerMixin
184
+ NautobotModelSerializer,
185
+ TaggedModelSerializerMixin,
186
+ TreeModelSerializerMixin,
239
187
  ):
240
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:location-detail")
241
- location_type = NestedLocationTypeSerializer()
242
- parent = NestedLocationSerializer(required=False, allow_null=True)
243
- tenant = NestedTenantSerializer(required=False, allow_null=True)
244
188
  time_zone = TimeZoneSerializerField(required=False, allow_null=True)
245
189
  circuit_count = serializers.IntegerField(read_only=True)
246
190
  device_count = serializers.IntegerField(read_only=True)
@@ -251,35 +195,8 @@ class LocationSerializer(
251
195
 
252
196
  class Meta:
253
197
  model = Location
254
- fields = [
255
- "url",
256
- "name",
257
- "slug",
258
- "status",
259
- "location_type",
260
- "parent",
261
- "tenant",
262
- "description",
263
- "tree_depth",
264
- "facility",
265
- "asn",
266
- "time_zone",
267
- "description",
268
- "physical_address",
269
- "shipping_address",
270
- "latitude",
271
- "longitude",
272
- "contact_name",
273
- "contact_phone",
274
- "contact_email",
275
- "comments",
276
- "circuit_count",
277
- "device_count",
278
- "prefix_count",
279
- "rack_count",
280
- "virtual_machine_count",
281
- "vlan_count",
282
- ]
198
+ fields = "__all__"
199
+ list_display_fields = ["name", "status", "parent", "tenant", "description", "tags"]
283
200
  # https://www.django-rest-framework.org/api-guide/validators/#optional-fields
284
201
  validators = []
285
202
 
@@ -300,23 +217,12 @@ class LocationSerializer(
300
217
 
301
218
 
302
219
  class RackGroupSerializer(NautobotModelSerializer, TreeModelSerializerMixin):
303
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:rackgroup-detail")
304
- location = NestedLocationSerializer()
305
- parent = NestedRackGroupSerializer(required=False, allow_null=True)
306
220
  rack_count = serializers.IntegerField(read_only=True)
307
221
 
308
222
  class Meta:
309
223
  model = RackGroup
310
- fields = [
311
- "url",
312
- "name",
313
- "slug",
314
- "location",
315
- "parent",
316
- "description",
317
- "rack_count",
318
- "tree_depth",
319
- ]
224
+ fields = "__all__"
225
+ list_display_fields = ["name", "location", "rack_count", "description"]
320
226
  # Omit the UniqueTogetherValidator that would be automatically added to validate (location, slug). This
321
227
  # prevents slug from being interpreted as a required field.
322
228
  # 2.0 TODO: Remove if/when slug is globally unique. This would be a breaking change.
@@ -336,42 +242,19 @@ class RackGroupSerializer(NautobotModelSerializer, TreeModelSerializerMixin):
336
242
 
337
243
 
338
244
  class RackSerializer(
339
- NautobotModelSerializer, TaggedModelSerializerMixin, StatusModelSerializerMixin, RoleModelSerializerMixin
245
+ NautobotModelSerializer,
246
+ TaggedModelSerializerMixin,
340
247
  ):
341
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:rack-detail")
342
- location = NestedLocationSerializer()
343
- rack_group = NestedRackGroupSerializer(required=False, allow_null=True, default=None)
344
- tenant = NestedTenantSerializer(required=False, allow_null=True)
345
248
  type = ChoiceField(choices=RackTypeChoices, allow_blank=True, required=False)
346
- width = ChoiceField(choices=RackWidthChoices, required=False)
249
+ width = ChoiceField(choices=RackWidthChoices, required=False, help_text="Rail-to-rail width (in inches)")
347
250
  outer_unit = ChoiceField(choices=RackDimensionUnitChoices, allow_blank=True, required=False)
348
251
  device_count = serializers.IntegerField(read_only=True)
349
252
  power_feed_count = serializers.IntegerField(read_only=True)
350
253
 
351
254
  class Meta:
352
255
  model = Rack
353
- fields = [
354
- "url",
355
- "name",
356
- "facility_id",
357
- "location",
358
- "rack_group",
359
- "tenant",
360
- "status",
361
- "role",
362
- "serial",
363
- "asset_tag",
364
- "type",
365
- "width",
366
- "u_height",
367
- "desc_units",
368
- "outer_width",
369
- "outer_depth",
370
- "outer_unit",
371
- "comments",
372
- "device_count",
373
- "power_feed_count",
374
- ]
256
+ fields = "__all__"
257
+ list_display_fields = ["name", "location", "rack_group", "status", "facility_id", "tenant", "role", "u_height"]
375
258
  # Omit the UniqueTogetherValidator that would be automatically added to validate (rack_group, facility_id).
376
259
  # This prevents facility_id from being interpreted as a required field.
377
260
  validators = [UniqueTogetherValidator(queryset=Rack.objects.all(), fields=("rack_group", "name"))]
@@ -396,26 +279,27 @@ class RackUnitSerializer(serializers.Serializer):
396
279
  id = serializers.IntegerField(read_only=True)
397
280
  name = serializers.CharField(read_only=True)
398
281
  face = ChoiceField(choices=DeviceFaceChoices, read_only=True)
399
- device = NestedDeviceSerializer(read_only=True)
400
282
  occupied = serializers.BooleanField(read_only=True)
401
283
 
402
284
 
403
285
  class RackReservationSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
404
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:rackreservation-detail")
405
- rack = NestedRackSerializer()
406
- user = NestedUserSerializer()
407
- tenant = NestedTenantSerializer(required=False, allow_null=True)
408
-
409
286
  class Meta:
410
287
  model = RackReservation
411
- fields = [
412
- "url",
413
- "rack",
414
- "units",
415
- "user",
416
- "tenant",
417
- "description",
418
- ]
288
+ fields = "__all__"
289
+ list_display_fields = ["pk", "rack", "units", "user", "description"]
290
+ extra_kwargs = {
291
+ "units": {"help_text": "List of rack unit numbers to reserve"},
292
+ "user": {
293
+ "help_text": "User to associate to reservations. If unspecified, the current user will be used.",
294
+ "required": False,
295
+ },
296
+ }
297
+
298
+ def to_internal_value(self, data):
299
+ """Add the requesting user as the owner of the RackReservation if not otherwise specified."""
300
+ if "user" not in data and not self.partial:
301
+ data["user"] = self.context["request"].user.id
302
+ return super().to_internal_value(data)
419
303
 
420
304
 
421
305
  class RackElevationDetailFilterSerializer(serializers.Serializer):
@@ -445,45 +329,26 @@ class RackElevationDetailFilterSerializer(serializers.Serializer):
445
329
 
446
330
 
447
331
  class ManufacturerSerializer(NautobotModelSerializer):
448
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:manufacturer-detail")
449
332
  device_type_count = serializers.IntegerField(read_only=True)
450
333
  inventory_item_count = serializers.IntegerField(read_only=True)
451
334
  platform_count = serializers.IntegerField(read_only=True)
452
335
 
453
336
  class Meta:
454
337
  model = Manufacturer
455
- fields = [
456
- "url",
457
- "name",
458
- "description",
459
- "device_type_count",
460
- "inventory_item_count",
461
- "platform_count",
462
- ]
338
+ fields = "__all__"
339
+ list_display_fields = ["name", "device_type_count", "platform_count", "description"]
463
340
 
464
341
 
465
342
  class DeviceTypeSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
466
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:devicetype-detail")
467
- manufacturer = NestedManufacturerSerializer()
468
343
  subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False)
344
+ front_image = serializers.ImageField(allow_null=True, required=False)
345
+ rear_image = serializers.ImageField(allow_null=True, required=False)
469
346
  device_count = serializers.IntegerField(read_only=True)
470
347
 
471
348
  class Meta:
472
349
  model = DeviceType
473
- fields = [
474
- "url",
475
- "manufacturer",
476
- "model",
477
- "slug",
478
- "part_number",
479
- "u_height",
480
- "is_full_depth",
481
- "subdevice_role",
482
- "front_image",
483
- "rear_image",
484
- "comments",
485
- "device_count",
486
- ]
350
+ fields = "__all__"
351
+ list_display_fields = ["model", "manufacturer", "part_number", "u_height", "is_full_depth", "device_count"]
487
352
  # Omit the UniqueTogetherValidator that would be automatically added to validate (manufacturer, slug). This
488
353
  # prevents slug from being interpreted as a required field.
489
354
  # 2.0 TODO: Remove if/when slug is globally unique. This would be a breaking change.
@@ -503,148 +368,66 @@ class DeviceTypeSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
503
368
 
504
369
 
505
370
  class ConsolePortTemplateSerializer(NautobotModelSerializer):
506
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:consoleporttemplate-detail")
507
- device_type = NestedDeviceTypeSerializer()
508
371
  type = ChoiceField(choices=ConsolePortTypeChoices, allow_blank=True, required=False)
509
372
 
510
373
  class Meta:
511
374
  model = ConsolePortTemplate
512
- fields = [
513
- "url",
514
- "device_type",
515
- "name",
516
- "label",
517
- "type",
518
- "description",
519
- ]
375
+ fields = "__all__"
520
376
 
521
377
 
522
378
  class ConsoleServerPortTemplateSerializer(NautobotModelSerializer):
523
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:consoleserverporttemplate-detail")
524
- device_type = NestedDeviceTypeSerializer()
525
379
  type = ChoiceField(choices=ConsolePortTypeChoices, allow_blank=True, required=False)
526
380
 
527
381
  class Meta:
528
382
  model = ConsoleServerPortTemplate
529
- fields = [
530
- "url",
531
- "device_type",
532
- "name",
533
- "label",
534
- "type",
535
- "description",
536
- ]
383
+ fields = "__all__"
537
384
 
538
385
 
539
386
  class PowerPortTemplateSerializer(NautobotModelSerializer):
540
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:powerporttemplate-detail")
541
- device_type = NestedDeviceTypeSerializer()
542
387
  type = ChoiceField(choices=PowerPortTypeChoices, allow_blank=True, required=False)
543
388
 
544
389
  class Meta:
545
390
  model = PowerPortTemplate
546
- fields = [
547
- "url",
548
- "device_type",
549
- "name",
550
- "label",
551
- "type",
552
- "maximum_draw",
553
- "allocated_draw",
554
- "description",
555
- ]
391
+ fields = "__all__"
556
392
 
557
393
 
558
394
  class PowerOutletTemplateSerializer(NautobotModelSerializer):
559
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:poweroutlettemplate-detail")
560
- device_type = NestedDeviceTypeSerializer()
561
395
  type = ChoiceField(choices=PowerOutletTypeChoices, allow_blank=True, required=False)
562
- power_port_template = NestedPowerPortTemplateSerializer(required=False)
563
396
  feed_leg = ChoiceField(choices=PowerOutletFeedLegChoices, allow_blank=True, required=False)
564
397
 
565
398
  class Meta:
566
399
  model = PowerOutletTemplate
567
- fields = [
568
- "url",
569
- "device_type",
570
- "name",
571
- "label",
572
- "type",
573
- "power_port_template",
574
- "feed_leg",
575
- "description",
576
- ]
400
+ fields = "__all__"
577
401
 
578
402
 
579
403
  class InterfaceTemplateSerializer(NautobotModelSerializer):
580
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:interfacetemplate-detail")
581
- device_type = NestedDeviceTypeSerializer()
582
404
  type = ChoiceField(choices=InterfaceTypeChoices)
583
405
 
584
406
  class Meta:
585
407
  model = InterfaceTemplate
586
- fields = [
587
- "url",
588
- "device_type",
589
- "name",
590
- "label",
591
- "type",
592
- "mgmt_only",
593
- "description",
594
- ]
408
+ fields = "__all__"
595
409
 
596
410
 
597
411
  class RearPortTemplateSerializer(NautobotModelSerializer):
598
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:rearporttemplate-detail")
599
- device_type = NestedDeviceTypeSerializer()
600
412
  type = ChoiceField(choices=PortTypeChoices)
601
413
 
602
414
  class Meta:
603
415
  model = RearPortTemplate
604
- fields = [
605
- "url",
606
- "device_type",
607
- "name",
608
- "label",
609
- "type",
610
- "positions",
611
- "description",
612
- ]
416
+ fields = "__all__"
613
417
 
614
418
 
615
419
  class FrontPortTemplateSerializer(NautobotModelSerializer):
616
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:frontporttemplate-detail")
617
- device_type = NestedDeviceTypeSerializer()
618
420
  type = ChoiceField(choices=PortTypeChoices)
619
- rear_port_template = NestedRearPortTemplateSerializer()
620
421
 
621
422
  class Meta:
622
423
  model = FrontPortTemplate
623
- fields = [
624
- "url",
625
- "device_type",
626
- "name",
627
- "label",
628
- "type",
629
- "rear_port_template",
630
- "rear_port_position",
631
- "description",
632
- ]
424
+ fields = "__all__"
633
425
 
634
426
 
635
427
  class DeviceBayTemplateSerializer(NautobotModelSerializer):
636
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:devicebaytemplate-detail")
637
- device_type = NestedDeviceTypeSerializer()
638
-
639
428
  class Meta:
640
429
  model = DeviceBayTemplate
641
- fields = [
642
- "url",
643
- "device_type",
644
- "name",
645
- "label",
646
- "description",
647
- ]
430
+ fields = "__all__"
648
431
 
649
432
 
650
433
  #
@@ -653,80 +436,58 @@ class DeviceBayTemplateSerializer(NautobotModelSerializer):
653
436
 
654
437
 
655
438
  class PlatformSerializer(NautobotModelSerializer):
656
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:platform-detail")
657
- manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
658
439
  device_count = serializers.IntegerField(read_only=True)
659
440
  virtual_machine_count = serializers.IntegerField(read_only=True)
660
441
 
661
442
  class Meta:
662
443
  model = Platform
663
- fields = [
664
- "url",
444
+ fields = "__all__"
445
+ list_display_fields = [
665
446
  "name",
666
447
  "manufacturer",
667
- "napalm_driver",
668
- "napalm_args",
669
- "description",
670
448
  "device_count",
671
449
  "virtual_machine_count",
450
+ "napalm_driver",
451
+ "description",
672
452
  ]
673
453
 
674
454
 
675
- class DeviceSerializer(
676
- NautobotModelSerializer,
677
- TaggedModelSerializerMixin,
678
- StatusModelSerializerMixin,
679
- RoleRequiredRoleModelSerializerMixin,
680
- ):
681
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:device-detail")
682
- device_type = NestedDeviceTypeSerializer()
683
- tenant = NestedTenantSerializer(required=False, allow_null=True)
684
- platform = NestedPlatformSerializer(required=False, allow_null=True)
685
- location = NestedLocationSerializer()
686
- rack = NestedRackSerializer(required=False, allow_null=True)
455
+ class DeviceBaySerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
456
+ class Meta:
457
+ model = DeviceBay
458
+ fields = "__all__"
459
+
460
+
461
+ class DeviceSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
687
462
  face = ChoiceField(choices=DeviceFaceChoices, allow_blank=True, required=False)
688
- primary_ip = NestedIPAddressSerializer(read_only=True)
689
- primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
690
- primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
691
- parent_device = serializers.SerializerMethodField()
692
- secrets_group = NestedSecretsGroupSerializer(required=False, allow_null=True)
693
- cluster = NestedClusterSerializer(required=False, allow_null=True)
694
- virtual_chassis = NestedVirtualChassisSerializer(required=False, allow_null=True)
695
- device_redundancy_group = NestedDeviceRedundancyGroupSerializer(required=False, allow_null=True)
696
- local_config_context_schema = NestedConfigContextSchemaSerializer(required=False, allow_null=True)
463
+ config_context = serializers.SerializerMethodField()
697
464
 
698
465
  class Meta:
699
466
  model = Device
700
- fields = [
701
- "url",
702
- "name",
703
- "device_type",
704
- "role",
705
- "tenant",
706
- "platform",
707
- "serial",
708
- "asset_tag",
709
- "location",
710
- "rack",
711
- "position",
712
- "face",
713
- "parent_device",
714
- "status",
715
- "primary_ip",
716
- "primary_ip4",
717
- "primary_ip6",
718
- "secrets_group",
719
- "cluster",
720
- "virtual_chassis",
721
- "vc_position",
722
- "vc_priority",
723
- "device_redundancy_group",
724
- "device_redundancy_group_priority",
725
- "comments",
726
- "local_config_context_schema",
727
- "local_config_context_data",
728
- ]
467
+ fields = "__all__"
468
+ list_display_fields = ["name", "status", "tenant", "location", "rack", "role", "device_type", "primary_ip"]
729
469
  validators = []
470
+ extra_kwargs = {
471
+ "parent_bay": {"required": False, "allow_null": True},
472
+ "vc_position": {"label": "Virtual chassis position"},
473
+ "vc_priority": {"label": "Virtual chassis priority"},
474
+ }
475
+
476
+ def get_field_names(self, declared_fields, info):
477
+ """
478
+ Add a couple of special fields to the serializer.
479
+
480
+ - As parent_bay is the reverse side of a OneToOneField, DRF can handle it but it isn't auto-included.
481
+ - Config context is expensive to compute and so it's opt-in only.
482
+ """
483
+ fields = list(super().get_field_names(declared_fields, info))
484
+ self.extend_field_names(fields, "parent_bay")
485
+ self.extend_field_names(fields, "config_context", opt_in_only=True)
486
+ return fields
487
+
488
+ @extend_schema_field(serializers.DictField)
489
+ def get_config_context(self, obj):
490
+ return obj.get_config_context()
730
491
 
731
492
  def validate(self, data):
732
493
  # Validate uniqueness of (rack, position, face) since we omitted the automatically-created validator from Meta.
@@ -743,28 +504,6 @@ class DeviceSerializer(
743
504
 
744
505
  return data
745
506
 
746
- @extend_schema_field(NestedDeviceSerializer)
747
- def get_parent_device(self, obj):
748
- try:
749
- device_bay = obj.parent_bay
750
- except DeviceBay.DoesNotExist:
751
- return None
752
- context = {"request": self.context["request"]}
753
- data = NestedDeviceSerializer(instance=device_bay.device, context=context).data
754
- data["device_bay"] = NestedDeviceBaySerializer(instance=device_bay, context=context).data
755
- return data
756
-
757
-
758
- class DeviceWithConfigContextSerializer(DeviceSerializer):
759
- config_context = serializers.SerializerMethodField()
760
-
761
- class Meta(DeviceSerializer.Meta):
762
- fields = DeviceSerializer.Meta.fields + ["config_context"]
763
-
764
- @extend_schema_field(serializers.DictField)
765
- def get_config_context(self, obj):
766
- return obj.get_config_context()
767
-
768
507
 
769
508
  class DeviceNAPALMSerializer(serializers.Serializer):
770
509
  method = serializers.DictField()
@@ -776,27 +515,12 @@ class ConsoleServerPortSerializer(
776
515
  CableTerminationModelSerializerMixin,
777
516
  PathEndpointModelSerializerMixin,
778
517
  ):
779
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:consoleserverport-detail")
780
- device = NestedDeviceSerializer()
781
518
  type = ChoiceField(choices=ConsolePortTypeChoices, allow_blank=True, required=False)
782
- cable = NestedCableSerializer(read_only=True)
783
519
 
784
520
  class Meta:
785
521
  model = ConsoleServerPort
786
- fields = [
787
- "url",
788
- "device",
789
- "name",
790
- "label",
791
- "type",
792
- "description",
793
- "cable",
794
- "cable_peer",
795
- "cable_peer_type",
796
- "connected_endpoint",
797
- "connected_endpoint_type",
798
- "connected_endpoint_reachable",
799
- ]
522
+ fields = "__all__"
523
+ extra_kwargs = {"cable": {"read_only": True}}
800
524
 
801
525
 
802
526
  class ConsolePortSerializer(
@@ -805,27 +529,12 @@ class ConsolePortSerializer(
805
529
  CableTerminationModelSerializerMixin,
806
530
  PathEndpointModelSerializerMixin,
807
531
  ):
808
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:consoleport-detail")
809
- device = NestedDeviceSerializer()
810
532
  type = ChoiceField(choices=ConsolePortTypeChoices, allow_blank=True, required=False)
811
- cable = NestedCableSerializer(read_only=True)
812
533
 
813
534
  class Meta:
814
535
  model = ConsolePort
815
- fields = [
816
- "url",
817
- "device",
818
- "name",
819
- "label",
820
- "type",
821
- "description",
822
- "cable",
823
- "cable_peer",
824
- "cable_peer_type",
825
- "connected_endpoint",
826
- "connected_endpoint_type",
827
- "connected_endpoint_reachable",
828
- ]
536
+ fields = "__all__"
537
+ extra_kwargs = {"cable": {"read_only": True}}
829
538
 
830
539
 
831
540
  class PowerOutletSerializer(
@@ -834,31 +543,13 @@ class PowerOutletSerializer(
834
543
  CableTerminationModelSerializerMixin,
835
544
  PathEndpointModelSerializerMixin,
836
545
  ):
837
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:poweroutlet-detail")
838
- device = NestedDeviceSerializer()
839
546
  type = ChoiceField(choices=PowerOutletTypeChoices, allow_blank=True, required=False)
840
- power_port = NestedPowerPortSerializer(required=False)
841
547
  feed_leg = ChoiceField(choices=PowerOutletFeedLegChoices, allow_blank=True, required=False)
842
- cable = NestedCableSerializer(read_only=True)
843
548
 
844
549
  class Meta:
845
550
  model = PowerOutlet
846
- fields = [
847
- "url",
848
- "device",
849
- "name",
850
- "label",
851
- "type",
852
- "power_port",
853
- "feed_leg",
854
- "description",
855
- "cable",
856
- "cable_peer",
857
- "cable_peer_type",
858
- "connected_endpoint",
859
- "connected_endpoint_type",
860
- "connected_endpoint_reachable",
861
- ]
551
+ fields = "__all__"
552
+ extra_kwargs = {"cable": {"read_only": True}}
862
553
 
863
554
 
864
555
  class PowerPortSerializer(
@@ -867,29 +558,12 @@ class PowerPortSerializer(
867
558
  CableTerminationModelSerializerMixin,
868
559
  PathEndpointModelSerializerMixin,
869
560
  ):
870
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:powerport-detail")
871
- device = NestedDeviceSerializer()
872
561
  type = ChoiceField(choices=PowerPortTypeChoices, allow_blank=True, required=False)
873
- cable = NestedCableSerializer(read_only=True)
874
562
 
875
563
  class Meta:
876
564
  model = PowerPort
877
- fields = [
878
- "url",
879
- "device",
880
- "name",
881
- "label",
882
- "type",
883
- "maximum_draw",
884
- "allocated_draw",
885
- "description",
886
- "cable",
887
- "cable_peer",
888
- "cable_peer_type",
889
- "connected_endpoint",
890
- "connected_endpoint_type",
891
- "connected_endpoint_reachable",
892
- ]
565
+ fields = "__all__"
566
+ extra_kwargs = {"cable": {"read_only": True}}
893
567
 
894
568
 
895
569
  class InterfaceCommonSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
@@ -915,60 +589,17 @@ class InterfaceSerializer(
915
589
  InterfaceCommonSerializer,
916
590
  CableTerminationModelSerializerMixin,
917
591
  PathEndpointModelSerializerMixin,
918
- StatusModelSerializerMixin,
919
592
  ):
920
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:interface-detail")
921
- device = NestedDeviceSerializer()
922
593
  type = ChoiceField(choices=InterfaceTypeChoices)
923
594
  mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
924
- untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
925
- tagged_vlans = SerializedPKRelatedField(
926
- queryset=VLAN.objects.all(),
927
- serializer=NestedVLANSerializer,
928
- required=False,
929
- many=True,
930
- )
931
- cable = NestedCableSerializer(read_only=True)
595
+ mac_address = serializers.CharField(allow_blank=True, allow_null=True, required=False)
932
596
  ip_address_count = serializers.IntegerField(read_only=True)
933
- ip_addresses = SerializedPKRelatedField(
934
- queryset=IPAddress.objects.all(),
935
- serializer=NestedIPAddressSerializer,
936
- required=False,
937
- many=True,
938
- )
939
- parent_interface = NestedInterfaceSerializer(required=False, allow_null=True)
940
- bridge = NestedInterfaceSerializer(required=False, allow_null=True)
941
- lag = NestedInterfaceSerializer(required=False, allow_null=True)
942
597
 
943
598
  class Meta:
944
599
  model = Interface
945
- fields = [
946
- "url",
947
- "device",
948
- "name",
949
- "status",
950
- "label",
951
- "type",
952
- "enabled",
953
- "parent_interface",
954
- "bridge",
955
- "lag",
956
- "mtu",
957
- "mac_address",
958
- "ip_addresses",
959
- "mgmt_only",
960
- "description",
961
- "mode",
962
- "untagged_vlan",
963
- "tagged_vlans",
964
- "cable",
965
- "cable_peer",
966
- "cable_peer_type",
967
- "connected_endpoint",
968
- "connected_endpoint_type",
969
- "connected_endpoint_reachable",
970
- "ip_address_count",
971
- ]
600
+ fields = "__all__"
601
+ list_display_fields = ["device", "name", "status", "label", "enabled", "type", "description"]
602
+ extra_kwargs = {"cable": {"read_only": True}}
972
603
 
973
604
  def validate(self, data):
974
605
  # Validate many-to-many VLAN assignments
@@ -988,94 +619,37 @@ class InterfaceSerializer(
988
619
 
989
620
 
990
621
  class RearPortSerializer(NautobotModelSerializer, TaggedModelSerializerMixin, CableTerminationModelSerializerMixin):
991
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:rearport-detail")
992
- device = NestedDeviceSerializer()
993
622
  type = ChoiceField(choices=PortTypeChoices)
994
- cable = NestedCableSerializer(read_only=True)
995
623
 
996
624
  class Meta:
997
625
  model = RearPort
998
- fields = [
999
- "url",
1000
- "device",
1001
- "name",
1002
- "label",
1003
- "type",
1004
- "positions",
1005
- "description",
1006
- "cable",
1007
- "cable_peer",
1008
- "cable_peer_type",
1009
- ]
1010
-
1011
-
1012
- class FrontPortRearPortSerializer(WritableNestedSerializer):
1013
- """
1014
- NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
1015
- """
1016
-
1017
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:rearport-detail")
1018
-
1019
- class Meta:
1020
- model = RearPort
1021
- fields = ["id", "url", "name", "label"]
626
+ fields = "__all__"
627
+ extra_kwargs = {"cable": {"read_only": True}}
1022
628
 
1023
629
 
1024
630
  class FrontPortSerializer(NautobotModelSerializer, TaggedModelSerializerMixin, CableTerminationModelSerializerMixin):
1025
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:frontport-detail")
1026
- device = NestedDeviceSerializer()
1027
631
  type = ChoiceField(choices=PortTypeChoices)
1028
- rear_port = FrontPortRearPortSerializer()
1029
- cable = NestedCableSerializer(read_only=True)
1030
632
 
1031
633
  class Meta:
1032
634
  model = FrontPort
1033
- fields = [
1034
- "url",
1035
- "device",
1036
- "name",
1037
- "label",
1038
- "type",
1039
- "rear_port",
1040
- "rear_port_position",
1041
- "description",
1042
- "cable",
1043
- "cable_peer",
1044
- "cable_peer_type",
1045
- ]
1046
-
1047
-
1048
- class DeviceBaySerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
1049
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:devicebay-detail")
1050
- device = NestedDeviceSerializer()
1051
- installed_device = NestedDeviceSerializer(required=False, allow_null=True)
1052
-
1053
- class Meta:
1054
- model = DeviceBay
1055
- fields = [
1056
- "url",
1057
- "device",
1058
- "name",
1059
- "label",
1060
- "description",
1061
- "installed_device",
1062
- ]
635
+ fields = "__all__"
636
+ extra_kwargs = {"cable": {"read_only": True}}
1063
637
 
1064
638
 
1065
- class DeviceRedundancyGroupSerializer(NautobotModelSerializer, TaggedModelSerializerMixin, StatusModelSerializerMixin):
1066
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:deviceredundancygroup-detail")
1067
- failover_strategy = ChoiceField(choices=DeviceRedundancyGroupFailoverStrategyChoices)
639
+ class DeviceRedundancyGroupSerializer(
640
+ NautobotModelSerializer,
641
+ TaggedModelSerializerMixin,
642
+ ):
643
+ failover_strategy = ChoiceField(
644
+ choices=DeviceRedundancyGroupFailoverStrategyChoices,
645
+ allow_blank=True,
646
+ default=DeviceRedundancyGroupFailoverStrategyChoices.FAILOVER_UNSPECIFIED,
647
+ )
1068
648
 
1069
649
  class Meta:
1070
650
  model = DeviceRedundancyGroup
1071
- fields = [
1072
- "url",
1073
- "name",
1074
- "description",
1075
- "failover_strategy",
1076
- "secrets_group",
1077
- "comments",
1078
- ]
651
+ fields = "__all__"
652
+ list_display_fields = ["name", "status", "failover_strategy", "device_count"]
1079
653
 
1080
654
 
1081
655
  #
@@ -1084,27 +658,9 @@ class DeviceRedundancyGroupSerializer(NautobotModelSerializer, TaggedModelSerial
1084
658
 
1085
659
 
1086
660
  class InventoryItemSerializer(NautobotModelSerializer, TaggedModelSerializerMixin, TreeModelSerializerMixin):
1087
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:inventoryitem-detail")
1088
- device = NestedDeviceSerializer()
1089
- parent = NestedInventoryItemSerializer(required=False, allow_null=True, default=None)
1090
- manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
1091
-
1092
661
  class Meta:
1093
662
  model = InventoryItem
1094
- fields = [
1095
- "url",
1096
- "device",
1097
- "parent",
1098
- "name",
1099
- "label",
1100
- "manufacturer",
1101
- "part_id",
1102
- "serial",
1103
- "asset_tag",
1104
- "discovered",
1105
- "description",
1106
- "tree_depth",
1107
- ]
663
+ fields = "__all__"
1108
664
  # https://www.django-rest-framework.org/api-guide/validators/#optional-fields
1109
665
  validators = []
1110
666
 
@@ -1127,30 +683,30 @@ class InventoryItemSerializer(NautobotModelSerializer, TaggedModelSerializerMixi
1127
683
  #
1128
684
 
1129
685
 
1130
- class CableSerializer(NautobotModelSerializer, TaggedModelSerializerMixin, StatusModelSerializerMixin):
1131
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:cable-detail")
686
+ class CableSerializer(
687
+ NautobotModelSerializer,
688
+ TaggedModelSerializerMixin,
689
+ ):
690
+ # TODO: termination_a_type/termination_b_type are a bit redundant with the full termination_a/termination_b dicts
1132
691
  termination_a_type = ContentTypeField(queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS))
1133
692
  termination_b_type = ContentTypeField(queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS))
1134
693
  termination_a = serializers.SerializerMethodField(read_only=True)
1135
694
  termination_b = serializers.SerializerMethodField(read_only=True)
1136
695
  length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
696
+ type = ChoiceField(choices=CableTypeChoices, allow_blank=True, required=False)
1137
697
 
1138
698
  class Meta:
1139
699
  model = Cable
1140
- fields = [
1141
- "url",
1142
- "termination_a_type",
1143
- "termination_a_id",
700
+ fields = "__all__"
701
+ extra_kwargs = {
702
+ "color": {"help_text": "RGB color in hexadecimal (e.g. 00ff00)"},
703
+ }
704
+ list_display_fields = [
705
+ "label",
1144
706
  "termination_a",
1145
- "termination_b_type",
1146
- "termination_b_id",
1147
707
  "termination_b",
1148
- "type",
1149
708
  "status",
1150
- "label",
1151
- "color",
1152
- "length",
1153
- "length_unit",
709
+ "type",
1154
710
  ]
1155
711
 
1156
712
  def _get_termination(self, obj, side):
@@ -1160,17 +716,16 @@ class CableSerializer(NautobotModelSerializer, TaggedModelSerializerMixin, Statu
1160
716
  if side.lower() not in ["a", "b"]:
1161
717
  raise ValueError("Termination side must be either A or B.")
1162
718
  termination = getattr(obj, f"termination_{side.lower()}")
1163
- serializer = get_serializer_for_model(termination, prefix="Nested")
1164
- context = {"request": self.context["request"]}
1165
- data = serializer(termination, context=context).data
1166
-
1167
- return data
719
+ depth = get_nested_serializer_depth(self)
720
+ return return_nested_serializer_data_based_on_depth(
721
+ self, depth, obj, termination, f"termination_{side.lower()}"
722
+ )
1168
723
 
1169
724
  @extend_schema_field(
1170
725
  PolymorphicProxySerializer(
1171
726
  component_name="CableTermination",
1172
727
  resource_type_field_name="object_type",
1173
- serializers=lambda: get_serializers_for_models(get_all_concrete_models(CableTermination), prefix="Nested"),
728
+ serializers=lambda: get_serializers_for_models(get_all_concrete_models(CableTermination)),
1174
729
  )
1175
730
  )
1176
731
  def get_termination_a(self, obj):
@@ -1180,32 +735,21 @@ class CableSerializer(NautobotModelSerializer, TaggedModelSerializerMixin, Statu
1180
735
  PolymorphicProxySerializer(
1181
736
  component_name="CableTermination",
1182
737
  resource_type_field_name="object_type",
1183
- serializers=lambda: get_serializers_for_models(get_all_concrete_models(CableTermination), prefix="Nested"),
738
+ serializers=lambda: get_serializers_for_models(get_all_concrete_models(CableTermination)),
1184
739
  )
1185
740
  )
1186
741
  def get_termination_b(self, obj):
1187
742
  return self._get_termination(obj, "b")
1188
743
 
1189
744
 
1190
- class TracedCableSerializer(StatusModelSerializerMixin, serializers.ModelSerializer):
745
+ class TracedCableSerializer(serializers.ModelSerializer):
1191
746
  """
1192
747
  Used only while tracing a cable path.
1193
748
  """
1194
749
 
1195
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:cable-detail")
1196
-
1197
750
  class Meta:
1198
751
  model = Cable
1199
- fields = [
1200
- "id",
1201
- "url",
1202
- "type",
1203
- "status",
1204
- "label",
1205
- "color",
1206
- "length",
1207
- "length_unit",
1208
- ]
752
+ fields = "__all__"
1209
753
 
1210
754
 
1211
755
  class CablePathSerializer(serializers.ModelSerializer):
@@ -1217,37 +761,27 @@ class CablePathSerializer(serializers.ModelSerializer):
1217
761
 
1218
762
  class Meta:
1219
763
  model = CablePath
1220
- fields = [
1221
- "id",
1222
- "origin_type",
1223
- "origin",
1224
- "destination_type",
1225
- "destination",
1226
- "path",
1227
- "is_active",
1228
- "is_split",
1229
- ]
764
+ fields = "__all__"
1230
765
 
1231
766
  @extend_schema_field(
1232
767
  PolymorphicProxySerializer(
1233
768
  component_name="PathEndpoint",
1234
769
  resource_type_field_name="object_type",
1235
- serializers=lambda: get_serializers_for_models(get_all_concrete_models(PathEndpoint), prefix="Nested"),
770
+ serializers=lambda: get_serializers_for_models(get_all_concrete_models(PathEndpoint)),
1236
771
  )
1237
772
  )
1238
773
  def get_origin(self, obj):
1239
774
  """
1240
775
  Return the appropriate serializer for the origin.
1241
776
  """
1242
- serializer = get_serializer_for_model(obj.origin, prefix="Nested")
1243
- context = {"request": self.context["request"]}
1244
- return serializer(obj.origin, context=context).data
777
+ depth = get_nested_serializer_depth(self)
778
+ return return_nested_serializer_data_based_on_depth(self, depth, obj, obj.origin, "origin")
1245
779
 
1246
780
  @extend_schema_field(
1247
781
  PolymorphicProxySerializer(
1248
782
  component_name="PathEndpoint",
1249
783
  resource_type_field_name="object_type",
1250
- serializers=lambda: get_serializers_for_models(get_all_concrete_models(PathEndpoint), prefix="Nested"),
784
+ serializers=lambda: get_serializers_for_models(get_all_concrete_models(PathEndpoint)),
1251
785
  allow_null=True,
1252
786
  )
1253
787
  )
@@ -1256,23 +790,22 @@ class CablePathSerializer(serializers.ModelSerializer):
1256
790
  Return the appropriate serializer for the destination, if any.
1257
791
  """
1258
792
  if obj.destination_id is not None:
1259
- serializer = get_serializer_for_model(obj.destination, prefix="Nested")
1260
- context = {"request": self.context["request"]}
1261
- return serializer(obj.destination, context=context).data
793
+ depth = get_nested_serializer_depth(self)
794
+ return return_nested_serializer_data_based_on_depth(self, depth, obj, obj.destination, "destination")
1262
795
  return None
1263
796
 
1264
797
  @extend_schema_field(
1265
798
  PolymorphicProxySerializer(
1266
799
  component_name="CableTermination",
1267
800
  resource_type_field_name="object_type",
1268
- serializers=lambda: get_serializers_for_models(get_all_concrete_models(CableTermination), prefix="Nested"),
801
+ serializers=lambda: get_serializers_for_models(get_all_concrete_models(CableTermination)),
1269
802
  many=True,
1270
803
  )
1271
804
  )
1272
805
  def get_path(self, obj):
1273
806
  ret = []
1274
807
  for node in obj.get_path():
1275
- serializer = get_serializer_for_model(node, prefix="Nested")
808
+ serializer = get_serializer_for_model(node)
1276
809
  context = {"request": self.context["request"]}
1277
810
  ret.append(serializer(node, context=context).data)
1278
811
  return ret
@@ -1285,17 +818,17 @@ class CablePathSerializer(serializers.ModelSerializer):
1285
818
 
1286
819
  class InterfaceConnectionSerializer(ValidatedModelSerializer):
1287
820
  interface_a = serializers.SerializerMethodField()
1288
- interface_b = NestedInterfaceSerializer(source="connected_endpoint")
821
+ interface_b = InterfaceSerializer(source="connected_endpoint")
1289
822
  connected_endpoint_reachable = serializers.SerializerMethodField(read_only=True)
1290
823
 
1291
824
  class Meta:
1292
825
  model = Interface
1293
826
  fields = ["interface_a", "interface_b", "connected_endpoint_reachable"]
1294
827
 
1295
- @extend_schema_field(NestedInterfaceSerializer)
828
+ @extend_schema_field(InterfaceSerializer)
1296
829
  def get_interface_a(self, obj):
1297
830
  context = {"request": self.context["request"]}
1298
- return NestedInterfaceSerializer(instance=obj, context=context).data
831
+ return InterfaceSerializer(instance=obj, context=context).data
1299
832
 
1300
833
  @extend_schema_field(serializers.BooleanField(allow_null=True))
1301
834
  def get_connected_endpoint_reachable(self, obj):
@@ -1310,19 +843,12 @@ class InterfaceConnectionSerializer(ValidatedModelSerializer):
1310
843
 
1311
844
 
1312
845
  class VirtualChassisSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
1313
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:virtualchassis-detail")
1314
- master = NestedDeviceSerializer(required=False, allow_null=True)
1315
846
  member_count = serializers.IntegerField(read_only=True)
1316
847
 
1317
848
  class Meta:
1318
849
  model = VirtualChassis
1319
- fields = [
1320
- "url",
1321
- "name",
1322
- "domain",
1323
- "master",
1324
- "member_count",
1325
- ]
850
+ fields = "__all__"
851
+ list_display_fields = ["name", "domain", "master", "member_count"]
1326
852
 
1327
853
 
1328
854
  #
@@ -1331,20 +857,11 @@ class VirtualChassisSerializer(NautobotModelSerializer, TaggedModelSerializerMix
1331
857
 
1332
858
 
1333
859
  class PowerPanelSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
1334
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:powerpanel-detail")
1335
- location = NestedLocationSerializer()
1336
- rack_group = NestedRackGroupSerializer(required=False, allow_null=True, default=None)
1337
860
  power_feed_count = serializers.IntegerField(read_only=True)
1338
861
 
1339
862
  class Meta:
1340
863
  model = PowerPanel
1341
- fields = [
1342
- "url",
1343
- "location",
1344
- "rack_group",
1345
- "name",
1346
- "power_feed_count",
1347
- ]
864
+ fields = "__all__"
1348
865
 
1349
866
 
1350
867
  class PowerFeedSerializer(
@@ -1352,35 +869,11 @@ class PowerFeedSerializer(
1352
869
  TaggedModelSerializerMixin,
1353
870
  CableTerminationModelSerializerMixin,
1354
871
  PathEndpointModelSerializerMixin,
1355
- StatusModelSerializerMixin,
1356
872
  ):
1357
- url = serializers.HyperlinkedIdentityField(view_name="dcim-api:powerfeed-detail")
1358
- power_panel = NestedPowerPanelSerializer()
1359
- rack = NestedRackSerializer(required=False, allow_null=True, default=None)
1360
873
  type = ChoiceField(choices=PowerFeedTypeChoices, default=PowerFeedTypeChoices.TYPE_PRIMARY)
1361
874
  supply = ChoiceField(choices=PowerFeedSupplyChoices, default=PowerFeedSupplyChoices.SUPPLY_AC)
1362
875
  phase = ChoiceField(choices=PowerFeedPhaseChoices, default=PowerFeedPhaseChoices.PHASE_SINGLE)
1363
- cable = NestedCableSerializer(read_only=True)
1364
876
 
1365
877
  class Meta:
1366
878
  model = PowerFeed
1367
- fields = [
1368
- "url",
1369
- "power_panel",
1370
- "rack",
1371
- "name",
1372
- "status",
1373
- "type",
1374
- "supply",
1375
- "phase",
1376
- "voltage",
1377
- "amperage",
1378
- "max_utilization",
1379
- "comments",
1380
- "cable",
1381
- "cable_peer",
1382
- "cable_peer_type",
1383
- "connected_endpoint",
1384
- "connected_endpoint_type",
1385
- "connected_endpoint_reachable",
1386
- ]
879
+ fields = "__all__"