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
@@ -1346,12 +1346,1166 @@
1346
1346
 
1347
1347
 
1348
1348
  <label class="md-nav__link" for="__nav_2_8" id="__nav_2_8_label" tabindex="0">
1349
+ Model Details
1350
+ <span class="md-nav__icon md-icon"></span>
1351
+ </label>
1352
+
1353
+ <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_8_label" aria-expanded="false">
1354
+ <label class="md-nav__title" for="__nav_2_8">
1355
+ <span class="md-nav__icon md-icon"></span>
1356
+ Model Details
1357
+ </label>
1358
+ <ul class="md-nav__list" data-md-scrollfix>
1359
+
1360
+
1361
+
1362
+
1363
+
1364
+
1365
+
1366
+ <li class="md-nav__item md-nav__item--nested">
1367
+
1368
+
1369
+
1370
+
1371
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_8_1" >
1372
+
1373
+
1374
+
1375
+ <label class="md-nav__link" for="__nav_2_8_1" id="__nav_2_8_1_label" tabindex="0">
1376
+ Circuits
1377
+ <span class="md-nav__icon md-icon"></span>
1378
+ </label>
1379
+
1380
+ <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_8_1_label" aria-expanded="false">
1381
+ <label class="md-nav__title" for="__nav_2_8_1">
1382
+ <span class="md-nav__icon md-icon"></span>
1383
+ Circuits
1384
+ </label>
1385
+ <ul class="md-nav__list" data-md-scrollfix>
1386
+
1387
+
1388
+
1389
+
1390
+
1391
+
1392
+ <li class="md-nav__item">
1393
+ <a href="../models/circuits/circuit.html" class="md-nav__link">
1394
+ Circuit
1395
+ </a>
1396
+ </li>
1397
+
1398
+
1399
+
1400
+
1401
+
1402
+
1403
+
1404
+
1405
+
1406
+ <li class="md-nav__item">
1407
+ <a href="../models/circuits/circuittermination.html" class="md-nav__link">
1408
+ Circuit Termination
1409
+ </a>
1410
+ </li>
1411
+
1412
+
1413
+
1414
+
1415
+
1416
+
1417
+
1418
+
1419
+
1420
+ <li class="md-nav__item">
1421
+ <a href="../models/circuits/circuittype.html" class="md-nav__link">
1422
+ Circuit Type
1423
+ </a>
1424
+ </li>
1425
+
1426
+
1427
+
1428
+
1429
+
1430
+
1431
+
1432
+
1433
+
1434
+ <li class="md-nav__item">
1435
+ <a href="../models/circuits/provider.html" class="md-nav__link">
1436
+ Circuit Provider
1437
+ </a>
1438
+ </li>
1439
+
1440
+
1441
+
1442
+
1443
+
1444
+
1445
+
1446
+
1447
+
1448
+ <li class="md-nav__item">
1449
+ <a href="../models/circuits/providernetwork.html" class="md-nav__link">
1450
+ Circuit Provider Network
1451
+ </a>
1452
+ </li>
1453
+
1454
+
1455
+
1456
+
1457
+ </ul>
1458
+ </nav>
1459
+ </li>
1460
+
1461
+
1462
+
1463
+
1464
+
1465
+
1466
+
1467
+
1468
+
1469
+
1470
+ <li class="md-nav__item md-nav__item--nested">
1471
+
1472
+
1473
+
1474
+
1475
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_8_2" >
1476
+
1477
+
1478
+
1479
+ <label class="md-nav__link" for="__nav_2_8_2" id="__nav_2_8_2_label" tabindex="0">
1480
+ DCIM
1481
+ <span class="md-nav__icon md-icon"></span>
1482
+ </label>
1483
+
1484
+ <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_8_2_label" aria-expanded="false">
1485
+ <label class="md-nav__title" for="__nav_2_8_2">
1486
+ <span class="md-nav__icon md-icon"></span>
1487
+ DCIM
1488
+ </label>
1489
+ <ul class="md-nav__list" data-md-scrollfix>
1490
+
1491
+
1492
+
1493
+
1494
+
1495
+
1496
+ <li class="md-nav__item">
1497
+ <a href="../models/dcim/cable.html" class="md-nav__link">
1498
+ Cable
1499
+ </a>
1500
+ </li>
1501
+
1502
+
1503
+
1504
+
1505
+
1506
+
1507
+
1508
+
1509
+
1510
+ <li class="md-nav__item">
1511
+ <a href="../models/dcim/consoleport.html" class="md-nav__link">
1512
+ Console Port
1513
+ </a>
1514
+ </li>
1515
+
1516
+
1517
+
1518
+
1519
+
1520
+
1521
+
1522
+
1523
+
1524
+ <li class="md-nav__item">
1525
+ <a href="../models/dcim/consoleporttemplate.html" class="md-nav__link">
1526
+ Console Port Template
1527
+ </a>
1528
+ </li>
1529
+
1530
+
1531
+
1532
+
1533
+
1534
+
1535
+
1536
+
1537
+
1538
+ <li class="md-nav__item">
1539
+ <a href="../models/dcim/consoleserverport.html" class="md-nav__link">
1540
+ Console Server Port
1541
+ </a>
1542
+ </li>
1543
+
1544
+
1545
+
1546
+
1547
+
1548
+
1549
+
1550
+
1551
+
1552
+ <li class="md-nav__item">
1553
+ <a href="../models/dcim/consoleserverporttemplate.html" class="md-nav__link">
1554
+ Console Server Port Template
1555
+ </a>
1556
+ </li>
1557
+
1558
+
1559
+
1560
+
1561
+
1562
+
1563
+
1564
+
1565
+
1566
+ <li class="md-nav__item">
1567
+ <a href="../models/dcim/device.html" class="md-nav__link">
1568
+ Device
1569
+ </a>
1570
+ </li>
1571
+
1572
+
1573
+
1574
+
1575
+
1576
+
1577
+
1578
+
1579
+
1580
+ <li class="md-nav__item">
1581
+ <a href="../models/dcim/devicebay.html" class="md-nav__link">
1582
+ Device Bay
1583
+ </a>
1584
+ </li>
1585
+
1586
+
1587
+
1588
+
1589
+
1590
+
1591
+
1592
+
1593
+
1594
+ <li class="md-nav__item">
1595
+ <a href="../models/dcim/devicebaytemplate.html" class="md-nav__link">
1596
+ Device Bay Template
1597
+ </a>
1598
+ </li>
1599
+
1600
+
1601
+
1602
+
1603
+
1604
+
1605
+
1606
+
1607
+
1608
+ <li class="md-nav__item">
1609
+ <a href="../models/dcim/deviceredundancygroup.html" class="md-nav__link">
1610
+ Device Redundancy Group
1611
+ </a>
1612
+ </li>
1613
+
1614
+
1615
+
1616
+
1617
+
1618
+
1619
+
1620
+
1621
+
1622
+ <li class="md-nav__item">
1623
+ <a href="../models/dcim/devicetype.html" class="md-nav__link">
1624
+ Device Type
1625
+ </a>
1626
+ </li>
1627
+
1628
+
1629
+
1630
+
1631
+
1632
+
1633
+
1634
+
1635
+
1636
+ <li class="md-nav__item">
1637
+ <a href="../models/dcim/frontport.html" class="md-nav__link">
1638
+ Front Port
1639
+ </a>
1640
+ </li>
1641
+
1642
+
1643
+
1644
+
1645
+
1646
+
1647
+
1648
+
1649
+
1650
+ <li class="md-nav__item">
1651
+ <a href="../models/dcim/frontporttemplate.html" class="md-nav__link">
1652
+ Front Port Template
1653
+ </a>
1654
+ </li>
1655
+
1656
+
1657
+
1658
+
1659
+
1660
+
1661
+
1662
+
1663
+
1664
+ <li class="md-nav__item">
1665
+ <a href="../models/dcim/interface.html" class="md-nav__link">
1666
+ Interface
1667
+ </a>
1668
+ </li>
1669
+
1670
+
1671
+
1672
+
1673
+
1674
+
1675
+
1676
+
1677
+
1678
+ <li class="md-nav__item">
1679
+ <a href="../models/dcim/interfacetemplate.html" class="md-nav__link">
1680
+ Interface Template
1681
+ </a>
1682
+ </li>
1683
+
1684
+
1685
+
1686
+
1687
+
1688
+
1689
+
1690
+
1691
+
1692
+ <li class="md-nav__item">
1693
+ <a href="../models/dcim/inventoryitem.html" class="md-nav__link">
1694
+ Inventory Item
1695
+ </a>
1696
+ </li>
1697
+
1698
+
1699
+
1700
+
1701
+
1702
+
1703
+
1704
+
1705
+
1706
+ <li class="md-nav__item">
1707
+ <a href="../models/dcim/location.html" class="md-nav__link">
1708
+ Location
1709
+ </a>
1710
+ </li>
1711
+
1712
+
1713
+
1714
+
1715
+
1716
+
1717
+
1718
+
1719
+
1720
+ <li class="md-nav__item">
1721
+ <a href="../models/dcim/locationtype.html" class="md-nav__link">
1722
+ Location Type
1723
+ </a>
1724
+ </li>
1725
+
1726
+
1727
+
1728
+
1729
+
1730
+
1731
+
1732
+
1733
+
1734
+ <li class="md-nav__item">
1735
+ <a href="../models/dcim/manufacturer.html" class="md-nav__link">
1736
+ Manufacturer
1737
+ </a>
1738
+ </li>
1739
+
1740
+
1741
+
1742
+
1743
+
1744
+
1745
+
1746
+
1747
+
1748
+ <li class="md-nav__item">
1749
+ <a href="../models/dcim/platform.html" class="md-nav__link">
1750
+ Platform
1751
+ </a>
1752
+ </li>
1753
+
1754
+
1755
+
1756
+
1757
+
1758
+
1759
+
1760
+
1761
+
1762
+ <li class="md-nav__item">
1763
+ <a href="../models/dcim/powerfeed.html" class="md-nav__link">
1764
+ Power Feed
1765
+ </a>
1766
+ </li>
1767
+
1768
+
1769
+
1770
+
1771
+
1772
+
1773
+
1774
+
1775
+
1776
+ <li class="md-nav__item">
1777
+ <a href="../models/dcim/poweroutlet.html" class="md-nav__link">
1778
+ Power Outlet
1779
+ </a>
1780
+ </li>
1781
+
1782
+
1783
+
1784
+
1785
+
1786
+
1787
+
1788
+
1789
+
1790
+ <li class="md-nav__item">
1791
+ <a href="../models/dcim/poweroutlettemplate.html" class="md-nav__link">
1792
+ Power Outlet Template
1793
+ </a>
1794
+ </li>
1795
+
1796
+
1797
+
1798
+
1799
+
1800
+
1801
+
1802
+
1803
+
1804
+ <li class="md-nav__item">
1805
+ <a href="../models/dcim/powerpanel.html" class="md-nav__link">
1806
+ Power Panel
1807
+ </a>
1808
+ </li>
1809
+
1810
+
1811
+
1812
+
1813
+
1814
+
1815
+
1816
+
1817
+
1818
+ <li class="md-nav__item">
1819
+ <a href="../models/dcim/powerport.html" class="md-nav__link">
1820
+ Power Port
1821
+ </a>
1822
+ </li>
1823
+
1824
+
1825
+
1826
+
1827
+
1828
+
1829
+
1830
+
1831
+
1832
+ <li class="md-nav__item">
1833
+ <a href="../models/dcim/powerporttemplate.html" class="md-nav__link">
1834
+ Power Port Template
1835
+ </a>
1836
+ </li>
1837
+
1838
+
1839
+
1840
+
1841
+
1842
+
1843
+
1844
+
1845
+
1846
+ <li class="md-nav__item">
1847
+ <a href="../models/dcim/rack.html" class="md-nav__link">
1848
+ Rack
1849
+ </a>
1850
+ </li>
1851
+
1852
+
1853
+
1854
+
1855
+
1856
+
1857
+
1858
+
1859
+
1860
+ <li class="md-nav__item">
1861
+ <a href="../models/dcim/rackgroup.html" class="md-nav__link">
1862
+ Rack Group
1863
+ </a>
1864
+ </li>
1865
+
1866
+
1867
+
1868
+
1869
+
1870
+
1871
+
1872
+
1873
+
1874
+ <li class="md-nav__item">
1875
+ <a href="../models/dcim/rackreservation.html" class="md-nav__link">
1876
+ Rack Reservation
1877
+ </a>
1878
+ </li>
1879
+
1880
+
1881
+
1882
+
1883
+
1884
+
1885
+
1886
+
1887
+
1888
+ <li class="md-nav__item">
1889
+ <a href="../models/dcim/rearport.html" class="md-nav__link">
1890
+ Rear Port
1891
+ </a>
1892
+ </li>
1893
+
1894
+
1895
+
1896
+
1897
+
1898
+
1899
+
1900
+
1901
+
1902
+ <li class="md-nav__item">
1903
+ <a href="../models/dcim/rearporttemplate.html" class="md-nav__link">
1904
+ Rear Port Template
1905
+ </a>
1906
+ </li>
1907
+
1908
+
1909
+
1910
+
1911
+
1912
+
1913
+
1914
+
1915
+
1916
+ <li class="md-nav__item">
1917
+ <a href="../models/dcim/virtualchassis.html" class="md-nav__link">
1918
+ Virtual Chassis
1919
+ </a>
1920
+ </li>
1921
+
1922
+
1923
+
1924
+
1925
+ </ul>
1926
+ </nav>
1927
+ </li>
1928
+
1929
+
1930
+
1931
+
1932
+
1933
+
1934
+
1935
+
1936
+
1937
+
1938
+ <li class="md-nav__item md-nav__item--nested">
1939
+
1940
+
1941
+
1942
+
1943
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_8_3" >
1944
+
1945
+
1946
+
1947
+ <label class="md-nav__link" for="__nav_2_8_3" id="__nav_2_8_3_label" tabindex="0">
1948
+ Extras
1949
+ <span class="md-nav__icon md-icon"></span>
1950
+ </label>
1951
+
1952
+ <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_8_3_label" aria-expanded="false">
1953
+ <label class="md-nav__title" for="__nav_2_8_3">
1954
+ <span class="md-nav__icon md-icon"></span>
1955
+ Extras
1956
+ </label>
1957
+ <ul class="md-nav__list" data-md-scrollfix>
1958
+
1959
+
1960
+
1961
+
1962
+
1963
+
1964
+ <li class="md-nav__item">
1965
+ <a href="../models/extras/configcontext.html" class="md-nav__link">
1966
+ Config Context
1967
+ </a>
1968
+ </li>
1969
+
1970
+
1971
+
1972
+
1973
+
1974
+
1975
+
1976
+
1977
+
1978
+ <li class="md-nav__item">
1979
+ <a href="../models/extras/configcontextschema.html" class="md-nav__link">
1980
+ Config Context Schema
1981
+ </a>
1982
+ </li>
1983
+
1984
+
1985
+
1986
+
1987
+
1988
+
1989
+
1990
+
1991
+
1992
+ <li class="md-nav__item">
1993
+ <a href="../models/extras/graphqlquery.html" class="md-nav__link">
1994
+ GraphQL Query
1995
+ </a>
1996
+ </li>
1997
+
1998
+
1999
+
2000
+
2001
+
2002
+
2003
+
2004
+
2005
+
2006
+ <li class="md-nav__item">
2007
+ <a href="../models/extras/imageattachment.html" class="md-nav__link">
2008
+ Image Attachment
2009
+ </a>
2010
+ </li>
2011
+
2012
+
2013
+
2014
+
2015
+
2016
+
2017
+
2018
+
2019
+
2020
+ <li class="md-nav__item">
2021
+ <a href="../models/extras/job.html" class="md-nav__link">
2022
+ Job
2023
+ </a>
2024
+ </li>
2025
+
2026
+
2027
+
2028
+
2029
+
2030
+
2031
+
2032
+
2033
+
2034
+ <li class="md-nav__item">
2035
+ <a href="../models/extras/joblogentry.html" class="md-nav__link">
2036
+ Job Log Entry
2037
+ </a>
2038
+ </li>
2039
+
2040
+
2041
+
2042
+
2043
+
2044
+
2045
+
2046
+
2047
+
2048
+ <li class="md-nav__item">
2049
+ <a href="../models/extras/jobresult.html" class="md-nav__link">
2050
+ Job Result
2051
+ </a>
2052
+ </li>
2053
+
2054
+
2055
+
2056
+
2057
+
2058
+
2059
+
2060
+
2061
+
2062
+ <li class="md-nav__item">
2063
+ <a href="../models/extras/role.html" class="md-nav__link">
2064
+ Role
2065
+ </a>
2066
+ </li>
2067
+
2068
+
2069
+
2070
+
2071
+
2072
+
2073
+
2074
+
2075
+
2076
+ <li class="md-nav__item">
2077
+ <a href="../models/extras/secret.html" class="md-nav__link">
2078
+ Secret
2079
+ </a>
2080
+ </li>
2081
+
2082
+
2083
+
2084
+
2085
+
2086
+
2087
+
2088
+
2089
+
2090
+ <li class="md-nav__item">
2091
+ <a href="../models/extras/secretsgroup.html" class="md-nav__link">
2092
+ Secrets group
2093
+ </a>
2094
+ </li>
2095
+
2096
+
2097
+
2098
+
2099
+ </ul>
2100
+ </nav>
2101
+ </li>
2102
+
2103
+
2104
+
2105
+
2106
+
2107
+
2108
+
2109
+
2110
+
2111
+
2112
+ <li class="md-nav__item md-nav__item--nested">
2113
+
2114
+
2115
+
2116
+
2117
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_8_4" >
2118
+
2119
+
2120
+
2121
+ <label class="md-nav__link" for="__nav_2_8_4" id="__nav_2_8_4_label" tabindex="0">
2122
+ IPAM
2123
+ <span class="md-nav__icon md-icon"></span>
2124
+ </label>
2125
+
2126
+ <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_8_4_label" aria-expanded="false">
2127
+ <label class="md-nav__title" for="__nav_2_8_4">
2128
+ <span class="md-nav__icon md-icon"></span>
2129
+ IPAM
2130
+ </label>
2131
+ <ul class="md-nav__list" data-md-scrollfix>
2132
+
2133
+
2134
+
2135
+
2136
+
2137
+
2138
+ <li class="md-nav__item">
2139
+ <a href="../models/ipam/ipaddress.html" class="md-nav__link">
2140
+ IP Address
2141
+ </a>
2142
+ </li>
2143
+
2144
+
2145
+
2146
+
2147
+
2148
+
2149
+
2150
+
2151
+
2152
+ <li class="md-nav__item">
2153
+ <a href="../models/ipam/prefix.html" class="md-nav__link">
2154
+ Prefix
2155
+ </a>
2156
+ </li>
2157
+
2158
+
2159
+
2160
+
2161
+
2162
+
2163
+
2164
+
2165
+
2166
+ <li class="md-nav__item">
2167
+ <a href="../models/ipam/rir.html" class="md-nav__link">
2168
+ Rir
2169
+ </a>
2170
+ </li>
2171
+
2172
+
2173
+
2174
+
2175
+
2176
+
2177
+
2178
+
2179
+
2180
+ <li class="md-nav__item">
2181
+ <a href="../models/ipam/routetarget.html" class="md-nav__link">
2182
+ Route Target
2183
+ </a>
2184
+ </li>
2185
+
2186
+
2187
+
2188
+
2189
+
2190
+
2191
+
2192
+
2193
+
2194
+ <li class="md-nav__item">
2195
+ <a href="../models/ipam/service.html" class="md-nav__link">
2196
+ Service
2197
+ </a>
2198
+ </li>
2199
+
2200
+
2201
+
2202
+
2203
+
2204
+
2205
+
2206
+
2207
+
2208
+ <li class="md-nav__item">
2209
+ <a href="../models/ipam/vlan.html" class="md-nav__link">
2210
+ VLAN
2211
+ </a>
2212
+ </li>
2213
+
2214
+
2215
+
2216
+
2217
+
2218
+
2219
+
2220
+
2221
+
2222
+ <li class="md-nav__item">
2223
+ <a href="../models/ipam/vlangroup.html" class="md-nav__link">
2224
+ VLAN Group
2225
+ </a>
2226
+ </li>
2227
+
2228
+
2229
+
2230
+
2231
+
2232
+
2233
+
2234
+
2235
+
2236
+ <li class="md-nav__item">
2237
+ <a href="../models/ipam/vrf.html" class="md-nav__link">
2238
+ VRF
2239
+ </a>
2240
+ </li>
2241
+
2242
+
2243
+
2244
+
2245
+ </ul>
2246
+ </nav>
2247
+ </li>
2248
+
2249
+
2250
+
2251
+
2252
+
2253
+
2254
+
2255
+
2256
+
2257
+
2258
+ <li class="md-nav__item md-nav__item--nested">
2259
+
2260
+
2261
+
2262
+
2263
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_8_5" >
2264
+
2265
+
2266
+
2267
+ <label class="md-nav__link" for="__nav_2_8_5" id="__nav_2_8_5_label" tabindex="0">
2268
+ Tenancy
2269
+ <span class="md-nav__icon md-icon"></span>
2270
+ </label>
2271
+
2272
+ <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_8_5_label" aria-expanded="false">
2273
+ <label class="md-nav__title" for="__nav_2_8_5">
2274
+ <span class="md-nav__icon md-icon"></span>
2275
+ Tenancy
2276
+ </label>
2277
+ <ul class="md-nav__list" data-md-scrollfix>
2278
+
2279
+
2280
+
2281
+
2282
+
2283
+
2284
+ <li class="md-nav__item">
2285
+ <a href="../models/tenancy/tenant.html" class="md-nav__link">
2286
+ Tenant
2287
+ </a>
2288
+ </li>
2289
+
2290
+
2291
+
2292
+
2293
+
2294
+
2295
+
2296
+
2297
+
2298
+ <li class="md-nav__item">
2299
+ <a href="../models/tenancy/tenantgroup.html" class="md-nav__link">
2300
+ Tenant Group
2301
+ </a>
2302
+ </li>
2303
+
2304
+
2305
+
2306
+
2307
+ </ul>
2308
+ </nav>
2309
+ </li>
2310
+
2311
+
2312
+
2313
+
2314
+
2315
+
2316
+
2317
+
2318
+
2319
+
2320
+ <li class="md-nav__item md-nav__item--nested">
2321
+
2322
+
2323
+
2324
+
2325
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_8_6" >
2326
+
2327
+
2328
+
2329
+ <label class="md-nav__link" for="__nav_2_8_6" id="__nav_2_8_6_label" tabindex="0">
2330
+ Users
2331
+ <span class="md-nav__icon md-icon"></span>
2332
+ </label>
2333
+
2334
+ <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_8_6_label" aria-expanded="false">
2335
+ <label class="md-nav__title" for="__nav_2_8_6">
2336
+ <span class="md-nav__icon md-icon"></span>
2337
+ Users
2338
+ </label>
2339
+ <ul class="md-nav__list" data-md-scrollfix>
2340
+
2341
+
2342
+
2343
+
2344
+
2345
+
2346
+ <li class="md-nav__item">
2347
+ <a href="../models/users/objectpermission.html" class="md-nav__link">
2348
+ Object Permission
2349
+ </a>
2350
+ </li>
2351
+
2352
+
2353
+
2354
+
2355
+
2356
+
2357
+
2358
+
2359
+
2360
+ <li class="md-nav__item">
2361
+ <a href="../models/users/token.html" class="md-nav__link">
2362
+ Token
2363
+ </a>
2364
+ </li>
2365
+
2366
+
2367
+
2368
+
2369
+ </ul>
2370
+ </nav>
2371
+ </li>
2372
+
2373
+
2374
+
2375
+
2376
+
2377
+
2378
+
2379
+
2380
+
2381
+
2382
+ <li class="md-nav__item md-nav__item--nested">
2383
+
2384
+
2385
+
2386
+
2387
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_8_7" >
2388
+
2389
+
2390
+
2391
+ <label class="md-nav__link" for="__nav_2_8_7" id="__nav_2_8_7_label" tabindex="0">
2392
+ Virtualization
2393
+ <span class="md-nav__icon md-icon"></span>
2394
+ </label>
2395
+
2396
+ <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_2_8_7_label" aria-expanded="false">
2397
+ <label class="md-nav__title" for="__nav_2_8_7">
2398
+ <span class="md-nav__icon md-icon"></span>
2399
+ Virtualization
2400
+ </label>
2401
+ <ul class="md-nav__list" data-md-scrollfix>
2402
+
2403
+
2404
+
2405
+
2406
+
2407
+
2408
+ <li class="md-nav__item">
2409
+ <a href="../models/virtualization/cluster.html" class="md-nav__link">
2410
+ Cluster
2411
+ </a>
2412
+ </li>
2413
+
2414
+
2415
+
2416
+
2417
+
2418
+
2419
+
2420
+
2421
+
2422
+ <li class="md-nav__item">
2423
+ <a href="../models/virtualization/clustergroup.html" class="md-nav__link">
2424
+ Cluster Group
2425
+ </a>
2426
+ </li>
2427
+
2428
+
2429
+
2430
+
2431
+
2432
+
2433
+
2434
+
2435
+
2436
+ <li class="md-nav__item">
2437
+ <a href="../models/virtualization/clustertype.html" class="md-nav__link">
2438
+ Cluster Type
2439
+ </a>
2440
+ </li>
2441
+
2442
+
2443
+
2444
+
2445
+
2446
+
2447
+
2448
+
2449
+
2450
+ <li class="md-nav__item">
2451
+ <a href="../models/virtualization/virtualmachine.html" class="md-nav__link">
2452
+ Virtual Machine
2453
+ </a>
2454
+ </li>
2455
+
2456
+
2457
+
2458
+
2459
+
2460
+
2461
+
2462
+
2463
+
2464
+ <li class="md-nav__item">
2465
+ <a href="../models/virtualization/vminterface.html" class="md-nav__link">
2466
+ VM Interface
2467
+ </a>
2468
+ </li>
2469
+
2470
+
2471
+
2472
+
2473
+ </ul>
2474
+ </nav>
2475
+ </li>
2476
+
2477
+
2478
+
2479
+
2480
+ </ul>
2481
+ </nav>
2482
+ </li>
2483
+
2484
+
2485
+
2486
+
2487
+
2488
+
2489
+
2490
+
2491
+
2492
+
2493
+ <li class="md-nav__item md-nav__item--nested">
2494
+
2495
+
2496
+
2497
+
2498
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_9" >
2499
+
2500
+
2501
+
2502
+ <label class="md-nav__link" for="__nav_2_9" id="__nav_2_9_label" tabindex="0">
1349
2503
  Additional Features
1350
2504
  <span class="md-nav__icon md-icon"></span>
1351
2505
  </label>
1352
2506
 
1353
- <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_8_label" aria-expanded="false">
1354
- <label class="md-nav__title" for="__nav_2_8">
2507
+ <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_9_label" aria-expanded="false">
2508
+ <label class="md-nav__title" for="__nav_2_9">
1355
2509
  <span class="md-nav__icon md-icon"></span>
1356
2510
  Additional Features
1357
2511
  </label>
@@ -1683,17 +2837,17 @@
1683
2837
 
1684
2838
 
1685
2839
 
1686
- <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_9" >
2840
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_10" >
1687
2841
 
1688
2842
 
1689
2843
 
1690
- <label class="md-nav__link" for="__nav_2_9" id="__nav_2_9_label" tabindex="0">
2844
+ <label class="md-nav__link" for="__nav_2_10" id="__nav_2_10_label" tabindex="0">
1691
2845
  REST API
1692
2846
  <span class="md-nav__icon md-icon"></span>
1693
2847
  </label>
1694
2848
 
1695
- <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_9_label" aria-expanded="false">
1696
- <label class="md-nav__title" for="__nav_2_9">
2849
+ <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_10_label" aria-expanded="false">
2850
+ <label class="md-nav__title" for="__nav_2_10">
1697
2851
  <span class="md-nav__icon md-icon"></span>
1698
2852
  REST API
1699
2853
  </label>
@@ -1741,6 +2895,20 @@
1741
2895
 
1742
2896
 
1743
2897
 
2898
+
2899
+
2900
+
2901
+
2902
+
2903
+ <li class="md-nav__item">
2904
+ <a href="../rest-api/ui-related-endpoints.html" class="md-nav__link">
2905
+ UI Endpoints
2906
+ </a>
2907
+ </li>
2908
+
2909
+
2910
+
2911
+
1744
2912
  </ul>
1745
2913
  </nav>
1746
2914
  </li>
@@ -1759,17 +2927,17 @@
1759
2927
 
1760
2928
 
1761
2929
 
1762
- <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_10" >
2930
+ <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_11" >
1763
2931
 
1764
2932
 
1765
2933
 
1766
- <label class="md-nav__link" for="__nav_2_10" id="__nav_2_10_label" tabindex="0">
2934
+ <label class="md-nav__link" for="__nav_2_11" id="__nav_2_11_label" tabindex="0">
1767
2935
  GraphQL API
1768
2936
  <span class="md-nav__icon md-icon"></span>
1769
2937
  </label>
1770
2938
 
1771
- <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_10_label" aria-expanded="false">
1772
- <label class="md-nav__title" for="__nav_2_10">
2939
+ <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_11_label" aria-expanded="false">
2940
+ <label class="md-nav__title" for="__nav_2_11">
1773
2941
  <span class="md-nav__icon md-icon"></span>
1774
2942
  GraphQL API
1775
2943
  </label>
@@ -2035,6 +3203,13 @@
2035
3203
  Adding Database Backed Config
2036
3204
  </a>
2037
3205
 
