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
@@ -3,32 +3,33 @@ import logging
3
3
  from django.conf import settings
4
4
  from django.contrib.contenttypes.models import ContentType
5
5
  from django.core.exceptions import ObjectDoesNotExist
6
- from django.urls import NoReverseMatch
7
6
  from drf_spectacular.utils import extend_schema_field
8
7
  from rest_framework import serializers
9
- from rest_framework.reverse import reverse
10
8
 
11
9
  from nautobot.core.api import (
10
+ BaseModelSerializer,
12
11
  ChoiceField,
13
12
  ContentTypeField,
14
- SerializedPKRelatedField,
13
+ CustomFieldModelSerializerMixin,
14
+ NautobotModelSerializer,
15
+ NotesSerializerMixin,
16
+ RelationshipModelSerializerMixin,
15
17
  ValidatedModelSerializer,
16
18
  )
17
19
  from nautobot.core.api.exceptions import SerializerNotFound
18
- from nautobot.core.api.mixins import LimitQuerysetChoicesSerializerMixin
19
- from nautobot.core.api.serializers import BaseModelSerializer, PolymorphicProxySerializer
20
- from nautobot.core.api.utils import get_serializer_for_model, get_serializers_for_models
20
+ from nautobot.core.api.serializers import PolymorphicProxySerializer
21
+ from nautobot.core.api.utils import (
22
+ get_nested_serializer_depth,
23
+ get_serializers_for_models,
24
+ return_nested_serializer_data_based_on_depth,
25
+ )
21
26
  from nautobot.core.models.utils import get_all_concrete_models