3206
+ </li>
3207
+
3208
+ <li class="md-nav__item">
3209
+ <a href="#overriding-default-model-views-in-nautobot-apps" class="md-nav__link">
3210
+ Overriding Default Model Views in Nautobot Apps
3211
+ </a>
3212
+
2038
3213
  </li>
2039
3214
 
2040
3215
  </ul>
@@ -3393,6 +4568,13 @@
3393
4568
  Adding Database Backed Config
3394
4569
  </a>
3395
4570
 
4571
+ </li>
4572
+
4573
+ <li class="md-nav__item">
4574
+ <a href="#overriding-default-model-views-in-nautobot-apps" class="md-nav__link">
4575
+ Overriding Default Model Views in Nautobot Apps
4576
+ </a>
4577
+
3396
4578
  </li>
3397
4579
 
3398
4580
  </ul>
@@ -3892,11 +5074,6 @@ Please see the <a href="../administration/nautobot-server.html#startplugin">Naut
3892
5074
  <td>Base path to use for app URLs</td>
3893
5075
  </tr>
3894
5076
  <tr>
3895
- <td><code>caching_config</code></td>
3896
- <td><code>{"*":{"ops":"all"}}</code></td>
3897
- <td>App-specific <a href="https://github.com/Suor/django-cacheops#setup">query caching configuration</a></td>
3898
- </tr>
3899
- <tr>
3900
5077
  <td><code>config_view_name</code></td>
3901
5078
  <td><code>None</code></td>
3902
5079
  <td><a href="#adding-links-to-the-installed-apps-view">URL name</a> for a "configuration" view defined by this app</td>
@@ -3957,6 +5134,10 @@ Please see the <a href="../administration/nautobot-server.html#startplugin">Naut
3957
5134
  <p class="admonition-title">Added in version 2.0.0</p>
3958
5135
  <p>Support for the <code>searchable_models</code> and <code>constance_config</code> attributes were added.</p>
3959
5136
  </div>
5137
+ <div class="admonition version-removed">
5138
+ <p class="admonition-title">Removed in version 2.0.0</p>
5139
+ <p>Support for <code>caching_config</code> was removed with the removal of <code>django-cacheops</code>.</p>
5140
+ </div>
3960
5141
  <div class="admonition note">
3961
5142
  <p class="admonition-title">Note</p>
3962
5143
  <p>All <code>required_settings</code> must be configured in <code>PLUGINS_CONFIG</code> in <code>nautobot_config.py</code> before the app can be used.</p>
@@ -4197,7 +5378,11 @@ Please see the <a href="../administration/nautobot-server.html#startplugin">Naut
4197
5378
  </code></pre></div>
4198
5379
  <h3 id="adding-navigation-menu-items">Adding Navigation Menu Items<a class="headerlink" href="#adding-navigation-menu-items" title="Permanent link">&para;</a></h3>
4199
5380
  <p>Apps can extend the existing navigation bar layout. By default, Nautobot looks for a <code>menu_items</code> list inside of <code>navigation.py</code>. (This can be overridden by setting <code>menu_items</code> to a custom value on the app's <code>NautobotAppConfig</code>.)</p>
4200
- <p>Using a key and weight system, a developer can integrate the app's menu additions amongst existing menu tabs, groups, items and buttons, and/or create entirely new menus as desired.</p>
5381
+ <p>Using a key and weight system, a developer can integrate the app's menu additions amongst existing menu tabs, groups, and items.</p>
5382
+ <div class="admonition version-removed">
5383
+ <p class="admonition-title">Removed in version 2.0.0</p>
5384
+ <p>As part of the Nautobot 2.0 UI redesign, the option for apps to add entirely new top-level menu "tabs" has been removed. Additionally, buttons can no longer be added to menu items.</p>
5385
+ </div>
4201
5386
  <p>More documentation and examples can be found in the <a href="../development/navigation-menu.html">Navigation Menu</a> guide.</p>
4202
5387
  <div class="admonition tip">
4203
5388
  <p class="admonition-title">Tip</p>
@@ -4261,6 +5446,44 @@ namedtuple to assist in the configurations.</p>
4261
5446
  <p class="admonition-title">Warning</p>
4262
5447
  <p>Do not store secrets in the constance_config, instead use Nautobot <a href="../models/extras/secret.html">Secrets</a>.</p>
4263
5448
  </div>
5449
+ <h3 id="overriding-default-model-views-in-nautobot-apps">Overriding Default Model Views in Nautobot Apps<a class="headerlink" href="#overriding-default-model-views-in-nautobot-apps" title="Permanent link">&para;</a></h3>
5450
+ <div class="admonition version-added">
5451
+ <p class="admonition-title">Added in version 2.0.0</p>
5452
+ </div>
5453
+ <p>In UI 2.0, Nautobot provides default model views (<code>ObjectListView</code>, <code>ObjectRetrieveView</code> and etc) for every model including App provided models unless they are explicitly overridden. For example, an app called <code>your_example_app</code> wants to override the default <code>ObjectRetrieveView</code> for its model called <code>YourExampleModel</code> with a customized view called <code>YourExampleView</code>. We need to go to the <code>index.js</code> file located in the <code>your_example_app/ui</code> folder and add a key <code>view_overrides</code> to the <code>app_config</code> dictionary variable.</p>
5454
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a>const app_config = {
5455
+ <a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a> ...
5456
+ <a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a> view_overrides: {}
5457
+ <a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a> ...
5458
+ <a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a>}
5459
+ </code></pre></div>
5460
+ <p>In <code>view_overrides</code>'s dictionary, you need to specify the app and the model you want to override the default view for in this format <code>{app_label}: {model_name}</code>. So in our case, it would be <code>"your-example-app": "your-example-model"</code>.</p>
5461
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a>...
5462
+ <a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a> view_overrides: {
5463
+ <a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a> &quot;your-example-app&quot;: &quot;your-example-model&quot;: {}
5464
+ <a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a> }
5465
+ <a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a>...
5466
+ </code></pre></div>
5467
+ <p>Finally, you need to specify the default view action you want to override and the new view in this format <code>{view_action}: {new_view}</code>. So in our case, it would be <code>"retrieve": "YourExampleView"</code>:</p>
5468
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a>...
5469
+ <a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a> view_overrides: {
5470
+ <a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a> &quot;your-example-app&quot;: &quot;your-example-model&quot;: {
5471
+ <a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a> &quot;retrieve&quot;: &quot;YourExampleView&quot;
5472
+ <a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a> }
5473
+ <a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a> }
5474
+ <a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a>...
5475
+ </code></pre></div>
5476
+ <p>Now if you go to <code>YourExampleModel</code>'s retrieve view, instead of the default <code>ObjectRetrieveView</code>, you will see the customized layout of <code>YourExampleView</code>.</p>
5477
+ <p>If you want to override the default <code>ObjectListView</code> as well for <code>YourExampleModel</code> with <code>YourExampleListView</code>, just append <code>"list": "YourExampleListView"</code> to the <code>"your-example-app": "your-example-model"</code> dictionary.</p>
5478
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a>...
5479
+ <a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a> view_overrides: {
5480
+ <a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a> &quot;your-example-app&quot;: &quot;your-example-model&quot;: {
5481
+ <a id="__codelineno-16-4" name="__codelineno-16-4" href="#__codelineno-16-4"></a> &quot;retrieve&quot;: &quot;YourExampleView&quot;,
5482
+ <a id="__codelineno-16-5" name="__codelineno-16-5" href="#__codelineno-16-5"></a> &quot;list&quot;: &quot;YourExampleListView&quot;,
5483
+ <a id="__codelineno-16-6" name="__codelineno-16-6" href="#__codelineno-16-6"></a> }
5484
+ <a id="__codelineno-16-7" name="__codelineno-16-7" href="#__codelineno-16-7"></a> }
5485
+ <a id="__codelineno-16-8" name="__codelineno-16-8" href="#__codelineno-16-8"></a>...
5486
+ </code></pre></div>
4264
5487
  <h2 id="extending-existing-functionality">Extending Existing Functionality<a class="headerlink" href="#extending-existing-functionality" title="Permanent link">&para;</a></h2>
4265
5488
  <h3 id="adding-jinja2-filters">Adding Jinja2 Filters<a class="headerlink" href="#adding-jinja2-filters" title="Permanent link">&para;</a></h3>
4266
5489
  <div class="admonition version-added">
@@ -4268,122 +5491,122 @@ namedtuple to assist in the configurations.</p>
4268
5491
  </div>
4269
5492
  <p>Apps can define custom Jinja2 filters to be used when rendering templates defined in computed fields. Check out the <a href="https://jinja.palletsprojects.com/en/3.0.x/api/#custom-filters">official Jinja2 documentation</a> on how to create filter functions.</p>
4270
5493
  <p>In the file that defines your filters (by default <code>jinja_filters.py</code>, but configurable in the <code>NautobotAppConfig</code> if desired), you must import the <code>library</code> module from the <code>django_jinja</code> library. Filters must then be decorated with <code>@library.filter</code>. See an example below that defines a filter called <code>leet_speak</code>.</p>
4271
- <div class="highlight"><pre><span></span><code><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="kn">from</span> <span class="nn">django_jinja</span> <span class="kn">import</span> <span class="n">library</span>
4272
- <a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a>
4273
- <a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a>
4274
- <a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="nd">@library</span><span class="o">.</span><span class="n">filter</span>
4275
- <a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a><span class="k">def</span> <span class="nf">leet_speak</span><span class="p">(</span><span class="n">input_str</span><span class="p">):</span>
4276
- <a id="__codelineno-13-6" name="__codelineno-13-6" href="#__codelineno-13-6"></a> <span class="n">charset</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;a&quot;</span><span class="p">:</span> <span class="s2">&quot;4&quot;</span><span class="p">,</span> <span class="s2">&quot;e&quot;</span><span class="p">:</span> <span class="s2">&quot;3&quot;</span><span class="p">,</span> <span class="s2">&quot;l&quot;</span><span class="p">:</span> <span class="s2">&quot;1&quot;</span><span class="p">,</span> <span class="s2">&quot;o&quot;</span><span class="p">:</span> <span class="s2">&quot;0&quot;</span><span class="p">,</span> <span class="s2">&quot;s&quot;</span><span class="p">:</span> <span class="s2">&quot;5&quot;</span><span class="p">,</span> <span class="s2">&quot;t&quot;</span><span class="p">:</span> <span class="s2">&quot;7&quot;</span><span class="p">}</span>
4277
- <a id="__codelineno-13-7" name="__codelineno-13-7" href="#__codelineno-13-7"></a> <span class="n">output_str</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
4278
- <a id="__codelineno-13-8" name="__codelineno-13-8" href="#__codelineno-13-8"></a> <span class="k">for</span> <span class="n">char</span> <span class="ow">in</span> <span class="n">input_str</span><span class="p">:</span>
4279
- <a id="__codelineno-13-9" name="__codelineno-13-9" href="#__codelineno-13-9"></a> <span class="n">output_str</span> <span class="o">+=</span> <span class="n">charset</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">char</span><span class="o">.</span><span class="n">lower</span><span class="p">(),</span> <span class="n">char</span><span class="p">)</span>
4280
- <a id="__codelineno-13-10" name="__codelineno-13-10" href="#__codelineno-13-10"></a> <span class="k">return</span> <span class="n">output_str</span>
5494
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="kn">from</span> <span class="nn">django_jinja</span> <span class="kn">import</span> <span class="n">library</span>
5495
+ <a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a>
5496
+ <a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a>
5497
+ <a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a><span class="nd">@library</span><span class="o">.</span><span class="n">filter</span>
5498
+ <a id="__codelineno-17-5" name="__codelineno-17-5" href="#__codelineno-17-5"></a><span class="k">def</span> <span class="nf">leet_speak</span><span class="p">(</span><span class="n">input_str</span><span class="p">):</span>
5499
+ <a id="__codelineno-17-6" name="__codelineno-17-6" href="#__codelineno-17-6"></a> <span class="n">charset</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;a&quot;</span><span class="p">:</span> <span class="s2">&quot;4&quot;</span><span class="p">,</span> <span class="s2">&quot;e&quot;</span><span class="p">:</span> <span class="s2">&quot;3&quot;</span><span class="p">,</span> <span class="s2">&quot;l&quot;</span><span class="p">:</span> <span class="s2">&quot;1&quot;</span><span class="p">,</span> <span class="s2">&quot;o&quot;</span><span class="p">:</span> <span class="s2">&quot;0&quot;</span><span class="p">,</span> <span class="s2">&quot;s&quot;</span><span class="p">:</span> <span class="s2">&quot;5&quot;</span><span class="p">,</span> <span class="s2">&quot;t&quot;</span><span class="p">:</span> <span class="s2">&quot;7&quot;</span><span class="p">}</span>
5500
+ <a id="__codelineno-17-7" name="__codelineno-17-7" href="#__codelineno-17-7"></a> <span class="n">output_str</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
5501
+ <a id="__codelineno-17-8" name="__codelineno-17-8" href="#__codelineno-17-8"></a> <span class="k">for</span> <span class="n">char</span> <span class="ow">in</span> <span class="n">input_str</span><span class="p">:</span>
5502
+ <a id="__codelineno-17-9" name="__codelineno-17-9" href="#__codelineno-17-9"></a> <span class="n">output_str</span> <span class="o">+=</span> <span class="n">charset</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">char</span><span class="o">.</span><span class="n">lower</span><span class="p">(),</span> <span class="n">char</span><span class="p">)</span>
5503
+ <a id="__codelineno-17-10" name="__codelineno-17-10" href="#__codelineno-17-10"></a> <span class="k">return</span> <span class="n">output_str</span>
4281
5504
  </code></pre></div>
4282
5505
  <p>This filter will then be available for use in computed field templates like so:</p>
4283
- <div class="highlight"><pre><span></span><code><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a>{{ &quot;HELLO WORLD&quot; | leet_speak }}
5506
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a>{{ &quot;HELLO WORLD&quot; | leet_speak }}
4284
5507
  </code></pre></div>
4285
5508
  <p>The output of this template results in the string <code>"H3110 W0R1D"</code>.</p>
4286
5509
  <h3 id="including-jobs">Including Jobs<a class="headerlink" href="#including-jobs" title="Permanent link">&para;</a></h3>
4287
5510
  <p>Apps can provide <a href="../additional-features/jobs.html">Jobs</a> to take advantage of all the built-in functionality provided by that feature (user input forms, background execution, results logging and reporting, etc.).</p>
4288
5511
  <p>By default, for each app, Nautobot looks for an iterable named <code>jobs</code> within a <code>jobs.py</code> file. (This can be overridden by setting <code>jobs</code> to a custom value on the app's <code>NautobotAppConfig</code>.) A brief example is below; for more details on Job design and implementation, refer to the Jobs feature documentation.</p>
4289
- <div class="highlight"><pre><span></span><code><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="c1"># jobs.py</span>
4290
- <a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="kn">from</span> <span class="nn">nautobot.extras.jobs</span> <span class="kn">import</span> <span class="n">Job</span>
4291
- <a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a>
4292
- <a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a>
4293
- <a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a><span class="k">class</span> <span class="nc">CreateDevices</span><span class="p">(</span><span class="n">Job</span><span class="p">):</span>
4294
- <a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a> <span class="o">...</span>
4295
- <a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a>
4296
- <a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a>
4297
- <a id="__codelineno-15-9" name="__codelineno-15-9" href="#__codelineno-15-9"></a><span class="k">class</span> <span class="nc">DeviceConnectionsReport</span><span class="p">(</span><span class="n">Job</span><span class="p">):</span>
4298
- <a id="__codelineno-15-10" name="__codelineno-15-10" href="#__codelineno-15-10"></a> <span class="o">...</span>
4299
- <a id="__codelineno-15-11" name="__codelineno-15-11" href="#__codelineno-15-11"></a>
4300
- <a id="__codelineno-15-12" name="__codelineno-15-12" href="#__codelineno-15-12"></a>
4301
- <a id="__codelineno-15-13" name="__codelineno-15-13" href="#__codelineno-15-13"></a><span class="k">class</span> <span class="nc">DeviceIPsReport</span><span class="p">(</span><span class="n">Job</span><span class="p">):</span>
4302
- <a id="__codelineno-15-14" name="__codelineno-15-14" href="#__codelineno-15-14"></a> <span class="o">...</span>
4303
- <a id="__codelineno-15-15" name="__codelineno-15-15" href="#__codelineno-15-15"></a>
4304
- <a id="__codelineno-15-16" name="__codelineno-15-16" href="#__codelineno-15-16"></a>
4305
- <a id="__codelineno-15-17" name="__codelineno-15-17" href="#__codelineno-15-17"></a><span class="n">jobs</span> <span class="o">=</span> <span class="p">[</span><span class="n">CreateDevices</span><span class="p">,</span> <span class="n">DeviceConnectionsReport</span><span class="p">,</span> <span class="n">DeviceIPsReport</span><span class="p">]</span>
5512
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a><span class="c1"># jobs.py</span>
5513
+ <a id="__codelineno-19-2" name="__codelineno-19-2" href="#__codelineno-19-2"></a><span class="kn">from</span> <span class="nn">nautobot.extras.jobs</span> <span class="kn">import</span> <span class="n">Job</span>
5514
+ <a id="__codelineno-19-3" name="__codelineno-19-3" href="#__codelineno-19-3"></a>
5515
+ <a id="__codelineno-19-4" name="__codelineno-19-4" href="#__codelineno-19-4"></a>
5516
+ <a id="__codelineno-19-5" name="__codelineno-19-5" href="#__codelineno-19-5"></a><span class="k">class</span> <span class="nc">CreateDevices</span><span class="p">(</span><span class="n">Job</span><span class="p">):</span>
5517
+ <a id="__codelineno-19-6" name="__codelineno-19-6" href="#__codelineno-19-6"></a> <span class="o">...</span>
5518
+ <a id="__codelineno-19-7" name="__codelineno-19-7" href="#__codelineno-19-7"></a>
5519
+ <a id="__codelineno-19-8" name="__codelineno-19-8" href="#__codelineno-19-8"></a>
5520
+ <a id="__codelineno-19-9" name="__codelineno-19-9" href="#__codelineno-19-9"></a><span class="k">class</span> <span class="nc">DeviceConnectionsReport</span><span class="p">(</span><span class="n">Job</span><span class="p">):</span>
5521
+ <a id="__codelineno-19-10" name="__codelineno-19-10" href="#__codelineno-19-10"></a> <span class="o">...</span>
5522
+ <a id="__codelineno-19-11" name="__codelineno-19-11" href="#__codelineno-19-11"></a>
5523
+ <a id="__codelineno-19-12" name="__codelineno-19-12" href="#__codelineno-19-12"></a>
5524
+ <a id="__codelineno-19-13" name="__codelineno-19-13" href="#__codelineno-19-13"></a><span class="k">class</span> <span class="nc">DeviceIPsReport</span><span class="p">(</span><span class="n">Job</span><span class="p">):</span>
5525
+ <a id="__codelineno-19-14" name="__codelineno-19-14" href="#__codelineno-19-14"></a> <span class="o">...</span>
5526
+ <a id="__codelineno-19-15" name="__codelineno-19-15" href="#__codelineno-19-15"></a>
5527
+ <a id="__codelineno-19-16" name="__codelineno-19-16" href="#__codelineno-19-16"></a>
5528
+ <a id="__codelineno-19-17" name="__codelineno-19-17" href="#__codelineno-19-17"></a><span class="n">jobs</span> <span class="o">=</span> <span class="p">[</span><span class="n">CreateDevices</span><span class="p">,</span> <span class="n">DeviceConnectionsReport</span><span class="p">,</span> <span class="n">DeviceIPsReport</span><span class="p">]</span>
4306
5529
  </code></pre></div>
4307
5530
  <h3 id="implementing-custom-validators">Implementing Custom Validators<a class="headerlink" href="#implementing-custom-validators" title="Permanent link">&para;</a></h3>
4308
5531
  <p>Apps can register custom validator classes which implement model validation logic to be executed during a model's <code>clean()</code> method. Like template extensions, custom validators are registered to a single model and offer a method which app authors override to implement their validation logic. This is accomplished by subclassing <code>CustomValidator</code> and implementing the <code>clean()</code> method.</p>
4309
5532
  <p>App authors must raise <code>django.core.exceptions.ValidationError</code> within the <code>clean()</code> method to trigger validation error messages which are propagated to the user and prevent saving of the model instance. A convenience method <code>validation_error()</code> may be used to simplify this process. Raising a <code>ValidationError</code> is no different than vanilla Django, and the convenience method will simply pass the provided message through to the exception.</p>
4310
5533
  <p>When a CustomValidator is instantiated, the model instance is assigned to context dictionary using the <code>object</code> key, much like TemplateExtension. E.g. <code>self.context['object']</code>.</p>
4311
5534
  <p>Declared subclasses should be gathered into a list or tuple for integration with Nautobot. By default, Nautobot looks for an iterable named <code>custom_validators</code> within a <code>custom_validators.py</code> file. (This can be overridden by setting <code>custom_validators</code> to a custom value on the app's <code>NautobotAppConfig</code>.) An example is below.</p>
4312
- <div class="highlight"><pre><span></span><code><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="c1"># custom_validators.py</span>
4313
- <a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a><span class="kn">from</span> <span class="nn">nautobot.apps.models</span> <span class="kn">import</span> <span class="n">CustomValidator</span>
4314
- <a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a>
4315
- <a id="__codelineno-16-4" name="__codelineno-16-4" href="#__codelineno-16-4"></a>
4316
- <a id="__codelineno-16-5" name="__codelineno-16-5" href="#__codelineno-16-5"></a><span class="k">class</span> <span class="nc">SiteValidator</span><span class="p">(</span><span class="n">CustomValidator</span><span class="p">):</span>
4317
- <a id="__codelineno-16-6" name="__codelineno-16-6" href="#__codelineno-16-6"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Custom validator for Sites to enforce that they must have a Region.&quot;&quot;&quot;</span>
4318
- <a id="__codelineno-16-7" name="__codelineno-16-7" href="#__codelineno-16-7"></a>
4319
- <a id="__codelineno-16-8" name="__codelineno-16-8" href="#__codelineno-16-8"></a> <span class="n">model</span> <span class="o">=</span> <span class="s1">&#39;dcim.site&#39;</span>
4320
- <a id="__codelineno-16-9" name="__codelineno-16-9" href="#__codelineno-16-9"></a>
4321
- <a id="__codelineno-16-10" name="__codelineno-16-10" href="#__codelineno-16-10"></a> <span class="k">def</span> <span class="nf">clean</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
4322
- <a id="__codelineno-16-11" name="__codelineno-16-11" href="#__codelineno-16-11"></a> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">context</span><span class="p">[</span><span class="s1">&#39;object&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">region</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
4323
- <a id="__codelineno-16-12" name="__codelineno-16-12" href="#__codelineno-16-12"></a> <span class="c1"># Enforce that all sites must be assigned to a region</span>
4324
- <a id="__codelineno-16-13" name="__codelineno-16-13" href="#__codelineno-16-13"></a> <span class="bp">self</span><span class="o">.</span><span class="n">validation_error</span><span class="p">({</span>
4325
- <a id="__codelineno-16-14" name="__codelineno-16-14" href="#__codelineno-16-14"></a> <span class="s2">&quot;region&quot;</span><span class="p">:</span> <span class="s2">&quot;All sites must be assigned to a region&quot;</span>
4326
- <a id="__codelineno-16-15" name="__codelineno-16-15" href="#__codelineno-16-15"></a> <span class="p">})</span>
4327
- <a id="__codelineno-16-16" name="__codelineno-16-16" href="#__codelineno-16-16"></a>
4328
- <a id="__codelineno-16-17" name="__codelineno-16-17" href="#__codelineno-16-17"></a>
4329
- <a id="__codelineno-16-18" name="__codelineno-16-18" href="#__codelineno-16-18"></a><span class="n">custom_validators</span> <span class="o">=</span> <span class="p">[</span><span class="n">SiteValidator</span><span class="p">]</span>
5535
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a><span class="c1"># custom_validators.py</span>
5536
+ <a id="__codelineno-20-2" name="__codelineno-20-2" href="#__codelineno-20-2"></a><span class="kn">from</span> <span class="nn">nautobot.apps.models</span> <span class="kn">import</span> <span class="n">CustomValidator</span>
5537
+ <a id="__codelineno-20-3" name="__codelineno-20-3" href="#__codelineno-20-3"></a>
5538
+ <a id="__codelineno-20-4" name="__codelineno-20-4" href="#__codelineno-20-4"></a>
5539
+ <a id="__codelineno-20-5" name="__codelineno-20-5" href="#__codelineno-20-5"></a><span class="k">class</span> <span class="nc">SiteValidator</span><span class="p">(</span><span class="n">CustomValidator</span><span class="p">):</span>
5540
+ <a id="__codelineno-20-6" name="__codelineno-20-6" href="#__codelineno-20-6"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Custom validator for Sites to enforce that they must have a Region.&quot;&quot;&quot;</span>
5541
+ <a id="__codelineno-20-7" name="__codelineno-20-7" href="#__codelineno-20-7"></a>
5542
+ <a id="__codelineno-20-8" name="__codelineno-20-8" href="#__codelineno-20-8"></a> <span class="n">model</span> <span class="o">=</span> <span class="s1">&#39;dcim.site&#39;</span>
5543
+ <a id="__codelineno-20-9" name="__codelineno-20-9" href="#__codelineno-20-9"></a>
5544
+ <a id="__codelineno-20-10" name="__codelineno-20-10" href="#__codelineno-20-10"></a> <span class="k">def</span> <span class="nf">clean</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
5545
+ <a id="__codelineno-20-11" name="__codelineno-20-11" href="#__codelineno-20-11"></a> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">context</span><span class="p">[</span><span class="s1">&#39;object&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">region</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
5546
+ <a id="__codelineno-20-12" name="__codelineno-20-12" href="#__codelineno-20-12"></a> <span class="c1"># Enforce that all sites must be assigned to a region</span>
5547
+ <a id="__codelineno-20-13" name="__codelineno-20-13" href="#__codelineno-20-13"></a> <span class="bp">self</span><span class="o">.</span><span class="n">validation_error</span><span class="p">({</span>
5548
+ <a id="__codelineno-20-14" name="__codelineno-20-14" href="#__codelineno-20-14"></a> <span class="s2">&quot;region&quot;</span><span class="p">:</span> <span class="s2">&quot;All sites must be assigned to a region&quot;</span>
5549
+ <a id="__codelineno-20-15" name="__codelineno-20-15" href="#__codelineno-20-15"></a> <span class="p">})</span>
5550
+ <a id="__codelineno-20-16" name="__codelineno-20-16" href="#__codelineno-20-16"></a>
5551
+ <a id="__codelineno-20-17" name="__codelineno-20-17" href="#__codelineno-20-17"></a>
5552
+ <a id="__codelineno-20-18" name="__codelineno-20-18" href="#__codelineno-20-18"></a><span class="n">custom_validators</span> <span class="o">=</span> <span class="p">[</span><span class="n">SiteValidator</span><span class="p">]</span>
4330
5553
  </code></pre></div>
4331
5554
  <h3 id="loading-data-from-a-git-repository">Loading Data from a Git Repository<a class="headerlink" href="#loading-data-from-a-git-repository" title="Permanent link">&para;</a></h3>
4332
5555
  <p>It's possible for an app to register additional types of data that can be provided by a <a href="../models/extras/gitrepository.html">Git repository</a> and be automatically notified when such a repository is refreshed with new data. By default, Nautobot looks for an iterable named <code>datasource_contents</code> within a <code>datasources.py</code> file. (This can be overridden by setting <code>datasource_contents</code> to a custom value on the app's <code>NautobotAppConfig</code>.) An example is below.</p>
4333
- <div class="highlight"><pre><span></span><code><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="c1"># datasources.py</span>
4334
- <a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a><span class="kn">import</span> <span class="nn">yaml</span>
4335
- <a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a><span class="kn">import</span> <span class="nn">os</span>
4336
- <a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a>
4337
- <a id="__codelineno-17-5" name="__codelineno-17-5" href="#__codelineno-17-5"></a><span class="kn">from</span> <span class="nn">nautobot.extras.choices</span> <span class="kn">import</span> <span class="n">LogLevelChoices</span>
4338
- <a id="__codelineno-17-6" name="__codelineno-17-6" href="#__codelineno-17-6"></a><span class="kn">from</span> <span class="nn">nautobot.apps.datasources</span> <span class="kn">import</span> <span class="n">DatasourceContent</span>
4339
- <a id="__codelineno-17-7" name="__codelineno-17-7" href="#__codelineno-17-7"></a>
4340
- <a id="__codelineno-17-8" name="__codelineno-17-8" href="#__codelineno-17-8"></a><span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Animal</span>
4341
- <a id="__codelineno-17-9" name="__codelineno-17-9" href="#__codelineno-17-9"></a>
4342
- <a id="__codelineno-17-10" name="__codelineno-17-10" href="#__codelineno-17-10"></a>
4343
- <a id="__codelineno-17-11" name="__codelineno-17-11" href="#__codelineno-17-11"></a><span class="k">def</span> <span class="nf">refresh_git_animals</span><span class="p">(</span><span class="n">repository_record</span><span class="p">,</span> <span class="n">job_result</span><span class="p">,</span> <span class="n">delete</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
4344
- <a id="__codelineno-17-12" name="__codelineno-17-12" href="#__codelineno-17-12"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Callback for GitRepository updates - refresh Animals managed by it.&quot;&quot;&quot;</span>
4345
- <a id="__codelineno-17-13" name="__codelineno-17-13" href="#__codelineno-17-13"></a> <span class="k">if</span> <span class="s1">&#39;nautobot_animal_sounds.Animal&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">repository_record</span><span class="o">.</span><span class="n">provided_contents</span> <span class="ow">or</span> <span class="n">delete</span><span class="p">:</span>
4346
- <a id="__codelineno-17-14" name="__codelineno-17-14" href="#__codelineno-17-14"></a> <span class="c1"># This repository is defined not to provide Animal records.</span>
4347
- <a id="__codelineno-17-15" name="__codelineno-17-15" href="#__codelineno-17-15"></a> <span class="c1"># In a more complete worked example, we might want to iterate over any</span>
4348
- <a id="__codelineno-17-16" name="__codelineno-17-16" href="#__codelineno-17-16"></a> <span class="c1"># Animals that might have been previously created by this GitRepository</span>
4349
- <a id="__codelineno-17-17" name="__codelineno-17-17" href="#__codelineno-17-17"></a> <span class="c1"># and ensure their deletion, but for now this is a no-op.</span>
4350
- <a id="__codelineno-17-18" name="__codelineno-17-18" href="#__codelineno-17-18"></a> <span class="k">return</span>
4351
- <a id="__codelineno-17-19" name="__codelineno-17-19" href="#__codelineno-17-19"></a>
4352
- <a id="__codelineno-17-20" name="__codelineno-17-20" href="#__codelineno-17-20"></a> <span class="c1"># We have decided that a Git repository can provide YAML files in a</span>
4353
- <a id="__codelineno-17-21" name="__codelineno-17-21" href="#__codelineno-17-21"></a> <span class="c1"># /animals/ directory at the repository root.</span>
4354
- <a id="__codelineno-17-22" name="__codelineno-17-22" href="#__codelineno-17-22"></a> <span class="n">animal_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">repository_record</span><span class="o">.</span><span class="n">filesystem_path</span><span class="p">,</span> <span class="s1">&#39;animals&#39;</span><span class="p">)</span>
4355
- <a id="__codelineno-17-23" name="__codelineno-17-23" href="#__codelineno-17-23"></a> <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">animal_path</span><span class="p">):</span>
4356
- <a id="__codelineno-17-24" name="__codelineno-17-24" href="#__codelineno-17-24"></a> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">animal_path</span><span class="p">,</span> <span class="n">filename</span><span class="p">))</span> <span class="k">as</span> <span class="n">fd</span><span class="p">:</span>
4357
- <a id="__codelineno-17-25" name="__codelineno-17-25" href="#__codelineno-17-25"></a> <span class="n">animal_data</span> <span class="o">=</span> <span class="n">yaml</span><span class="o">.</span><span class="n">safe_load</span><span class="p">(</span><span class="n">fd</span><span class="p">)</span>
4358
- <a id="__codelineno-17-26" name="__codelineno-17-26" href="#__codelineno-17-26"></a>
4359
- <a id="__codelineno-17-27" name="__codelineno-17-27" href="#__codelineno-17-27"></a> <span class="c1"># Create or update an Animal record based on the provided data</span>
4360
- <a id="__codelineno-17-28" name="__codelineno-17-28" href="#__codelineno-17-28"></a> <span class="n">animal_record</span><span class="p">,</span> <span class="n">created</span> <span class="o">=</span> <span class="n">Animal</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">update_or_create</span><span class="p">(</span>
4361
- <a id="__codelineno-17-29" name="__codelineno-17-29" href="#__codelineno-17-29"></a> <span class="n">name</span><span class="o">=</span><span class="n">animal_data</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">],</span>
4362
- <a id="__codelineno-17-30" name="__codelineno-17-30" href="#__codelineno-17-30"></a> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;sound&#39;</span><span class="p">:</span> <span class="n">animal_data</span><span class="p">[</span><span class="s1">&#39;sound&#39;</span><span class="p">]}</span>
4363
- <a id="__codelineno-17-31" name="__codelineno-17-31" href="#__codelineno-17-31"></a> <span class="p">)</span>
4364
- <a id="__codelineno-17-32" name="__codelineno-17-32" href="#__codelineno-17-32"></a>
4365
- <a id="__codelineno-17-33" name="__codelineno-17-33" href="#__codelineno-17-33"></a> <span class="c1"># Record the outcome in the JobResult record</span>
4366
- <a id="__codelineno-17-34" name="__codelineno-17-34" href="#__codelineno-17-34"></a> <span class="n">job_result</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
4367
- <a id="__codelineno-17-35" name="__codelineno-17-35" href="#__codelineno-17-35"></a> <span class="s2">&quot;Successfully created/updated animal&quot;</span><span class="p">,</span>
4368
- <a id="__codelineno-17-36" name="__codelineno-17-36" href="#__codelineno-17-36"></a> <span class="n">obj</span><span class="o">=</span><span class="n">animal_record</span><span class="p">,</span>
4369
- <a id="__codelineno-17-37" name="__codelineno-17-37" href="#__codelineno-17-37"></a> <span class="n">level_choice</span><span class="o">=</span><span class="n">LogLevelChoices</span><span class="o">.</span><span class="n">LOG_SUCCESS</span><span class="p">,</span>
4370
- <a id="__codelineno-17-38" name="__codelineno-17-38" href="#__codelineno-17-38"></a> <span class="n">grouping</span><span class="o">=</span><span class="s2">&quot;animals&quot;</span><span class="p">,</span>
4371
- <a id="__codelineno-17-39" name="__codelineno-17-39" href="#__codelineno-17-39"></a> <span class="p">)</span>
4372
- <a id="__codelineno-17-40" name="__codelineno-17-40" href="#__codelineno-17-40"></a>
4373
- <a id="__codelineno-17-41" name="__codelineno-17-41" href="#__codelineno-17-41"></a>
4374
- <a id="__codelineno-17-42" name="__codelineno-17-42" href="#__codelineno-17-42"></a><span class="c1"># Register that Animal records can be loaded from a Git repository,</span>
4375
- <a id="__codelineno-17-43" name="__codelineno-17-43" href="#__codelineno-17-43"></a><span class="c1"># and register the callback function used to do so</span>
4376
- <a id="__codelineno-17-44" name="__codelineno-17-44" href="#__codelineno-17-44"></a><span class="n">datasource_contents</span> <span class="o">=</span> <span class="p">[</span>
4377
- <a id="__codelineno-17-45" name="__codelineno-17-45" href="#__codelineno-17-45"></a> <span class="p">(</span>
4378
- <a id="__codelineno-17-46" name="__codelineno-17-46" href="#__codelineno-17-46"></a> <span class="s1">&#39;extras.gitrepository&#39;</span><span class="p">,</span> <span class="c1"># datasource class we are registering for</span>
4379
- <a id="__codelineno-17-47" name="__codelineno-17-47" href="#__codelineno-17-47"></a> <span class="n">DatasourceContent</span><span class="p">(</span>
4380
- <a id="__codelineno-17-48" name="__codelineno-17-48" href="#__codelineno-17-48"></a> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;animals&#39;</span><span class="p">,</span> <span class="c1"># human-readable name to display in the UI</span>
4381
- <a id="__codelineno-17-49" name="__codelineno-17-49" href="#__codelineno-17-49"></a> <span class="n">content_identifier</span><span class="o">=</span><span class="s1">&#39;nautobot_animal_sounds.animal&#39;</span><span class="p">,</span> <span class="c1"># internal slug to identify the data type</span>
4382
- <a id="__codelineno-17-50" name="__codelineno-17-50" href="#__codelineno-17-50"></a> <span class="n">icon</span><span class="o">=</span><span class="s1">&#39;mdi-paw&#39;</span><span class="p">,</span> <span class="c1"># Material Design Icons icon to use in UI</span>
4383
- <a id="__codelineno-17-51" name="__codelineno-17-51" href="#__codelineno-17-51"></a> <span class="n">callback</span><span class="o">=</span><span class="n">refresh_git_animals</span><span class="p">,</span> <span class="c1"># callback function on GitRepository refresh</span>
4384
- <a id="__codelineno-17-52" name="__codelineno-17-52" href="#__codelineno-17-52"></a> <span class="p">)</span>
4385
- <a id="__codelineno-17-53" name="__codelineno-17-53" href="#__codelineno-17-53"></a> <span class="p">)</span>
4386
- <a id="__codelineno-17-54" name="__codelineno-17-54" href="#__codelineno-17-54"></a><span class="p">]</span>
5556
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a><span class="c1"># datasources.py</span>
5557
+ <a id="__codelineno-21-2" name="__codelineno-21-2" href="#__codelineno-21-2"></a><span class="kn">import</span> <span class="nn">yaml</span>
5558
+ <a id="__codelineno-21-3" name="__codelineno-21-3" href="#__codelineno-21-3"></a><span class="kn">import</span> <span class="nn">os</span>
5559
+ <a id="__codelineno-21-4" name="__codelineno-21-4" href="#__codelineno-21-4"></a>
5560
+ <a id="__codelineno-21-5" name="__codelineno-21-5" href="#__codelineno-21-5"></a><span class="kn">from</span> <span class="nn">nautobot.extras.choices</span> <span class="kn">import</span> <span class="n">LogLevelChoices</span>
5561
+ <a id="__codelineno-21-6" name="__codelineno-21-6" href="#__codelineno-21-6"></a><span class="kn">from</span> <span class="nn">nautobot.apps.datasources</span> <span class="kn">import</span> <span class="n">DatasourceContent</span>
5562
+ <a id="__codelineno-21-7" name="__codelineno-21-7" href="#__codelineno-21-7"></a>
5563
+ <a id="__codelineno-21-8" name="__codelineno-21-8" href="#__codelineno-21-8"></a><span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Animal</span>
5564
+ <a id="__codelineno-21-9" name="__codelineno-21-9" href="#__codelineno-21-9"></a>
5565
+ <a id="__codelineno-21-10" name="__codelineno-21-10" href="#__codelineno-21-10"></a>
5566
+ <a id="__codelineno-21-11" name="__codelineno-21-11" href="#__codelineno-21-11"></a><span class="k">def</span> <span class="nf">refresh_git_animals</span><span class="p">(</span><span class="n">repository_record</span><span class="p">,</span> <span class="n">job_result</span><span class="p">,</span> <span class="n">delete</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
5567
+ <a id="__codelineno-21-12" name="__codelineno-21-12" href="#__codelineno-21-12"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Callback for GitRepository updates - refresh Animals managed by it.&quot;&quot;&quot;</span>
5568
+ <a id="__codelineno-21-13" name="__codelineno-21-13" href="#__codelineno-21-13"></a> <span class="k">if</span> <span class="s1">&#39;nautobot_animal_sounds.Animal&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">repository_record</span><span class="o">.</span><span class="n">provided_contents</span> <span class="ow">or</span> <span class="n">delete</span><span class="p">:</span>
5569
+ <a id="__codelineno-21-14" name="__codelineno-21-14" href="#__codelineno-21-14"></a> <span class="c1"># This repository is defined not to provide Animal records.</span>
5570
+ <a id="__codelineno-21-15" name="__codelineno-21-15" href="#__codelineno-21-15"></a> <span class="c1"># In a more complete worked example, we might want to iterate over any</span>
5571
+ <a id="__codelineno-21-16" name="__codelineno-21-16" href="#__codelineno-21-16"></a> <span class="c1"># Animals that might have been previously created by this GitRepository</span>
5572
+ <a id="__codelineno-21-17" name="__codelineno-21-17" href="#__codelineno-21-17"></a> <span class="c1"># and ensure their deletion, but for now this is a no-op.</span>
5573
+ <a id="__codelineno-21-18" name="__codelineno-21-18" href="#__codelineno-21-18"></a> <span class="k">return</span>
5574
+ <a id="__codelineno-21-19" name="__codelineno-21-19" href="#__codelineno-21-19"></a>
5575
+ <a id="__codelineno-21-20" name="__codelineno-21-20" href="#__codelineno-21-20"></a> <span class="c1"># We have decided that a Git repository can provide YAML files in a</span>
5576
+ <a id="__codelineno-21-21" name="__codelineno-21-21" href="#__codelineno-21-21"></a> <span class="c1"># /animals/ directory at the repository root.</span>
5577
+ <a id="__codelineno-21-22" name="__codelineno-21-22" href="#__codelineno-21-22"></a> <span class="n">animal_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">repository_record</span><span class="o">.</span><span class="n">filesystem_path</span><span class="p">,</span> <span class="s1">&#39;animals&#39;</span><span class="p">)</span>
5578
+ <a id="__codelineno-21-23" name="__codelineno-21-23" href="#__codelineno-21-23"></a> <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">animal_path</span><span class="p">):</span>
5579
+ <a id="__codelineno-21-24" name="__codelineno-21-24" href="#__codelineno-21-24"></a> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">animal_path</span><span class="p">,</span> <span class="n">filename</span><span class="p">))</span> <span class="k">as</span> <span class="n">fd</span><span class="p">:</span>
5580
+ <a id="__codelineno-21-25" name="__codelineno-21-25" href="#__codelineno-21-25"></a> <span class="n">animal_data</span> <span class="o">=</span> <span class="n">yaml</span><span class="o">.</span><span class="n">safe_load</span><span class="p">(</span><span class="n">fd</span><span class="p">)</span>
5581
+ <a id="__codelineno-21-26" name="__codelineno-21-26" href="#__codelineno-21-26"></a>
5582
+ <a id="__codelineno-21-27" name="__codelineno-21-27" href="#__codelineno-21-27"></a> <span class="c1"># Create or update an Animal record based on the provided data</span>
5583
+ <a id="__codelineno-21-28" name="__codelineno-21-28" href="#__codelineno-21-28"></a> <span class="n">animal_record</span><span class="p">,</span> <span class="n">created</span> <span class="o">=</span> <span class="n">Animal</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">update_or_create</span><span class="p">(</span>
5584
+ <a id="__codelineno-21-29" name="__codelineno-21-29" href="#__codelineno-21-29"></a> <span class="n">name</span><span class="o">=</span><span class="n">animal_data</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">],</span>
5585
+ <a id="__codelineno-21-30" name="__codelineno-21-30" href="#__codelineno-21-30"></a> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;sound&#39;</span><span class="p">:</span> <span class="n">animal_data</span><span class="p">[</span><span class="s1">&#39;sound&#39;</span><span class="p">]}</span>
5586
+ <a id="__codelineno-21-31" name="__codelineno-21-31" href="#__codelineno-21-31"></a> <span class="p">)</span>
5587
+ <a id="__codelineno-21-32" name="__codelineno-21-32" href="#__codelineno-21-32"></a>
5588
+ <a id="__codelineno-21-33" name="__codelineno-21-33" href="#__codelineno-21-33"></a> <span class="c1"># Record the outcome in the JobResult record</span>
5589
+ <a id="__codelineno-21-34" name="__codelineno-21-34" href="#__codelineno-21-34"></a> <span class="n">job_result</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
5590
+ <a id="__codelineno-21-35" name="__codelineno-21-35" href="#__codelineno-21-35"></a> <span class="s2">&quot;Successfully created/updated animal&quot;</span><span class="p">,</span>
5591
+ <a id="__codelineno-21-36" name="__codelineno-21-36" href="#__codelineno-21-36"></a> <span class="n">obj</span><span class="o">=</span><span class="n">animal_record</span><span class="p">,</span>
5592
+ <a id="__codelineno-21-37" name="__codelineno-21-37" href="#__codelineno-21-37"></a> <span class="n">level_choice</span><span class="o">=</span><span class="n">LogLevelChoices</span><span class="o">.</span><span class="n">LOG_INFO</span><span class="p">,</span>
5593
+ <a id="__codelineno-21-38" name="__codelineno-21-38" href="#__codelineno-21-38"></a> <span class="n">grouping</span><span class="o">=</span><span class="s2">&quot;animals&quot;</span><span class="p">,</span>
5594
+ <a id="__codelineno-21-39" name="__codelineno-21-39" href="#__codelineno-21-39"></a> <span class="p">)</span>
5595
+ <a id="__codelineno-21-40" name="__codelineno-21-40" href="#__codelineno-21-40"></a>
5596
+ <a id="__codelineno-21-41" name="__codelineno-21-41" href="#__codelineno-21-41"></a>
5597
+ <a id="__codelineno-21-42" name="__codelineno-21-42" href="#__codelineno-21-42"></a><span class="c1"># Register that Animal records can be loaded from a Git repository,</span>
5598
+ <a id="__codelineno-21-43" name="__codelineno-21-43" href="#__codelineno-21-43"></a><span class="c1"># and register the callback function used to do so</span>
5599
+ <a id="__codelineno-21-44" name="__codelineno-21-44" href="#__codelineno-21-44"></a><span class="n">datasource_contents</span> <span class="o">=</span> <span class="p">[</span>
5600
+ <a id="__codelineno-21-45" name="__codelineno-21-45" href="#__codelineno-21-45"></a> <span class="p">(</span>
5601
+ <a id="__codelineno-21-46" name="__codelineno-21-46" href="#__codelineno-21-46"></a> <span class="s1">&#39;extras.gitrepository&#39;</span><span class="p">,</span> <span class="c1"># datasource class we are registering for</span>
5602
+ <a id="__codelineno-21-47" name="__codelineno-21-47" href="#__codelineno-21-47"></a> <span class="n">DatasourceContent</span><span class="p">(</span>
5603
+ <a id="__codelineno-21-48" name="__codelineno-21-48" href="#__codelineno-21-48"></a> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;animals&#39;</span><span class="p">,</span> <span class="c1"># human-readable name to display in the UI</span>
5604
+ <a id="__codelineno-21-49" name="__codelineno-21-49" href="#__codelineno-21-49"></a> <span class="n">content_identifier</span><span class="o">=</span><span class="s1">&#39;nautobot_animal_sounds.animal&#39;</span><span class="p">,</span> <span class="c1"># internal slug to identify the data type</span>
5605
+ <a id="__codelineno-21-50" name="__codelineno-21-50" href="#__codelineno-21-50"></a> <span class="n">icon</span><span class="o">=</span><span class="s1">&#39;mdi-paw&#39;</span><span class="p">,</span> <span class="c1"># Material Design Icons icon to use in UI</span>
5606
+ <a id="__codelineno-21-51" name="__codelineno-21-51" href="#__codelineno-21-51"></a> <span class="n">callback</span><span class="o">=</span><span class="n">refresh_git_animals</span><span class="p">,</span> <span class="c1"># callback function on GitRepository refresh</span>
5607
+ <a id="__codelineno-21-52" name="__codelineno-21-52" href="#__codelineno-21-52"></a> <span class="p">)</span>
5608
+ <a id="__codelineno-21-53" name="__codelineno-21-53" href="#__codelineno-21-53"></a> <span class="p">)</span>
5609
+ <a id="__codelineno-21-54" name="__codelineno-21-54" href="#__codelineno-21-54"></a><span class="p">]</span>
4387
5610
  </code></pre></div>
4388
5611
  <p>With this code, once your app is installed, the Git repository creation/editing UI will now include "Animals" as an option for the type(s) of data that a given repository may provide. If this option is selected for a given Git repository, your <code>refresh_git_animals</code> function will be automatically called when the repository is synced.</p>
4389
5612
  <h3 id="populating-extensibility-features">Populating Extensibility Features<a class="headerlink" href="#populating-extensibility-features" title="Permanent link">&para;</a></h3>
@@ -4393,47 +5616,47 @@ namedtuple to assist in the configurations.</p>
4393
5616
  <p>In many cases, an app may wish to make use of Nautobot's various extensibility features, such as <a href="../models/extras/customfield.html">custom fields</a> or <a href="../models/extras/relationship.html">relationships</a>. It can be useful for an app to automatically create a custom field definition or relationship definition as a consequence of being installed and activated, so that everyday usage of the app can rely upon these definitions to be present.</p>
4394
5617
  <p>To make this possible, Nautobot provides a custom <a href="https://docs.djangoproject.com/en/stable/topics/signals/">signal</a>, <code>nautobot_database_ready</code>, that apps can register to listen for. This signal is triggered when <code>nautobot-server migrate</code> or <code>nautobot-server post_upgrade</code> is run after installing an app, and provides an opportunity for the app to make any desired additions to the database at this time.</p>
4395
5618
  <p>For example, maybe we want our app to make use of a Relationship allowing each Site to be linked to our Animal model. We would define our callback function that makes sure this Relationship exists, by convention in a <code>signals.py</code> file:</p>
4396
- <div class="highlight"><pre><span></span><code><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a><span class="c1"># signals.py</span>
4397
- <a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a>
4398
- <a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a><span class="kn">from</span> <span class="nn">nautobot.extras.choices</span> <span class="kn">import</span> <span class="n">RelationshipTypeChoices</span>
4399
- <a id="__codelineno-18-4" name="__codelineno-18-4" href="#__codelineno-18-4"></a>
4400
- <a id="__codelineno-18-5" name="__codelineno-18-5" href="#__codelineno-18-5"></a><span class="k">def</span> <span class="nf">create_site_to_animal_relationship</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">apps</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
4401
- <a id="__codelineno-18-6" name="__codelineno-18-6" href="#__codelineno-18-6"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Create a Site-to-Animal Relationship if it doesn&#39;t already exist.&quot;&quot;&quot;</span>
4402
- <a id="__codelineno-18-7" name="__codelineno-18-7" href="#__codelineno-18-7"></a> <span class="c1"># Use apps.get_model to look up Nautobot core models</span>
4403
- <a id="__codelineno-18-8" name="__codelineno-18-8" href="#__codelineno-18-8"></a> <span class="n">ContentType</span> <span class="o">=</span> <span class="n">apps</span><span class="o">.</span><span class="n">get_model</span><span class="p">(</span><span class="s2">&quot;contenttypes&quot;</span><span class="p">,</span> <span class="s2">&quot;ContentType&quot;</span><span class="p">)</span>
4404
- <a id="__codelineno-18-9" name="__codelineno-18-9" href="#__codelineno-18-9"></a> <span class="n">Relationship</span> <span class="o">=</span> <span class="n">apps</span><span class="o">.</span><span class="n">get_model</span><span class="p">(</span><span class="s2">&quot;extras&quot;</span><span class="p">,</span> <span class="s2">&quot;Relationship&quot;</span><span class="p">)</span>
4405
- <a id="__codelineno-18-10" name="__codelineno-18-10" href="#__codelineno-18-10"></a> <span class="n">Site</span> <span class="o">=</span> <span class="n">apps</span><span class="o">.</span><span class="n">get_model</span><span class="p">(</span><span class="s2">&quot;dcim&quot;</span><span class="p">,</span> <span class="s2">&quot;Site&quot;</span><span class="p">)</span>
4406
- <a id="__codelineno-18-11" name="__codelineno-18-11" href="#__codelineno-18-11"></a> <span class="c1"># Use sender.get_model to look up models from this app</span>
4407
- <a id="__codelineno-18-12" name="__codelineno-18-12" href="#__codelineno-18-12"></a> <span class="n">Animal</span> <span class="o">=</span> <span class="n">sender</span><span class="o">.</span><span class="n">get_model</span><span class="p">(</span><span class="s2">&quot;Animal&quot;</span><span class="p">)</span>
4408
- <a id="__codelineno-18-13" name="__codelineno-18-13" href="#__codelineno-18-13"></a>
4409
- <a id="__codelineno-18-14" name="__codelineno-18-14" href="#__codelineno-18-14"></a> <span class="c1"># Ensure that the Relationship exists</span>
4410
- <a id="__codelineno-18-15" name="__codelineno-18-15" href="#__codelineno-18-15"></a> <span class="n">Relationship</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">update_or_create</span><span class="p">(</span>
4411
- <a id="__codelineno-18-16" name="__codelineno-18-16" href="#__codelineno-18-16"></a> <span class="n">slug</span><span class="o">=</span><span class="s2">&quot;site-favorite-animal&quot;</span><span class="p">,</span>
4412
- <a id="__codelineno-18-17" name="__codelineno-18-17" href="#__codelineno-18-17"></a> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span>
4413
- <a id="__codelineno-18-18" name="__codelineno-18-18" href="#__codelineno-18-18"></a> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Site&#39;s Favorite Animal&quot;</span><span class="p">,</span>
4414
- <a id="__codelineno-18-19" name="__codelineno-18-19" href="#__codelineno-18-19"></a> <span class="s2">&quot;type&quot;</span><span class="p">:</span> <span class="n">RelationshipTypeChoices</span><span class="o">.</span><span class="n">TYPE_ONE_TO_MANY</span><span class="p">,</span>
4415
- <a id="__codelineno-18-20" name="__codelineno-18-20" href="#__codelineno-18-20"></a> <span class="s2">&quot;source_type&quot;</span><span class="p">:</span> <span class="n">ContentType</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get_for_model</span><span class="p">(</span><span class="n">Animal</span><span class="p">),</span>
4416
- <a id="__codelineno-18-21" name="__codelineno-18-21" href="#__codelineno-18-21"></a> <span class="s2">&quot;source_label&quot;</span><span class="p">:</span> <span class="s2">&quot;Sites that love this Animal&quot;</span><span class="p">,</span>
4417
- <a id="__codelineno-18-22" name="__codelineno-18-22" href="#__codelineno-18-22"></a> <span class="s2">&quot;destination_type&quot;</span><span class="p">:</span> <span class="n">ContentType</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get_for_model</span><span class="p">(</span><span class="n">Site</span><span class="p">),</span>
4418
- <a id="__codelineno-18-23" name="__codelineno-18-23" href="#__codelineno-18-23"></a> <span class="s2">&quot;destination_label&quot;</span><span class="p">:</span> <span class="s2">&quot;Favorite Animal&quot;</span><span class="p">,</span>
4419
- <a id="__codelineno-18-24" name="__codelineno-18-24" href="#__codelineno-18-24"></a> <span class="p">},</span>
4420
- <a id="__codelineno-18-25" name="__codelineno-18-25" href="#__codelineno-18-25"></a> <span class="p">)</span>
5619
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a><span class="c1"># signals.py</span>
5620
+ <a id="__codelineno-22-2" name="__codelineno-22-2" href="#__codelineno-22-2"></a>
5621
+ <a id="__codelineno-22-3" name="__codelineno-22-3" href="#__codelineno-22-3"></a><span class="kn">from</span> <span class="nn">nautobot.extras.choices</span> <span class="kn">import</span> <span class="n">RelationshipTypeChoices</span>
5622
+ <a id="__codelineno-22-4" name="__codelineno-22-4" href="#__codelineno-22-4"></a>
5623
+ <a id="__codelineno-22-5" name="__codelineno-22-5" href="#__codelineno-22-5"></a><span class="k">def</span> <span class="nf">create_site_to_animal_relationship</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">apps</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
5624
+ <a id="__codelineno-22-6" name="__codelineno-22-6" href="#__codelineno-22-6"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Create a Site-to-Animal Relationship if it doesn&#39;t already exist.&quot;&quot;&quot;</span>
5625
+ <a id="__codelineno-22-7" name="__codelineno-22-7" href="#__codelineno-22-7"></a> <span class="c1"># Use apps.get_model to look up Nautobot core models</span>
5626
+ <a id="__codelineno-22-8" name="__codelineno-22-8" href="#__codelineno-22-8"></a> <span class="n">ContentType</span> <span class="o">=</span> <span class="n">apps</span><span class="o">.</span><span class="n">get_model</span><span class="p">(</span><span class="s2">&quot;contenttypes&quot;</span><span class="p">,</span> <span class="s2">&quot;ContentType&quot;</span><span class="p">)</span>
5627
+ <a id="__codelineno-22-9" name="__codelineno-22-9" href="#__codelineno-22-9"></a> <span class="n">Relationship</span> <span class="o">=</span> <span class="n">apps</span><span class="o">.</span><span class="n">get_model</span><span class="p">(</span><span class="s2">&quot;extras&quot;</span><span class="p">,</span> <span class="s2">&quot;Relationship&quot;</span><span class="p">)</span>
5628
+ <a id="__codelineno-22-10" name="__codelineno-22-10" href="#__codelineno-22-10"></a> <span class="n">Site</span> <span class="o">=</span> <span class="n">apps</span><span class="o">.</span><span class="n">get_model</span><span class="p">(</span><span class="s2">&quot;dcim&quot;</span><span class="p">,</span> <span class="s2">&quot;Site&quot;</span><span class="p">)</span>
5629
+ <a id="__codelineno-22-11" name="__codelineno-22-11" href="#__codelineno-22-11"></a> <span class="c1"># Use sender.get_model to look up models from this app</span>
5630
+ <a id="__codelineno-22-12" name="__codelineno-22-12" href="#__codelineno-22-12"></a> <span class="n">Animal</span> <span class="o">=</span> <span class="n">sender</span><span class="o">.</span><span class="n">get_model</span><span class="p">(</span><span class="s2">&quot;Animal&quot;</span><span class="p">)</span>
5631
+ <a id="__codelineno-22-13" name="__codelineno-22-13" href="#__codelineno-22-13"></a>
5632
+ <a id="__codelineno-22-14" name="__codelineno-22-14" href="#__codelineno-22-14"></a> <span class="c1"># Ensure that the Relationship exists</span>
5633
+ <a id="__codelineno-22-15" name="__codelineno-22-15" href="#__codelineno-22-15"></a> <span class="n">Relationship</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">update_or_create</span><span class="p">(</span>
5634
+ <a id="__codelineno-22-16" name="__codelineno-22-16" href="#__codelineno-22-16"></a> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;site_favorite_animal&quot;</span><span class="p">,</span>
5635
+ <a id="__codelineno-22-17" name="__codelineno-22-17" href="#__codelineno-22-17"></a> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span>
5636
+ <a id="__codelineno-22-18" name="__codelineno-22-18" href="#__codelineno-22-18"></a> <span class="s2">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Site&#39;s Favorite Animal&quot;</span><span class="p">,</span>
5637
+ <a id="__codelineno-22-19" name="__codelineno-22-19" href="#__codelineno-22-19"></a> <span class="s2">&quot;type&quot;</span><span class="p">:</span> <span class="n">RelationshipTypeChoices</span><span class="o">.</span><span class="n">TYPE_ONE_TO_MANY</span><span class="p">,</span>
5638
+ <a id="__codelineno-22-20" name="__codelineno-22-20" href="#__codelineno-22-20"></a> <span class="s2">&quot;source_type&quot;</span><span class="p">:</span> <span class="n">ContentType</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get_for_model</span><span class="p">(</span><span class="n">Animal</span><span class="p">),</span>
5639
+ <a id="__codelineno-22-21" name="__codelineno-22-21" href="#__codelineno-22-21"></a> <span class="s2">&quot;source_label&quot;</span><span class="p">:</span> <span class="s2">&quot;Sites that love this Animal&quot;</span><span class="p">,</span>
5640
+ <a id="__codelineno-22-22" name="__codelineno-22-22" href="#__codelineno-22-22"></a> <span class="s2">&quot;destination_type&quot;</span><span class="p">:</span> <span class="n">ContentType</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get_for_model</span><span class="p">(</span><span class="n">Site</span><span class="p">),</span>
5641
+ <a id="__codelineno-22-23" name="__codelineno-22-23" href="#__codelineno-22-23"></a> <span class="s2">&quot;destination_label&quot;</span><span class="p">:</span> <span class="s2">&quot;Favorite Animal&quot;</span><span class="p">,</span>
5642
+ <a id="__codelineno-22-24" name="__codelineno-22-24" href="#__codelineno-22-24"></a> <span class="p">},</span>
5643
+ <a id="__codelineno-22-25" name="__codelineno-22-25" href="#__codelineno-22-25"></a> <span class="p">)</span>
4421
5644
  </code></pre></div>