22
- from nautobot.core.utils.deprecation import class_deprecated_in_favor_of
23
- from nautobot.core.utils.lookup import get_route_for_model
24
- from nautobot.dcim.api.nested_serializers import (
25
- NestedDeviceSerializer,
26
- NestedDeviceTypeSerializer,
27
- NestedLocationSerializer,
28
- NestedPlatformSerializer,
29
- NestedRackSerializer,
27
+ from nautobot.dcim.api.serializers import (
28
+ DeviceSerializer,
29
+ LocationSerializer,
30
+ RackSerializer,
30
31
  )
31
- from nautobot.dcim.models import DeviceType, Location, Platform
32
+ from nautobot.extras import choices, models
32
33
  from nautobot.extras.choices import (
33
34
  CustomFieldFilterLogicChoices,
34
35
  CustomFieldTypeChoices,
@@ -36,6 +37,9 @@ from nautobot.extras.choices import (
36
37
  JobResultStatusChoices,
37
38
  ObjectChangeActionChoices,
38
39
  )
40
+ from nautobot.extras.api.mixins import (
41
+ TaggedModelSerializerMixin,
42
+ )
39
43
  from nautobot.extras.datasources import get_datasource_content_choices
40
44
  from nautobot.extras.models import (
41
45
  ComputedField,
@@ -70,54 +74,8 @@ from nautobot.extras.models import (
70
74
  )
71
75
  from nautobot.extras.models.mixins import NotesMixin
72
76
  from nautobot.extras.utils import ChangeLoggedModelsQuery, FeatureQuery, RoleModelsQuery, TaggableClassesQuery
73
- from nautobot.tenancy.api.nested_serializers import (
74
- NestedTenantSerializer,
75
- NestedTenantGroupSerializer,
76
- )
77
- from nautobot.tenancy.models import Tenant, TenantGroup
78
- from nautobot.users.api.nested_serializers import NestedUserSerializer
79
- from nautobot.virtualization.api.nested_serializers import (
80
- NestedClusterGroupSerializer,
81
- NestedClusterSerializer,
82
- )
83
- from nautobot.virtualization.models import Cluster, ClusterGroup
84
-
85
- from .customfields import CustomFieldModelSerializerMixin
86
- from .fields import MultipleChoiceJSONField, RoleSerializerField, StatusSerializerField
87
- from .relationships import RelationshipModelSerializerMixin
88
-
89
- # Not all of these variable(s) are not actually used anywhere in this file, but required for the
90
- # automagically replacing a Serializer with its corresponding NestedSerializer.
91
- from .nested_serializers import ( # noqa: F401
92
- NestedComputedFieldSerializer,
93
- NestedConfigContextSchemaSerializer,
94
- NestedConfigContextSerializer,
95
- NestedCustomFieldChoiceSerializer,
96
- NestedCustomFieldSerializer,
97
- NestedCustomLinkSerializer,
98
- NestedDynamicGroupSerializer,
99
- NestedDynamicGroupMembershipSerializer,
100
- NestedExportTemplateSerializer,
101
- NestedGitRepositorySerializer,
102
- NestedGraphQLQuerySerializer,
103
- NestedImageAttachmentSerializer,
104
- NestedJobButtonSerializer,
105
- NestedJobHookSerializer,
106
- NestedJobSerializer,
107
- NestedJobResultSerializer,
108
- NestedNoteSerializer,
109
- NestedRelationshipAssociationSerializer,
110
- NestedRelationshipSerializer,
111
- NestedRoleSerializer,
112
- NestedScheduledJobSerializer,
113
- NestedScheduledJobCreationSerializer,
114
- NestedSecretSerializer,
115
- NestedSecretsGroupSerializer,
116
- NestedSecretsGroupAssociationSerializer,
117
- NestedStatusSerializer,
118
- NestedTagSerializer,
119
- NestedWebhookSerializer,
120
- )
77
+
78
+ from .fields import MultipleChoiceJSONField
121
79
 
122
80
  #
123
81
  # Mixins and Base Classes
@@ -126,130 +84,19 @@ from .nested_serializers import ( # noqa: F401
126
84
  logger = logging.getLogger(__name__)
127
85
 
128
86
 
129
- class NotesSerializerMixin(BaseModelSerializer):
130
- """Extend Serializer with a `notes` field."""
131
-
132
- notes_url = serializers.SerializerMethodField()
133
-
134
- def get_field_names(self, declared_fields, info):
135
- """Ensure that fields includes "notes_url" field if applicable."""
136
- fields = list(super().get_field_names(declared_fields, info))
137
- if hasattr(self.Meta.model, "notes"):
138
- self.extend_field_names(fields, "notes_url")
139
- return fields
140
-
141
- @extend_schema_field(serializers.URLField())
142
- def get_notes_url(self, instance):
143
- try:
144
- notes_url = get_route_for_model(instance, "notes", api=True)
145
- return reverse(notes_url, args=[instance.id], request=self.context["request"])
146
- except NoReverseMatch:
147
- model_name = type(instance).__name__
148
- logger.warning(
149
- (
150
- f"Notes feature is not available for model {model_name}. "
151
- "Please make sure to: "
152
- f"1. Include NotesMixin from nautobot.extras.model.mixins in the {model_name} class definition "
153
- f"2. Include NotesViewSetMixin from nautobot.extras.api.mixins in the {model_name}ViewSet "
154
- "before including NotesSerializerMixin in the model serializer"
155
- )
156
- )
157
-
158
- return None
159
-
160
-
161
- class NautobotModelSerializer(
162
- RelationshipModelSerializerMixin, CustomFieldModelSerializerMixin, NotesSerializerMixin, ValidatedModelSerializer
163
- ):
164
- """Base class to use for serializers based on OrganizationalModel or PrimaryModel.
165
-
166
- Can also be used for models derived from BaseModel, so long as they support custom fields and relationships.
167
- """
168
-
169
-
170
- class StatusModelSerializerMixin(BaseModelSerializer):
171
- """Mixin to add `status` choice field to model serializers."""
172
-
173
- status = StatusSerializerField(required=True)
174
-
175
- def get_field_names(self, declared_fields, info):
176
- """Ensure that "status" field is always present."""
177
- fields = list(super().get_field_names(declared_fields, info))
178
- self.extend_field_names(fields, "status")
179
- return fields
180
-
181
-
182
- class TagSerializerField(LimitQuerysetChoicesSerializerMixin, NestedTagSerializer):
183
- """NestedSerializer field for `Tag` object fields."""
184
-
185
-
186
- class TaggedModelSerializerMixin(BaseModelSerializer):
187
- tags = TagSerializerField(many=True, required=False)
188
-
189
- def get_field_names(self, declared_fields, info):
190
- """Ensure that 'tags' field is always present."""
191
- fields = list(super().get_field_names(declared_fields, info))
192
- self.extend_field_names(fields, "tags")
193
- return fields
194
-
195
- def create(self, validated_data):
196
- tags = validated_data.pop("tags", None)
197
- instance = super().create(validated_data)
198
-
199
- if tags is not None:
200
- return self._save_tags(instance, tags)
201
- return instance
202
-
203
- def update(self, instance, validated_data):
204
- tags = validated_data.pop("tags", None)
205
-
206
- # Cache tags on instance for change logging
207
- instance._tags = tags or []
208
-
209
- instance = super().update(instance, validated_data)
210
-
211
- if tags is not None:
212
- return self._save_tags(instance, tags)
213
- return instance
214
-
215
- def _save_tags(self, instance, tags):
216
- if tags:
217
- instance.tags.set([t.name for t in tags])
218
- else:
219
- instance.tags.clear()
220
-
221
- return instance
222
-
223
-
224
- # TODO: remove in 2.2
225
- @class_deprecated_in_favor_of(TaggedModelSerializerMixin)
226
- class TaggedObjectSerializer(TaggedModelSerializerMixin):
227
- pass
228
-
229
-
230
87
  #
231
88
  # Computed Fields
232
89
  #
233
90
 
234
91
 
235
92
  class ComputedFieldSerializer(ValidatedModelSerializer, NotesSerializerMixin):
236
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:computedfield-detail")
237
93
  content_type = ContentTypeField(
238
94
  queryset=ContentType.objects.filter(FeatureQuery("custom_fields").get_query()).order_by("app_label", "model"),
239
95
  )
240
96
 
241
97
  class Meta:
242
98
  model = ComputedField
243
- fields = (
244
- "url",
245
- "slug",
246
- "label",
247
- "description",
248
- "content_type",
249
- "template",
250
- "fallback_value",
251
- "weight",
252
- )
99
+ fields = "__all__"
253
100
 
254
101
 
255
102
  #
@@ -257,8 +104,7 @@ class ComputedFieldSerializer(ValidatedModelSerializer, NotesSerializerMixin):
257
104
  #
258
105
 
259
106
 
260
- class ConfigContextSerializer(ValidatedModelSerializer, NotesSerializerMixin):
261
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:configcontext-detail")
107
+ class ConfigContextSerializer(ValidatedModelSerializer, TaggedModelSerializerMixin, NotesSerializerMixin):
262
108
  owner_content_type = ContentTypeField(
263
109
  queryset=ContentType.objects.filter(FeatureQuery("config_context_owners").get_query()),
264
110
  required=False,
@@ -266,63 +112,6 @@ class ConfigContextSerializer(ValidatedModelSerializer, NotesSerializerMixin):
266
112
  default=None,
267
113
  )
268
114
  owner = serializers.SerializerMethodField(read_only=True)
269
- config_context_schema = NestedConfigContextSchemaSerializer(required=False, allow_null=True)
270
- locations = SerializedPKRelatedField(
271
- queryset=Location.objects.all(),
272
- serializer=NestedLocationSerializer,
273
- required=False,
274
- many=True,
275
- )
276
- roles = SerializedPKRelatedField(
277
- queryset=Role.objects.all(),
278
- serializer=NestedRoleSerializer,
279
- required=False,
280
- many=True,
281
- )
282
- device_types = SerializedPKRelatedField(
283
- queryset=DeviceType.objects.all(),
284
- serializer=NestedDeviceTypeSerializer,
285
- required=False,
286
- many=True,
287
- )
288
- platforms = SerializedPKRelatedField(
289
- queryset=Platform.objects.all(),
290
- serializer=NestedPlatformSerializer,
291
- required=False,
292
- many=True,
293
- )
294
- cluster_groups = SerializedPKRelatedField(
295
- queryset=ClusterGroup.objects.all(),
296
- serializer=NestedClusterGroupSerializer,
297
- required=False,
298
- many=True,
299
- )
300
- clusters = SerializedPKRelatedField(
301
- queryset=Cluster.objects.all(),
302
- serializer=NestedClusterSerializer,
303
- required=False,
304
- many=True,
305
- )
306
- tenant_groups = SerializedPKRelatedField(
307
- queryset=TenantGroup.objects.all(),
308
- serializer=NestedTenantGroupSerializer,
309
- required=False,
310
- many=True,
311
- )
312
- tenants = SerializedPKRelatedField(
313
- queryset=Tenant.objects.all(),
314
- serializer=NestedTenantSerializer,
315
- required=False,
316
- many=True,
317
- )
318
- tags = serializers.SlugRelatedField(queryset=Tag.objects.all(), slug_field="slug", required=False, many=True)
319
-
320
- dynamic_groups = SerializedPKRelatedField(
321
- queryset=DynamicGroup.objects.all(),
322
- serializer=NestedDynamicGroupSerializer,
323
- required=False,
324
- many=True,
325
- )
326
115
 
327
116
  # Conditional enablement of dynamic groups filtering
328
117
  def __init__(self, *args, **kwargs):
@@ -333,45 +122,21 @@ class ConfigContextSerializer(ValidatedModelSerializer, NotesSerializerMixin):
333
122
 
334
123
  class Meta:
335
124
  model = ConfigContext
336
- fields = [
337
- "url",
338
- "name",
339
- "owner_content_type",
340
- "owner_object_id",
341
- "owner",
342
- "weight",
343
- "description",
344
- "config_context_schema",
345
- "is_active",
346
- "locations",
347
- "roles",
348
- "device_types",
349
- "platforms",
350
- "cluster_groups",
351
- "clusters",
352
- "tenant_groups",
353
- "tenants",
354
- "tags",
355
- "dynamic_groups",
356
- "data",
357
- ]
125
+ fields = "__all__"
358
126
 
359
127
  @extend_schema_field(
360
128
  PolymorphicProxySerializer(
361
129
  component_name="ConfigContextOwner",
362
130
  resource_type_field_name="object_type",
363
- serializers=lambda: get_serializers_for_models(
364
- FeatureQuery("config_context_owners").list_subclasses(), prefix="Nested"
365
- ),
131
+ serializers=lambda: get_serializers_for_models(FeatureQuery("config_context_owners").list_subclasses()),
366
132
  allow_null=True,
367
133
  )
368
134
  )
369
135
  def get_owner(self, obj):
370
136
  if obj.owner is None:
371
137
  return None
372
- serializer = get_serializer_for_model(obj.owner, prefix="Nested")
373
- context = {"request": self.context["request"]}
374
- return serializer(obj.owner, context=context).data
138
+ depth = get_nested_serializer_depth(self)
139
+ return return_nested_serializer_data_based_on_depth(self, depth, obj, obj.owner, "owner")
375
140
 
376
141
 
377
142
  #
@@ -380,7 +145,6 @@ class ConfigContextSerializer(ValidatedModelSerializer, NotesSerializerMixin):
380
145
 
381
146
 
382
147
  class ConfigContextSchemaSerializer(NautobotModelSerializer):
383
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:configcontextschema-detail")
384
148
  owner_content_type = ContentTypeField(
385
149
  queryset=ContentType.objects.filter(FeatureQuery("config_context_owners").get_query()),
386
150
  required=False,
@@ -391,33 +155,21 @@ class ConfigContextSchemaSerializer(NautobotModelSerializer):
391
155
 
392
156
  class Meta:
393
157
  model = ConfigContextSchema
394
- fields = [
395
- "url",
396
- "name",
397
- "slug",
398
- "owner_content_type",
399
- "owner_object_id",
400
- "owner",
401
- "description",
402
- "data_schema",
403
- ]
158
+ fields = "__all__"
404
159
 
405
160
  @extend_schema_field(
406
161
  PolymorphicProxySerializer(
407
162
  component_name="ConfigContextSchemaOwner",
408
163
  resource_type_field_name="object_type",
409
- serializers=lambda: get_serializers_for_models(
410
- FeatureQuery("config_context_owners").list_subclasses(), prefix="Nested"
411
- ),
164
+ serializers=lambda: get_serializers_for_models(FeatureQuery("config_context_owners").list_subclasses()),
412
165
  allow_null=True,
413
166
  )
414
167
  )
415
168
  def get_owner(self, obj):
416
169
  if obj.owner is None:
417
170
  return None
418
- serializer = get_serializer_for_model(obj.owner, prefix="Nested")
419
- context = {"request": self.context["request"]}
420
- return serializer(obj.owner, context=context).data
171
+ depth = get_nested_serializer_depth(self)
172
+ return return_nested_serializer_data_based_on_depth(self, depth, obj, obj.owner, "owner")
421
173
 
422
174
 
423
175
  #
@@ -431,7 +183,7 @@ class ContentTypeSerializer(BaseModelSerializer):
431
183
 
432
184
  class Meta:
433
185
  model = ContentType
434
- fields = ["url", "app_label", "model"]
186
+ fields = "__all__"
435
187
 
436
188
  @extend_schema_field(serializers.CharField)
437
189
  def get_display(self, obj):
@@ -444,7 +196,6 @@ class ContentTypeSerializer(BaseModelSerializer):
444
196
 
445
197
 
446
198
  class CustomFieldSerializer(ValidatedModelSerializer, NotesSerializerMixin):
447
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:customfield-detail")
448
199
  content_types = ContentTypeField(
449
200
  queryset=ContentType.objects.filter(FeatureQuery("custom_fields").get_query()),
450
201
  many=True,
@@ -455,30 +206,13 @@ class CustomFieldSerializer(ValidatedModelSerializer, NotesSerializerMixin):
455
206
 
456
207
  class Meta:
457
208
  model = CustomField
458
- fields = [
459
- "url",
460
- "content_types",
461
- "type",
462
- "label",
463
- "key",
464
- "description",
465
- "required",
466
- "filter_logic",
467
- "default",
468
- "weight",
469
- "validation_minimum",
470
- "validation_maximum",
471
- "validation_regex",
472
- ]
209
+ fields = "__all__"
473
210
 
474
211
 
475
212
  class CustomFieldChoiceSerializer(ValidatedModelSerializer):
476
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:customfieldchoice-detail")
477
- custom_field = NestedCustomFieldSerializer()
478
-
479
213
  class Meta:
480
214
  model = CustomFieldChoice
481
- fields = ["url", "custom_field", "value", "weight"]
215
+ fields = "__all__"
482
216
 
483
217
 
484
218
  #
@@ -487,24 +221,13 @@ class CustomFieldChoiceSerializer(ValidatedModelSerializer):
487
221
 
488
222
 
489
223
  class CustomLinkSerializer(ValidatedModelSerializer, NotesSerializerMixin):
490
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:customlink-detail")
491
224
  content_type = ContentTypeField(
492
225
  queryset=ContentType.objects.filter(FeatureQuery("custom_links").get_query()).order_by("app_label", "model"),
493
226
  )
494
227
 
495
228
  class Meta:
496
229
  model = CustomLink
497
- fields = (
498
- "url",
499
- "target_url",
500
- "name",
501
- "content_type",
502
- "text",
503
- "weight",
504
- "group_name",
505
- "button_class",
506
- "new_window",
507
- )
230
+ fields = "__all__"
508
231
 
509
232
 
510
233
  #
@@ -512,36 +235,24 @@ class CustomLinkSerializer(ValidatedModelSerializer, NotesSerializerMixin):
512
235
  #
513
236
 
514
237
 
238
+ class DynamicGroupMembershipSerializer(ValidatedModelSerializer):
239
+ class Meta:
240
+ model = DynamicGroupMembership
241
+ fields = "__all__"
242
+
243
+
515
244
  class DynamicGroupSerializer(NautobotModelSerializer):
516
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:dynamicgroup-detail")
517
245
  content_type = ContentTypeField(
518
246
  queryset=ContentType.objects.filter(FeatureQuery("dynamic_groups").get_query()).order_by("app_label", "model"),
519
247
  )
520
- # Read-only because m2m is hard. Easier to just create # `DynamicGroupMemberships` explicitly
521
- # using their own endpoint at /api/extras/dynamic-group-memberships/.
522
- children = NestedDynamicGroupMembershipSerializer(source="dynamic_group_memberships", read_only=True, many=True)
523
248
 
524
249
  class Meta:
525
250
  model = DynamicGroup
526
- fields = [
527
- "url",
528
- "name",
529
- "description",
530
- "content_type",
531
- "filter",
532
- "children",
533
- ]
534
- extra_kwargs = {"filter": {"read_only": False}}
535
-
536
-
537
- class DynamicGroupMembershipSerializer(ValidatedModelSerializer):
538
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:dynamicgroupmembership-detail")
539
- group = NestedDynamicGroupSerializer()
540
- parent_group = NestedDynamicGroupSerializer()
541
-
542
- class Meta:
543
- model = DynamicGroupMembership
544
- fields = ["url", "group", "parent_group", "operator", "weight"]
251
+ fields = "__all__"
252
+ extra_kwargs = {
253
+ "children": {"source": "dynamic_group_memberships", "read_only": True},
254
+ "filter": {"read_only": False},
255
+ }
545
256
 
546
257
 
547
258
  #
@@ -551,7 +262,6 @@ class DynamicGroupMembershipSerializer(ValidatedModelSerializer):
551
262
 
552
263
  # TODO: export-templates don't support custom-fields, is this omission intentional?
553
264
  class ExportTemplateSerializer(RelationshipModelSerializerMixin, ValidatedModelSerializer, NotesSerializerMixin):
554
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:exporttemplate-detail")
555
265
  content_type = ContentTypeField(
556
266
  queryset=ContentType.objects.filter(FeatureQuery("export_templates").get_query()),
557
267
  )
@@ -565,35 +275,21 @@ class ExportTemplateSerializer(RelationshipModelSerializerMixin, ValidatedModelS
565
275
 
566
276
  class Meta:
567
277
  model = ExportTemplate
568
- fields = [
569
- "url",
570
- "content_type",
571
- "owner_content_type",
572
- "owner_object_id",
573
- "owner",
574
- "name",
575
- "description",
576
- "template_code",
577
- "mime_type",
578
- "file_extension",
579
- ]
278
+ fields = "__all__"
580
279
 
581
280
  @extend_schema_field(
582
281
  PolymorphicProxySerializer(
583
282
  component_name="ExportTemplateOwner",
584
283
  resource_type_field_name="object_type",
585
- serializers=lambda: get_serializers_for_models(
586
- FeatureQuery("export_template_owners").list_subclasses(), prefix="Nested"
587
- ),
284
+ serializers=lambda: get_serializers_for_models(FeatureQuery("export_template_owners").list_subclasses()),
588
285
  allow_null=True,
589
286
  )
590
287
  )
591
288
  def get_owner(self, obj):
592
289
  if obj.owner is None:
593
290
  return None
594
- serializer = get_serializer_for_model(obj.owner, prefix="Nested")
595
- context = {"request": self.context["request"]}
596
- return serializer(obj.owner, context=context).data
291
+ depth = get_nested_serializer_depth(self)
292
+ return return_nested_serializer_data_based_on_depth(self, depth, obj, obj.owner, "owner")
597
293
 
598
294
 
599
295
  #
@@ -604,10 +300,6 @@ class ExportTemplateSerializer(RelationshipModelSerializerMixin, ValidatedModelS
604
300
  class GitRepositorySerializer(NautobotModelSerializer):
605
301
  """Git repositories defined as a data source."""
606
302
 
607
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:gitrepository-detail")
608
-
609
- secrets_group = NestedSecretsGroupSerializer(required=False, allow_null=True)
610
-
611
303
  provided_contents = MultipleChoiceJSONField(
612
304
  choices=lambda: get_datasource_content_choices("extras.gitrepository"),
613
305
  allow_blank=True,
@@ -616,23 +308,7 @@ class GitRepositorySerializer(NautobotModelSerializer):
616
308
 
617
309
  class Meta:
618
310
  model = GitRepository
619
- fields = [
620
- "url",
621
- "name",
622
- "slug",
623
- "remote_url",
624
- "branch",
625
- "secrets_group",
626
- "current_head",
627
- "provided_contents",
628
- ]
629
-
630
- def validate(self, data):
631
- """
632
- Add the originating Request as a parameter to be passed when creating/updating a GitRepository.
633
- """
634
- data["request"] = self.context["request"]
635
- return super().validate(data)
311
+ fields = "__all__"
636
312
 
637
313
 
638
314
  #
@@ -641,17 +317,11 @@ class GitRepositorySerializer(NautobotModelSerializer):
641
317
 
642
318
 
643
319
  class GraphQLQuerySerializer(ValidatedModelSerializer, NotesSerializerMixin):
644
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:graphqlquery-detail")
645
320
  variables = serializers.DictField(required=False, allow_null=True, default={})
646
321
 
647
322
  class Meta:
648
323
  model = GraphQLQuery
649
- fields = (
650
- "url",
651
- "name",
652
- "query",
653
- "variables",
654
- )
324
+ fields = "__all__"
655
325
 
656
326
 
657
327
  class GraphQLQueryInputSerializer(serializers.Serializer):
@@ -668,23 +338,11 @@ class GraphQLQueryOutputSerializer(serializers.Serializer):
668
338
 
669
339
 
670
340
  class ImageAttachmentSerializer(ValidatedModelSerializer):
671
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:imageattachment-detail")
672
341
  content_type = ContentTypeField(queryset=ContentType.objects.all())
673
- parent = serializers.SerializerMethodField(read_only=True)
674
342
 
675
343
  class Meta:
676
344
  model = ImageAttachment
677
- fields = [
678
- "url",
679
- "content_type",
680
- "object_id",
681
- "parent",
682
- "name",
683
- "image",
684
- "image_height",
685
- "image_width",
686
- "created",
687
- ]
345
+ fields = "__all__"
688
346
 
689
347
  def validate(self, data):
690
348
  # Validate that the parent object exists
@@ -703,15 +361,15 @@ class ImageAttachmentSerializer(ValidatedModelSerializer):
703
361
  component_name="ImageAttachmentParent",
704
362
  resource_type_field_name="object_type",
705
363
  serializers=[
706
- NestedDeviceSerializer,
707
- NestedLocationSerializer,
708
- NestedRackSerializer,
364
+ DeviceSerializer,
365
+ LocationSerializer,
366
+ RackSerializer,
709
367
  ],
710
368
  )
711
369
  )
712
370
  def get_parent(self, obj):
713
- serializer = get_serializer_for_model(obj.parent, prefix="Nested")
714
- return serializer(obj.parent, context={"request": self.context["request"]}).data
371
+ depth = get_nested_serializer_depth(self)
372
+ return return_nested_serializer_data_based_on_depth(self, depth, obj, obj.parent, "parent")
715
373
 
716
374
 
717
375
  #
@@ -720,44 +378,9 @@ class ImageAttachmentSerializer(ValidatedModelSerializer):
720
378
 
721
379
 
722
380
  class JobSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
723
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:job-detail")
724
-
725
381
  class Meta:
726
382
  model = Job
727
- fields = [
728
- "url",
729
- "source",
730
- "module_name",
731
- "job_class_name",
732
- "grouping",
733
- "grouping_override",
734
- "name",
735
- "name_override",
736
- "slug",
737
- "description",
738
- "description_override",
739
- "installed",
740
- "enabled",
741
- "is_job_hook_receiver",
742
- "is_job_button_receiver",
743
- "has_sensitive_variables",
744
- "has_sensitive_variables_override",
745
- "approval_required",
746
- "approval_required_override",
747
- "commit_default",
748
- "commit_default_override",
749
- "hidden",
750
- "hidden_override",
751
- "read_only",
752
- "read_only_override",
753
- "soft_time_limit",
754
- "soft_time_limit_override",
755
- "time_limit",
756
- "time_limit_override",
757
- "task_queues",
758
- "task_queues_override",
759
- "tags",
760
- ]
383
+ fields = "__all__"
761
384
 
762
385
  def validate(self, data):
763
386
  # note no validation for on creation of jobs because we do not support user creation of Job records via API
@@ -788,7 +411,6 @@ class JobVariableSerializer(serializers.Serializer):
788
411
  help_text = serializers.CharField(read_only=True, required=False)
789
412
  default = serializers.JSONField(read_only=True, required=False)
790
413
  required = serializers.BooleanField(read_only=True, required=False)
791
-
792
414
  min_length = serializers.IntegerField(read_only=True, required=False)
793
415
  max_length = serializers.IntegerField(read_only=True, required=False)
794
416
  min_value = serializers.IntegerField(read_only=True, required=False)
@@ -797,11 +419,17 @@ class JobVariableSerializer(serializers.Serializer):
797
419
  model = serializers.CharField(read_only=True, required=False)
798
420
 
799
421
 
800
- class JobRunResponseSerializer(serializers.Serializer):
801
- """Serializer representing responses from the JobModelViewSet.run() POST endpoint."""
422
+ #
423
+ # Scheduled Jobs
424
+ #
425
+
802
426
 
803
- schedule = NestedScheduledJobSerializer(read_only=True, required=False)
804
- job_result = NestedJobResultSerializer(read_only=True, required=False)
427
+ class ScheduledJobSerializer(BaseModelSerializer):
428
+ # start_time = serializers.DateTimeField(format=None, required=False)
429
+
430
+ class Meta:
431
+ model = ScheduledJob
432
+ fields = "__all__"
805
433
 
806
434
 
807
435
  #
@@ -810,63 +438,18 @@ class JobRunResponseSerializer(serializers.Serializer):
810
438
 
811
439
 
812
440
  class JobResultSerializer(CustomFieldModelSerializerMixin, BaseModelSerializer):
813
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:jobresult-detail")
814
- user = NestedUserSerializer(read_only=True)
815
441
  status = ChoiceField(choices=JobResultStatusChoices, read_only=True)
816
- job_model = NestedJobSerializer(read_only=True)
817
- obj_type = ContentTypeField(read_only=True)
818
- scheduled_job = NestedScheduledJobSerializer(read_only=True)
819
442
 
820
443
  class Meta:
821
444
  model = JobResult
822
- fields = [
823
- "url",
824
- "date_created",
825
- "date_done",
826
- "name",
827
- "job_model",
828
- "obj_type",
829
- "status",
830
- "user",
831
- "data",
832
- "task_id",
833
- "task_kwargs",
834
- "scheduled_job",
835
- ]
445
+ fields = "__all__"
836
446
 
837
447
 
838
- #
839
- # Scheduled Jobs
840
- #
841
-
842
-
843
- class ScheduledJobSerializer(BaseModelSerializer):
844
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:scheduledjob-detail")
845
- user = NestedUserSerializer(read_only=True)
846
- job_model = NestedJobSerializer(read_only=True)
847
- approved_by_user = NestedUserSerializer(read_only=True)
448
+ class JobRunResponseSerializer(serializers.Serializer):
449
+ """Serializer representing responses from the JobModelViewSet.run() POST endpoint."""
848
450
 
849
- class Meta:
850
- model = ScheduledJob
851
- fields = [
852
- "url",
853
- "name",
854
- "user",
855
- "job_model",
856
- "task",
857
- "interval",
858
- "queue",
859
- "job_class",
860
- "last_run_at",
861
- "total_run_count",
862
- "date_changed",
863
- "description",
864
- "user",
865
- "approved_by_user",
866
- "approval_required",
867
- "approved_at",
868
- "crontab",
869
- ]
451
+ schedule = ScheduledJobSerializer(read_only=True, required=False)
452
+ job_result = JobResultSerializer(read_only=True, required=False)
870
453
 
871
454
 
872
455
  #
@@ -887,7 +470,6 @@ class JobClassSerializer(serializers.Serializer):
887
470
  description = serializers.CharField(max_length=255, required=False, read_only=True)
888
471
  test_methods = serializers.ListField(child=serializers.CharField(max_length=255))
889
472
  vars = serializers.SerializerMethodField(read_only=True)
890
- result = NestedJobResultSerializer(required=False)
891
473
 
892
474
  @extend_schema_field(serializers.DictField)
893
475
  def get_vars(self, instance):
@@ -910,7 +492,6 @@ class JobClassDetailSerializer(JobClassSerializer):
910
492
 
911
493
 
912
494
  class JobHookSerializer(NautobotModelSerializer):
913
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:jobhook-detail")
914
495
  content_types = ContentTypeField(
915
496
  queryset=ChangeLoggedModelsQuery().as_queryset(),
916
497
  many=True,
@@ -918,17 +499,7 @@ class JobHookSerializer(NautobotModelSerializer):
918
499
 
919
500
  class Meta:
920
501
  model = JobHook
921
- fields = [
922
- "id",
923
- "url",
924
- "name",
925
- "content_types",
926
- "job",
927
- "enabled",
928
- "type_create",
929
- "type_update",
930
- "type_delete",
931
- ]
502
+ fields = "__all__"
932
503
 
933
504
  def validate(self, data):
934
505
  validated_data = super().validate(data)
@@ -948,17 +519,58 @@ class JobHookSerializer(NautobotModelSerializer):
948
519
  return validated_data
949
520
 
950
521
 
522
+ class JobCreationSerializer(BaseModelSerializer):
523
+ """
524
+ Nested serializer specifically for use with `JobInputSerializer.schedule`.
525
+
526
+ We don't use `WritableNestedSerializer` here because this is not used to look up
527
+ an existing `ScheduledJob`, but instead used to specify parameters for creating one.
528
+ """
529
+
530
+ url = serializers.HyperlinkedIdentityField(view_name="extras-api:scheduledjob-detail")
531
+ name = serializers.CharField(max_length=255, required=False)
532
+ start_time = serializers.DateTimeField(format=None, required=False)
533
+
534
+ class Meta:
535
+ model = ScheduledJob
536
+ fields = ["url", "name", "start_time", "interval", "crontab"]
537
+
538
+ def validate(self, data):
539
+ data = super().validate(data)
540
+
541
+ if data["interval"] in choices.JobExecutionType.SCHEDULE_CHOICES:
542
+ if "name" not in data:
543
+ raise serializers.ValidationError({"name": "Please provide a name for the job schedule."})
544
+
545
+ if ("start_time" not in data and data["interval"] != choices.JobExecutionType.TYPE_CUSTOM) or (
546
+ "start_time" in data and data["start_time"] < models.ScheduledJob.earliest_possible_time()
547
+ ):
548
+ raise serializers.ValidationError(
549
+ {
550
+ "start_time": "Please enter a valid date and time greater than or equal to the current date and time."
551
+ }
552
+ )
553
+
554
+ if data["interval"] == choices.JobExecutionType.TYPE_CUSTOM:
555
+ if data.get("crontab") is None:
556
+ raise serializers.ValidationError({"crontab": "Please enter a valid crontab."})
557
+ try:
558
+ models.ScheduledJob.get_crontab(data["crontab"])
559
+ except Exception as e:
560
+ raise serializers.ValidationError({"crontab": e})
561
+
562
+ return data
563
+
564
+
951
565
  class JobInputSerializer(serializers.Serializer):
952
566
  data = serializers.JSONField(required=False, default=dict)
953
- commit = serializers.BooleanField(required=False, default=None)
954
- schedule = NestedScheduledJobCreationSerializer(required=False)
567
+ schedule = JobCreationSerializer(required=False)
955
568
  task_queue = serializers.CharField(required=False, allow_blank=True)
956
569
 
957
570
 
958
571
  class JobMultiPartInputSerializer(serializers.Serializer):
959
572
  """JobMultiPartInputSerializer is a "flattened" version of JobInputSerializer for use with multipart/form-data submissions which only accept key-value pairs"""
960
573
 
961
- _commit = serializers.BooleanField(required=False, default=None)
962
574
  _schedule_name = serializers.CharField(max_length=255, required=False)
963
575
  _schedule_start_time = serializers.DateTimeField(format=None, required=False)
964
576
  _schedule_interval = ChoiceField(choices=JobExecutionType, required=False)
@@ -993,25 +605,9 @@ class JobMultiPartInputSerializer(serializers.Serializer):
993
605
 
994
606
 
995
607
  class JobLogEntrySerializer(BaseModelSerializer):
996
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:joblogentry-detail")
997
- display = serializers.SerializerMethodField()
998
-
999
608
  class Meta:
1000
609
  model = JobLogEntry
1001
- fields = [
1002
- "url",
1003
- "absolute_url",
1004
- "created",
1005
- "grouping",
1006
- "job_result",
1007
- "log_level",
1008
- "log_object",
1009
- "message",
1010
- ]
1011
-
1012
- @extend_schema_field(serializers.CharField)
1013
- def get_display(self, obj):
1014
- return obj.created.isoformat()
610
+ fields = "__all__"
1015
611
 
1016
612
 
1017
613
  #
@@ -1020,22 +616,11 @@ class JobLogEntrySerializer(BaseModelSerializer):
1020
616
 
1021
617
 
1022
618
  class JobButtonSerializer(ValidatedModelSerializer, NotesSerializerMixin):
1023
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:jobbutton-detail")
1024
619
  content_types = ContentTypeField(queryset=ContentType.objects.all(), many=True)
1025
620
 
1026
621
  class Meta:
1027
622
  model = JobButton
1028
- fields = (
1029
- "url",
1030
- "job",
1031
- "name",
1032
- "content_types",
1033
- "text",
1034
- "weight",
1035
- "group_name",
1036
- "button_class",
1037
- "confirmation",
1038
- )
623
+ fields = "__all__"
1039
624
 
1040
625
 
1041
626
  #
@@ -1044,29 +629,19 @@ class JobButtonSerializer(ValidatedModelSerializer, NotesSerializerMixin):
1044
629
 
1045
630
 
1046
631
  class NoteSerializer(BaseModelSerializer):
1047
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:note-detail")
1048
- user = NestedUserSerializer(read_only=True)
1049
632
  assigned_object_type = ContentTypeField(queryset=ContentType.objects.all())
1050
633
  assigned_object = serializers.SerializerMethodField()
1051
634
 
1052
635
  class Meta:
1053
636
  model = Note
1054
- fields = [
1055
- "url",
1056
- "user",
1057
- "user_name",
1058
- "assigned_object_type",
1059
- "assigned_object_id",
1060
- "assigned_object",
1061
- "note",
1062
- "slug",
1063
- ]
637
+ fields = "__all__"
638
+ list_display_fields = ["note", "assigned_object_type", "assigned_object_id", "user"]
1064
639
 
1065
640
  @extend_schema_field(
1066
641
  PolymorphicProxySerializer(
1067
642
  component_name="NoteAssignedObject",
1068
643
  resource_type_field_name="object_type",
1069
- serializers=lambda: get_serializers_for_models(get_all_concrete_models(NotesMixin), prefix="Nested"),
644
+ serializers=lambda: get_serializers_for_models(get_all_concrete_models(NotesMixin)),
1070
645
  allow_null=True,
1071
646
  )
1072
647
  )
@@ -1074,9 +649,10 @@ class NoteSerializer(BaseModelSerializer):
1074
649
  if obj.assigned_object is None:
1075
650
  return None
1076
651
  try:
1077
- serializer = get_serializer_for_model(obj.assigned_object, prefix="Nested")
1078
- context = {"request": self.context["request"]}
1079
- return serializer(obj.assigned_object, context=context).data
652
+ depth = get_nested_serializer_depth(self)
653
+ return return_nested_serializer_data_based_on_depth(
654
+ self, depth, obj, obj.assigned_object, "assigned_object"
655
+ )
1080
656
  except SerializerNotFound:
1081
657
  return None
1082
658
 
@@ -1091,34 +667,20 @@ class NoteInputSerializer(serializers.Serializer):
1091
667
 
1092
668
 
1093
669
  class ObjectChangeSerializer(BaseModelSerializer):
1094
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:objectchange-detail")
1095
- user = NestedUserSerializer(read_only=True)
1096
670
  action = ChoiceField(choices=ObjectChangeActionChoices, read_only=True)
1097
671
  changed_object_type = ContentTypeField(read_only=True)
1098
672
  changed_object = serializers.SerializerMethodField(read_only=True)
1099
673
 
1100
674
  class Meta:
1101
675
  model = ObjectChange
1102
- fields = [
1103
- "url",
1104
- "time",
1105
- "user",
1106
- "user_name",
1107
- "request_id",
1108
- "action",
1109
- "changed_object_type",
1110
- "changed_object_id",
1111
- "changed_object",
1112
- "object_data",
1113
- ]
676
+ fields = "__all__"
677
+ list_display_fields = ["changed_object_id", "related_object_id", "related_object_type", "user"]
1114
678
 
1115
679
  @extend_schema_field(
1116
680
  PolymorphicProxySerializer(
1117
681
  component_name="ObjectChangeChangedObject",
1118
682
  resource_type_field_name="object_type",
1119
- serializers=lambda: get_serializers_for_models(
1120
- ChangeLoggedModelsQuery().list_subclasses(), prefix="Nested"
1121
- ),
683
+ serializers=lambda: get_serializers_for_models(ChangeLoggedModelsQuery().list_subclasses()),
1122
684
  allow_null=True,
1123
685
  )
1124
686
  )
@@ -1128,15 +690,11 @@ class ObjectChangeSerializer(BaseModelSerializer):
1128
690
  """
1129
691
  if obj.changed_object is None:
1130
692
  return None
1131
-
1132
693
  try:
1133
- serializer = get_serializer_for_model(obj.changed_object, prefix="Nested")
694
+ depth = get_nested_serializer_depth(self)
695
+ return return_nested_serializer_data_based_on_depth(self, depth, obj, obj.changed_object, "changed_object")
1134
696
  except SerializerNotFound:
1135
697
  return obj.object_repr
1136
- context = {"request": self.context["request"]}
1137
- data = serializer(obj.changed_object, context=context).data
1138
-
1139
- return data
1140
698
 
1141
699
 
1142
700
  #
@@ -1145,8 +703,6 @@ class ObjectChangeSerializer(BaseModelSerializer):
1145
703
 
1146
704
 
1147
705
  class RelationshipSerializer(ValidatedModelSerializer, NotesSerializerMixin):
1148
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:relationship-detail")
1149
-
1150
706
  source_type = ContentTypeField(
1151
707
  queryset=ContentType.objects.filter(FeatureQuery("relationships").get_query()),
1152
708
  )
@@ -1157,27 +713,10 @@ class RelationshipSerializer(ValidatedModelSerializer, NotesSerializerMixin):
1157
713
 
1158
714
  class Meta:
1159
715
  model = Relationship
1160
- fields = [
1161
- "url",
1162
- "name",
1163
- "slug",
1164
- "description",
1165
- "type",
1166
- "required_on",
1167
- "source_type",
1168
- "source_label",
1169
- "source_hidden",
1170
- "source_filter",
1171
- "destination_type",
1172
- "destination_label",
1173
- "destination_hidden",
1174
- "destination_filter",
1175
- ]
716
+ fields = "__all__"
1176
717
 
1177
718
 
1178
719
  class RelationshipAssociationSerializer(ValidatedModelSerializer):
1179
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:relationshipassociation-detail")
1180
-
1181
720
  source_type = ContentTypeField(
1182
721
  queryset=ContentType.objects.filter(FeatureQuery("relationships").get_query()),
1183
722
  )
@@ -1186,18 +725,9 @@ class RelationshipAssociationSerializer(ValidatedModelSerializer):
1186
725
  queryset=ContentType.objects.filter(FeatureQuery("relationships").get_query()),
1187
726
  )
1188
727
 
1189
- relationship = NestedRelationshipSerializer()
1190
-
1191
728
  class Meta:
1192
729
  model = RelationshipAssociation
1193
- fields = [
1194
- "url",
1195
- "relationship",
1196
- "source_type",
1197
- "source_id",
1198
- "destination_type",
1199
- "destination_id",
1200
- ]
730
+ fields = "__all__"
1201
731
 
1202
732
 
1203
733
  #
@@ -1205,22 +735,9 @@ class RelationshipAssociationSerializer(ValidatedModelSerializer):
1205
735
  #
1206
736
 
1207
737
 
1208
- class RoleModelSerializerMixin(BaseModelSerializer):
1209
- """Mixin to add `role` choice field to model serializers."""
1210
-
1211
- role = RoleSerializerField(required=False)
1212
-
1213
-
1214
- class RoleRequiredRoleModelSerializerMixin(BaseModelSerializer):
1215
- """Mixin to add `role` choice field to model serializers."""
1216
-
1217
- role = RoleSerializerField()
1218
-
1219
-
1220
738
  class RoleSerializer(NautobotModelSerializer):
1221
739
  """Serializer for `Role` objects."""
1222
740
 
1223
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:role-detail")
1224
741
  content_types = ContentTypeField(
1225
742
  queryset=RoleModelsQuery().as_queryset(),
1226
743
  many=True,
@@ -1228,13 +745,10 @@ class RoleSerializer(NautobotModelSerializer):
1228
745
 
1229
746
  class Meta:
1230
747
  model = Role
1231
- fields = [
1232
- "url",
1233
- "content_types",
1234
- "name",
1235
- "color",
1236
- "weight",
1237
- ]
748
+ fields = "__all__"
749
+ extra_kwargs = {
750
+ "color": {"help_text": "RGB color in hexadecimal (e.g. 00ff00)"},
751
+ }
1238
752
 
1239
753
 
1240
754
  #
@@ -1245,57 +759,33 @@ class RoleSerializer(NautobotModelSerializer):
1245
759
  class SecretSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
1246
760
  """Serializer for `Secret` objects."""
1247
761
 
1248
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:secret-detail")
1249
-
1250
762
  class Meta:
1251
763
  model = Secret
1252
- fields = [
1253
- "url",
1254
- "name",
1255
- "description",
1256
- "provider",
1257
- "parameters",
1258
- ]
764
+ fields = "__all__"
1259
765
 
1260
766
 
1261
- class SecretsGroupSerializer(NautobotModelSerializer):
1262
- """Serializer for `SecretsGroup` objects."""
1263
-
1264
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:secretsgroup-detail")
1265
-
1266
- # TODO: it would be **awesome** if we could create/update SecretsGroupAssociations
1267
- # alongside creating/updating the base SecretsGroup, but since this is a ManyToManyField with
1268
- # a `through` table, that appears very non-trivial to implement. For now we have this as a
1269
- # read-only field; to create/update SecretsGroupAssociations you must make separate calls to the
1270
- # api/extras/secrets-group-associations/ REST endpoint as appropriate.
1271
- secrets = NestedSecretsGroupAssociationSerializer(source="secrets_group_associations", many=True, read_only=True)
767
+ class SecretsGroupAssociationSerializer(ValidatedModelSerializer):
768
+ """Serializer for `SecretsGroupAssociation` objects."""
1272
769
 
1273
770
  class Meta:
1274
- model = SecretsGroup
1275
- fields = [
1276
- "url",
1277
- "name",
1278
- "description",
1279
- "secrets",
1280
- ]
1281
-
771
+ model = SecretsGroupAssociation
772
+ fields = "__all__"
1282
773
 
1283
- class SecretsGroupAssociationSerializer(ValidatedModelSerializer):
1284
- """Serializer for `SecretsGroupAssociation` objects."""
1285
774
 
1286
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:secretsgroupassociation-detail")
1287
- secrets_group = NestedSecretsGroupSerializer()
1288
- secret = NestedSecretSerializer()
775
+ class SecretsGroupSerializer(NautobotModelSerializer):
776
+ """Serializer for `SecretsGroup` objects."""
1289
777
 
1290
778
  class Meta:
1291
- model = SecretsGroupAssociation
1292
- fields = [
1293
- "url",
1294
- "secrets_group",
1295
- "access_type",
1296
- "secret_type",
1297
- "secret",
1298
- ]
779
+ model = SecretsGroup
780
+ fields = "__all__"
781
+ # TODO: it would be **awesome** if we could create/update SecretsGroupAssociations
782
+ # alongside creating/updating the base SecretsGroup, but since this is a ManyToManyField with
783
+ # a `through` table, that appears very non-trivial to implement. For now we have this as a
784
+ # read-only field; to create/update SecretsGroupAssociations you must make separate calls to the
785
+ # api/extras/secrets-group-associations/ REST endpoint as appropriate.
786
+ extra_kwargs = {
787
+ "secrets": {"source": "secrets_group_associations", "read_only": True},
788
+ }
1299
789
 
1300
790
 
1301
791
  #
@@ -1306,7 +796,6 @@ class SecretsGroupAssociationSerializer(ValidatedModelSerializer):
1306
796
  class StatusSerializer(NautobotModelSerializer):
1307
797
  """Serializer for `Status` objects."""
1308
798
 
1309
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:status-detail")
1310
799
  content_types = ContentTypeField(
1311
800
  queryset=ContentType.objects.filter(FeatureQuery("statuses").get_query()),
1312
801
  many=True,
@@ -1314,12 +803,10 @@ class StatusSerializer(NautobotModelSerializer):
1314
803
 
1315
804
  class Meta:
1316
805
  model = Status
1317
- fields = [
1318
- "url",
1319
- "content_types",
1320
- "name",
1321
- "color",
1322
- ]
806
+ fields = "__all__"
807
+ extra_kwargs = {
808
+ "color": {"help_text": "RGB color in hexadecimal (e.g. 00ff00)"},
809
+ }
1323
810
 
1324
811
 
1325
812
  #
@@ -1328,7 +815,6 @@ class StatusSerializer(NautobotModelSerializer):
1328
815
 
1329
816
 
1330
817
  class TagSerializer(NautobotModelSerializer):
1331
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:tag-detail")
1332
818
  tagged_items = serializers.IntegerField(read_only=True)
1333
819
  content_types = ContentTypeField(
1334
820
  queryset=TaggableClassesQuery().as_queryset(),
@@ -1338,15 +824,10 @@ class TagSerializer(NautobotModelSerializer):
1338
824
 
1339
825
  class Meta:
1340
826
  model = Tag
1341
- fields = [
1342
- "url",
1343
- "name",
1344
- "slug",
1345
- "color",
1346
- "description",
1347
- "tagged_items",
1348
- "content_types",
1349
- ]
827
+ fields = "__all__"
828
+ extra_kwargs = {
829
+ "color": {"help_text": "RGB color in hexadecimal (e.g. 00ff00)"},
830
+ }
1350
831
 
1351
832
  def validate(self, data):
1352
833
  data = super().validate(data)
@@ -1368,7 +849,6 @@ class TagSerializer(NautobotModelSerializer):
1368
849
 
1369
850
 
1370
851
  class WebhookSerializer(ValidatedModelSerializer, NotesSerializerMixin):
1371
- url = serializers.HyperlinkedIdentityField(view_name="extras-api:webhook-detail")
1372
852
  content_types = ContentTypeField(
1373
853
  queryset=ContentType.objects.filter(FeatureQuery("webhooks").get_query()).order_by("app_label", "model"),
1374
854
  many=True,
@@ -1376,22 +856,7 @@ class WebhookSerializer(ValidatedModelSerializer, NotesSerializerMixin):
1376
856
 
1377
857
  class Meta:
1378
858
  model = Webhook
1379
- fields = [
1380
- "url",
1381
- "content_types",
1382
- "name",
1383
- "type_create",
1384
- "type_update",
1385
- "type_delete",
1386
- "payload_url",
1387
- "http_method",
1388
- "http_content_type",
1389
- "additional_headers",
1390
- "body_template",
1391
- "secret",
1392
- "ssl_verification",
1393
- "ca_file_path",
1394
- ]
859
+ fields = "__all__"
1395
860
 
1396
861
  def validate(self, data):
1397
862
  validated_data = super().validate(data)