4422
5645
  <p>Then, in the <code>NautobotAppConfig</code> <code>ready()</code> function, we connect this callback function to the <code>nautobot_database_ready</code> signal:</p>
4423
- <div class="highlight"><pre><span></span><code><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a><span class="c1"># __init__.py</span>
4424
- <a id="__codelineno-19-2" name="__codelineno-19-2" href="#__codelineno-19-2"></a>
4425
- <a id="__codelineno-19-3" name="__codelineno-19-3" href="#__codelineno-19-3"></a><span class="kn">from</span> <span class="nn">nautobot.apps</span> <span class="kn">import</span> <span class="n">nautobot_database_ready</span><span class="p">,</span> <span class="n">NautobotAppConfig</span>
4426
- <a id="__codelineno-19-4" name="__codelineno-19-4" href="#__codelineno-19-4"></a>
4427
- <a id="__codelineno-19-5" name="__codelineno-19-5" href="#__codelineno-19-5"></a><span class="kn">from</span> <span class="nn">.signals</span> <span class="kn">import</span> <span class="n">create_site_to_animal_relationship</span>
4428
- <a id="__codelineno-19-6" name="__codelineno-19-6" href="#__codelineno-19-6"></a>
4429
- <a id="__codelineno-19-7" name="__codelineno-19-7" href="#__codelineno-19-7"></a><span class="k">class</span> <span class="nc">AnimalSoundsConfig</span><span class="p">(</span><span class="n">NautobotAppConfig</span><span class="p">):</span>
4430
- <a id="__codelineno-19-8" name="__codelineno-19-8" href="#__codelineno-19-8"></a> <span class="c1"># ...</span>
4431
- <a id="__codelineno-19-9" name="__codelineno-19-9" href="#__codelineno-19-9"></a>
4432
- <a id="__codelineno-19-10" name="__codelineno-19-10" href="#__codelineno-19-10"></a> <span class="k">def</span> <span class="nf">ready</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
4433
- <a id="__codelineno-19-11" name="__codelineno-19-11" href="#__codelineno-19-11"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">ready</span><span class="p">()</span>
4434
- <a id="__codelineno-19-12" name="__codelineno-19-12" href="#__codelineno-19-12"></a> <span class="n">nautobot_database_ready</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">create_site_to_animal_relationship</span><span class="p">,</span> <span class="n">sender</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
4435
- <a id="__codelineno-19-13" name="__codelineno-19-13" href="#__codelineno-19-13"></a>
4436
- <a id="__codelineno-19-14" name="__codelineno-19-14" href="#__codelineno-19-14"></a><span class="n">config</span> <span class="o">=</span> <span class="n">AnimalSoundsConfig</span>
5646
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a><span class="c1"># __init__.py</span>
5647
+ <a id="__codelineno-23-2" name="__codelineno-23-2" href="#__codelineno-23-2"></a>
5648
+ <a id="__codelineno-23-3" name="__codelineno-23-3" href="#__codelineno-23-3"></a><span class="kn">from</span> <span class="nn">nautobot.apps</span> <span class="kn">import</span> <span class="n">nautobot_database_ready</span><span class="p">,</span> <span class="n">NautobotAppConfig</span>
5649
+ <a id="__codelineno-23-4" name="__codelineno-23-4" href="#__codelineno-23-4"></a>
5650
+ <a id="__codelineno-23-5" name="__codelineno-23-5" href="#__codelineno-23-5"></a><span class="kn">from</span> <span class="nn">.signals</span> <span class="kn">import</span> <span class="n">create_site_to_animal_relationship</span>
5651
+ <a id="__codelineno-23-6" name="__codelineno-23-6" href="#__codelineno-23-6"></a>
5652
+ <a id="__codelineno-23-7" name="__codelineno-23-7" href="#__codelineno-23-7"></a><span class="k">class</span> <span class="nc">AnimalSoundsConfig</span><span class="p">(</span><span class="n">NautobotAppConfig</span><span class="p">):</span>
5653
+ <a id="__codelineno-23-8" name="__codelineno-23-8" href="#__codelineno-23-8"></a> <span class="c1"># ...</span>
5654
+ <a id="__codelineno-23-9" name="__codelineno-23-9" href="#__codelineno-23-9"></a>
5655
+ <a id="__codelineno-23-10" name="__codelineno-23-10" href="#__codelineno-23-10"></a> <span class="k">def</span> <span class="nf">ready</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
5656
+ <a id="__codelineno-23-11" name="__codelineno-23-11" href="#__codelineno-23-11"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">ready</span><span class="p">()</span>
5657
+ <a id="__codelineno-23-12" name="__codelineno-23-12" href="#__codelineno-23-12"></a> <span class="n">nautobot_database_ready</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">create_site_to_animal_relationship</span><span class="p">,</span> <span class="n">sender</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
5658
+ <a id="__codelineno-23-13" name="__codelineno-23-13" href="#__codelineno-23-13"></a>
5659
+ <a id="__codelineno-23-14" name="__codelineno-23-14" href="#__codelineno-23-14"></a><span class="n">config</span> <span class="o">=</span> <span class="n">AnimalSoundsConfig</span>
4437
5660
  </code></pre></div>
4438
5661
  <p>After writing this code, run <code>nautobot-server migrate</code> or <code>nautobot-server post_upgrade</code>, then restart the Nautobot server, and you should see that this custom Relationship has now been automatically created.</p>
4439
5662
  <h3 id="implementing-secrets-providers">Implementing Secrets Providers<a class="headerlink" href="#implementing-secrets-providers" title="Permanent link">&para;</a></h3>
@@ -4453,48 +5676,48 @@ namedtuple to assist in the configurations.</p>
4453
5676
  <p class="admonition-title">Warning</p>
4454
5677
  <p>This is an intentionally simplistic example and should not be used in practice! Sensitive secret data should never be stored directly in Nautobot's database itself.</p>
4455
5678
  </div>
4456
- <div class="highlight"><pre><span></span><code><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a><span class="c1"># secrets.py</span>
4457
- <a id="__codelineno-20-2" name="__codelineno-20-2" href="#__codelineno-20-2"></a><span class="kn">from</span> <span class="nn">django</span> <span class="kn">import</span> <span class="n">forms</span>
4458
- <a id="__codelineno-20-3" name="__codelineno-20-3" href="#__codelineno-20-3"></a><span class="kn">from</span> <span class="nn">nautobot.apps.secrets</span> <span class="kn">import</span> <span class="n">SecretsProvider</span>
4459
- <a id="__codelineno-20-4" name="__codelineno-20-4" href="#__codelineno-20-4"></a><span class="kn">from</span> <span class="nn">nautobot.utilities.forms</span> <span class="kn">import</span> <span class="n">BootstrapMixin</span>
4460
- <a id="__codelineno-20-5" name="__codelineno-20-5" href="#__codelineno-20-5"></a>
4461
- <a id="__codelineno-20-6" name="__codelineno-20-6" href="#__codelineno-20-6"></a>
4462
- <a id="__codelineno-20-7" name="__codelineno-20-7" href="#__codelineno-20-7"></a><span class="k">class</span> <span class="nc">ConstantValueSecretsProvider</span><span class="p">(</span><span class="n">SecretsProvider</span><span class="p">):</span>
4463
- <a id="__codelineno-20-8" name="__codelineno-20-8" href="#__codelineno-20-8"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4464
- <a id="__codelineno-20-9" name="__codelineno-20-9" href="#__codelineno-20-9"></a><span class="sd"> Example SecretsProvider - this one just returns a user-specified constant value.</span>
4465
- <a id="__codelineno-20-10" name="__codelineno-20-10" href="#__codelineno-20-10"></a>
4466
- <a id="__codelineno-20-11" name="__codelineno-20-11" href="#__codelineno-20-11"></a><span class="sd"> Obviously this is insecure and not something you&#39;d want to actually use!</span>
4467
- <a id="__codelineno-20-12" name="__codelineno-20-12" href="#__codelineno-20-12"></a><span class="sd"> &quot;&quot;&quot;</span>
4468
- <a id="__codelineno-20-13" name="__codelineno-20-13" href="#__codelineno-20-13"></a>
4469
- <a id="__codelineno-20-14" name="__codelineno-20-14" href="#__codelineno-20-14"></a> <span class="n">slug</span> <span class="o">=</span> <span class="s2">&quot;constant-value&quot;</span>
4470
- <a id="__codelineno-20-15" name="__codelineno-20-15" href="#__codelineno-20-15"></a> <span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;Constant Value&quot;</span>
4471
- <a id="__codelineno-20-16" name="__codelineno-20-16" href="#__codelineno-20-16"></a>
4472
- <a id="__codelineno-20-17" name="__codelineno-20-17" href="#__codelineno-20-17"></a> <span class="k">class</span> <span class="nc">ParametersForm</span><span class="p">(</span><span class="n">BootstrapMixin</span><span class="p">,</span> <span class="n">forms</span><span class="o">.</span><span class="n">Form</span><span class="p">):</span>
4473
- <a id="__codelineno-20-18" name="__codelineno-20-18" href="#__codelineno-20-18"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4474
- <a id="__codelineno-20-19" name="__codelineno-20-19" href="#__codelineno-20-19"></a><span class="sd"> User-friendly form for specifying the required parameters of this provider.</span>
4475
- <a id="__codelineno-20-20" name="__codelineno-20-20" href="#__codelineno-20-20"></a><span class="sd"> &quot;&quot;&quot;</span>
4476
- <a id="__codelineno-20-21" name="__codelineno-20-21" href="#__codelineno-20-21"></a> <span class="n">constant</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span>
4477
- <a id="__codelineno-20-22" name="__codelineno-20-22" href="#__codelineno-20-22"></a> <span class="n">required</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
4478
- <a id="__codelineno-20-23" name="__codelineno-20-23" href="#__codelineno-20-23"></a> <span class="n">help_text</span><span class="o">=</span><span class="s2">&quot;Constant secret value. &lt;strong&gt;DO NOT USE FOR REAL DATA&lt;/strong&gt;&quot;</span>
4479
- <a id="__codelineno-20-24" name="__codelineno-20-24" href="#__codelineno-20-24"></a> <span class="p">)</span>
4480
- <a id="__codelineno-20-25" name="__codelineno-20-25" href="#__codelineno-20-25"></a>
4481
- <a id="__codelineno-20-26" name="__codelineno-20-26" href="#__codelineno-20-26"></a> <span class="nd">@classmethod</span>
4482
- <a id="__codelineno-20-27" name="__codelineno-20-27" href="#__codelineno-20-27"></a> <span class="k">def</span> <span class="nf">get_value_for_secret</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">secret</span><span class="p">,</span> <span class="n">obj</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
4483
- <a id="__codelineno-20-28" name="__codelineno-20-28" href="#__codelineno-20-28"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4484
- <a id="__codelineno-20-29" name="__codelineno-20-29" href="#__codelineno-20-29"></a><span class="sd"> Return the value defined in the Secret.parameters &quot;constant&quot; key.</span>
4485
- <a id="__codelineno-20-30" name="__codelineno-20-30" href="#__codelineno-20-30"></a>
4486
- <a id="__codelineno-20-31" name="__codelineno-20-31" href="#__codelineno-20-31"></a><span class="sd"> A more realistic SecretsProvider would make calls to external APIs, etc.,</span>
4487
- <a id="__codelineno-20-32" name="__codelineno-20-32" href="#__codelineno-20-32"></a><span class="sd"> to retrieve a secret from another system as desired.</span>
4488
- <a id="__codelineno-20-33" name="__codelineno-20-33" href="#__codelineno-20-33"></a>
4489
- <a id="__codelineno-20-34" name="__codelineno-20-34" href="#__codelineno-20-34"></a><span class="sd"> Args:</span>
4490
- <a id="__codelineno-20-35" name="__codelineno-20-35" href="#__codelineno-20-35"></a><span class="sd"> secret (nautobot.extras.models.Secret): The secret whose value should be retrieved.</span>
4491
- <a id="__codelineno-20-36" name="__codelineno-20-36" href="#__codelineno-20-36"></a><span class="sd"> obj (object): The object (Django model or similar) providing context for the secret&#39;s</span>
4492
- <a id="__codelineno-20-37" name="__codelineno-20-37" href="#__codelineno-20-37"></a><span class="sd"> parameters.</span>
4493
- <a id="__codelineno-20-38" name="__codelineno-20-38" href="#__codelineno-20-38"></a><span class="sd"> &quot;&quot;&quot;</span>
4494
- <a id="__codelineno-20-39" name="__codelineno-20-39" href="#__codelineno-20-39"></a> <span class="k">return</span> <span class="n">secret</span><span class="o">.</span><span class="n">rendered_parameters</span><span class="p">(</span><span class="n">obj</span><span class="o">=</span><span class="n">obj</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;constant&quot;</span><span class="p">)</span>
4495
- <a id="__codelineno-20-40" name="__codelineno-20-40" href="#__codelineno-20-40"></a>
4496
- <a id="__codelineno-20-41" name="__codelineno-20-41" href="#__codelineno-20-41"></a>
4497
- <a id="__codelineno-20-42" name="__codelineno-20-42" href="#__codelineno-20-42"></a><span class="n">secrets_providers</span> <span class="o">=</span> <span class="p">[</span><span class="n">ConstantValueSecretsProvider</span><span class="p">]</span>
5679
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a><span class="c1"># secrets.py</span>
5680
+ <a id="__codelineno-24-2" name="__codelineno-24-2" href="#__codelineno-24-2"></a><span class="kn">from</span> <span class="nn">django</span> <span class="kn">import</span> <span class="n">forms</span>
5681
+ <a id="__codelineno-24-3" name="__codelineno-24-3" href="#__codelineno-24-3"></a><span class="kn">from</span> <span class="nn">nautobot.apps.secrets</span> <span class="kn">import</span> <span class="n">SecretsProvider</span>
5682
+ <a id="__codelineno-24-4" name="__codelineno-24-4" href="#__codelineno-24-4"></a><span class="kn">from</span> <span class="nn">nautobot.utilities.forms</span> <span class="kn">import</span> <span class="n">BootstrapMixin</span>
5683
+ <a id="__codelineno-24-5" name="__codelineno-24-5" href="#__codelineno-24-5"></a>
5684
+ <a id="__codelineno-24-6" name="__codelineno-24-6" href="#__codelineno-24-6"></a>
5685
+ <a id="__codelineno-24-7" name="__codelineno-24-7" href="#__codelineno-24-7"></a><span class="k">class</span> <span class="nc">ConstantValueSecretsProvider</span><span class="p">(</span><span class="n">SecretsProvider</span><span class="p">):</span>
5686
+ <a id="__codelineno-24-8" name="__codelineno-24-8" href="#__codelineno-24-8"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5687
+ <a id="__codelineno-24-9" name="__codelineno-24-9" href="#__codelineno-24-9"></a><span class="sd"> Example SecretsProvider - this one just returns a user-specified constant value.</span>
5688
+ <a id="__codelineno-24-10" name="__codelineno-24-10" href="#__codelineno-24-10"></a>
5689
+ <a id="__codelineno-24-11" name="__codelineno-24-11" href="#__codelineno-24-11"></a><span class="sd"> Obviously this is insecure and not something you&#39;d want to actually use!</span>
5690
+ <a id="__codelineno-24-12" name="__codelineno-24-12" href="#__codelineno-24-12"></a><span class="sd"> &quot;&quot;&quot;</span>
5691
+ <a id="__codelineno-24-13" name="__codelineno-24-13" href="#__codelineno-24-13"></a>
5692
+ <a id="__codelineno-24-14" name="__codelineno-24-14" href="#__codelineno-24-14"></a> <span class="n">slug</span> <span class="o">=</span> <span class="s2">&quot;constant-value&quot;</span>
5693
+ <a id="__codelineno-24-15" name="__codelineno-24-15" href="#__codelineno-24-15"></a> <span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;Constant Value&quot;</span>
5694
+ <a id="__codelineno-24-16" name="__codelineno-24-16" href="#__codelineno-24-16"></a>
5695
+ <a id="__codelineno-24-17" name="__codelineno-24-17" href="#__codelineno-24-17"></a> <span class="k">class</span> <span class="nc">ParametersForm</span><span class="p">(</span><span class="n">BootstrapMixin</span><span class="p">,</span> <span class="n">forms</span><span class="o">.</span><span class="n">Form</span><span class="p">):</span>
5696
+ <a id="__codelineno-24-18" name="__codelineno-24-18" href="#__codelineno-24-18"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5697
+ <a id="__codelineno-24-19" name="__codelineno-24-19" href="#__codelineno-24-19"></a><span class="sd"> User-friendly form for specifying the required parameters of this provider.</span>
5698
+ <a id="__codelineno-24-20" name="__codelineno-24-20" href="#__codelineno-24-20"></a><span class="sd"> &quot;&quot;&quot;</span>
5699
+ <a id="__codelineno-24-21" name="__codelineno-24-21" href="#__codelineno-24-21"></a> <span class="n">constant</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span>
5700
+ <a id="__codelineno-24-22" name="__codelineno-24-22" href="#__codelineno-24-22"></a> <span class="n">required</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
5701
+ <a id="__codelineno-24-23" name="__codelineno-24-23" href="#__codelineno-24-23"></a> <span class="n">help_text</span><span class="o">=</span><span class="s2">&quot;Constant secret value. &lt;strong&gt;DO NOT USE FOR REAL DATA&lt;/strong&gt;&quot;</span>
5702
+ <a id="__codelineno-24-24" name="__codelineno-24-24" href="#__codelineno-24-24"></a> <span class="p">)</span>
5703
+ <a id="__codelineno-24-25" name="__codelineno-24-25" href="#__codelineno-24-25"></a>
5704
+ <a id="__codelineno-24-26" name="__codelineno-24-26" href="#__codelineno-24-26"></a> <span class="nd">@classmethod</span>
5705
+ <a id="__codelineno-24-27" name="__codelineno-24-27" href="#__codelineno-24-27"></a> <span class="k">def</span> <span class="nf">get_value_for_secret</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">secret</span><span class="p">,</span> <span class="n">obj</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
5706
+ <a id="__codelineno-24-28" name="__codelineno-24-28" href="#__codelineno-24-28"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5707
+ <a id="__codelineno-24-29" name="__codelineno-24-29" href="#__codelineno-24-29"></a><span class="sd"> Return the value defined in the Secret.parameters &quot;constant&quot; key.</span>
5708
+ <a id="__codelineno-24-30" name="__codelineno-24-30" href="#__codelineno-24-30"></a>
5709
+ <a id="__codelineno-24-31" name="__codelineno-24-31" href="#__codelineno-24-31"></a><span class="sd"> A more realistic SecretsProvider would make calls to external APIs, etc.,</span>
5710
+ <a id="__codelineno-24-32" name="__codelineno-24-32" href="#__codelineno-24-32"></a><span class="sd"> to retrieve a secret from another system as desired.</span>
5711
+ <a id="__codelineno-24-33" name="__codelineno-24-33" href="#__codelineno-24-33"></a>
5712
+ <a id="__codelineno-24-34" name="__codelineno-24-34" href="#__codelineno-24-34"></a><span class="sd"> Args:</span>
5713
+ <a id="__codelineno-24-35" name="__codelineno-24-35" href="#__codelineno-24-35"></a><span class="sd"> secret (nautobot.extras.models.Secret): The secret whose value should be retrieved.</span>
5714
+ <a id="__codelineno-24-36" name="__codelineno-24-36" href="#__codelineno-24-36"></a><span class="sd"> obj (object): The object (Django model or similar) providing context for the secret&#39;s</span>
5715
+ <a id="__codelineno-24-37" name="__codelineno-24-37" href="#__codelineno-24-37"></a><span class="sd"> parameters.</span>
5716
+ <a id="__codelineno-24-38" name="__codelineno-24-38" href="#__codelineno-24-38"></a><span class="sd"> &quot;&quot;&quot;</span>
5717
+ <a id="__codelineno-24-39" name="__codelineno-24-39" href="#__codelineno-24-39"></a> <span class="k">return</span> <span class="n">secret</span><span class="o">.</span><span class="n">rendered_parameters</span><span class="p">(</span><span class="n">obj</span><span class="o">=</span><span class="n">obj</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;constant&quot;</span><span class="p">)</span>
5718
+ <a id="__codelineno-24-40" name="__codelineno-24-40" href="#__codelineno-24-40"></a>
5719
+ <a id="__codelineno-24-41" name="__codelineno-24-41" href="#__codelineno-24-41"></a>
5720
+ <a id="__codelineno-24-42" name="__codelineno-24-42" href="#__codelineno-24-42"></a><span class="n">secrets_providers</span> <span class="o">=</span> <span class="p">[</span><span class="n">ConstantValueSecretsProvider</span><span class="p">]</span>
4498
5721
  </code></pre></div>
4499
5722
  <p>After installing and enabling your app, you should now be able to navigate to <code>Secrets &gt; Secrets</code> and create a new Secret, at which point <code>"constant-value"</code> should now be available as a new secrets provider to use.</p>
4500
5723
  <h3 id="extending-filters">Extending Filters<a class="headerlink" href="#extending-filters" title="Permanent link">&para;</a></h3>
@@ -4626,61 +5849,61 @@ namedtuple to assist in the configurations.</p>
4626
5849
  </tbody>
4627
5850
  </table>
4628
5851
  <p>Below is an example <code>models.py</code> file containing a basic model with two character fields:</p>
4629
- <div class="highlight"><pre><span></span><code><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a><span class="c1"># models.py</span>
4630
- <a id="__codelineno-21-2" name="__codelineno-21-2" href="#__codelineno-21-2"></a><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
4631
- <a id="__codelineno-21-3" name="__codelineno-21-3" href="#__codelineno-21-3"></a>
4632
- <a id="__codelineno-21-4" name="__codelineno-21-4" href="#__codelineno-21-4"></a><span class="kn">from</span> <span class="nn">nautobot.apps.models</span> <span class="kn">import</span> <span class="n">BaseModel</span>
4633
- <a id="__codelineno-21-5" name="__codelineno-21-5" href="#__codelineno-21-5"></a>
4634
- <a id="__codelineno-21-6" name="__codelineno-21-6" href="#__codelineno-21-6"></a>
4635
- <a id="__codelineno-21-7" name="__codelineno-21-7" href="#__codelineno-21-7"></a><span class="k">class</span> <span class="nc">Animal</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
4636
- <a id="__codelineno-21-8" name="__codelineno-21-8" href="#__codelineno-21-8"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Base model for animals.&quot;&quot;&quot;</span>
4637
- <a id="__codelineno-21-9" name="__codelineno-21-9" href="#__codelineno-21-9"></a>
4638
- <a id="__codelineno-21-10" name="__codelineno-21-10" href="#__codelineno-21-10"></a> <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
4639
- <a id="__codelineno-21-11" name="__codelineno-21-11" href="#__codelineno-21-11"></a> <span class="n">sound</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
4640
- <a id="__codelineno-21-12" name="__codelineno-21-12" href="#__codelineno-21-12"></a>
4641
- <a id="__codelineno-21-13" name="__codelineno-21-13" href="#__codelineno-21-13"></a> <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
4642
- <a id="__codelineno-21-14" name="__codelineno-21-14" href="#__codelineno-21-14"></a> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
4643
- <a id="__codelineno-21-15" name="__codelineno-21-15" href="#__codelineno-21-15"></a>
4644
- <a id="__codelineno-21-16" name="__codelineno-21-16" href="#__codelineno-21-16"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
4645
- <a id="__codelineno-21-17" name="__codelineno-21-17" href="#__codelineno-21-17"></a> <span class="n">unique_together</span> <span class="o">=</span> <span class="p">[[</span><span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="s2">&quot;sound&quot;</span><span class="p">]]</span>
5852
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a><span class="c1"># models.py</span>
5853
+ <a id="__codelineno-25-2" name="__codelineno-25-2" href="#__codelineno-25-2"></a><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
5854
+ <a id="__codelineno-25-3" name="__codelineno-25-3" href="#__codelineno-25-3"></a>
5855
+ <a id="__codelineno-25-4" name="__codelineno-25-4" href="#__codelineno-25-4"></a><span class="kn">from</span> <span class="nn">nautobot.apps.models</span> <span class="kn">import</span> <span class="n">BaseModel</span>
5856
+ <a id="__codelineno-25-5" name="__codelineno-25-5" href="#__codelineno-25-5"></a>
5857
+ <a id="__codelineno-25-6" name="__codelineno-25-6" href="#__codelineno-25-6"></a>
5858
+ <a id="__codelineno-25-7" name="__codelineno-25-7" href="#__codelineno-25-7"></a><span class="k">class</span> <span class="nc">Animal</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
5859
+ <a id="__codelineno-25-8" name="__codelineno-25-8" href="#__codelineno-25-8"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Base model for animals.&quot;&quot;&quot;</span>
5860
+ <a id="__codelineno-25-9" name="__codelineno-25-9" href="#__codelineno-25-9"></a>
5861
+ <a id="__codelineno-25-10" name="__codelineno-25-10" href="#__codelineno-25-10"></a> <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
5862
+ <a id="__codelineno-25-11" name="__codelineno-25-11" href="#__codelineno-25-11"></a> <span class="n">sound</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
5863
+ <a id="__codelineno-25-12" name="__codelineno-25-12" href="#__codelineno-25-12"></a>
5864
+ <a id="__codelineno-25-13" name="__codelineno-25-13" href="#__codelineno-25-13"></a> <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
5865
+ <a id="__codelineno-25-14" name="__codelineno-25-14" href="#__codelineno-25-14"></a> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
5866
+ <a id="__codelineno-25-15" name="__codelineno-25-15" href="#__codelineno-25-15"></a>
5867
+ <a id="__codelineno-25-16" name="__codelineno-25-16" href="#__codelineno-25-16"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
5868
+ <a id="__codelineno-25-17" name="__codelineno-25-17" href="#__codelineno-25-17"></a> <span class="n">unique_together</span> <span class="o">=</span> <span class="p">[[</span><span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="s2">&quot;sound&quot;</span><span class="p">]]</span>
4646
5869
  </code></pre></div>
4647
5870
  <p>Once you have defined the model(s) for your app, you'll need to create the database schema migrations. A migration file is essentially a set of instructions for manipulating the database to support your new model, or to alter existing models.</p>
4648
5871
  <p>Creating migrations can be done automatically using the <code>nautobot-server makemigrations &lt;app_name&gt;</code> management command, where <code>&lt;app_name&gt;</code> is the name of the Python package for your app (e.g. <code>animal_sounds</code>):</p>
4649
- <div class="highlight"><pre><span></span><code><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a>nautobot-server makemigrations nautobot_animal_sounds
5872
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a>nautobot-server makemigrations nautobot_animal_sounds
4650
5873
  </code></pre></div>
4651
5874
  <div class="admonition note">
4652
5875
  <p class="admonition-title">Note</p>
4653
5876
  <p>An app must be installed before it can be used with Django management commands. If you skipped this step above, run <code>poetry install</code> from the app's root directory.</p>
4654
5877
  </div>
4655
- <div class="highlight"><pre><span></span><code><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a>nautobot-server makemigrations nautobot_animal_sounds
5878
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a>nautobot-server makemigrations nautobot_animal_sounds
4656
5879
  </code></pre></div>
4657
5880
  <p>Example output:</p>
4658
- <div class="highlight"><pre><span></span><code><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a>Migrations for &#39;nautobot_animal_sounds&#39;:
4659
- <a id="__codelineno-24-2" name="__codelineno-24-2" href="#__codelineno-24-2"></a> /home/bjones/animal_sounds/nautobot_animal_sounds/migrations/0001_initial.py
4660
- <a id="__codelineno-24-3" name="__codelineno-24-3" href="#__codelineno-24-3"></a> - Create model Animal
5881
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a>Migrations for &#39;nautobot_animal_sounds&#39;:
5882
+ <a id="__codelineno-28-2" name="__codelineno-28-2" href="#__codelineno-28-2"></a> /home/bjones/animal_sounds/nautobot_animal_sounds/migrations/0001_initial.py
5883
+ <a id="__codelineno-28-3" name="__codelineno-28-3" href="#__codelineno-28-3"></a> - Create model Animal
4661
5884
  </code></pre></div>
4662
5885
  <p>Next, apply the migration to the database with the <code>nautobot-server migrate &lt;app_name&gt;</code> command:</p>
4663
- <div class="highlight"><pre><span></span><code><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a>nautobot-server migrate nautobot_animal_sounds
5886
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a>nautobot-server migrate nautobot_animal_sounds
4664
5887
  </code></pre></div>
4665
5888
  <p>Example output:</p>
4666
- <div class="highlight"><pre><span></span><code><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a>Operations to perform:
4667
- <a id="__codelineno-26-2" name="__codelineno-26-2" href="#__codelineno-26-2"></a> Apply all migrations: nautobot_animal_sounds
4668
- <a id="__codelineno-26-3" name="__codelineno-26-3" href="#__codelineno-26-3"></a>Running migrations:
4669
- <a id="__codelineno-26-4" name="__codelineno-26-4" href="#__codelineno-26-4"></a> Applying nautobot_animal_sounds.0001_initial... OK
5889
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a>Operations to perform:
5890
+ <a id="__codelineno-30-2" name="__codelineno-30-2" href="#__codelineno-30-2"></a> Apply all migrations: nautobot_animal_sounds
5891
+ <a id="__codelineno-30-3" name="__codelineno-30-3" href="#__codelineno-30-3"></a>Running migrations:
5892
+ <a id="__codelineno-30-4" name="__codelineno-30-4" href="#__codelineno-30-4"></a> Applying nautobot_animal_sounds.0001_initial... OK
4670
5893
  </code></pre></div>
4671
5894
  <p>For more background on schema migrations, see the <a href="https://docs.djangoproject.com/en/stable/topics/migrations/">Django documentation</a>.</p>
4672
5895
  <h3 id="using-the-django-admin-interface">Using the Django Admin Interface<a class="headerlink" href="#using-the-django-admin-interface" title="Permanent link">&para;</a></h3>
4673
5896
  <p>Apps can optionally expose their models via Django's built-in <a href="https://docs.djangoproject.com/en/stable/ref/contrib/admin/">administrative interface</a>. This can greatly improve troubleshooting ability, particularly during development. To expose a model, simply register it using Django's <code>admin.register()</code> function. An example <code>admin.py</code> file for the above model is shown below:</p>
4674
- <div class="highlight"><pre><span></span><code><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a><span class="c1"># admin.py</span>
4675
- <a id="__codelineno-27-2" name="__codelineno-27-2" href="#__codelineno-27-2"></a><span class="kn">from</span> <span class="nn">django.contrib</span> <span class="kn">import</span> <span class="n">admin</span>
4676
- <a id="__codelineno-27-3" name="__codelineno-27-3" href="#__codelineno-27-3"></a><span class="kn">from</span> <span class="nn">nautobot.apps.admin</span> <span class="kn">import</span> <span class="n">NautobotModelAdmin</span>
4677
- <a id="__codelineno-27-4" name="__codelineno-27-4" href="#__codelineno-27-4"></a>
4678
- <a id="__codelineno-27-5" name="__codelineno-27-5" href="#__codelineno-27-5"></a><span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Animal</span>
4679
- <a id="__codelineno-27-6" name="__codelineno-27-6" href="#__codelineno-27-6"></a>
4680
- <a id="__codelineno-27-7" name="__codelineno-27-7" href="#__codelineno-27-7"></a>
4681
- <a id="__codelineno-27-8" name="__codelineno-27-8" href="#__codelineno-27-8"></a><span class="nd">@admin</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Animal</span><span class="p">)</span>
4682
- <a id="__codelineno-27-9" name="__codelineno-27-9" href="#__codelineno-27-9"></a><span class="k">class</span> <span class="nc">AnimalAdmin</span><span class="p">(</span><span class="n">NautobotModelAdmin</span><span class="p">):</span>
4683
- <a id="__codelineno-27-10" name="__codelineno-27-10" href="#__codelineno-27-10"></a> <span class="n">list_display</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="s1">&#39;sound&#39;</span><span class="p">)</span>
5897
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-31-1" name="__codelineno-31-1" href="#__codelineno-31-1"></a><span class="c1"># admin.py</span>
5898
+ <a id="__codelineno-31-2" name="__codelineno-31-2" href="#__codelineno-31-2"></a><span class="kn">from</span> <span class="nn">django.contrib</span> <span class="kn">import</span> <span class="n">admin</span>
5899
+ <a id="__codelineno-31-3" name="__codelineno-31-3" href="#__codelineno-31-3"></a><span class="kn">from</span> <span class="nn">nautobot.apps.admin</span> <span class="kn">import</span> <span class="n">NautobotModelAdmin</span>
5900
+ <a id="__codelineno-31-4" name="__codelineno-31-4" href="#__codelineno-31-4"></a>
5901
+ <a id="__codelineno-31-5" name="__codelineno-31-5" href="#__codelineno-31-5"></a><span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Animal</span>
5902
+ <a id="__codelineno-31-6" name="__codelineno-31-6" href="#__codelineno-31-6"></a>
5903
+ <a id="__codelineno-31-7" name="__codelineno-31-7" href="#__codelineno-31-7"></a>
5904
+ <a id="__codelineno-31-8" name="__codelineno-31-8" href="#__codelineno-31-8"></a><span class="nd">@admin</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Animal</span><span class="p">)</span>
5905
+ <a id="__codelineno-31-9" name="__codelineno-31-9" href="#__codelineno-31-9"></a><span class="k">class</span> <span class="nc">AnimalAdmin</span><span class="p">(</span><span class="n">NautobotModelAdmin</span><span class="p">):</span>
5906
+ <a id="__codelineno-31-10" name="__codelineno-31-10" href="#__codelineno-31-10"></a> <span class="n">list_display</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="s1">&#39;sound&#39;</span><span class="p">)</span>
4684
5907
  </code></pre></div>
4685
5908
  <p>This will display the app and its model in the admin UI. Staff users can create, change, and delete model instances via the admin UI without needing to create a custom view.</p>
4686
5909
  <p><img alt="Nautobot app in the admin UI" src="../media/plugins/plugin_admin_ui.png" /></p>
@@ -4689,9 +5912,9 @@ namedtuple to assist in the configurations.</p>
4689
5912
  <p class="admonition-title">Added in version 2.0.0</p>
4690
5913
  </div>
4691
5914
  <p>Simply define a <code>searchable_models</code> array on the NautobotAppConfig for your app, listing the lowercase names of the model(s) from your app that you wish to include in the Nautobot global search.</p>
4692
- <div class="highlight"><pre><span></span><code><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a><span class="k">class</span> <span class="nc">AnimalSoundsConfig</span><span class="p">(</span><span class="n">NautobotAppConfig</span><span class="p">):</span>
4693
- <a id="__codelineno-28-2" name="__codelineno-28-2" href="#__codelineno-28-2"></a> <span class="o">...</span>
4694
- <a id="__codelineno-28-3" name="__codelineno-28-3" href="#__codelineno-28-3"></a> <span class="n">searchable_models</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;animal&quot;</span><span class="p">]</span>
5915
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-32-1" name="__codelineno-32-1" href="#__codelineno-32-1"></a><span class="k">class</span> <span class="nc">AnimalSoundsConfig</span><span class="p">(</span><span class="n">NautobotAppConfig</span><span class="p">):</span>
5916
+ <a id="__codelineno-32-2" name="__codelineno-32-2" href="#__codelineno-32-2"></a> <span class="o">...</span>
5917
+ <a id="__codelineno-32-3" name="__codelineno-32-3" href="#__codelineno-32-3"></a> <span class="n">searchable_models</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;animal&quot;</span><span class="p">]</span>
4695
5918
  </code></pre></div>
4696
5919
  <h3 id="integrating-with-graphql">Integrating with GraphQL<a class="headerlink" href="#integrating-with-graphql" title="Permanent link">&para;</a></h3>
4697
5920
  <p>Apps can optionally expose their models via the GraphQL interface to allow the models to be part of the Graph and to be queried easily. There are two mutually exclusive ways to expose a model to the GraphQL interface.</p>
@@ -4708,21 +5931,21 @@ namedtuple to assist in the configurations.</p>
4708
5931
  </ul>
4709
5932
  <h4 id="using-the-extras_features-decorator-for-graphql">Using the <code>@extras_features</code> Decorator for GraphQL<a class="headerlink" href="#using-the-extras_features-decorator-for-graphql" title="Permanent link">&para;</a></h4>
4710
5933
  <p>To expose a model via GraphQL, simply register it using the <code>@extras_features("graphql")</code> decorator. Nautobot will detect this and will automatically create a GraphQL type definition based on the model. Additionally, if a <code>FilterSet</code> is available at <code>&lt;app_name&gt;.filters.&lt;ModelName&gt;FilterSet</code>, Nautobot will automatically use the filterset to generate GraphQL filtering options for this type as well.</p>
4711
- <div class="highlight"><pre><span></span><code><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a><span class="c1"># models.py</span>
4712
- <a id="__codelineno-29-2" name="__codelineno-29-2" href="#__codelineno-29-2"></a><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
4713
- <a id="__codelineno-29-3" name="__codelineno-29-3" href="#__codelineno-29-3"></a>
4714
- <a id="__codelineno-29-4" name="__codelineno-29-4" href="#__codelineno-29-4"></a><span class="kn">from</span> <span class="nn">nautobot.apps.models</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">extras_features</span>
4715
- <a id="__codelineno-29-5" name="__codelineno-29-5" href="#__codelineno-29-5"></a>
4716
- <a id="__codelineno-29-6" name="__codelineno-29-6" href="#__codelineno-29-6"></a>
4717
- <a id="__codelineno-29-7" name="__codelineno-29-7" href="#__codelineno-29-7"></a><span class="nd">@extras_features</span><span class="p">(</span><span class="s2">&quot;graphql&quot;</span><span class="p">)</span>
4718
- <a id="__codelineno-29-8" name="__codelineno-29-8" href="#__codelineno-29-8"></a><span class="k">class</span> <span class="nc">Animal</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
4719
- <a id="__codelineno-29-9" name="__codelineno-29-9" href="#__codelineno-29-9"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Base model for animals.&quot;&quot;&quot;</span>
4720
- <a id="__codelineno-29-10" name="__codelineno-29-10" href="#__codelineno-29-10"></a>
4721
- <a id="__codelineno-29-11" name="__codelineno-29-11" href="#__codelineno-29-11"></a> <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
4722
- <a id="__codelineno-29-12" name="__codelineno-29-12" href="#__codelineno-29-12"></a> <span class="n">sound</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
4723
- <a id="__codelineno-29-13" name="__codelineno-29-13" href="#__codelineno-29-13"></a>
4724
- <a id="__codelineno-29-14" name="__codelineno-29-14" href="#__codelineno-29-14"></a> <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
4725
- <a id="__codelineno-29-15" name="__codelineno-29-15" href="#__codelineno-29-15"></a> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
5934
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-33-1" name="__codelineno-33-1" href="#__codelineno-33-1"></a><span class="c1"># models.py</span>
5935
+ <a id="__codelineno-33-2" name="__codelineno-33-2" href="#__codelineno-33-2"></a><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
5936
+ <a id="__codelineno-33-3" name="__codelineno-33-3" href="#__codelineno-33-3"></a>
5937
+ <a id="__codelineno-33-4" name="__codelineno-33-4" href="#__codelineno-33-4"></a><span class="kn">from</span> <span class="nn">nautobot.apps.models</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">extras_features</span>
5938
+ <a id="__codelineno-33-5" name="__codelineno-33-5" href="#__codelineno-33-5"></a>
5939
+ <a id="__codelineno-33-6" name="__codelineno-33-6" href="#__codelineno-33-6"></a>
5940
+ <a id="__codelineno-33-7" name="__codelineno-33-7" href="#__codelineno-33-7"></a><span class="nd">@extras_features</span><span class="p">(</span><span class="s2">&quot;graphql&quot;</span><span class="p">)</span>
5941
+ <a id="__codelineno-33-8" name="__codelineno-33-8" href="#__codelineno-33-8"></a><span class="k">class</span> <span class="nc">Animal</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
5942
+ <a id="__codelineno-33-9" name="__codelineno-33-9" href="#__codelineno-33-9"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Base model for animals.&quot;&quot;&quot;</span>
5943
+ <a id="__codelineno-33-10" name="__codelineno-33-10" href="#__codelineno-33-10"></a>
5944
+ <a id="__codelineno-33-11" name="__codelineno-33-11" href="#__codelineno-33-11"></a> <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
5945
+ <a id="__codelineno-33-12" name="__codelineno-33-12" href="#__codelineno-33-12"></a> <span class="n">sound</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
5946
+ <a id="__codelineno-33-13" name="__codelineno-33-13" href="#__codelineno-33-13"></a>
5947
+ <a id="__codelineno-33-14" name="__codelineno-33-14" href="#__codelineno-33-14"></a> <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
5948
+ <a id="__codelineno-33-15" name="__codelineno-33-15" href="#__codelineno-33-15"></a> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
4726
5949
  </code></pre></div>
4727
5950
  <h4 id="creating-your-own-graphql-type-object">Creating Your Own GraphQL Type Object<a class="headerlink" href="#creating-your-own-graphql-type-object" title="Permanent link">&para;</a></h4>
4728
5951
  <p>In some cases, such as when a model is using Generic Foreign Keys, or when a model has constructed fields that should also be reflected in GraphQL, the default GraphQL type definition generated by the <code>@extras_features</code> decorator may not work as the developer intends, and it will be preferable to provide custom GraphQL types.</p>
@@ -4732,21 +5955,21 @@ namedtuple to assist in the configurations.</p>
4732
5955
  <p class="admonition-title">Warning</p>
4733
5956
  <p>When defining types this way, do <strong>not</strong> use the <code>@extras_features("graphql")</code> decorator on the corresponding Model class, as no auto-generated GraphQL type is desired for this model.</p>
4734
5957
  </div>
4735
- <div class="highlight"><pre><span></span><code><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a><span class="c1"># graphql/types.py</span>
4736
- <a id="__codelineno-30-2" name="__codelineno-30-2" href="#__codelineno-30-2"></a><span class="kn">import</span> <span class="nn">graphene_django_optimizer</span> <span class="k">as</span> <span class="nn">gql_optimizer</span>
4737
- <a id="__codelineno-30-3" name="__codelineno-30-3" href="#__codelineno-30-3"></a>
4738
- <a id="__codelineno-30-4" name="__codelineno-30-4" href="#__codelineno-30-4"></a><span class="kn">from</span> <span class="nn">nautobot_animal_sounds.models</span> <span class="kn">import</span> <span class="n">Animal</span>
4739
- <a id="__codelineno-30-5" name="__codelineno-30-5" href="#__codelineno-30-5"></a>
4740
- <a id="__codelineno-30-6" name="__codelineno-30-6" href="#__codelineno-30-6"></a>
4741
- <a id="__codelineno-30-7" name="__codelineno-30-7" href="#__codelineno-30-7"></a><span class="k">class</span> <span class="nc">AnimalType</span><span class="p">(</span><span class="n">gql_optimizer</span><span class="o">.</span><span class="n">OptimizedDjangoObjectType</span><span class="p">):</span>
4742
- <a id="__codelineno-30-8" name="__codelineno-30-8" href="#__codelineno-30-8"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;GraphQL Type for Animal&quot;&quot;&quot;</span>
4743
- <a id="__codelineno-30-9" name="__codelineno-30-9" href="#__codelineno-30-9"></a>
4744
- <a id="__codelineno-30-10" name="__codelineno-30-10" href="#__codelineno-30-10"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
4745
- <a id="__codelineno-30-11" name="__codelineno-30-11" href="#__codelineno-30-11"></a> <span class="n">model</span> <span class="o">=</span> <span class="n">Animal</span>
4746
- <a id="__codelineno-30-12" name="__codelineno-30-12" href="#__codelineno-30-12"></a> <span class="n">exclude</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;sound&quot;</span><span class="p">]</span>
4747
- <a id="__codelineno-30-13" name="__codelineno-30-13" href="#__codelineno-30-13"></a>
4748
- <a id="__codelineno-30-14" name="__codelineno-30-14" href="#__codelineno-30-14"></a>
4749
- <a id="__codelineno-30-15" name="__codelineno-30-15" href="#__codelineno-30-15"></a><span class="n">graphql_types</span> <span class="o">=</span> <span class="p">[</span><span class="n">AnimalType</span><span class="p">]</span>
5958
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-34-1" name="__codelineno-34-1" href="#__codelineno-34-1"></a><span class="c1"># graphql/types.py</span>
5959
+ <a id="__codelineno-34-2" name="__codelineno-34-2" href="#__codelineno-34-2"></a><span class="kn">import</span> <span class="nn">graphene_django_optimizer</span> <span class="k">as</span> <span class="nn">gql_optimizer</span>
5960
+ <a id="__codelineno-34-3" name="__codelineno-34-3" href="#__codelineno-34-3"></a>
5961
+ <a id="__codelineno-34-4" name="__codelineno-34-4" href="#__codelineno-34-4"></a><span class="kn">from</span> <span class="nn">nautobot_animal_sounds.models</span> <span class="kn">import</span> <span class="n">Animal</span>
5962
+ <a id="__codelineno-34-5" name="__codelineno-34-5" href="#__codelineno-34-5"></a>
5963
+ <a id="__codelineno-34-6" name="__codelineno-34-6" href="#__codelineno-34-6"></a>
5964
+ <a id="__codelineno-34-7" name="__codelineno-34-7" href="#__codelineno-34-7"></a><span class="k">class</span> <span class="nc">AnimalType</span><span class="p">(</span><span class="n">gql_optimizer</span><span class="o">.</span><span class="n">OptimizedDjangoObjectType</span><span class="p">):</span>
5965
+ <a id="__codelineno-34-8" name="__codelineno-34-8" href="#__codelineno-34-8"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;GraphQL Type for Animal&quot;&quot;&quot;</span>
5966
+ <a id="__codelineno-34-9" name="__codelineno-34-9" href="#__codelineno-34-9"></a>
5967
+ <a id="__codelineno-34-10" name="__codelineno-34-10" href="#__codelineno-34-10"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
5968
+ <a id="__codelineno-34-11" name="__codelineno-34-11" href="#__codelineno-34-11"></a> <span class="n">model</span> <span class="o">=</span> <span class="n">Animal</span>
5969
+ <a id="__codelineno-34-12" name="__codelineno-34-12" href="#__codelineno-34-12"></a> <span class="n">exclude</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;sound&quot;</span><span class="p">]</span>
5970
+ <a id="__codelineno-34-13" name="__codelineno-34-13" href="#__codelineno-34-13"></a>
5971
+ <a id="__codelineno-34-14" name="__codelineno-34-14" href="#__codelineno-34-14"></a>
5972
+ <a id="__codelineno-34-15" name="__codelineno-34-15" href="#__codelineno-34-15"></a><span class="n">graphql_types</span> <span class="o">=</span> <span class="p">[</span><span class="n">AnimalType</span><span class="p">]</span>
4750
5973
  </code></pre></div>
4751
5974
  <h4 id="using-graphql-orm-utilities">Using GraphQL ORM Utilities<a class="headerlink" href="#using-graphql-orm-utilities" title="Permanent link">&para;</a></h4>
4752
5975
  <p>GraphQL utility functions:</p>
@@ -4788,31 +6011,36 @@ namedtuple to assist in the configurations.</p>
4788
6011
  <p>Note that this ViewSet is catered specifically to the UI, not the API.</p>
4789
6012
  <p>Concrete examples on how to use <code>NautobotUIViewSet</code> resides in <code>nautobot.circuits.views</code>.</p>
4790
6013
  <p>Below we provide an example on how to use <code>NautobotUIViewSet</code> on a theoretical app model.</p>
4791
- <div class="highlight"><pre><span></span><code><a id="__codelineno-31-1" name="__codelineno-31-1" href="#__codelineno-31-1"></a><span class="kn">from</span> <span class="nn">nautobot.apps.views</span> <span class="kn">import</span> <span class="n">NautobotUIViewset</span>
4792
- <a id="__codelineno-31-2" name="__codelineno-31-2" href="#__codelineno-31-2"></a>
4793
- <a id="__codelineno-31-3" name="__codelineno-31-3" href="#__codelineno-31-3"></a><span class="k">class</span> <span class="nc">YourAppModelUIViewSet</span><span class="p">(</span><span class="n">NautobotUIViewSet</span><span class="p">):</span>
4794
- <a id="__codelineno-31-4" name="__codelineno-31-4" href="#__codelineno-31-4"></a> <span class="n">bulk_create_form_class</span> <span class="o">=</span> <span class="n">YourAppModelCSVForm</span>
4795
- <a id="__codelineno-31-5" name="__codelineno-31-5" href="#__codelineno-31-5"></a> <span class="n">bulk_update_form_class</span> <span class="o">=</span> <span class="n">YourAppModelBulkEditForm</span>
4796
- <a id="__codelineno-31-6" name="__codelineno-31-6" href="#__codelineno-31-6"></a> <span class="n">filterset_class</span> <span class="o">=</span> <span class="n">YourAppModelFilterSet</span>
4797
- <a id="__codelineno-31-7" name="__codelineno-31-7" href="#__codelineno-31-7"></a> <span class="n">filterset_form_class</span> <span class="o">=</span> <span class="n">YourAppModelFilterForm</span>
4798
- <a id="__codelineno-31-8" name="__codelineno-31-8" href="#__codelineno-31-8"></a> <span class="n">form_class</span> <span class="o">=</span> <span class="n">YourAppModelForm</span>
4799
- <a id="__codelineno-31-9" name="__codelineno-31-9" href="#__codelineno-31-9"></a> <span class="n">queryset</span> <span class="o">=</span> <span class="n">YourAppModel</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
4800
- <a id="__codelineno-31-10" name="__codelineno-31-10" href="#__codelineno-31-10"></a> <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">YourAppModelSerializer</span>
4801
- <a id="__codelineno-31-11" name="__codelineno-31-11" href="#__codelineno-31-11"></a> <span class="n">table_class</span> <span class="o">=</span> <span class="n">YourAppModelTable</span>
6014
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-35-1" name="__codelineno-35-1" href="#__codelineno-35-1"></a><span class="kn">from</span> <span class="nn">nautobot.apps.views</span> <span class="kn">import</span> <span class="n">NautobotUIViewSet</span>
6015
+ <a id="__codelineno-35-2" name="__codelineno-35-2" href="#__codelineno-35-2"></a><span class="kn">from</span> <span class="nn">yourapp</span> <span class="kn">import</span> <span class="n">filters</span><span class="p">,</span> <span class="n">forms</span><span class="p">,</span> <span class="n">models</span><span class="p">,</span> <span class="n">tables</span>
6016
+ <a id="__codelineno-35-3" name="__codelineno-35-3" href="#__codelineno-35-3"></a><span class="kn">from</span> <span class="nn">yourapp.api</span> <span class="kn">import</span> <span class="n">serializers</span>
6017
+ <a id="__codelineno-35-4" name="__codelineno-35-4" href="#__codelineno-35-4"></a>
6018
+ <a id="__codelineno-35-5" name="__codelineno-35-5" href="#__codelineno-35-5"></a><span class="k">class</span> <span class="nc">YourAppModelUIViewSet</span><span class="p">(</span><span class="n">NautobotUIViewSet</span><span class="p">):</span>
6019
+ <a id="__codelineno-35-6" name="__codelineno-35-6" href="#__codelineno-35-6"></a> <span class="n">bulk_update_form_class</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">YourAppModelBulkEditForm</span>
6020
+ <a id="__codelineno-35-7" name="__codelineno-35-7" href="#__codelineno-35-7"></a> <span class="n">filterset_class</span> <span class="o">=</span> <span class="n">filters</span><span class="o">.</span><span class="n">YourAppModelFilterSet</span>
6021
+ <a id="__codelineno-35-8" name="__codelineno-35-8" href="#__codelineno-35-8"></a> <span class="n">filterset_form_class</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">YourAppModelFilterForm</span>
6022
+ <a id="__codelineno-35-9" name="__codelineno-35-9" href="#__codelineno-35-9"></a> <span class="n">form_class</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">YourAppModelForm</span>
6023
+ <a id="__codelineno-35-10" name="__codelineno-35-10" href="#__codelineno-35-10"></a> <span class="n">queryset</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">YourAppModel</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
6024
+ <a id="__codelineno-35-11" name="__codelineno-35-11" href="#__codelineno-35-11"></a> <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">YourAppModelSerializer</span>
6025
+ <a id="__codelineno-35-12" name="__codelineno-35-12" href="#__codelineno-35-12"></a> <span class="n">table_class</span> <span class="o">=</span> <span class="n">tables</span><span class="o">.</span><span class="n">YourAppModelTable</span>
4802
6026
  </code></pre></div>
4803
6027
  <h4 id="setting-viewset-attributes">Setting ViewSet Attributes<a class="headerlink" href="#setting-viewset-attributes" title="Permanent link">&para;</a></h4>
4804
6028
  <p><strong>One caveat of using the NautobotUIViewSet is that the <code>queryset</code>, <code>serializer_class</code> and <code>table_class</code> attribute of the <code>YourAppModelUIViewSet</code> has to be set before most of the <code>NautobotUIViewSet</code> functionalities will become available.</strong></p>
4805
- <p>By default the URL patterns generated by a <code>NautobotUIViewSet</code> are based on the model's <code>slug</code> (<code>/model-name/&lt;slug&gt;/</code> for the detail view, <code>/model-name/&lt;slug&gt;/edit/</code> for the edit view, etc.). If your model lacks a <code>slug</code> field, or if you otherwise need to use a different field to look up an object, just override the default <code>lookup_field</code> in your ViewSet attributes:</p>
4806
- <div class="highlight"><pre><span></span><code><a id="__codelineno-32-1" name="__codelineno-32-1" href="#__codelineno-32-1"></a><span class="kn">from</span> <span class="nn">nautobot.apps.views</span> <span class="kn">import</span> <span class="n">NautobotUIViewset</span>
4807
- <a id="__codelineno-32-2" name="__codelineno-32-2" href="#__codelineno-32-2"></a>
4808
- <a id="__codelineno-32-3" name="__codelineno-32-3" href="#__codelineno-32-3"></a><span class="k">class</span> <span class="nc">YourAppModelUIViewSet</span><span class="p">(</span><span class="n">NautobotUIViewSet</span><span class="p">):</span>
4809
- <a id="__codelineno-32-4" name="__codelineno-32-4" href="#__codelineno-32-4"></a> <span class="o">...</span>
4810
- <a id="__codelineno-32-5" name="__codelineno-32-5" href="#__codelineno-32-5"></a> <span class="n">lookup_field</span> <span class="o">=</span> <span class="s2">&quot;pk&quot;</span>
4811
- <a id="__codelineno-32-6" name="__codelineno-32-6" href="#__codelineno-32-6"></a> <span class="o">...</span>
6029
+ <p>By default the URL patterns generated by a <code>NautobotUIViewSet</code> are based on the model's <code>pk</code> (<code>/model-name/&lt;pk&gt;/</code> for the detail view, <code>/model-name/&lt;pk&gt;/edit/</code> for the edit view, etc.). if you need to use a different field to look up an object, just override the default <code>lookup_field</code> in your ViewSet attributes:</p>
6030
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-36-1" name="__codelineno-36-1" href="#__codelineno-36-1"></a><span class="kn">from</span> <span class="nn">nautobot.apps.views</span> <span class="kn">import</span> <span class="n">NautobotUIViewSet</span>
6031
+ <a id="__codelineno-36-2" name="__codelineno-36-2" href="#__codelineno-36-2"></a>
6032
+ <a id="__codelineno-36-3" name="__codelineno-36-3" href="#__codelineno-36-3"></a><span class="k">class</span> <span class="nc">YourAppModelUIViewSet</span><span class="p">(</span><span class="n">NautobotUIViewSet</span><span class="p">):</span>
6033
+ <a id="__codelineno-36-4" name="__codelineno-36-4" href="#__codelineno-36-4"></a> <span class="o">...</span>
6034
+ <a id="__codelineno-36-5" name="__codelineno-36-5" href="#__codelineno-36-5"></a> <span class="n">lookup_field</span> <span class="o">=</span> <span class="s2">&quot;slug&quot;</span>
6035
+ <a id="__codelineno-36-6" name="__codelineno-36-6" href="#__codelineno-36-6"></a> <span class="o">...</span>
4812
6036
  </code></pre></div>
6037
+ <div class="admonition version-changed">
6038
+ <p class="admonition-title">Changed in version 2.0.0</p>
6039
+ <p>The default <code>lookup_field</code> for <code>NautobotUIViewSet</code> has been changed from <code>"slug"</code> to <code>"pk"</code>.</p>
6040
+ </div>
4813
6041
  <div class="admonition note">
4814
6042
  <p class="admonition-title">Note</p>
4815
- <p>Using a field other than the default <code>slug</code> or the alternative field <code>pk</code> (as shown in the example above), may result in certain pieces of the UI not displaying (for example, the edit and delete buttons on the object detail view). This is due to the URL expecting a named key of slug or pk, rather than id.</p>
6043
+ <p>Using a field other than the default <code>pk</code> or the alternative field <code>slug</code> (as shown in the example above), may result in certain pieces of the UI not displaying (for example, the edit and delete buttons on the object detail view). This is due to the URL expecting a named key of slug or pk, rather than id.</p>
4816
6044
  </div>
4817
6045
  <h4 id="view-template-context">View Template Context<a class="headerlink" href="#view-template-context" title="Permanent link">&para;</a></h4>
4818
6046
  <p>Templates can benefit from a very rich context passed down from the views and renderer, including forms, tables, as well as any other information that may be helpful for rendering templates. The keys it provides are as follows:</p>
@@ -4829,18 +6057,18 @@ namedtuple to assist in the configurations.</p>
4829
6057
  <li><code>verbose_name_plural</code>: The plural form of the model's name</li>
4830
6058
  </ul>
4831
6059
  <p>An example from editing a Provider object:</p>
4832
- <div class="highlight"><pre><span></span><code><a id="__codelineno-33-1" name="__codelineno-33-1" href="#__codelineno-33-1"></a><span class="p">{</span>
4833
- <a id="__codelineno-33-2" name="__codelineno-33-2" href="#__codelineno-33-2"></a> <span class="s1">&#39;content_type&#39;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">ContentType</span><span class="p">:</span> <span class="n">circuits</span> <span class="o">|</span> <span class="n">provider</span><span class="o">&gt;</span><span class="p">,</span>
4834
- <a id="__codelineno-33-3" name="__codelineno-33-3" href="#__codelineno-33-3"></a> <span class="s1">&#39;filter_form&#39;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">ProviderFilterForm</span> <span class="n">bound</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">valid</span><span class="o">=</span><span class="n">Unknown</span><span class="p">,</span> <span class="n">fields</span><span class="o">=</span><span class="p">(</span><span class="n">region</span><span class="p">;</span><span class="n">site</span><span class="p">;</span><span class="n">location</span><span class="p">;</span><span class="n">q</span><span class="p">;</span><span class="n">asn</span><span class="p">;</span><span class="n">tag</span><span class="p">)</span><span class="o">&gt;</span><span class="p">,</span>
4835
- <a id="__codelineno-33-4" name="__codelineno-33-4" href="#__codelineno-33-4"></a> <span class="s1">&#39;form&#39;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">ProviderForm</span> <span class="n">bound</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">valid</span><span class="o">=</span><span class="n">Unknown</span><span class="p">,</span> <span class="n">fields</span><span class="o">=</span><span class="p">(</span><span class="n">name</span><span class="p">;</span><span class="n">slug</span><span class="p">;</span><span class="n">asn</span><span class="p">;</span><span class="n">account</span><span class="p">;</span><span class="n">portal_url</span><span class="p">;</span><span class="n">noc_contact</span><span class="p">;</span><span class="n">admin_contact</span><span class="p">;</span><span class="n">comments</span><span class="p">;</span><span class="n">tags</span><span class="p">;</span><span class="n">object_note</span><span class="p">)</span><span class="o">&gt;</span><span class="p">,</span>
4836
- <a id="__codelineno-33-5" name="__codelineno-33-5" href="#__codelineno-33-5"></a> <span class="s1">&#39;object&#39;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Provider</span><span class="p">:</span> <span class="n">NautobotProvider</span><span class="o">&gt;</span><span class="p">,</span>
4837
- <a id="__codelineno-33-6" name="__codelineno-33-6" href="#__codelineno-33-6"></a> <span class="s1">&#39;permissions&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;add&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s1">&#39;change&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s1">&#39;delete&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s1">&#39;view&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">},</span>
4838
- <a id="__codelineno-33-7" name="__codelineno-33-7" href="#__codelineno-33-7"></a> <span class="s1">&#39;return_url&#39;</span><span class="p">:</span> <span class="s1">&#39;/circuits/providers/nautobotprovider&#39;</span><span class="p">,</span>
4839
- <a id="__codelineno-33-8" name="__codelineno-33-8" href="#__codelineno-33-8"></a> <span class="s1">&#39;table&#39;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
4840
- <a id="__codelineno-33-9" name="__codelineno-33-9" href="#__codelineno-33-9"></a> <span class="s1">&#39;table_config_form&#39;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
4841
- <a id="__codelineno-33-10" name="__codelineno-33-10" href="#__codelineno-33-10"></a> <span class="s1">&#39;verbose_name&#39;</span><span class="p">:</span> <span class="s1">&#39;provider&#39;</span><span class="p">,</span>
4842
- <a id="__codelineno-33-11" name="__codelineno-33-11" href="#__codelineno-33-11"></a> <span class="s1">&#39;verbose_name_plural&#39;</span><span class="p">:</span> <span class="s1">&#39;providers&#39;</span>
4843
- <a id="__codelineno-33-12" name="__codelineno-33-12" href="#__codelineno-33-12"></a><span class="p">}</span>
6060
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-37-1" name="__codelineno-37-1" href="#__codelineno-37-1"></a><span class="p">{</span>
6061
+ <a id="__codelineno-37-2" name="__codelineno-37-2" href="#__codelineno-37-2"></a> <span class="s1">&#39;content_type&#39;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">ContentType</span><span class="p">:</span> <span class="n">circuits</span> <span class="o">|</span> <span class="n">provider</span><span class="o">&gt;</span><span class="p">,</span>
6062
+ <a id="__codelineno-37-3" name="__codelineno-37-3" href="#__codelineno-37-3"></a> <span class="s1">&#39;filter_form&#39;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">ProviderFilterForm</span> <span class="n">bound</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">valid</span><span class="o">=</span><span class="n">Unknown</span><span class="p">,</span> <span class="n">fields</span><span class="o">=</span><span class="p">(</span><span class="n">region</span><span class="p">;</span><span class="n">site</span><span class="p">;</span><span class="n">location</span><span class="p">;</span><span class="n">q</span><span class="p">;</span><span class="n">asn</span><span class="p">;</span><span class="n">tag</span><span class="p">)</span><span class="o">&gt;</span><span class="p">,</span>
6063
+ <a id="__codelineno-37-4" name="__codelineno-37-4" href="#__codelineno-37-4"></a> <span class="s1">&#39;form&#39;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">ProviderForm</span> <span class="n">bound</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">valid</span><span class="o">=</span><span class="n">Unknown</span><span class="p">,</span> <span class="n">fields</span><span class="o">=</span><span class="p">(</span><span class="n">name</span><span class="p">;</span><span class="n">slug</span><span class="p">;</span><span class="n">asn</span><span class="p">;</span><span class="n">account</span><span class="p">;</span><span class="n">portal_url</span><span class="p">;</span><span class="n">noc_contact</span><span class="p">;</span><span class="n">admin_contact</span><span class="p">;</span><span class="n">comments</span><span class="p">;</span><span class="n">tags</span><span class="p">;</span><span class="n">object_note</span><span class="p">)</span><span class="o">&gt;</span><span class="p">,</span>
6064
+ <a id="__codelineno-37-5" name="__codelineno-37-5" href="#__codelineno-37-5"></a> <span class="s1">&#39;object&#39;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Provider</span><span class="p">:</span> <span class="n">NautobotProvider</span><span class="o">&gt;</span><span class="p">,</span>
6065
+ <a id="__codelineno-37-6" name="__codelineno-37-6" href="#__codelineno-37-6"></a> <span class="s1">&#39;permissions&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;add&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s1">&#39;change&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s1">&#39;delete&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s1">&#39;view&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">},</span>
6066
+ <a id="__codelineno-37-7" name="__codelineno-37-7" href="#__codelineno-37-7"></a> <span class="s1">&#39;return_url&#39;</span><span class="p">:</span> <span class="s1">&#39;/circuits/providers/nautobotprovider&#39;</span><span class="p">,</span>
6067
+ <a id="__codelineno-37-8" name="__codelineno-37-8" href="#__codelineno-37-8"></a> <span class="s1">&#39;table&#39;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
6068
+ <a id="__codelineno-37-9" name="__codelineno-37-9" href="#__codelineno-37-9"></a> <span class="s1">&#39;table_config_form&#39;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
6069
+ <a id="__codelineno-37-10" name="__codelineno-37-10" href="#__codelineno-37-10"></a> <span class="s1">&#39;verbose_name&#39;</span><span class="p">:</span> <span class="s1">&#39;provider&#39;</span><span class="p">,</span>
6070
+ <a id="__codelineno-37-11" name="__codelineno-37-11" href="#__codelineno-37-11"></a> <span class="s1">&#39;verbose_name_plural&#39;</span><span class="p">:</span> <span class="s1">&#39;providers&#39;</span>
6071
+ <a id="__codelineno-37-12" name="__codelineno-37-12" href="#__codelineno-37-12"></a><span class="p">}</span>
4844
6072
  </code></pre></div>
4845
6073
  <p>Other context keys may be available for certain views:</p>
4846
6074
  <ul>
@@ -4849,36 +6077,39 @@ namedtuple to assist in the configurations.</p>
4849
6077
  </ul>
4850
6078
  <p>You may see other context keys as well, but any not documented above should not be relied upon as they may be removed in a future release. Some examples of those are:</p>
4851
6079
  <ul>
4852
- <li><code>changelog_url</code>: This can now be retrieved from the object itself, via <code>object.get_changelog_url</code>, if the object supports change-logging</li>
4853
6080
  <li><code>obj</code>: Please use <code>object</code> instead</li>
4854
6081
  <li><code>obj_type</code>: Please use <code>verbose_name</code> instead</li>
4855
6082
  <li><code>obj_type_plural</code>: Please use <code>verbose_name_plural</code> instead</li>
4856
6083
  </ul>
6084
+ <div class="admonition version-removed">
6085
+ <p class="admonition-title">Removed in version 2.0.0</p>
6086
+ <p>The <code>changelog_url</code> context key was removed. Use <code>object.get_changelog_url</code> instead.</p>
6087
+ </div>
4857
6088
  <h4 id="excluding-viewmixins-from-nautobotuiviewset">Excluding ViewMixins from NautobotUIViewSet<a class="headerlink" href="#excluding-viewmixins-from-nautobotuiviewset" title="Permanent link">&para;</a></h4>
4858
6089
  <p>For app models that do not require certain views, simply inherit directly from the <code>ViewMixin</code> classes available in <code>nautobot.apps.views</code> instead of <code>NautobotUIViewSet</code>.</p>
4859
6090
  <p>Concrete examples for excluding <code>ViewMixins</code>, checkout <code>CircuitTerminationUIViewSet</code> and <code>CircuitTypeUIViewSet</code> in <code>nautobot.circuits.views</code>.</p>
4860
- <div class="highlight"><pre><span></span><code><a id="__codelineno-34-1" name="__codelineno-34-1" href="#__codelineno-34-1"></a><span class="c1">## An app model viewset that does not support bulk views and operations</span>
4861
- <a id="__codelineno-34-2" name="__codelineno-34-2" href="#__codelineno-34-2"></a><span class="kn">import</span> <span class="nn">nautobot.apps.views</span>
4862
- <a id="__codelineno-34-3" name="__codelineno-34-3" href="#__codelineno-34-3"></a>
4863
- <a id="__codelineno-34-4" name="__codelineno-34-4" href="#__codelineno-34-4"></a><span class="k">class</span> <span class="nc">YourAppModelUIViewSet</span><span class="p">(</span>
4864
- <a id="__codelineno-34-5" name="__codelineno-34-5" href="#__codelineno-34-5"></a> <span class="n">nautobot</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">views</span><span class="o">.</span><span class="n">ObjectListViewMixin</span><span class="p">,</span>
4865
- <a id="__codelineno-34-6" name="__codelineno-34-6" href="#__codelineno-34-6"></a> <span class="n">nautobot</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">views</span><span class="o">.</span><span class="n">ObjectDetailViewMixin</span><span class="p">,</span>
4866
- <a id="__codelineno-34-7" name="__codelineno-34-7" href="#__codelineno-34-7"></a> <span class="n">nautobot</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">views</span><span class="o">.</span><span class="n">ObjectEditViewMixin</span><span class="p">,</span>
4867
- <a id="__codelineno-34-8" name="__codelineno-34-8" href="#__codelineno-34-8"></a> <span class="n">nautobot</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">views</span><span class="o">.</span><span class="n">ObjectDestroyViewMixin</span><span class="p">,</span>
4868
- <a id="__codelineno-34-9" name="__codelineno-34-9" href="#__codelineno-34-9"></a><span class="p">):</span>
4869
- <a id="__codelineno-34-10" name="__codelineno-34-10" href="#__codelineno-34-10"></a>
4870
- <a id="__codelineno-34-11" name="__codelineno-34-11" href="#__codelineno-34-11"></a> <span class="n">filterset_class</span> <span class="o">=</span> <span class="n">YourAppModelFilterSet</span>
4871
- <a id="__codelineno-34-12" name="__codelineno-34-12" href="#__codelineno-34-12"></a> <span class="n">filterset_form_class</span> <span class="o">=</span> <span class="n">YourAppModelFilterForm</span>
4872
- <a id="__codelineno-34-13" name="__codelineno-34-13" href="#__codelineno-34-13"></a> <span class="n">form_class</span> <span class="o">=</span> <span class="n">YourAppModelForm</span>
4873
- <a id="__codelineno-34-14" name="__codelineno-34-14" href="#__codelineno-34-14"></a> <span class="n">queryset</span> <span class="o">=</span> <span class="n">YourAppModel</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
4874
- <a id="__codelineno-34-15" name="__codelineno-34-15" href="#__codelineno-34-15"></a> <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">YourAppModelSerializer</span>
4875
- <a id="__codelineno-34-16" name="__codelineno-34-16" href="#__codelineno-34-16"></a> <span class="n">table_class</span> <span class="o">=</span> <span class="n">YourAppModelTable</span>
4876
- <a id="__codelineno-34-17" name="__codelineno-34-17" href="#__codelineno-34-17"></a> <span class="c1"># You do not need to specify attributes that are not needed.</span>
6091
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-38-1" name="__codelineno-38-1" href="#__codelineno-38-1"></a><span class="c1">## An app model viewset that does not support bulk views and operations</span>
6092
+ <a id="__codelineno-38-2" name="__codelineno-38-2" href="#__codelineno-38-2"></a><span class="kn">import</span> <span class="nn">nautobot.apps.views</span>
6093
+ <a id="__codelineno-38-3" name="__codelineno-38-3" href="#__codelineno-38-3"></a>
6094
+ <a id="__codelineno-38-4" name="__codelineno-38-4" href="#__codelineno-38-4"></a><span class="k">class</span> <span class="nc">YourAppModelUIViewSet</span><span class="p">(</span>
6095
+ <a id="__codelineno-38-5" name="__codelineno-38-5" href="#__codelineno-38-5"></a> <span class="n">nautobot</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">views</span><span class="o">.</span><span class="n">ObjectListViewMixin</span><span class="p">,</span>
6096
+ <a id="__codelineno-38-6" name="__codelineno-38-6" href="#__codelineno-38-6"></a> <span class="n">nautobot</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">views</span><span class="o">.</span><span class="n">ObjectDetailViewMixin</span><span class="p">,</span>
6097
+ <a id="__codelineno-38-7" name="__codelineno-38-7" href="#__codelineno-38-7"></a> <span class="n">nautobot</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">views</span><span class="o">.</span><span class="n">ObjectEditViewMixin</span><span class="p">,</span>
6098
+ <a id="__codelineno-38-8" name="__codelineno-38-8" href="#__codelineno-38-8"></a> <span class="n">nautobot</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">views</span><span class="o">.</span><span class="n">ObjectDestroyViewMixin</span><span class="p">,</span>
6099
+ <a id="__codelineno-38-9" name="__codelineno-38-9" href="#__codelineno-38-9"></a><span class="p">):</span>
6100
+ <a id="__codelineno-38-10" name="__codelineno-38-10" href="#__codelineno-38-10"></a>
6101
+ <a id="__codelineno-38-11" name="__codelineno-38-11" href="#__codelineno-38-11"></a> <span class="n">filterset_class</span> <span class="o">=</span> <span class="n">YourAppModelFilterSet</span>
6102
+ <a id="__codelineno-38-12" name="__codelineno-38-12" href="#__codelineno-38-12"></a> <span class="n">filterset_form_class</span> <span class="o">=</span> <span class="n">YourAppModelFilterForm</span>
6103
+ <a id="__codelineno-38-13" name="__codelineno-38-13" href="#__codelineno-38-13"></a> <span class="n">form_class</span> <span class="o">=</span> <span class="n">YourAppModelForm</span>
6104
+ <a id="__codelineno-38-14" name="__codelineno-38-14" href="#__codelineno-38-14"></a> <span class="n">queryset</span> <span class="o">=</span> <span class="n">YourAppModel</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
6105
+ <a id="__codelineno-38-15" name="__codelineno-38-15" href="#__codelineno-38-15"></a> <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">YourAppModelSerializer</span>
6106
+ <a id="__codelineno-38-16" name="__codelineno-38-16" href="#__codelineno-38-16"></a> <span class="n">table_class</span> <span class="o">=</span> <span class="n">YourAppModelTable</span>
6107
+ <a id="__codelineno-38-17" name="__codelineno-38-17" href="#__codelineno-38-17"></a> <span class="c1"># You do not need to specify attributes that are not needed.</span>
4877
6108
  </code></pre></div>
4878
6109
  <p>Excluding unwanted urls from <code>NautobotUIViewSetRouter</code> is done for you at the ViewSet level. If you do not inherit the unwanted ViewMixins, the corresponding route from the router will not be published.</p>
4879
- <div class="highlight"><pre><span></span><code><a id="__codelineno-35-1" name="__codelineno-35-1" href="#__codelineno-35-1"></a><span class="c1"># urls.py</span>
4880
- <a id="__codelineno-35-2" name="__codelineno-35-2" href="#__codelineno-35-2"></a><span class="c1"># All the urls correspond to BulkViewMixins will not be published when you register your ViewSet with the router.</span>
4881
- <a id="__codelineno-35-3" name="__codelineno-35-3" href="#__codelineno-35-3"></a><span class="n">router</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">&quot;yourappmodel&quot;</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">YourAppModelUIViewSet</span><span class="p">)</span>
6110
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-39-1" name="__codelineno-39-1" href="#__codelineno-39-1"></a><span class="c1"># urls.py</span>
6111
+ <a id="__codelineno-39-2" name="__codelineno-39-2" href="#__codelineno-39-2"></a><span class="c1"># All the urls correspond to BulkViewMixins will not be published when you register your ViewSet with the router.</span>
6112
+ <a id="__codelineno-39-3" name="__codelineno-39-3" href="#__codelineno-39-3"></a><span class="n">router</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">&quot;yourappmodel&quot;</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">YourAppModelUIViewSet</span><span class="p">)</span>
4882
6113
  </code></pre></div>
4883
6114
  <h4 id="template-naming-for-nautobotuiviewset">Template Naming for NautobotUIViewSet<a class="headerlink" href="#template-naming-for-nautobotuiviewset" title="Permanent link">&para;</a></h4>
4884
6115
  <p>Template naming is very intuitive in NautobotUIViewSet. In <code>templates/yourapp</code> folder, name your templates following the convention <code>{model_name}_{action}.html</code>.</p>
@@ -4927,34 +6158,34 @@ namedtuple to assist in the configurations.</p>
4927
6158
  <p>With <code>NautobotUIViewSet</code> as the base UI ViewSet for <code>YourAppModel</code>, it is required to register your urls with the help of <code>NautobotUIViewSetRouter</code>.</p>
4928
6159
  <p>For a concrete example on how to use <code>NautobotUIViewSetRouter</code>, see <code>nautobot.circuits.urls</code>.</p>
4929
6160
  <p>Below is a theoretical <code>urls.py</code> file for <code>YourAppModel</code>:</p>
4930
- <div class="highlight"><pre><span></span><code><a id="__codelineno-36-1" name="__codelineno-36-1" href="#__codelineno-36-1"></a><span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">path</span>
4931
- <a id="__codelineno-36-2" name="__codelineno-36-2" href="#__codelineno-36-2"></a>
4932
- <a id="__codelineno-36-3" name="__codelineno-36-3" href="#__codelineno-36-3"></a><span class="kn">from</span> <span class="nn">nautobot.apps.urls</span> <span class="kn">import</span> <span class="n">NautobotUIViewSetRouter</span>
4933
- <a id="__codelineno-36-4" name="__codelineno-36-4" href="#__codelineno-36-4"></a><span class="kn">from</span> <span class="nn">your_app</span> <span class="kn">import</span> <span class="n">views</span>
4934
- <a id="__codelineno-36-5" name="__codelineno-36-5" href="#__codelineno-36-5"></a>
4935
- <a id="__codelineno-36-6" name="__codelineno-36-6" href="#__codelineno-36-6"></a>
4936
- <a id="__codelineno-36-7" name="__codelineno-36-7" href="#__codelineno-36-7"></a><span class="n">router</span> <span class="o">=</span> <span class="n">NautobotUIViewSetRouter</span><span class="p">()</span>
4937
- <a id="__codelineno-36-8" name="__codelineno-36-8" href="#__codelineno-36-8"></a><span class="n">router</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">&quot;yourappmodel&quot;</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">YourAppModelUIViewSet</span><span class="p">)</span>
4938
- <a id="__codelineno-36-9" name="__codelineno-36-9" href="#__codelineno-36-9"></a>
4939
- <a id="__codelineno-36-10" name="__codelineno-36-10" href="#__codelineno-36-10"></a><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
4940
- <a id="__codelineno-36-11" name="__codelineno-36-11" href="#__codelineno-36-11"></a> <span class="c1"># Extra urls that do not follow the patterns of `NautobotUIViewSetRouter` go here.</span>
4941
- <a id="__codelineno-36-12" name="__codelineno-36-12" href="#__codelineno-36-12"></a> <span class="c1"># changelog, notes and etc.</span>
4942
- <a id="__codelineno-36-13" name="__codelineno-36-13" href="#__codelineno-36-13"></a> <span class="o">...</span>
4943
- <a id="__codelineno-36-14" name="__codelineno-36-14" href="#__codelineno-36-14"></a> <span class="n">path</span><span class="p">(</span>
4944
- <a id="__codelineno-36-15" name="__codelineno-36-15" href="#__codelineno-36-15"></a> <span class="s2">&quot;yourappmodels/&lt;slug:slug&gt;/changelog/&quot;</span><span class="p">,</span>
4945
- <a id="__codelineno-36-16" name="__codelineno-36-16" href="#__codelineno-36-16"></a> <span class="n">ObjectChangeLogView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
4946
- <a id="__codelineno-36-17" name="__codelineno-36-17" href="#__codelineno-36-17"></a> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;yourappmodel_changelog&quot;</span><span class="p">,</span>
4947
- <a id="__codelineno-36-18" name="__codelineno-36-18" href="#__codelineno-36-18"></a> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;model&quot;</span><span class="p">:</span> <span class="n">yourappmodel</span><span class="p">},</span>
4948
- <a id="__codelineno-36-19" name="__codelineno-36-19" href="#__codelineno-36-19"></a> <span class="p">),</span>
4949
- <a id="__codelineno-36-20" name="__codelineno-36-20" href="#__codelineno-36-20"></a> <span class="n">path</span><span class="p">(</span>
4950
- <a id="__codelineno-36-21" name="__codelineno-36-21" href="#__codelineno-36-21"></a> <span class="s2">&quot;yourappmodels/&lt;slug:slug&gt;/notes/&quot;</span><span class="p">,</span>
4951
- <a id="__codelineno-36-22" name="__codelineno-36-22" href="#__codelineno-36-22"></a> <span class="n">ObjectNotesView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
4952
- <a id="__codelineno-36-23" name="__codelineno-36-23" href="#__codelineno-36-23"></a> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;yourappmodel_notes&quot;</span><span class="p">,</span>
4953
- <a id="__codelineno-36-24" name="__codelineno-36-24" href="#__codelineno-36-24"></a> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;model&quot;</span><span class="p">:</span> <span class="n">yourappmodel</span><span class="p">},</span>
4954
- <a id="__codelineno-36-25" name="__codelineno-36-25" href="#__codelineno-36-25"></a> <span class="p">),</span>
4955
- <a id="__codelineno-36-26" name="__codelineno-36-26" href="#__codelineno-36-26"></a> <span class="o">...</span>
4956
- <a id="__codelineno-36-27" name="__codelineno-36-27" href="#__codelineno-36-27"></a><span class="p">]</span>
4957
- <a id="__codelineno-36-28" name="__codelineno-36-28" href="#__codelineno-36-28"></a><span class="n">urlpatterns</span> <span class="o">+=</span> <span class="n">router</span><span class="o">.</span><span class="n">urls</span>
6161
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-40-1" name="__codelineno-40-1" href="#__codelineno-40-1"></a><span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">path</span>
6162
+ <a id="__codelineno-40-2" name="__codelineno-40-2" href="#__codelineno-40-2"></a>
6163
+ <a id="__codelineno-40-3" name="__codelineno-40-3" href="#__codelineno-40-3"></a><span class="kn">from</span> <span class="nn">nautobot.apps.urls</span> <span class="kn">import</span> <span class="n">NautobotUIViewSetRouter</span>
6164
+ <a id="__codelineno-40-4" name="__codelineno-40-4" href="#__codelineno-40-4"></a><span class="kn">from</span> <span class="nn">your_app</span> <span class="kn">import</span> <span class="n">views</span>
6165
+ <a id="__codelineno-40-5" name="__codelineno-40-5" href="#__codelineno-40-5"></a>
6166
+ <a id="__codelineno-40-6" name="__codelineno-40-6" href="#__codelineno-40-6"></a>
6167
+ <a id="__codelineno-40-7" name="__codelineno-40-7" href="#__codelineno-40-7"></a><span class="n">router</span> <span class="o">=</span> <span class="n">NautobotUIViewSetRouter</span><span class="p">()</span>
6168
+ <a id="__codelineno-40-8" name="__codelineno-40-8" href="#__codelineno-40-8"></a><span class="n">router</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">&quot;yourappmodel&quot;</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">YourAppModelUIViewSet</span><span class="p">)</span>
6169
+ <a id="__codelineno-40-9" name="__codelineno-40-9" href="#__codelineno-40-9"></a>
6170
+ <a id="__codelineno-40-10" name="__codelineno-40-10" href="#__codelineno-40-10"></a><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
6171
+ <a id="__codelineno-40-11" name="__codelineno-40-11" href="#__codelineno-40-11"></a> <span class="c1"># Extra urls that do not follow the patterns of `NautobotUIViewSetRouter` go here.</span>
6172
+ <a id="__codelineno-40-12" name="__codelineno-40-12" href="#__codelineno-40-12"></a> <span class="c1"># changelog, notes and etc.</span>
6173
+ <a id="__codelineno-40-13" name="__codelineno-40-13" href="#__codelineno-40-13"></a> <span class="o">...</span>
6174
+ <a id="__codelineno-40-14" name="__codelineno-40-14" href="#__codelineno-40-14"></a> <span class="n">path</span><span class="p">(</span>
6175
+ <a id="__codelineno-40-15" name="__codelineno-40-15" href="#__codelineno-40-15"></a> <span class="s2">&quot;yourappmodels/&lt;uuid:pk&gt;/changelog/&quot;</span><span class="p">,</span>
6176
+ <a id="__codelineno-40-16" name="__codelineno-40-16" href="#__codelineno-40-16"></a> <span class="n">ObjectChangeLogView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
6177
+ <a id="__codelineno-40-17" name="__codelineno-40-17" href="#__codelineno-40-17"></a> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;yourappmodel_changelog&quot;</span><span class="p">,</span>
6178
+ <a id="__codelineno-40-18" name="__codelineno-40-18" href="#__codelineno-40-18"></a> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;model&quot;</span><span class="p">:</span> <span class="n">yourappmodel</span><span class="p">},</span>
6179
+ <a id="__codelineno-40-19" name="__codelineno-40-19" href="#__codelineno-40-19"></a> <span class="p">),</span>
6180
+ <a id="__codelineno-40-20" name="__codelineno-40-20" href="#__codelineno-40-20"></a> <span class="n">path</span><span class="p">(</span>
6181
+ <a id="__codelineno-40-21" name="__codelineno-40-21" href="#__codelineno-40-21"></a> <span class="s2">&quot;yourappmodels/&lt;uuid:pk&gt;/notes/&quot;</span><span class="p">,</span>
6182
+ <a id="__codelineno-40-22" name="__codelineno-40-22" href="#__codelineno-40-22"></a> <span class="n">ObjectNotesView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
6183
+ <a id="__codelineno-40-23" name="__codelineno-40-23" href="#__codelineno-40-23"></a> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;yourappmodel_notes&quot;</span><span class="p">,</span>
6184
+ <a id="__codelineno-40-24" name="__codelineno-40-24" href="#__codelineno-40-24"></a> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;model&quot;</span><span class="p">:</span> <span class="n">yourappmodel</span><span class="p">},</span>
6185
+ <a id="__codelineno-40-25" name="__codelineno-40-25" href="#__codelineno-40-25"></a> <span class="p">),</span>
6186
+ <a id="__codelineno-40-26" name="__codelineno-40-26" href="#__codelineno-40-26"></a> <span class="o">...</span>
6187
+ <a id="__codelineno-40-27" name="__codelineno-40-27" href="#__codelineno-40-27"></a><span class="p">]</span>
6188
+ <a id="__codelineno-40-28" name="__codelineno-40-28" href="#__codelineno-40-28"></a><span class="n">urlpatterns</span> <span class="o">+=</span> <span class="n">router</span><span class="o">.</span><span class="n">urls</span>
4958
6189
  </code></pre></div>
4959
6190
  <div class="admonition version-added">
4960
6191
  <p class="admonition-title">Added in version 1.5.1</p>
@@ -4962,21 +6193,21 @@ namedtuple to assist in the configurations.</p>
4962
6193
  </div>
4963
6194
  <h3 id="utilizing-generic-django-views">Utilizing Generic Django Views<a class="headerlink" href="#utilizing-generic-django-views" title="Permanent link">&para;</a></h3>
4964
6195
  <p>The use of <code>generic</code> Django views can aid in app development. As an example, let's write a view which displays a random animal and the sound it makes. First, create the view in <code>views.py</code>:</p>
4965
- <div class="highlight"><pre><span></span><code><a id="__codelineno-37-1" name="__codelineno-37-1" href="#__codelineno-37-1"></a><span class="c1"># views.py</span>
4966
- <a id="__codelineno-37-2" name="__codelineno-37-2" href="#__codelineno-37-2"></a><span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">render</span>
4967
- <a id="__codelineno-37-3" name="__codelineno-37-3" href="#__codelineno-37-3"></a><span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">View</span>
4968
- <a id="__codelineno-37-4" name="__codelineno-37-4" href="#__codelineno-37-4"></a>
4969
- <a id="__codelineno-37-5" name="__codelineno-37-5" href="#__codelineno-37-5"></a><span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Animal</span>
4970
- <a id="__codelineno-37-6" name="__codelineno-37-6" href="#__codelineno-37-6"></a>
4971
- <a id="__codelineno-37-7" name="__codelineno-37-7" href="#__codelineno-37-7"></a>
4972
- <a id="__codelineno-37-8" name="__codelineno-37-8" href="#__codelineno-37-8"></a><span class="k">class</span> <span class="nc">RandomAnimalView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
4973
- <a id="__codelineno-37-9" name="__codelineno-37-9" href="#__codelineno-37-9"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Display a randomly-selected Animal.&quot;&quot;&quot;</span>
4974
- <a id="__codelineno-37-10" name="__codelineno-37-10" href="#__codelineno-37-10"></a>
4975
- <a id="__codelineno-37-11" name="__codelineno-37-11" href="#__codelineno-37-11"></a> <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
4976
- <a id="__codelineno-37-12" name="__codelineno-37-12" href="#__codelineno-37-12"></a> <span class="n">animal</span> <span class="o">=</span> <span class="n">Animal</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;?&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
4977
- <a id="__codelineno-37-13" name="__codelineno-37-13" href="#__codelineno-37-13"></a> <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">&#39;nautobot_animal_sounds/animal.html&#39;</span><span class="p">,</span> <span class="p">{</span>
4978
- <a id="__codelineno-37-14" name="__codelineno-37-14" href="#__codelineno-37-14"></a> <span class="s1">&#39;animal&#39;</span><span class="p">:</span> <span class="n">animal</span><span class="p">,</span>
4979
- <a id="__codelineno-37-15" name="__codelineno-37-15" href="#__codelineno-37-15"></a> <span class="p">})</span>
6196
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-41-1" name="__codelineno-41-1" href="#__codelineno-41-1"></a><span class="c1"># views.py</span>
6197
+ <a id="__codelineno-41-2" name="__codelineno-41-2" href="#__codelineno-41-2"></a><span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">render</span>
6198
+ <a id="__codelineno-41-3" name="__codelineno-41-3" href="#__codelineno-41-3"></a><span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">View</span>
6199
+ <a id="__codelineno-41-4" name="__codelineno-41-4" href="#__codelineno-41-4"></a>
6200
+ <a id="__codelineno-41-5" name="__codelineno-41-5" href="#__codelineno-41-5"></a><span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Animal</span>
6201
+ <a id="__codelineno-41-6" name="__codelineno-41-6" href="#__codelineno-41-6"></a>
6202
+ <a id="__codelineno-41-7" name="__codelineno-41-7" href="#__codelineno-41-7"></a>
6203
+ <a id="__codelineno-41-8" name="__codelineno-41-8" href="#__codelineno-41-8"></a><span class="k">class</span> <span class="nc">RandomAnimalView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
6204
+ <a id="__codelineno-41-9" name="__codelineno-41-9" href="#__codelineno-41-9"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Display a randomly-selected Animal.&quot;&quot;&quot;</span>
6205
+ <a id="__codelineno-41-10" name="__codelineno-41-10" href="#__codelineno-41-10"></a>
6206
+ <a id="__codelineno-41-11" name="__codelineno-41-11" href="#__codelineno-41-11"></a> <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
6207
+ <a id="__codelineno-41-12" name="__codelineno-41-12" href="#__codelineno-41-12"></a> <span class="n">animal</span> <span class="o">=</span> <span class="n">Animal</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;?&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
6208
+ <a id="__codelineno-41-13" name="__codelineno-41-13" href="#__codelineno-41-13"></a> <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">&#39;nautobot_animal_sounds/animal.html&#39;</span><span class="p">,</span> <span class="p">{</span>
6209
+ <a id="__codelineno-41-14" name="__codelineno-41-14" href="#__codelineno-41-14"></a> <span class="s1">&#39;animal&#39;</span><span class="p">:</span> <span class="n">animal</span><span class="p">,</span>
6210
+ <a id="__codelineno-41-15" name="__codelineno-41-15" href="#__codelineno-41-15"></a> <span class="p">})</span>
4980
6211
  </code></pre></div>
4981
6212
  <p>This view retrieves a random animal from the database and and passes it as a context variable when rendering a template named <code>animal.html</code>, which doesn't exist yet. To create this template, first create a directory named <code>templates/nautobot_animal_sounds/</code> within the app source directory. (We use the app's name as a subdirectory to guard against naming collisions with other apps.) Then, create a template named <code>animal.html</code> as described below.</p>
4982
6213
  <h3 id="utilizing-nautobot-generic-views">Utilizing Nautobot Generic Views<a class="headerlink" href="#utilizing-nautobot-generic-views" title="Permanent link">&para;</a></h3>
@@ -4994,25 +6225,25 @@ namedtuple to assist in the configurations.</p>
4994
6225
  <li><code>javascript</code> - A section at the end of the page for including Javascript code</li>
4995
6226
  </ul>
4996
6227
  <p>For more information on how template blocks work, consult the <a href="https://docs.djangoproject.com/en/stable/ref/templates/builtins/#block">Django documentation</a>.</p>
4997
- <div class="highlight"><pre><span></span><code><a id="__codelineno-38-1" name="__codelineno-38-1" href="#__codelineno-38-1"></a>{# templates/nautobot_animal_sounds/animal.html #}
4998
- <a id="__codelineno-38-2" name="__codelineno-38-2" href="#__codelineno-38-2"></a>{% extends &#39;base.html&#39; %}
4999
- <a id="__codelineno-38-3" name="__codelineno-38-3" href="#__codelineno-38-3"></a>
5000
- <a id="__codelineno-38-4" name="__codelineno-38-4" href="#__codelineno-38-4"></a>{% block content %}
5001
- <a id="__codelineno-38-5" name="__codelineno-38-5" href="#__codelineno-38-5"></a> {% with config=settings.PLUGINS_CONFIG.nautobot_animal_sounds %}
5002
- <a id="__codelineno-38-6" name="__codelineno-38-6" href="#__codelineno-38-6"></a> &lt;h2 class=&quot;text-center&quot; style=&quot;margin-top: 200px&quot;&gt;
5003
- <a id="__codelineno-38-7" name="__codelineno-38-7" href="#__codelineno-38-7"></a> {% if animal %}
5004
- <a id="__codelineno-38-8" name="__codelineno-38-8" href="#__codelineno-38-8"></a> The {{ animal.name|lower }} says
5005
- <a id="__codelineno-38-9" name="__codelineno-38-9" href="#__codelineno-38-9"></a> {% if config.loud %}
5006
- <a id="__codelineno-38-10" name="__codelineno-38-10" href="#__codelineno-38-10"></a> {{ animal.sound|upper }}!
5007
- <a id="__codelineno-38-11" name="__codelineno-38-11" href="#__codelineno-38-11"></a> {% else %}
5008
- <a id="__codelineno-38-12" name="__codelineno-38-12" href="#__codelineno-38-12"></a> {{ animal.sound }}
5009
- <a id="__codelineno-38-13" name="__codelineno-38-13" href="#__codelineno-38-13"></a> {% endif %}
5010
- <a id="__codelineno-38-14" name="__codelineno-38-14" href="#__codelineno-38-14"></a> {% else %}
5011
- <a id="__codelineno-38-15" name="__codelineno-38-15" href="#__codelineno-38-15"></a> No animals have been created yet!
5012
- <a id="__codelineno-38-16" name="__codelineno-38-16" href="#__codelineno-38-16"></a> {% endif %}
5013
- <a id="__codelineno-38-17" name="__codelineno-38-17" href="#__codelineno-38-17"></a> &lt;/h2&gt;
5014
- <a id="__codelineno-38-18" name="__codelineno-38-18" href="#__codelineno-38-18"></a> {% endwith %}
5015
- <a id="__codelineno-38-19" name="__codelineno-38-19" href="#__codelineno-38-19"></a>{% endblock %}
6228
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-42-1" name="__codelineno-42-1" href="#__codelineno-42-1"></a>{# templates/nautobot_animal_sounds/animal.html #}
6229
+ <a id="__codelineno-42-2" name="__codelineno-42-2" href="#__codelineno-42-2"></a>{% extends &#39;base.html&#39; %}
6230
+ <a id="__codelineno-42-3" name="__codelineno-42-3" href="#__codelineno-42-3"></a>
6231
+ <a id="__codelineno-42-4" name="__codelineno-42-4" href="#__codelineno-42-4"></a>{% block content %}
6232
+ <a id="__codelineno-42-5" name="__codelineno-42-5" href="#__codelineno-42-5"></a> {% with config=settings.PLUGINS_CONFIG.nautobot_animal_sounds %}
6233
+ <a id="__codelineno-42-6" name="__codelineno-42-6" href="#__codelineno-42-6"></a> &lt;h2 class=&quot;text-center&quot; style=&quot;margin-top: 200px&quot;&gt;
6234
+ <a id="__codelineno-42-7" name="__codelineno-42-7" href="#__codelineno-42-7"></a> {% if animal %}
6235
+ <a id="__codelineno-42-8" name="__codelineno-42-8" href="#__codelineno-42-8"></a> The {{ animal.name|lower }} says
6236
+ <a id="__codelineno-42-9" name="__codelineno-42-9" href="#__codelineno-42-9"></a> {% if config.loud %}
6237
+ <a id="__codelineno-42-10" name="__codelineno-42-10" href="#__codelineno-42-10"></a> {{ animal.sound|upper }}!
6238
+ <a id="__codelineno-42-11" name="__codelineno-42-11" href="#__codelineno-42-11"></a> {% else %}
6239
+ <a id="__codelineno-42-12" name="__codelineno-42-12" href="#__codelineno-42-12"></a> {{ animal.sound }}
6240
+ <a id="__codelineno-42-13" name="__codelineno-42-13" href="#__codelineno-42-13"></a> {% endif %}
6241
+ <a id="__codelineno-42-14" name="__codelineno-42-14" href="#__codelineno-42-14"></a> {% else %}
6242
+ <a id="__codelineno-42-15" name="__codelineno-42-15" href="#__codelineno-42-15"></a> No animals have been created yet!
6243
+ <a id="__codelineno-42-16" name="__codelineno-42-16" href="#__codelineno-42-16"></a> {% endif %}
6244
+ <a id="__codelineno-42-17" name="__codelineno-42-17" href="#__codelineno-42-17"></a> &lt;/h2&gt;
6245
+ <a id="__codelineno-42-18" name="__codelineno-42-18" href="#__codelineno-42-18"></a> {% endwith %}
6246
+ <a id="__codelineno-42-19" name="__codelineno-42-19" href="#__codelineno-42-19"></a>{% endblock %}
5016
6247
  </code></pre></div>
5017
6248
  <p>The first line of the template instructs Django to extend the Nautobot base template and inject our custom content within its <code>content</code> block.</p>
5018
6249
  <div class="admonition note">
@@ -5021,15 +6252,15 @@ namedtuple to assist in the configurations.</p>
5021
6252
  </div>
5022
6253
  <h3 id="registering-url-patterns">Registering URL Patterns<a class="headerlink" href="#registering-url-patterns" title="Permanent link">&para;</a></h3>
5023
6254
  <p>Finally, to make the view accessible to users, we need to register a URL for it. We do this in <code>urls.py</code> by defining a <code>urlpatterns</code> variable containing a list of paths.</p>
5024
- <div class="highlight"><pre><span></span><code><a id="__codelineno-39-1" name="__codelineno-39-1" href="#__codelineno-39-1"></a><span class="c1"># urls.py</span>
5025
- <a id="__codelineno-39-2" name="__codelineno-39-2" href="#__codelineno-39-2"></a><span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">path</span>
5026
- <a id="__codelineno-39-3" name="__codelineno-39-3" href="#__codelineno-39-3"></a>
5027
- <a id="__codelineno-39-4" name="__codelineno-39-4" href="#__codelineno-39-4"></a><span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">views</span>
5028
- <a id="__codelineno-39-5" name="__codelineno-39-5" href="#__codelineno-39-5"></a>
5029
- <a id="__codelineno-39-6" name="__codelineno-39-6" href="#__codelineno-39-6"></a>
5030
- <a id="__codelineno-39-7" name="__codelineno-39-7" href="#__codelineno-39-7"></a><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
5031
- <a id="__codelineno-39-8" name="__codelineno-39-8" href="#__codelineno-39-8"></a> <span class="n">path</span><span class="p">(</span><span class="s1">&#39;random/&#39;</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">RandomAnimalView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;random_animal&#39;</span><span class="p">),</span>
5032
- <a id="__codelineno-39-9" name="__codelineno-39-9" href="#__codelineno-39-9"></a><span class="p">]</span>
6255
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-43-1" name="__codelineno-43-1" href="#__codelineno-43-1"></a><span class="c1"># urls.py</span>
6256
+ <a id="__codelineno-43-2" name="__codelineno-43-2" href="#__codelineno-43-2"></a><span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">path</span>
6257
+ <a id="__codelineno-43-3" name="__codelineno-43-3" href="#__codelineno-43-3"></a>
6258
+ <a id="__codelineno-43-4" name="__codelineno-43-4" href="#__codelineno-43-4"></a><span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">views</span>
6259
+ <a id="__codelineno-43-5" name="__codelineno-43-5" href="#__codelineno-43-5"></a>
6260
+ <a id="__codelineno-43-6" name="__codelineno-43-6" href="#__codelineno-43-6"></a>
6261
+ <a id="__codelineno-43-7" name="__codelineno-43-7" href="#__codelineno-43-7"></a><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
6262
+ <a id="__codelineno-43-8" name="__codelineno-43-8" href="#__codelineno-43-8"></a> <span class="n">path</span><span class="p">(</span><span class="s1">&#39;random/&#39;</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">RandomAnimalView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;random_animal&#39;</span><span class="p">),</span>
6263
+ <a id="__codelineno-43-9" name="__codelineno-43-9" href="#__codelineno-43-9"></a><span class="p">]</span>
5033
6264
  </code></pre></div>
5034
6265
  <p>A URL pattern has three components:</p>
5035
6266
  <ul>
@@ -5045,51 +6276,51 @@ namedtuple to assist in the configurations.</p>
5045
6276
  <h2 id="adding-rest-api-endpoints">Adding REST API Endpoints<a class="headerlink" href="#adding-rest-api-endpoints" title="Permanent link">&para;</a></h2>
5046
6277
  <p>Apps can declare custom endpoints on Nautobot's REST API to retrieve or manipulate models or other data. These behave very similarly to views, except that instead of rendering arbitrary content using a template, data is returned in JSON format using a serializer. Nautobot uses the <a href="https://www.django-rest-framework.org/">Django REST Framework</a>, which makes writing API serializers and views very simple.</p>
5047
6278
  <p>First, create a serializer for the <code>Animal</code> model, in <code>api/serializers.py</code>:</p>
5048
- <div class="highlight"><pre><span></span><code><a id="__codelineno-40-1" name="__codelineno-40-1" href="#__codelineno-40-1"></a><span class="c1"># api/serializers.py</span>
5049
- <a id="__codelineno-40-2" name="__codelineno-40-2" href="#__codelineno-40-2"></a><span class="kn">from</span> <span class="nn">nautobot.apps.api</span> <span class="kn">import</span> <span class="n">ValidatedModelSerializer</span>
5050
- <a id="__codelineno-40-3" name="__codelineno-40-3" href="#__codelineno-40-3"></a>
5051
- <a id="__codelineno-40-4" name="__codelineno-40-4" href="#__codelineno-40-4"></a><span class="kn">from</span> <span class="nn">nautobot_animal_sounds.models</span> <span class="kn">import</span> <span class="n">Animal</span>
5052
- <a id="__codelineno-40-5" name="__codelineno-40-5" href="#__codelineno-40-5"></a>
5053
- <a id="__codelineno-40-6" name="__codelineno-40-6" href="#__codelineno-40-6"></a>
5054
- <a id="__codelineno-40-7" name="__codelineno-40-7" href="#__codelineno-40-7"></a><span class="k">class</span> <span class="nc">AnimalSerializer</span><span class="p">(</span><span class="n">ValidatedModelSerializer</span><span class="p">):</span>
5055
- <a id="__codelineno-40-8" name="__codelineno-40-8" href="#__codelineno-40-8"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;API serializer for interacting with Animal objects.&quot;&quot;&quot;</span>
5056
- <a id="__codelineno-40-9" name="__codelineno-40-9" href="#__codelineno-40-9"></a>
5057
- <a id="__codelineno-40-10" name="__codelineno-40-10" href="#__codelineno-40-10"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
5058
- <a id="__codelineno-40-11" name="__codelineno-40-11" href="#__codelineno-40-11"></a> <span class="n">model</span> <span class="o">=</span> <span class="n">Animal</span>
5059
- <a id="__codelineno-40-12" name="__codelineno-40-12" href="#__codelineno-40-12"></a> <span class="n">fields</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="s1">&#39;sound&#39;</span><span class="p">)</span>
6279
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-44-1" name="__codelineno-44-1" href="#__codelineno-44-1"></a><span class="c1"># api/serializers.py</span>
6280
+ <a id="__codelineno-44-2" name="__codelineno-44-2" href="#__codelineno-44-2"></a><span class="kn">from</span> <span class="nn">nautobot.apps.api</span> <span class="kn">import</span> <span class="n">ValidatedModelSerializer</span>
6281
+ <a id="__codelineno-44-3" name="__codelineno-44-3" href="#__codelineno-44-3"></a>
6282
+ <a id="__codelineno-44-4" name="__codelineno-44-4" href="#__codelineno-44-4"></a><span class="kn">from</span> <span class="nn">nautobot_animal_sounds.models</span> <span class="kn">import</span> <span class="n">Animal</span>
6283
+ <a id="__codelineno-44-5" name="__codelineno-44-5" href="#__codelineno-44-5"></a>
6284
+ <a id="__codelineno-44-6" name="__codelineno-44-6" href="#__codelineno-44-6"></a>
6285
+ <a id="__codelineno-44-7" name="__codelineno-44-7" href="#__codelineno-44-7"></a><span class="k">class</span> <span class="nc">AnimalSerializer</span><span class="p">(</span><span class="n">ValidatedModelSerializer</span><span class="p">):</span>
6286
+ <a id="__codelineno-44-8" name="__codelineno-44-8" href="#__codelineno-44-8"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;API serializer for interacting with Animal objects.&quot;&quot;&quot;</span>
6287
+ <a id="__codelineno-44-9" name="__codelineno-44-9" href="#__codelineno-44-9"></a>
6288
+ <a id="__codelineno-44-10" name="__codelineno-44-10" href="#__codelineno-44-10"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
6289
+ <a id="__codelineno-44-11" name="__codelineno-44-11" href="#__codelineno-44-11"></a> <span class="n">model</span> <span class="o">=</span> <span class="n">Animal</span>
6290
+ <a id="__codelineno-44-12" name="__codelineno-44-12" href="#__codelineno-44-12"></a> <span class="n">fields</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="s1">&#39;sound&#39;</span><span class="p">)</span>
5060
6291
  </code></pre></div>
5061
6292
  <div class="admonition tip">
5062
6293
  <p class="admonition-title">Tip</p>
5063
6294
  <p>For more full-featured models, you should use one of the other base classes from <code>nautobot.apps.api</code> such as <code>NautobotModelSerializer</code>.</p>
5064
6295
  </div>
5065
6296
  <p>Next, create a generic API view set that allows basic CRUD (create, read, update, and delete) operations for Animal instances. This is defined in <code>api/views.py</code>:</p>
5066
- <div class="highlight"><pre><span></span><code><a id="__codelineno-41-1" name="__codelineno-41-1" href="#__codelineno-41-1"></a><span class="c1"># api/views.py</span>
5067
- <a id="__codelineno-41-2" name="__codelineno-41-2" href="#__codelineno-41-2"></a><span class="kn">from</span> <span class="nn">rest_framework.viewsets</span> <span class="kn">import</span> <span class="n">ModelViewSet</span>
5068
- <a id="__codelineno-41-3" name="__codelineno-41-3" href="#__codelineno-41-3"></a>
5069
- <a id="__codelineno-41-4" name="__codelineno-41-4" href="#__codelineno-41-4"></a><span class="kn">from</span> <span class="nn">nautobot_animal_sounds.models</span> <span class="kn">import</span> <span class="n">Animal</span>
5070
- <a id="__codelineno-41-5" name="__codelineno-41-5" href="#__codelineno-41-5"></a><span class="kn">from</span> <span class="nn">.serializers</span> <span class="kn">import</span> <span class="n">AnimalSerializer</span>
5071
- <a id="__codelineno-41-6" name="__codelineno-41-6" href="#__codelineno-41-6"></a>
5072
- <a id="__codelineno-41-7" name="__codelineno-41-7" href="#__codelineno-41-7"></a>
5073
- <a id="__codelineno-41-8" name="__codelineno-41-8" href="#__codelineno-41-8"></a><span class="k">class</span> <span class="nc">AnimalViewSet</span><span class="p">(</span><span class="n">ModelViewSet</span><span class="p">):</span>
5074
- <a id="__codelineno-41-9" name="__codelineno-41-9" href="#__codelineno-41-9"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;API viewset for interacting with Animal objects.&quot;&quot;&quot;</span>
5075
- <a id="__codelineno-41-10" name="__codelineno-41-10" href="#__codelineno-41-10"></a>
5076
- <a id="__codelineno-41-11" name="__codelineno-41-11" href="#__codelineno-41-11"></a> <span class="n">queryset</span> <span class="o">=</span> <span class="n">Animal</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
5077
- <a id="__codelineno-41-12" name="__codelineno-41-12" href="#__codelineno-41-12"></a> <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">AnimalSerializer</span>
6297
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-45-1" name="__codelineno-45-1" href="#__codelineno-45-1"></a><span class="c1"># api/views.py</span>
6298
+ <a id="__codelineno-45-2" name="__codelineno-45-2" href="#__codelineno-45-2"></a><span class="kn">from</span> <span class="nn">rest_framework.viewsets</span> <span class="kn">import</span> <span class="n">ModelViewSet</span>
6299
+ <a id="__codelineno-45-3" name="__codelineno-45-3" href="#__codelineno-45-3"></a>
6300
+ <a id="__codelineno-45-4" name="__codelineno-45-4" href="#__codelineno-45-4"></a><span class="kn">from</span> <span class="nn">nautobot_animal_sounds.models</span> <span class="kn">import</span> <span class="n">Animal</span>
6301
+ <a id="__codelineno-45-5" name="__codelineno-45-5" href="#__codelineno-45-5"></a><span class="kn">from</span> <span class="nn">.serializers</span> <span class="kn">import</span> <span class="n">AnimalSerializer</span>
6302
+ <a id="__codelineno-45-6" name="__codelineno-45-6" href="#__codelineno-45-6"></a>
6303
+ <a id="__codelineno-45-7" name="__codelineno-45-7" href="#__codelineno-45-7"></a>
6304
+ <a id="__codelineno-45-8" name="__codelineno-45-8" href="#__codelineno-45-8"></a><span class="k">class</span> <span class="nc">AnimalViewSet</span><span class="p">(</span><span class="n">ModelViewSet</span><span class="p">):</span>
6305
+ <a id="__codelineno-45-9" name="__codelineno-45-9" href="#__codelineno-45-9"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;API viewset for interacting with Animal objects.&quot;&quot;&quot;</span>
6306
+ <a id="__codelineno-45-10" name="__codelineno-45-10" href="#__codelineno-45-10"></a>
6307
+ <a id="__codelineno-45-11" name="__codelineno-45-11" href="#__codelineno-45-11"></a> <span class="n">queryset</span> <span class="o">=</span> <span class="n">Animal</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
6308
+ <a id="__codelineno-45-12" name="__codelineno-45-12" href="#__codelineno-45-12"></a> <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">AnimalSerializer</span>
5078
6309
  </code></pre></div>
5079
6310
  <div class="admonition tip">
5080
6311
  <p class="admonition-title">Tip</p>
5081
6312
  <p>For more full-featured models, you should probably use <code>nautobot.apps.api.NautobotModelViewSet</code> as a base class.</p>
5082
6313
  </div>
5083
6314
  <p>Finally, register a URL for our endpoint in <code>api/urls.py</code>. This file <strong>must</strong> define a variable named <code>urlpatterns</code>.</p>
5084
- <div class="highlight"><pre><span></span><code><a id="__codelineno-42-1" name="__codelineno-42-1" href="#__codelineno-42-1"></a><span class="c1"># api/urls.py</span>
5085
- <a id="__codelineno-42-2" name="__codelineno-42-2" href="#__codelineno-42-2"></a><span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">routers</span>
5086
- <a id="__codelineno-42-3" name="__codelineno-42-3" href="#__codelineno-42-3"></a>
5087
- <a id="__codelineno-42-4" name="__codelineno-42-4" href="#__codelineno-42-4"></a><span class="kn">from</span> <span class="nn">.views</span> <span class="kn">import</span> <span class="n">AnimalViewSet</span>
5088
- <a id="__codelineno-42-5" name="__codelineno-42-5" href="#__codelineno-42-5"></a>
5089
- <a id="__codelineno-42-6" name="__codelineno-42-6" href="#__codelineno-42-6"></a>
5090
- <a id="__codelineno-42-7" name="__codelineno-42-7" href="#__codelineno-42-7"></a><span class="n">router</span> <span class="o">=</span> <span class="n">routers</span><span class="o">.</span><span class="n">DefaultRouter</span><span class="p">()</span>
5091
- <a id="__codelineno-42-8" name="__codelineno-42-8" href="#__codelineno-42-8"></a><span class="n">router</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s1">&#39;animals&#39;</span><span class="p">,</span> <span class="n">AnimalViewSet</span><span class="p">)</span>
5092
- <a id="__codelineno-42-9" name="__codelineno-42-9" href="#__codelineno-42-9"></a><span class="n">urlpatterns</span> <span class="o">=</span> <span class="n">router</span><span class="o">.</span><span class="n">urls</span>
6315
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-46-1" name="__codelineno-46-1" href="#__codelineno-46-1"></a><span class="c1"># api/urls.py</span>
6316
+ <a id="__codelineno-46-2" name="__codelineno-46-2" href="#__codelineno-46-2"></a><span class="kn">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">routers</span>
6317
+ <a id="__codelineno-46-3" name="__codelineno-46-3" href="#__codelineno-46-3"></a>
6318
+ <a id="__codelineno-46-4" name="__codelineno-46-4" href="#__codelineno-46-4"></a><span class="kn">from</span> <span class="nn">.views</span> <span class="kn">import</span> <span class="n">AnimalViewSet</span>
6319
+ <a id="__codelineno-46-5" name="__codelineno-46-5" href="#__codelineno-46-5"></a>
6320
+ <a id="__codelineno-46-6" name="__codelineno-46-6" href="#__codelineno-46-6"></a>
6321
+ <a id="__codelineno-46-7" name="__codelineno-46-7" href="#__codelineno-46-7"></a><span class="n">router</span> <span class="o">=</span> <span class="n">routers</span><span class="o">.</span><span class="n">DefaultRouter</span><span class="p">()</span>
6322
+ <a id="__codelineno-46-8" name="__codelineno-46-8" href="#__codelineno-46-8"></a><span class="n">router</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s1">&#39;animals&#39;</span><span class="p">,</span> <span class="n">AnimalViewSet</span><span class="p">)</span>
6323
+ <a id="__codelineno-46-9" name="__codelineno-46-9" href="#__codelineno-46-9"></a><span class="n">urlpatterns</span> <span class="o">=</span> <span class="n">router</span><span class="o">.</span><span class="n">urls</span>
5093
6324
  </code></pre></div>
5094
6325
  <p>With these three components in place, we can request <code>/api/plugins/animal-sounds/animals/</code> to retrieve a list of all Animal objects defined.</p>
5095
6326
  <p><img alt="Nautobot REST API app endpoint" src="../media/plugins/plugin_rest_api_endpoint.png" /></p>
@@ -5099,13 +6330,13 @@ namedtuple to assist in the configurations.</p>
5099
6330
  </div>
5100
6331
  <h2 id="adding-help-documentation">Adding Help Documentation<a class="headerlink" href="#adding-help-documentation" title="Permanent link">&para;</a></h2>
5101
6332
  <p>If you are using the <code>generic.ObjectEditView</code> from Nautobot for your object, the form can automatically include a help icon with a link to that object's documentation. For this to happen, Nautobot must be able to find the documentation for this object in a specific directory tree within your app:</p>
5102
- <div class="highlight"><pre><span></span><code><a id="__codelineno-43-1" name="__codelineno-43-1" href="#__codelineno-43-1"></a>app_name/ # &quot;nautobot_animal_sounds&quot;
5103
- <a id="__codelineno-43-2" name="__codelineno-43-2" href="#__codelineno-43-2"></a> - static/
5104
- <a id="__codelineno-43-3" name="__codelineno-43-3" href="#__codelineno-43-3"></a> - app_name/ # &quot;nautobot_animal_sounds&quot;
5105
- <a id="__codelineno-43-4" name="__codelineno-43-4" href="#__codelineno-43-4"></a> - docs/
5106
- <a id="__codelineno-43-5" name="__codelineno-43-5" href="#__codelineno-43-5"></a> - index.html
5107
- <a id="__codelineno-43-6" name="__codelineno-43-6" href="#__codelineno-43-6"></a> - models/
5108
- <a id="__codelineno-43-7" name="__codelineno-43-7" href="#__codelineno-43-7"></a> - object_model.html # &quot;animal.html&quot;
6333
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-47-1" name="__codelineno-47-1" href="#__codelineno-47-1"></a>app_name/ # &quot;nautobot_animal_sounds&quot;
6334
+ <a id="__codelineno-47-2" name="__codelineno-47-2" href="#__codelineno-47-2"></a> - static/
6335
+ <a id="__codelineno-47-3" name="__codelineno-47-3" href="#__codelineno-47-3"></a> - app_name/ # &quot;nautobot_animal_sounds&quot;
6336
+ <a id="__codelineno-47-4" name="__codelineno-47-4" href="#__codelineno-47-4"></a> - docs/
6337
+ <a id="__codelineno-47-5" name="__codelineno-47-5" href="#__codelineno-47-5"></a> - index.html
6338
+ <a id="__codelineno-47-6" name="__codelineno-47-6" href="#__codelineno-47-6"></a> - models/
6339
+ <a id="__codelineno-47-7" name="__codelineno-47-7" href="#__codelineno-47-7"></a> - object_model.html # &quot;animal.html&quot;
5109
6340
  </code></pre></div>
5110
6341
  <h2 id="overriding-existing-functionality">Overriding Existing Functionality<a class="headerlink" href="#overriding-existing-functionality" title="Permanent link">&para;</a></h2>
5111
6342
  <h3 id="replacing-views">Replacing Views<a class="headerlink" href="#replacing-views" title="Permanent link">&para;</a></h3>
@@ -5115,42 +6346,42 @@ namedtuple to assist in the configurations.</p>
5115
6346
  <p>You may override any of the core or app views by providing an <code>override_views</code> <code>dict</code> in an app's <code>views.py</code> file.</p>
5116
6347
  <p>To override a view, you must specify the view's fully qualified name as the <code>dict</code> key which consists of the app name followed by the view's name separated by a colon, for instance <code>dcim:device</code>. The <code>dict</code> value should be the overriding view function.</p>
5117
6348
  <p>A simple example to override the device detail view:</p>
5118
- <div class="highlight"><pre><span></span><code><a id="__codelineno-44-1" name="__codelineno-44-1" href="#__codelineno-44-1"></a><span class="c1"># views.py</span>
5119
- <a id="__codelineno-44-2" name="__codelineno-44-2" href="#__codelineno-44-2"></a><span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">HttpResponse</span>
5120
- <a id="__codelineno-44-3" name="__codelineno-44-3" href="#__codelineno-44-3"></a><span class="kn">from</span> <span class="nn">django.views</span> <span class="kn">import</span> <span class="n">generic</span>
5121
- <a id="__codelineno-44-4" name="__codelineno-44-4" href="#__codelineno-44-4"></a>
5122
- <a id="__codelineno-44-5" name="__codelineno-44-5" href="#__codelineno-44-5"></a>
5123
- <a id="__codelineno-44-6" name="__codelineno-44-6" href="#__codelineno-44-6"></a><span class="k">class</span> <span class="nc">DeviceViewOverride</span><span class="p">(</span><span class="n">generic</span><span class="o">.</span><span class="n">View</span><span class="p">):</span>
5124
- <a id="__codelineno-44-7" name="__codelineno-44-7" href="#__codelineno-44-7"></a> <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
5125
- <a id="__codelineno-44-8" name="__codelineno-44-8" href="#__codelineno-44-8"></a> <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">((</span><span class="s2">&quot;Hello world! I&#39;m a view which &quot;</span>
5126
- <a id="__codelineno-44-9" name="__codelineno-44-9" href="#__codelineno-44-9"></a> <span class="s2">&quot;overrides the device object detail view.&quot;</span><span class="p">))</span>
5127
- <a id="__codelineno-44-10" name="__codelineno-44-10" href="#__codelineno-44-10"></a>
5128
- <a id="__codelineno-44-11" name="__codelineno-44-11" href="#__codelineno-44-11"></a>
5129
- <a id="__codelineno-44-12" name="__codelineno-44-12" href="#__codelineno-44-12"></a><span class="n">override_views</span> <span class="o">=</span> <span class="p">{</span>
5130
- <a id="__codelineno-44-13" name="__codelineno-44-13" href="#__codelineno-44-13"></a> <span class="s2">&quot;dcim:device&quot;</span><span class="p">:</span> <span class="n">DeviceViewOverride</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
5131
- <a id="__codelineno-44-14" name="__codelineno-44-14" href="#__codelineno-44-14"></a><span class="p">}</span>
6349
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-48-1" name="__codelineno-48-1" href="#__codelineno-48-1"></a><span class="c1"># views.py</span>
6350
+ <a id="__codelineno-48-2" name="__codelineno-48-2" href="#__codelineno-48-2"></a><span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">HttpResponse</span>
6351
+ <a id="__codelineno-48-3" name="__codelineno-48-3" href="#__codelineno-48-3"></a><span class="kn">from</span> <span class="nn">django.views</span> <span class="kn">import</span> <span class="n">generic</span>
6352
+ <a id="__codelineno-48-4" name="__codelineno-48-4" href="#__codelineno-48-4"></a>
6353
+ <a id="__codelineno-48-5" name="__codelineno-48-5" href="#__codelineno-48-5"></a>
6354
+ <a id="__codelineno-48-6" name="__codelineno-48-6" href="#__codelineno-48-6"></a><span class="k">class</span> <span class="nc">DeviceViewOverride</span><span class="p">(</span><span class="n">generic</span><span class="o">.</span><span class="n">View</span><span class="p">):</span>
6355
+ <a id="__codelineno-48-7" name="__codelineno-48-7" href="#__codelineno-48-7"></a> <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
6356
+ <a id="__codelineno-48-8" name="__codelineno-48-8" href="#__codelineno-48-8"></a> <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">((</span><span class="s2">&quot;Hello world! I&#39;m a view which &quot;</span>
6357
+ <a id="__codelineno-48-9" name="__codelineno-48-9" href="#__codelineno-48-9"></a> <span class="s2">&quot;overrides the device object detail view.&quot;</span><span class="p">))</span>
6358
+ <a id="__codelineno-48-10" name="__codelineno-48-10" href="#__codelineno-48-10"></a>
6359
+ <a id="__codelineno-48-11" name="__codelineno-48-11" href="#__codelineno-48-11"></a>
6360
+ <a id="__codelineno-48-12" name="__codelineno-48-12" href="#__codelineno-48-12"></a><span class="n">override_views</span> <span class="o">=</span> <span class="p">{</span>
6361
+ <a id="__codelineno-48-13" name="__codelineno-48-13" href="#__codelineno-48-13"></a> <span class="s2">&quot;dcim:device&quot;</span><span class="p">:</span> <span class="n">DeviceViewOverride</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
6362
+ <a id="__codelineno-48-14" name="__codelineno-48-14" href="#__codelineno-48-14"></a><span class="p">}</span>
5132
6363
  </code></pre></div>
5133
6364
  <h2 id="note-url-endpoint">Note URL Endpoint<a class="headerlink" href="#note-url-endpoint" title="Permanent link">&para;</a></h2>
5134
6365
  <div class="admonition version-added">
5135
6366
  <p class="admonition-title">Added in version 1.4.0</p>
5136
6367
  </div>
5137
6368
  <p>Models that inherit from <code>PrimaryModel</code> and <code>OrganizationalModel</code> can have notes associated. In order to utilize this new feature you will need to add the endpoint to <code>urls.py</code>. Here is an option to be able to support both 1.4+ and older versions of Nautobot:</p>
5138
- <div class="highlight"><pre><span></span><code><a id="__codelineno-45-1" name="__codelineno-45-1" href="#__codelineno-45-1"></a><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
5139
- <a id="__codelineno-45-2" name="__codelineno-45-2" href="#__codelineno-45-2"></a> <span class="n">path</span><span class="p">(</span><span class="s1">&#39;random/&#39;</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">RandomAnimalView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;random_animal&#39;</span><span class="p">),</span>
5140
- <a id="__codelineno-45-3" name="__codelineno-45-3" href="#__codelineno-45-3"></a><span class="p">]</span>
5141
- <a id="__codelineno-45-4" name="__codelineno-45-4" href="#__codelineno-45-4"></a>
5142
- <a id="__codelineno-45-5" name="__codelineno-45-5" href="#__codelineno-45-5"></a><span class="k">try</span><span class="p">:</span>
5143
- <a id="__codelineno-45-6" name="__codelineno-45-6" href="#__codelineno-45-6"></a> <span class="kn">from</span> <span class="nn">nautobot.extras.views</span> <span class="kn">import</span> <span class="n">ObjectNotesView</span>
5144
- <a id="__codelineno-45-7" name="__codelineno-45-7" href="#__codelineno-45-7"></a> <span class="n">urlpatterns</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
5145
- <a id="__codelineno-45-8" name="__codelineno-45-8" href="#__codelineno-45-8"></a> <span class="n">path</span><span class="p">(</span>
5146
- <a id="__codelineno-45-9" name="__codelineno-45-9" href="#__codelineno-45-9"></a> <span class="s1">&#39;random/&lt;slug:slug&gt;/notes/),</span>
5147
- <a id="__codelineno-45-10" name="__codelineno-45-10" href="#__codelineno-45-10"></a> <span class="n">ObjectNotesView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
5148
- <a id="__codelineno-45-11" name="__codelineno-45-11" href="#__codelineno-45-11"></a> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;random_notes&quot;</span><span class="p">,</span>
5149
- <a id="__codelineno-45-12" name="__codelineno-45-12" href="#__codelineno-45-12"></a> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;model&quot;</span><span class="p">:</span> <span class="n">Random</span><span class="p">},</span>
5150
- <a id="__codelineno-45-13" name="__codelineno-45-13" href="#__codelineno-45-13"></a> <span class="p">)</span>
5151
- <a id="__codelineno-45-14" name="__codelineno-45-14" href="#__codelineno-45-14"></a> <span class="p">)</span>
5152
- <a id="__codelineno-45-15" name="__codelineno-45-15" href="#__codelineno-45-15"></a><span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
5153
- <a id="__codelineno-45-16" name="__codelineno-45-16" href="#__codelineno-45-16"></a> <span class="k">pass</span>
6369
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-49-1" name="__codelineno-49-1" href="#__codelineno-49-1"></a><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
6370
+ <a id="__codelineno-49-2" name="__codelineno-49-2" href="#__codelineno-49-2"></a> <span class="n">path</span><span class="p">(</span><span class="s1">&#39;random/&#39;</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">RandomAnimalView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;random_animal&#39;</span><span class="p">),</span>
6371
+ <a id="__codelineno-49-3" name="__codelineno-49-3" href="#__codelineno-49-3"></a><span class="p">]</span>
6372
+ <a id="__codelineno-49-4" name="__codelineno-49-4" href="#__codelineno-49-4"></a>
6373
+ <a id="__codelineno-49-5" name="__codelineno-49-5" href="#__codelineno-49-5"></a><span class="k">try</span><span class="p">:</span>
6374
+ <a id="__codelineno-49-6" name="__codelineno-49-6" href="#__codelineno-49-6"></a> <span class="kn">from</span> <span class="nn">nautobot.extras.views</span> <span class="kn">import</span> <span class="n">ObjectNotesView</span>
6375
+ <a id="__codelineno-49-7" name="__codelineno-49-7" href="#__codelineno-49-7"></a> <span class="n">urlpatterns</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
6376
+ <a id="__codelineno-49-8" name="__codelineno-49-8" href="#__codelineno-49-8"></a> <span class="n">path</span><span class="p">(</span>
6377
+ <a id="__codelineno-49-9" name="__codelineno-49-9" href="#__codelineno-49-9"></a> <span class="s1">&#39;random/&lt;uuid:pk&gt;/notes/),</span>
6378
+ <a id="__codelineno-49-10" name="__codelineno-49-10" href="#__codelineno-49-10"></a> <span class="n">ObjectNotesView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
6379
+ <a id="__codelineno-49-11" name="__codelineno-49-11" href="#__codelineno-49-11"></a> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;random_notes&quot;</span><span class="p">,</span>
6380
+ <a id="__codelineno-49-12" name="__codelineno-49-12" href="#__codelineno-49-12"></a> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;model&quot;</span><span class="p">:</span> <span class="n">Random</span><span class="p">},</span>
6381
+ <a id="__codelineno-49-13" name="__codelineno-49-13" href="#__codelineno-49-13"></a> <span class="p">)</span>
6382
+ <a id="__codelineno-49-14" name="__codelineno-49-14" href="#__codelineno-49-14"></a> <span class="p">)</span>
6383
+ <a id="__codelineno-49-15" name="__codelineno-49-15" href="#__codelineno-49-15"></a><span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
6384
+ <a id="__codelineno-49-16" name="__codelineno-49-16" href="#__codelineno-49-16"></a> <span class="k">pass</span>
5154
6385
  </code></pre></div>
5155
6386
  <h2 id="prometheus-metrics">Prometheus Metrics<a class="headerlink" href="#prometheus-metrics" title="Permanent link">&para;</a></h2>
5156
6387
  <div class="admonition version-added">
@@ -5161,20 +6392,20 @@ namedtuple to assist in the configurations.</p>
5161
6392
  <li>Use the <code>prometheus_client</code> library directly in your app code. Depending on whether that code runs in the web server or the worker context, the metric will show up in the respective <code>/metrics</code> endpoint(s) (i.e. metrics generated in the worker context show up in the worker's endpoint and those generated in the web application's context show up in the web application's endpoint).</li>
5162
6393
  <li>If the metric cannot be generated alongside existing code, apps can implement individual metric generator functions and register them into a list called <code>metrics</code> in a file named <code>metrics.py</code> at the root of the app. Nautobot will automatically read these and expose them via its <code>/metrics</code> endpoint. The following code snippet shows an example metric defined this way:</li>
5163
6394
  </ol>
5164
- <div class="highlight"><pre><span></span><code><a id="__codelineno-46-1" name="__codelineno-46-1" href="#__codelineno-46-1"></a><span class="c1"># metrics.py</span>
5165
- <a id="__codelineno-46-2" name="__codelineno-46-2" href="#__codelineno-46-2"></a><span class="kn">from</span> <span class="nn">prometheus_client.metrics_core</span> <span class="kn">import</span> <span class="n">GaugeMetricFamily</span>
5166
- <a id="__codelineno-46-3" name="__codelineno-46-3" href="#__codelineno-46-3"></a>
5167
- <a id="__codelineno-46-4" name="__codelineno-46-4" href="#__codelineno-46-4"></a><span class="kn">from</span> <span class="nn">nautobot_animal_sounds.models</span> <span class="kn">import</span> <span class="n">Animal</span>
5168
- <a id="__codelineno-46-5" name="__codelineno-46-5" href="#__codelineno-46-5"></a>
5169
- <a id="__codelineno-46-6" name="__codelineno-46-6" href="#__codelineno-46-6"></a>
5170
- <a id="__codelineno-46-7" name="__codelineno-46-7" href="#__codelineno-46-7"></a><span class="k">def</span> <span class="nf">metric_animals</span><span class="p">():</span>
5171
- <a id="__codelineno-46-8" name="__codelineno-46-8" href="#__codelineno-46-8"></a> <span class="n">gauges</span> <span class="o">=</span> <span class="n">GaugeMetricFamily</span><span class="p">(</span><span class="s2">&quot;nautobot_noisy_animals_count&quot;</span><span class="p">,</span> <span class="s2">&quot;Nautobot Noisy Animals Count&quot;</span><span class="p">,</span> <span class="n">labels</span><span class="o">=</span><span class="p">[])</span>
5172
- <a id="__codelineno-46-9" name="__codelineno-46-9" href="#__codelineno-46-9"></a> <span class="n">screaming_animal_count</span> <span class="o">=</span> <span class="n">Animal</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">loudness</span><span class="o">=</span><span class="s2">&quot;noisy&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
5173
- <a id="__codelineno-46-10" name="__codelineno-46-10" href="#__codelineno-46-10"></a> <span class="n">gauges</span><span class="o">.</span><span class="n">add_metric</span><span class="p">(</span><span class="n">labels</span><span class="o">=</span><span class="p">[],</span> <span class="n">value</span><span class="o">=</span><span class="n">screaming_animal_count</span><span class="p">)</span>
5174
- <a id="__codelineno-46-11" name="__codelineno-46-11" href="#__codelineno-46-11"></a> <span class="k">yield</span> <span class="n">gauges</span>
5175
- <a id="__codelineno-46-12" name="__codelineno-46-12" href="#__codelineno-46-12"></a>
5176
- <a id="__codelineno-46-13" name="__codelineno-46-13" href="#__codelineno-46-13"></a>
5177
- <a id="__codelineno-46-14" name="__codelineno-46-14" href="#__codelineno-46-14"></a><span class="n">metrics</span> <span class="o">=</span> <span class="p">[</span><span class="n">metric_example</span><span class="p">]</span>
6395
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-50-1" name="__codelineno-50-1" href="#__codelineno-50-1"></a><span class="c1"># metrics.py</span>
6396
+ <a id="__codelineno-50-2" name="__codelineno-50-2" href="#__codelineno-50-2"></a><span class="kn">from</span> <span class="nn">prometheus_client.metrics_core</span> <span class="kn">import</span> <span class="n">GaugeMetricFamily</span>
6397
+ <a id="__codelineno-50-3" name="__codelineno-50-3" href="#__codelineno-50-3"></a>
6398
+ <a id="__codelineno-50-4" name="__codelineno-50-4" href="#__codelineno-50-4"></a><span class="kn">from</span> <span class="nn">nautobot_animal_sounds.models</span> <span class="kn">import</span> <span class="n">Animal</span>
6399
+ <a id="__codelineno-50-5" name="__codelineno-50-5" href="#__codelineno-50-5"></a>
6400
+ <a id="__codelineno-50-6" name="__codelineno-50-6" href="#__codelineno-50-6"></a>
6401
+ <a id="__codelineno-50-7" name="__codelineno-50-7" href="#__codelineno-50-7"></a><span class="k">def</span> <span class="nf">metric_animals</span><span class="p">():</span>
6402
+ <a id="__codelineno-50-8" name="__codelineno-50-8" href="#__codelineno-50-8"></a> <span class="n">gauges</span> <span class="o">=</span> <span class="n">GaugeMetricFamily</span><span class="p">(</span><span class="s2">&quot;nautobot_noisy_animals_count&quot;</span><span class="p">,</span> <span class="s2">&quot;Nautobot Noisy Animals Count&quot;</span><span class="p">,</span> <span class="n">labels</span><span class="o">=</span><span class="p">[])</span>
6403
+ <a id="__codelineno-50-9" name="__codelineno-50-9" href="#__codelineno-50-9"></a> <span class="n">screaming_animal_count</span> <span class="o">=</span> <span class="n">Animal</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">loudness</span><span class="o">=</span><span class="s2">&quot;noisy&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
6404
+ <a id="__codelineno-50-10" name="__codelineno-50-10" href="#__codelineno-50-10"></a> <span class="n">gauges</span><span class="o">.</span><span class="n">add_metric</span><span class="p">(</span><span class="n">labels</span><span class="o">=</span><span class="p">[],</span> <span class="n">value</span><span class="o">=</span><span class="n">screaming_animal_count</span><span class="p">)</span>
6405
+ <a id="__codelineno-50-11" name="__codelineno-50-11" href="#__codelineno-50-11"></a> <span class="k">yield</span> <span class="n">gauges</span>
6406
+ <a id="__codelineno-50-12" name="__codelineno-50-12" href="#__codelineno-50-12"></a>
6407
+ <a id="__codelineno-50-13" name="__codelineno-50-13" href="#__codelineno-50-13"></a>
6408
+ <a id="__codelineno-50-14" name="__codelineno-50-14" href="#__codelineno-50-14"></a><span class="n">metrics</span> <span class="o">=</span> <span class="p">[</span><span class="n">metric_example</span><span class="p">]</span>
5178
6409
  </code></pre></div>
5179
6410
  <h2 id="testing-apps">Testing Apps<a class="headerlink" href="#testing-apps" title="Permanent link">&para;</a></h2>
5180
6411
  <p>In general apps can be tested like other Django apps. In most cases you'll want to run your automated tests via the <code>nautobot-server test &lt;app_module&gt;</code> command or, if using the <code>coverage</code> Python library, <code>coverage run --module nautobot.core.cli test &lt;app_module&gt;</code>.</p>
@@ -5192,31 +6423,31 @@ namedtuple to assist in the configurations.</p>
5192
6423
  <p><code>NautobotPerformanceTestRunner</code> is used by adding the flag <code>--testrunner nautobot.core.tests.runner.NautobotPerformanceTestRunner</code> to the <code>coverage run</code> command used for unit tests. This flag will replace the default <code>NautobotTestRunner</code> while retaining all its functionalities with the addition of performance evaluation after test
5193
6424
  runs.
5194
6425
  Checkout <a href="../development/testing.html#performance-tests">Performance Tests</a> for more detail.</p>
5195
- <div class="highlight"><pre><span></span><code><a id="__codelineno-47-1" name="__codelineno-47-1" href="#__codelineno-47-1"></a><span class="nd">@tag</span><span class="p">(</span><span class="s2">&quot;performance&quot;</span><span class="p">)</span>
5196
- <a id="__codelineno-47-2" name="__codelineno-47-2" href="#__codelineno-47-2"></a><span class="k">def</span> <span class="nf">test_your_app</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
5197
- <a id="__codelineno-47-3" name="__codelineno-47-3" href="#__codelineno-47-3"></a> <span class="k">pass</span>
5198
- <a id="__codelineno-47-4" name="__codelineno-47-4" href="#__codelineno-47-4"></a><span class="o">...</span>
6426
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-51-1" name="__codelineno-51-1" href="#__codelineno-51-1"></a><span class="nd">@tag</span><span class="p">(</span><span class="s2">&quot;performance&quot;</span><span class="p">)</span>
6427
+ <a id="__codelineno-51-2" name="__codelineno-51-2" href="#__codelineno-51-2"></a><span class="k">def</span> <span class="nf">test_your_app</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
6428
+ <a id="__codelineno-51-3" name="__codelineno-51-3" href="#__codelineno-51-3"></a> <span class="k">pass</span>
6429
+ <a id="__codelineno-51-4" name="__codelineno-51-4" href="#__codelineno-51-4"></a><span class="o">...</span>
5199
6430
  </code></pre></div>
5200
6431
  <h3 id="gathering-performance-test-baseline-data">Gathering Performance Test Baseline Data<a class="headerlink" href="#gathering-performance-test-baseline-data" title="Permanent link">&para;</a></h3>
5201
6432
  <p>If you want to add baselines for your own test to <code>nautobot/core/tests/performance_baselines.yml</code> or have your own baseline yaml file for performance testing, specify a different file path for <code>TEST_PERFORMANCE_BASELINE_FILE</code> in app's development/test <code>nautobot_config.py</code>, and store the output of <code>invoke performance-test --performance-snapshot</code> command in that file. <code>--performance-snapshot</code> flag will store the results of your performance test to <code>report.yml</code> and all you need to do is copy/paste the result to the file set by <code>TEST_PERFORMANCE_BASELINE_FILE</code>. Now you have baselines for your own tests!
5202
6433
  Example output of <code>invoke performance-test --performance-snapshot</code>:</p>
5203
- <div class="highlight"><pre><span></span><code><a id="__codelineno-48-1" name="__codelineno-48-1" href="#__codelineno-48-1"></a><span class="nt">tests</span><span class="p">:</span>
5204
- <a id="__codelineno-48-2" name="__codelineno-48-2" href="#__codelineno-48-2"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">&gt;-</span>
5205
- <a id="__codelineno-48-3" name="__codelineno-48-3" href="#__codelineno-48-3"></a><span class="w"> </span><span class="no">test_run_job_with_sensitive_variables_and_requires_approval</span>
5206
- <a id="__codelineno-48-4" name="__codelineno-48-4" href="#__codelineno-48-4"></a><span class="w"> </span><span class="no">(nautobot.extras.tests.test_views.JobTestCase)</span>
5207
- <a id="__codelineno-48-5" name="__codelineno-48-5" href="#__codelineno-48-5"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">4.799533</span>
5208
- <a id="__codelineno-48-6" name="__codelineno-48-6" href="#__codelineno-48-6"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">test_run_missing_schedule (nautobot.extras.tests.test_views.JobTestCase)</span>
5209
- <a id="__codelineno-48-7" name="__codelineno-48-7" href="#__codelineno-48-7"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">4.367563</span>
5210
- <a id="__codelineno-48-8" name="__codelineno-48-8" href="#__codelineno-48-8"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">test_run_now_missing_args (nautobot.extras.tests.test_views.JobTestCase)</span>
5211
- <a id="__codelineno-48-9" name="__codelineno-48-9" href="#__codelineno-48-9"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">4.363194</span>
5212
- <a id="__codelineno-48-10" name="__codelineno-48-10" href="#__codelineno-48-10"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">&gt;-</span>
5213
- <a id="__codelineno-48-11" name="__codelineno-48-11" href="#__codelineno-48-11"></a><span class="w"> </span><span class="no">test_create_object_with_constrained_permission</span>
5214
- <a id="__codelineno-48-12" name="__codelineno-48-12" href="#__codelineno-48-12"></a><span class="w"> </span><span class="no">(nautobot.extras.tests.test_views.GraphQLQueriesTestCase)</span>
5215
- <a id="__codelineno-48-13" name="__codelineno-48-13" href="#__codelineno-48-13"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">3.474244</span>
5216
- <a id="__codelineno-48-14" name="__codelineno-48-14" href="#__codelineno-48-14"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">&gt;-</span>
5217
- <a id="__codelineno-48-15" name="__codelineno-48-15" href="#__codelineno-48-15"></a><span class="w"> </span><span class="no">test_run_now_constrained_permissions</span>
5218
- <a id="__codelineno-48-16" name="__codelineno-48-16" href="#__codelineno-48-16"></a><span class="w"> </span><span class="no">(nautobot.extras.tests.test_views.JobTestCase)</span>
5219
- <a id="__codelineno-48-17" name="__codelineno-48-17" href="#__codelineno-48-17"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">2.727531</span>
6434
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-52-1" name="__codelineno-52-1" href="#__codelineno-52-1"></a><span class="nt">tests</span><span class="p">:</span>
6435
+ <a id="__codelineno-52-2" name="__codelineno-52-2" href="#__codelineno-52-2"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">&gt;-</span>
6436
+ <a id="__codelineno-52-3" name="__codelineno-52-3" href="#__codelineno-52-3"></a><span class="w"> </span><span class="no">test_run_job_with_sensitive_variables_and_requires_approval</span>
6437
+ <a id="__codelineno-52-4" name="__codelineno-52-4" href="#__codelineno-52-4"></a><span class="w"> </span><span class="no">(nautobot.extras.tests.test_views.JobTestCase)</span>
6438
+ <a id="__codelineno-52-5" name="__codelineno-52-5" href="#__codelineno-52-5"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">4.799533</span>
6439
+ <a id="__codelineno-52-6" name="__codelineno-52-6" href="#__codelineno-52-6"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">test_run_missing_schedule (nautobot.extras.tests.test_views.JobTestCase)</span>
6440
+ <a id="__codelineno-52-7" name="__codelineno-52-7" href="#__codelineno-52-7"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">4.367563</span>
6441
+ <a id="__codelineno-52-8" name="__codelineno-52-8" href="#__codelineno-52-8"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">test_run_now_missing_args (nautobot.extras.tests.test_views.JobTestCase)</span>
6442
+ <a id="__codelineno-52-9" name="__codelineno-52-9" href="#__codelineno-52-9"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">4.363194</span>
6443
+ <a id="__codelineno-52-10" name="__codelineno-52-10" href="#__codelineno-52-10"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">&gt;-</span>
6444
+ <a id="__codelineno-52-11" name="__codelineno-52-11" href="#__codelineno-52-11"></a><span class="w"> </span><span class="no">test_create_object_with_constrained_permission</span>
6445
+ <a id="__codelineno-52-12" name="__codelineno-52-12" href="#__codelineno-52-12"></a><span class="w"> </span><span class="no">(nautobot.extras.tests.test_views.GraphQLQueriesTestCase)</span>
6446
+ <a id="__codelineno-52-13" name="__codelineno-52-13" href="#__codelineno-52-13"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">3.474244</span>
6447
+ <a id="__codelineno-52-14" name="__codelineno-52-14" href="#__codelineno-52-14"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">&gt;-</span>
6448
+ <a id="__codelineno-52-15" name="__codelineno-52-15" href="#__codelineno-52-15"></a><span class="w"> </span><span class="no">test_run_now_constrained_permissions</span>
6449
+ <a id="__codelineno-52-16" name="__codelineno-52-16" href="#__codelineno-52-16"></a><span class="w"> </span><span class="no">(nautobot.extras.tests.test_views.JobTestCase)</span>
6450
+ <a id="__codelineno-52-17" name="__codelineno-52-17" href="#__codelineno-52-17"></a><span class="w"> </span><span class="nt">execution_time</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">2.727531</span>
5220
6451
  </code></pre></div>
5221
6452
  <p>We recommend adding <a href="https://pypi.org/project/django-slowtests/"><code>django-slowtests</code></a> to your app's development dependencies to leverage this functionality to build better performing apps.</p>
5222
6453