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>
@@ -2061,6 +3229,13 @@
2061
3229
  present_in_database
2062
3230
  </a>
2063
3231
 
3232
+ </li>
3233
+
3234
+ <li class="md-nav__item">
3235
+ <a href="#nautobot.core.models.BaseModel.get_absolute_url" class="md-nav__link">
3236
+ get_absolute_url()
3237
+ </a>
3238
+
2064
3239
  </li>
2065
3240
 
2066
3241
  <li class="md-nav__item">
@@ -2980,6 +4155,13 @@
2980
4155
  present_in_database
2981
4156
  </a>
2982
4157
 
4158
+ </li>
4159
+
4160
+ <li class="md-nav__item">
4161
+ <a href="#nautobot.core.models.BaseModel.get_absolute_url" class="md-nav__link">
4162
+ get_absolute_url()
4163
+ </a>
4164
+
2983
4165
  </li>
2984
4166
 
2985
4167
  <li class="md-nav__item">
@@ -3206,9 +4388,7 @@ can be used for the same purpose in most cases.</p>
3206
4388
 
3207
4389
  <details class="quote">
3208
4390
  <summary>Source code in <code>nautobot/core/models/__init__.py</code></summary>
3209
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-11"> 11</a></span>
3210
- <span class="normal"><a href="#__codelineno-0-12"> 12</a></span>
3211
- <span class="normal"><a href="#__codelineno-0-13"> 13</a></span>
4391
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-13"> 13</a></span>
3212
4392
  <span class="normal"><a href="#__codelineno-0-14"> 14</a></span>
3213
4393
  <span class="normal"><a href="#__codelineno-0-15"> 15</a></span>
3214
4394
  <span class="normal"><a href="#__codelineno-0-16"> 16</a></span>
@@ -3377,178 +4557,226 @@ can be used for the same purpose in most cases.</p>
3377
4557
  <span class="normal"><a href="#__codelineno-0-179">179</a></span>
3378
4558
  <span class="normal"><a href="#__codelineno-0-180">180</a></span>
3379
4559
  <span class="normal"><a href="#__codelineno-0-181">181</a></span>
3380
- <span class="normal"><a href="#__codelineno-0-182">182</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-11" name="__codelineno-0-11"></a><span class="k">class</span> <span class="nc">BaseModel</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
3381
- <a id="__codelineno-0-12" name="__codelineno-0-12"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3382
- <a id="__codelineno-0-13" name="__codelineno-0-13"></a><span class="sd"> Base model class that all models should inherit from.</span>
3383
- <a id="__codelineno-0-14" name="__codelineno-0-14"></a>
3384
- <a id="__codelineno-0-15" name="__codelineno-0-15"></a><span class="sd"> This abstract base provides globally common fields and functionality.</span>
4560
+ <span class="normal"><a href="#__codelineno-0-182">182</a></span>
4561
+ <span class="normal"><a href="#__codelineno-0-183">183</a></span>
4562
+ <span class="normal"><a href="#__codelineno-0-184">184</a></span>
4563
+ <span class="normal"><a href="#__codelineno-0-185">185</a></span>
4564
+ <span class="normal"><a href="#__codelineno-0-186">186</a></span>
4565
+ <span class="normal"><a href="#__codelineno-0-187">187</a></span>
4566
+ <span class="normal"><a href="#__codelineno-0-188">188</a></span>
4567
+ <span class="normal"><a href="#__codelineno-0-189">189</a></span>
4568
+ <span class="normal"><a href="#__codelineno-0-190">190</a></span>
4569
+ <span class="normal"><a href="#__codelineno-0-191">191</a></span>
4570
+ <span class="normal"><a href="#__codelineno-0-192">192</a></span>
4571
+ <span class="normal"><a href="#__codelineno-0-193">193</a></span>
4572
+ <span class="normal"><a href="#__codelineno-0-194">194</a></span>
4573
+ <span class="normal"><a href="#__codelineno-0-195">195</a></span>
4574
+ <span class="normal"><a href="#__codelineno-0-196">196</a></span>
4575
+ <span class="normal"><a href="#__codelineno-0-197">197</a></span>
4576
+ <span class="normal"><a href="#__codelineno-0-198">198</a></span>
4577
+ <span class="normal"><a href="#__codelineno-0-199">199</a></span>
4578
+ <span class="normal"><a href="#__codelineno-0-200">200</a></span>
4579
+ <span class="normal"><a href="#__codelineno-0-201">201</a></span>
4580
+ <span class="normal"><a href="#__codelineno-0-202">202</a></span>
4581
+ <span class="normal"><a href="#__codelineno-0-203">203</a></span>
4582
+ <span class="normal"><a href="#__codelineno-0-204">204</a></span>
4583
+ <span class="normal"><a href="#__codelineno-0-205">205</a></span>
4584
+ <span class="normal"><a href="#__codelineno-0-206">206</a></span>
4585
+ <span class="normal"><a href="#__codelineno-0-207">207</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-13" name="__codelineno-0-13"></a><span class="k">class</span> <span class="nc">BaseModel</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
4586
+ <a id="__codelineno-0-14" name="__codelineno-0-14"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4587
+ <a id="__codelineno-0-15" name="__codelineno-0-15"></a><span class="sd"> Base model class that all models should inherit from.</span>
3385
4588
  <a id="__codelineno-0-16" name="__codelineno-0-16"></a>
3386
- <a id="__codelineno-0-17" name="__codelineno-0-17"></a><span class="sd"> Here we define the primary key to be a UUID field and set its default to</span>
3387
- <a id="__codelineno-0-18" name="__codelineno-0-18"></a><span class="sd"> automatically generate a random UUID value. Note however, this does not</span>
3388
- <a id="__codelineno-0-19" name="__codelineno-0-19"></a><span class="sd"> operate in the same way as a traditional auto incrementing field for which</span>
3389
- <a id="__codelineno-0-20" name="__codelineno-0-20"></a><span class="sd"> the value is issued by the database upon initial insert. In the case of</span>
3390
- <a id="__codelineno-0-21" name="__codelineno-0-21"></a><span class="sd"> the UUID field, Django creates the value upon object instantiation. This</span>
3391
- <a id="__codelineno-0-22" name="__codelineno-0-22"></a><span class="sd"> means the canonical pattern in Django of checking `self.pk is None` to tell</span>
3392
- <a id="__codelineno-0-23" name="__codelineno-0-23"></a><span class="sd"> if an object has been created in the actual database does not work because</span>
3393
- <a id="__codelineno-0-24" name="__codelineno-0-24"></a><span class="sd"> the object will always have the value populated prior to being saved to the</span>
3394
- <a id="__codelineno-0-25" name="__codelineno-0-25"></a><span class="sd"> database for the first time. An alternate pattern of checking `not self.present_in_database`</span>
3395
- <a id="__codelineno-0-26" name="__codelineno-0-26"></a><span class="sd"> can be used for the same purpose in most cases.</span>
3396
- <a id="__codelineno-0-27" name="__codelineno-0-27"></a><span class="sd"> &quot;&quot;&quot;</span>
3397
- <a id="__codelineno-0-28" name="__codelineno-0-28"></a>
3398
- <a id="__codelineno-0-29" name="__codelineno-0-29"></a> <span class="nb">id</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">UUIDField</span><span class="p">(</span><span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">uuid</span><span class="o">.</span><span class="n">uuid4</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">editable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
4589
+ <a id="__codelineno-0-17" name="__codelineno-0-17"></a><span class="sd"> This abstract base provides globally common fields and functionality.</span>
4590
+ <a id="__codelineno-0-18" name="__codelineno-0-18"></a>
4591
+ <a id="__codelineno-0-19" name="__codelineno-0-19"></a><span class="sd"> Here we define the primary key to be a UUID field and set its default to</span>
4592
+ <a id="__codelineno-0-20" name="__codelineno-0-20"></a><span class="sd"> automatically generate a random UUID value. Note however, this does not</span>
4593
+ <a id="__codelineno-0-21" name="__codelineno-0-21"></a><span class="sd"> operate in the same way as a traditional auto incrementing field for which</span>
4594
+ <a id="__codelineno-0-22" name="__codelineno-0-22"></a><span class="sd"> the value is issued by the database upon initial insert. In the case of</span>
4595
+ <a id="__codelineno-0-23" name="__codelineno-0-23"></a><span class="sd"> the UUID field, Django creates the value upon object instantiation. This</span>
4596
+ <a id="__codelineno-0-24" name="__codelineno-0-24"></a><span class="sd"> means the canonical pattern in Django of checking `self.pk is None` to tell</span>
4597
+ <a id="__codelineno-0-25" name="__codelineno-0-25"></a><span class="sd"> if an object has been created in the actual database does not work because</span>
4598
+ <a id="__codelineno-0-26" name="__codelineno-0-26"></a><span class="sd"> the object will always have the value populated prior to being saved to the</span>
4599
+ <a id="__codelineno-0-27" name="__codelineno-0-27"></a><span class="sd"> database for the first time. An alternate pattern of checking `not self.present_in_database`</span>
4600
+ <a id="__codelineno-0-28" name="__codelineno-0-28"></a><span class="sd"> can be used for the same purpose in most cases.</span>
4601
+ <a id="__codelineno-0-29" name="__codelineno-0-29"></a><span class="sd"> &quot;&quot;&quot;</span>
3399
4602
  <a id="__codelineno-0-30" name="__codelineno-0-30"></a>
3400
- <a id="__codelineno-0-31" name="__codelineno-0-31"></a> <span class="n">objects</span> <span class="o">=</span> <span class="n">BaseManager</span><span class="o">.</span><span class="n">from_queryset</span><span class="p">(</span><span class="n">RestrictedQuerySet</span><span class="p">)()</span>
4603
+ <a id="__codelineno-0-31" name="__codelineno-0-31"></a> <span class="nb">id</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">UUIDField</span><span class="p">(</span><span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">uuid</span><span class="o">.</span><span class="n">uuid4</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">editable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
3401
4604
  <a id="__codelineno-0-32" name="__codelineno-0-32"></a>
3402
- <a id="__codelineno-0-33" name="__codelineno-0-33"></a> <span class="nd">@property</span>
3403
- <a id="__codelineno-0-34" name="__codelineno-0-34"></a> <span class="k">def</span> <span class="nf">present_in_database</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
3404
- <a id="__codelineno-0-35" name="__codelineno-0-35"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3405
- <a id="__codelineno-0-36" name="__codelineno-0-36"></a><span class="sd"> True if the record exists in the database, False if it does not.</span>
3406
- <a id="__codelineno-0-37" name="__codelineno-0-37"></a><span class="sd"> &quot;&quot;&quot;</span>
3407
- <a id="__codelineno-0-38" name="__codelineno-0-38"></a> <span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_state</span><span class="o">.</span><span class="n">adding</span>
3408
- <a id="__codelineno-0-39" name="__codelineno-0-39"></a>
3409
- <a id="__codelineno-0-40" name="__codelineno-0-40"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
3410
- <a id="__codelineno-0-41" name="__codelineno-0-41"></a> <span class="n">abstract</span> <span class="o">=</span> <span class="kc">True</span>
3411
- <a id="__codelineno-0-42" name="__codelineno-0-42"></a>
3412
- <a id="__codelineno-0-43" name="__codelineno-0-43"></a> <span class="k">def</span> <span class="nf">validated_save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
3413
- <a id="__codelineno-0-44" name="__codelineno-0-44"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3414
- <a id="__codelineno-0-45" name="__codelineno-0-45"></a><span class="sd"> Perform model validation during instance save.</span>
4605
+ <a id="__codelineno-0-33" name="__codelineno-0-33"></a> <span class="n">objects</span> <span class="o">=</span> <span class="n">BaseManager</span><span class="o">.</span><span class="n">from_queryset</span><span class="p">(</span><span class="n">RestrictedQuerySet</span><span class="p">)()</span>
4606
+ <a id="__codelineno-0-34" name="__codelineno-0-34"></a>
4607
+ <a id="__codelineno-0-35" name="__codelineno-0-35"></a> <span class="nd">@property</span>
4608
+ <a id="__codelineno-0-36" name="__codelineno-0-36"></a> <span class="k">def</span> <span class="nf">present_in_database</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
4609
+ <a id="__codelineno-0-37" name="__codelineno-0-37"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4610
+ <a id="__codelineno-0-38" name="__codelineno-0-38"></a><span class="sd"> True if the record exists in the database, False if it does not.</span>
4611
+ <a id="__codelineno-0-39" name="__codelineno-0-39"></a><span class="sd"> &quot;&quot;&quot;</span>
4612
+ <a id="__codelineno-0-40" name="__codelineno-0-40"></a> <span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_state</span><span class="o">.</span><span class="n">adding</span>
4613
+ <a id="__codelineno-0-41" name="__codelineno-0-41"></a>
4614
+ <a id="__codelineno-0-42" name="__codelineno-0-42"></a> <span class="k">def</span> <span class="nf">get_absolute_url</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">api</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
4615
+ <a id="__codelineno-0-43" name="__codelineno-0-43"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4616
+ <a id="__codelineno-0-44" name="__codelineno-0-44"></a><span class="sd"> Return the canonical URL for this object in either the UI or the REST API.</span>
4617
+ <a id="__codelineno-0-45" name="__codelineno-0-45"></a><span class="sd"> &quot;&quot;&quot;</span>
3415
4618
  <a id="__codelineno-0-46" name="__codelineno-0-46"></a>
3416
- <a id="__codelineno-0-47" name="__codelineno-0-47"></a><span class="sd"> This is a convenience method that first calls `self.full_clean()` and then `self.save()`</span>
3417
- <a id="__codelineno-0-48" name="__codelineno-0-48"></a><span class="sd"> which in effect enforces model validation prior to saving the instance, without having</span>
3418
- <a id="__codelineno-0-49" name="__codelineno-0-49"></a><span class="sd"> to manually make these calls seperately. This is a slight departure from Django norms,</span>
3419
- <a id="__codelineno-0-50" name="__codelineno-0-50"></a><span class="sd"> but is intended to offer an optional, simplified interface for performing this common</span>
3420
- <a id="__codelineno-0-51" name="__codelineno-0-51"></a><span class="sd"> workflow. The intended use is for user defined Jobs and scripts run via the `nautobot-server nbshell`</span>
3421
- <a id="__codelineno-0-52" name="__codelineno-0-52"></a><span class="sd"> command.</span>
3422
- <a id="__codelineno-0-53" name="__codelineno-0-53"></a><span class="sd"> &quot;&quot;&quot;</span>
3423
- <a id="__codelineno-0-54" name="__codelineno-0-54"></a> <span class="bp">self</span><span class="o">.</span><span class="n">full_clean</span><span class="p">()</span>
3424
- <a id="__codelineno-0-55" name="__codelineno-0-55"></a> <span class="bp">self</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
3425
- <a id="__codelineno-0-56" name="__codelineno-0-56"></a>
3426
- <a id="__codelineno-0-57" name="__codelineno-0-57"></a> <span class="k">def</span> <span class="nf">natural_key</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">:</span>
3427
- <a id="__codelineno-0-58" name="__codelineno-0-58"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3428
- <a id="__codelineno-0-59" name="__codelineno-0-59"></a><span class="sd"> Smarter default implementation of natural key construction.</span>
3429
- <a id="__codelineno-0-60" name="__codelineno-0-60"></a>
3430
- <a id="__codelineno-0-61" name="__codelineno-0-61"></a><span class="sd"> 1. Handles nullable foreign keys (https://github.com/wq/django-natural-keys/issues/18)</span>
3431
- <a id="__codelineno-0-62" name="__codelineno-0-62"></a><span class="sd"> 2. Handles variadic natural-keys (e.g. Location model - [name, parent__name, parent__parent__name, ...].)</span>
3432
- <a id="__codelineno-0-63" name="__codelineno-0-63"></a><span class="sd"> &quot;&quot;&quot;</span>
3433
- <a id="__codelineno-0-64" name="__codelineno-0-64"></a> <span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
3434
- <a id="__codelineno-0-65" name="__codelineno-0-65"></a> <span class="k">for</span> <span class="n">lookups</span> <span class="ow">in</span> <span class="p">[</span><span class="n">lookup</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;__&quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">lookup</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">natural_key_field_lookups</span><span class="p">]:</span>
3435
- <a id="__codelineno-0-66" name="__codelineno-0-66"></a> <span class="n">val</span> <span class="o">=</span> <span class="bp">self</span>
3436
- <a id="__codelineno-0-67" name="__codelineno-0-67"></a> <span class="k">for</span> <span class="n">lookup</span> <span class="ow">in</span> <span class="n">lookups</span><span class="p">:</span>
3437
- <a id="__codelineno-0-68" name="__codelineno-0-68"></a> <span class="n">val</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="n">lookup</span><span class="p">)</span>
3438
- <a id="__codelineno-0-69" name="__codelineno-0-69"></a> <span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
3439
- <a id="__codelineno-0-70" name="__codelineno-0-70"></a> <span class="k">break</span>
3440
- <a id="__codelineno-0-71" name="__codelineno-0-71"></a> <span class="n">vals</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
3441
- <a id="__codelineno-0-72" name="__codelineno-0-72"></a> <span class="c1"># Strip trailing Nones from vals</span>
3442
- <a id="__codelineno-0-73" name="__codelineno-0-73"></a> <span class="k">while</span> <span class="n">vals</span> <span class="ow">and</span> <span class="n">vals</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
3443
- <a id="__codelineno-0-74" name="__codelineno-0-74"></a> <span class="n">vals</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
3444
- <a id="__codelineno-0-75" name="__codelineno-0-75"></a> <span class="k">return</span> <span class="n">vals</span>
3445
- <a id="__codelineno-0-76" name="__codelineno-0-76"></a>
3446
- <a id="__codelineno-0-77" name="__codelineno-0-77"></a> <span class="nd">@property</span>
3447
- <a id="__codelineno-0-78" name="__codelineno-0-78"></a> <span class="k">def</span> <span class="nf">natural_key_slug</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
3448
- <a id="__codelineno-0-79" name="__codelineno-0-79"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3449
- <a id="__codelineno-0-80" name="__codelineno-0-80"></a><span class="sd"> Automatic &quot;slug&quot; string derived from this model&#39;s natural key, suitable for use in URLs etc.</span>
4619
+ <a id="__codelineno-0-47" name="__codelineno-0-47"></a> <span class="c1"># Iterate the pk-like fields and try to get a URL, or return None.</span>
4620
+ <a id="__codelineno-0-48" name="__codelineno-0-48"></a> <span class="n">fields</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;pk&quot;</span><span class="p">,</span> <span class="s2">&quot;slug&quot;</span><span class="p">]</span> <span class="c1"># TODO: Eventually all PKs</span>
4621
+ <a id="__codelineno-0-49" name="__codelineno-0-49"></a> <span class="n">actions</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;retrieve&quot;</span><span class="p">,</span> <span class="s2">&quot;detail&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">]</span> <span class="c1"># TODO: Eventually all retrieve</span>
4622
+ <a id="__codelineno-0-50" name="__codelineno-0-50"></a>
4623
+ <a id="__codelineno-0-51" name="__codelineno-0-51"></a> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">fields</span><span class="p">:</span>
4624
+ <a id="__codelineno-0-52" name="__codelineno-0-52"></a> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
4625
+ <a id="__codelineno-0-53" name="__codelineno-0-53"></a> <span class="k">continue</span>
4626
+ <a id="__codelineno-0-54" name="__codelineno-0-54"></a>
4627
+ <a id="__codelineno-0-55" name="__codelineno-0-55"></a> <span class="k">for</span> <span class="n">action</span> <span class="ow">in</span> <span class="n">actions</span><span class="p">:</span>
4628
+ <a id="__codelineno-0-56" name="__codelineno-0-56"></a> <span class="n">route</span> <span class="o">=</span> <span class="n">get_route_for_model</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">api</span><span class="o">=</span><span class="n">api</span><span class="p">)</span>
4629
+ <a id="__codelineno-0-57" name="__codelineno-0-57"></a>
4630
+ <a id="__codelineno-0-58" name="__codelineno-0-58"></a> <span class="k">try</span><span class="p">:</span>
4631
+ <a id="__codelineno-0-59" name="__codelineno-0-59"></a> <span class="k">return</span> <span class="n">reverse</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="n">field</span><span class="p">:</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)})</span>
4632
+ <a id="__codelineno-0-60" name="__codelineno-0-60"></a> <span class="k">except</span> <span class="n">NoReverseMatch</span><span class="p">:</span>
4633
+ <a id="__codelineno-0-61" name="__codelineno-0-61"></a> <span class="k">continue</span>
4634
+ <a id="__codelineno-0-62" name="__codelineno-0-62"></a>
4635
+ <a id="__codelineno-0-63" name="__codelineno-0-63"></a> <span class="k">return</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Cannot find a URL for </span><span class="si">{</span><span class="bp">self</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">app_label</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">model_name</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
4636
+ <a id="__codelineno-0-64" name="__codelineno-0-64"></a>
4637
+ <a id="__codelineno-0-65" name="__codelineno-0-65"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
4638
+ <a id="__codelineno-0-66" name="__codelineno-0-66"></a> <span class="n">abstract</span> <span class="o">=</span> <span class="kc">True</span>
4639
+ <a id="__codelineno-0-67" name="__codelineno-0-67"></a>
4640
+ <a id="__codelineno-0-68" name="__codelineno-0-68"></a> <span class="k">def</span> <span class="nf">validated_save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
4641
+ <a id="__codelineno-0-69" name="__codelineno-0-69"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4642
+ <a id="__codelineno-0-70" name="__codelineno-0-70"></a><span class="sd"> Perform model validation during instance save.</span>
4643
+ <a id="__codelineno-0-71" name="__codelineno-0-71"></a>
4644
+ <a id="__codelineno-0-72" name="__codelineno-0-72"></a><span class="sd"> This is a convenience method that first calls `self.full_clean()` and then `self.save()`</span>
4645
+ <a id="__codelineno-0-73" name="__codelineno-0-73"></a><span class="sd"> which in effect enforces model validation prior to saving the instance, without having</span>
4646
+ <a id="__codelineno-0-74" name="__codelineno-0-74"></a><span class="sd"> to manually make these calls seperately. This is a slight departure from Django norms,</span>
4647
+ <a id="__codelineno-0-75" name="__codelineno-0-75"></a><span class="sd"> but is intended to offer an optional, simplified interface for performing this common</span>
4648
+ <a id="__codelineno-0-76" name="__codelineno-0-76"></a><span class="sd"> workflow. The intended use is for user defined Jobs run via the `nautobot-server nbshell`</span>
4649
+ <a id="__codelineno-0-77" name="__codelineno-0-77"></a><span class="sd"> command.</span>
4650
+ <a id="__codelineno-0-78" name="__codelineno-0-78"></a><span class="sd"> &quot;&quot;&quot;</span>
4651
+ <a id="__codelineno-0-79" name="__codelineno-0-79"></a> <span class="bp">self</span><span class="o">.</span><span class="n">full_clean</span><span class="p">()</span>
4652
+ <a id="__codelineno-0-80" name="__codelineno-0-80"></a> <span class="bp">self</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
3450
4653
  <a id="__codelineno-0-81" name="__codelineno-0-81"></a>
3451
- <a id="__codelineno-0-82" name="__codelineno-0-82"></a><span class="sd"> A less naïve implementation than django-natural-keys provides by default, based around URL percent-encoding.</span>
3452
- <a id="__codelineno-0-83" name="__codelineno-0-83"></a><span class="sd"> &quot;&quot;&quot;</span>
3453
- <a id="__codelineno-0-84" name="__codelineno-0-84"></a> <span class="k">return</span> <span class="n">construct_natural_key_slug</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">natural_key</span><span class="p">())</span>
4654
+ <a id="__codelineno-0-82" name="__codelineno-0-82"></a> <span class="k">def</span> <span class="nf">natural_key</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">:</span>
4655
+ <a id="__codelineno-0-83" name="__codelineno-0-83"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4656
+ <a id="__codelineno-0-84" name="__codelineno-0-84"></a><span class="sd"> Smarter default implementation of natural key construction.</span>
3454
4657
  <a id="__codelineno-0-85" name="__codelineno-0-85"></a>
3455
- <a id="__codelineno-0-86" name="__codelineno-0-86"></a> <span class="nd">@classproperty</span> <span class="c1"># https://github.com/PyCQA/pylint-django/issues/240</span>
3456
- <a id="__codelineno-0-87" name="__codelineno-0-87"></a> <span class="k">def</span> <span class="nf">natural_key_field_lookups</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span> <span class="c1"># pylint: disable=no-self-argument</span>
3457
- <a id="__codelineno-0-88" name="__codelineno-0-88"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3458
- <a id="__codelineno-0-89" name="__codelineno-0-89"></a><span class="sd"> List of lookups (possibly including nested lookups for related models) that make up this model&#39;s natural key.</span>
3459
- <a id="__codelineno-0-90" name="__codelineno-0-90"></a>
3460
- <a id="__codelineno-0-91" name="__codelineno-0-91"></a><span class="sd"> BaseModel provides a &quot;smart&quot; implementation that tries to determine this automatically,</span>
3461
- <a id="__codelineno-0-92" name="__codelineno-0-92"></a><span class="sd"> but you can also explicitly set `natural_key_field_names` on a given model subclass if desired.</span>
3462
- <a id="__codelineno-0-93" name="__codelineno-0-93"></a>
3463
- <a id="__codelineno-0-94" name="__codelineno-0-94"></a><span class="sd"> This property is based on a consolidation of `django-natural-keys` `ForeignKeyModel.get_natural_key_info()`,</span>
3464
- <a id="__codelineno-0-95" name="__codelineno-0-95"></a><span class="sd"> `ForeignKeyModel.get_natural_key_def()`, and `ForeignKeyModel.get_natural_key_fields()`.</span>
3465
- <a id="__codelineno-0-96" name="__codelineno-0-96"></a>
3466
- <a id="__codelineno-0-97" name="__codelineno-0-97"></a><span class="sd"> Unlike `get_natural_key_def()`, this doesn&#39;t auto-exclude all AutoField and BigAutoField fields,</span>
3467
- <a id="__codelineno-0-98" name="__codelineno-0-98"></a><span class="sd"> but instead explicitly discounts the `id` field (only) as a candidate.</span>
3468
- <a id="__codelineno-0-99" name="__codelineno-0-99"></a><span class="sd"> &quot;&quot;&quot;</span>
3469
- <a id="__codelineno-0-100" name="__codelineno-0-100"></a> <span class="c1"># First, figure out which local fields comprise the natural key:</span>
3470
- <a id="__codelineno-0-101" name="__codelineno-0-101"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="p">[]</span>
3471
- <a id="__codelineno-0-102" name="__codelineno-0-102"></a> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s2">&quot;natural_key_field_names&quot;</span><span class="p">):</span>
3472
- <a id="__codelineno-0-103" name="__codelineno-0-103"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">natural_key_field_names</span>
3473
- <a id="__codelineno-0-104" name="__codelineno-0-104"></a> <span class="k">else</span><span class="p">:</span>
3474
- <a id="__codelineno-0-105" name="__codelineno-0-105"></a> <span class="c1"># Does this model have any new-style UniqueConstraints? If so, pick the first one</span>
3475
- <a id="__codelineno-0-106" name="__codelineno-0-106"></a> <span class="k">for</span> <span class="n">constraint</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">constraints</span><span class="p">:</span>
3476
- <a id="__codelineno-0-107" name="__codelineno-0-107"></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">constraint</span><span class="p">,</span> <span class="n">models</span><span class="o">.</span><span class="n">UniqueConstraint</span><span class="p">):</span>
3477
- <a id="__codelineno-0-108" name="__codelineno-0-108"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="n">constraint</span><span class="o">.</span><span class="n">fields</span>
3478
- <a id="__codelineno-0-109" name="__codelineno-0-109"></a> <span class="k">break</span>
3479
- <a id="__codelineno-0-110" name="__codelineno-0-110"></a> <span class="k">else</span><span class="p">:</span>
3480
- <a id="__codelineno-0-111" name="__codelineno-0-111"></a> <span class="c1"># Else, does this model have any old-style unique_together? If so, pick the first one.</span>
3481
- <a id="__codelineno-0-112" name="__codelineno-0-112"></a> <span class="k">if</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">unique_together</span><span class="p">:</span>
3482
- <a id="__codelineno-0-113" name="__codelineno-0-113"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">unique_together</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
3483
- <a id="__codelineno-0-114" name="__codelineno-0-114"></a> <span class="k">else</span><span class="p">:</span>
3484
- <a id="__codelineno-0-115" name="__codelineno-0-115"></a> <span class="c1"># Else, do we have any individual unique=True fields? If so, pick the first one.</span>
3485
- <a id="__codelineno-0-116" name="__codelineno-0-116"></a> <span class="n">unique_fields</span> <span class="o">=</span> <span class="p">[</span><span class="n">field</span> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">fields</span> <span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">unique</span> <span class="ow">and</span> <span class="n">field</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;id&quot;</span><span class="p">]</span>
3486
- <a id="__codelineno-0-117" name="__codelineno-0-117"></a> <span class="k">if</span> <span class="n">unique_fields</span><span class="p">:</span>
3487
- <a id="__codelineno-0-118" name="__codelineno-0-118"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="p">(</span><span class="n">unique_fields</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">name</span><span class="p">,)</span>
3488
- <a id="__codelineno-0-119" name="__codelineno-0-119"></a>
3489
- <a id="__codelineno-0-120" name="__codelineno-0-120"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">natural_key_field_names</span><span class="p">:</span>
3490
- <a id="__codelineno-0-121" name="__codelineno-0-121"></a> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
3491
- <a id="__codelineno-0-122" name="__codelineno-0-122"></a> <span class="sa">f</span><span class="s2">&quot;Unable to identify an intrinsic natural-key definition for </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">. &quot;</span>
3492
- <a id="__codelineno-0-123" name="__codelineno-0-123"></a> <span class="s2">&quot;If there isn&#39;t at least one UniqueConstraint, unique_together, or field with unique=True, &quot;</span>
3493
- <a id="__codelineno-0-124" name="__codelineno-0-124"></a> <span class="s2">&quot;you probably need to explicitly declare the &#39;natural_key_field_names&#39; for this model, &quot;</span>
3494
- <a id="__codelineno-0-125" name="__codelineno-0-125"></a> <span class="s2">&quot;or potentially override the default &#39;natural_key_field_lookups&#39; implementation for this model.&quot;</span>
3495
- <a id="__codelineno-0-126" name="__codelineno-0-126"></a> <span class="p">)</span>
3496
- <a id="__codelineno-0-127" name="__codelineno-0-127"></a>
3497
- <a id="__codelineno-0-128" name="__codelineno-0-128"></a> <span class="c1"># Next, for any natural key fields that have related models, get the natural key for the related model if known</span>
3498
- <a id="__codelineno-0-129" name="__codelineno-0-129"></a> <span class="n">natural_key_field_lookups</span> <span class="o">=</span> <span class="p">[]</span>
3499
- <a id="__codelineno-0-130" name="__codelineno-0-130"></a> <span class="k">for</span> <span class="n">field_name</span> <span class="ow">in</span> <span class="n">natural_key_field_names</span><span class="p">:</span>
3500
- <a id="__codelineno-0-131" name="__codelineno-0-131"></a> <span class="n">field</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">get_field</span><span class="p">(</span><span class="n">field_name</span><span class="p">)</span>
3501
- <a id="__codelineno-0-132" name="__codelineno-0-132"></a> <span class="k">if</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="s2">&quot;remote_field&quot;</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
3502
- <a id="__codelineno-0-133" name="__codelineno-0-133"></a> <span class="c1"># Not a related field, so the field name is the field lookup</span>
3503
- <a id="__codelineno-0-134" name="__codelineno-0-134"></a> <span class="n">natural_key_field_lookups</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">field_name</span><span class="p">)</span>
3504
- <a id="__codelineno-0-135" name="__codelineno-0-135"></a> <span class="k">continue</span>
3505
- <a id="__codelineno-0-136" name="__codelineno-0-136"></a>
3506
- <a id="__codelineno-0-137" name="__codelineno-0-137"></a> <span class="n">related_model</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">remote_field</span><span class="o">.</span><span class="n">model</span>
3507
- <a id="__codelineno-0-138" name="__codelineno-0-138"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="kc">None</span>
3508
- <a id="__codelineno-0-139" name="__codelineno-0-139"></a> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">related_model</span><span class="p">,</span> <span class="s2">&quot;natural_key_field_lookups&quot;</span><span class="p">):</span>
3509
- <a id="__codelineno-0-140" name="__codelineno-0-140"></a> <span class="c1"># TODO: generic handling for self-referential case, as seen in Location</span>
3510
- <a id="__codelineno-0-141" name="__codelineno-0-141"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="n">related_model</span><span class="o">.</span><span class="n">natural_key_field_lookups</span>
3511
- <a id="__codelineno-0-142" name="__codelineno-0-142"></a> <span class="k">else</span><span class="p">:</span>
3512
- <a id="__codelineno-0-143" name="__codelineno-0-143"></a> <span class="c1"># Related model isn&#39;t a Nautobot model and so doesn&#39;t have a `natural_key_field_lookups`.</span>
3513
- <a id="__codelineno-0-144" name="__codelineno-0-144"></a> <span class="c1"># The common case we&#39;ve encountered so far is the contenttypes.ContentType model:</span>
3514
- <a id="__codelineno-0-145" name="__codelineno-0-145"></a> <span class="k">if</span> <span class="n">related_model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">app_label</span> <span class="o">==</span> <span class="s2">&quot;contenttypes&quot;</span> <span class="ow">and</span> <span class="n">related_model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">model_name</span> <span class="o">==</span> <span class="s2">&quot;contenttype&quot;</span><span class="p">:</span>
3515
- <a id="__codelineno-0-146" name="__codelineno-0-146"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;app_label&quot;</span><span class="p">,</span> <span class="s2">&quot;model&quot;</span><span class="p">]</span>
3516
- <a id="__codelineno-0-147" name="__codelineno-0-147"></a> <span class="c1"># Additional special cases can be added here</span>
3517
- <a id="__codelineno-0-148" name="__codelineno-0-148"></a>
3518
- <a id="__codelineno-0-149" name="__codelineno-0-149"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">related_natural_key_field_lookups</span><span class="p">:</span>
3519
- <a id="__codelineno-0-150" name="__codelineno-0-150"></a> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
3520
- <a id="__codelineno-0-151" name="__codelineno-0-151"></a> <span class="sa">f</span><span class="s2">&quot;Unable to determine the related natural-key fields for </span><span class="si">{</span><span class="n">related_model</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> &quot;</span>
3521
- <a id="__codelineno-0-152" name="__codelineno-0-152"></a> <span class="sa">f</span><span class="s2">&quot;(as referenced from </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="n">field_name</span><span class="si">}</span><span class="s2">). If the related model is a non-Nautobot &quot;</span>
3522
- <a id="__codelineno-0-153" name="__codelineno-0-153"></a> <span class="s2">&quot;model (such as ContentType) then it may be appropriate to add special-case handling for this &quot;</span>
3523
- <a id="__codelineno-0-154" name="__codelineno-0-154"></a> <span class="s2">&quot;model in BaseModel.natural_key_field_lookups; alternately you may be able to solve this for &quot;</span>
3524
- <a id="__codelineno-0-155" name="__codelineno-0-155"></a> <span class="sa">f</span><span class="s2">&quot;a single special case by explicitly defining </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.natural_key_field_lookups.&quot;</span>
3525
- <a id="__codelineno-0-156" name="__codelineno-0-156"></a> <span class="p">)</span>
3526
- <a id="__codelineno-0-157" name="__codelineno-0-157"></a>
3527
- <a id="__codelineno-0-158" name="__codelineno-0-158"></a> <span class="k">for</span> <span class="n">field_lookup</span> <span class="ow">in</span> <span class="n">related_natural_key_field_lookups</span><span class="p">:</span>
3528
- <a id="__codelineno-0-159" name="__codelineno-0-159"></a> <span class="n">natural_key_field_lookups</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">field_name</span><span class="si">}</span><span class="s2">__</span><span class="si">{</span><span class="n">field_lookup</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
3529
- <a id="__codelineno-0-160" name="__codelineno-0-160"></a>
3530
- <a id="__codelineno-0-161" name="__codelineno-0-161"></a> <span class="k">return</span> <span class="n">natural_key_field_lookups</span>
3531
- <a id="__codelineno-0-162" name="__codelineno-0-162"></a>
3532
- <a id="__codelineno-0-163" name="__codelineno-0-163"></a> <span class="nd">@classmethod</span>
3533
- <a id="__codelineno-0-164" name="__codelineno-0-164"></a> <span class="k">def</span> <span class="nf">natural_key_args_to_kwargs</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">args</span><span class="p">):</span>
3534
- <a id="__codelineno-0-165" name="__codelineno-0-165"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3535
- <a id="__codelineno-0-166" name="__codelineno-0-166"></a><span class="sd"> Helper function to map a list of natural key field values to actual kwargs suitable for lookup and filtering.</span>
3536
- <a id="__codelineno-0-167" name="__codelineno-0-167"></a>
3537
- <a id="__codelineno-0-168" name="__codelineno-0-168"></a><span class="sd"> Based on `django-natural-keys` `NaturalKeyQuerySet.natural_key_kwargs()` method.</span>
3538
- <a id="__codelineno-0-169" name="__codelineno-0-169"></a><span class="sd"> &quot;&quot;&quot;</span>
3539
- <a id="__codelineno-0-170" name="__codelineno-0-170"></a> <span class="n">args</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
3540
- <a id="__codelineno-0-171" name="__codelineno-0-171"></a> <span class="n">natural_key_field_lookups</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">natural_key_field_lookups</span>
3541
- <a id="__codelineno-0-172" name="__codelineno-0-172"></a> <span class="c1"># Because `natural_key` strips trailing `None` from the natural key to handle the variadic-natural-key case,</span>
3542
- <a id="__codelineno-0-173" name="__codelineno-0-173"></a> <span class="c1"># we may need to add trailing `None` back on to make the number of args match back up.</span>
3543
- <a id="__codelineno-0-174" name="__codelineno-0-174"></a> <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">):</span>
3544
- <a id="__codelineno-0-175" name="__codelineno-0-175"></a> <span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
3545
- <a id="__codelineno-0-176" name="__codelineno-0-176"></a> <span class="c1"># However, if we have *too many* args, that&#39;s just incorrect usage:</span>
3546
- <a id="__codelineno-0-177" name="__codelineno-0-177"></a> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">):</span>
3547
- <a id="__codelineno-0-178" name="__codelineno-0-178"></a> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
3548
- <a id="__codelineno-0-179" name="__codelineno-0-179"></a> <span class="sa">f</span><span class="s2">&quot;Wrong number of natural-key args for </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.natural_key_args_to_kwargs() -- &quot;</span>
3549
- <a id="__codelineno-0-180" name="__codelineno-0-180"></a> <span class="sa">f</span><span class="s2">&quot;expected no more than </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">)</span><span class="si">}</span><span class="s2"> but got </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span><span class="si">}</span><span class="s2">.&quot;</span>
3550
- <a id="__codelineno-0-181" name="__codelineno-0-181"></a> <span class="p">)</span>
3551
- <a id="__codelineno-0-182" name="__codelineno-0-182"></a> <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">,</span> <span class="n">args</span><span class="p">))</span>
4658
+ <a id="__codelineno-0-86" name="__codelineno-0-86"></a><span class="sd"> 1. Handles nullable foreign keys (https://github.com/wq/django-natural-keys/issues/18)</span>
4659
+ <a id="__codelineno-0-87" name="__codelineno-0-87"></a><span class="sd"> 2. Handles variadic natural-keys (e.g. Location model - [name, parent__name, parent__parent__name, ...].)</span>
4660
+ <a id="__codelineno-0-88" name="__codelineno-0-88"></a><span class="sd"> &quot;&quot;&quot;</span>
4661
+ <a id="__codelineno-0-89" name="__codelineno-0-89"></a> <span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
4662
+ <a id="__codelineno-0-90" name="__codelineno-0-90"></a> <span class="k">for</span> <span class="n">lookups</span> <span class="ow">in</span> <span class="p">[</span><span class="n">lookup</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;__&quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">lookup</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">natural_key_field_lookups</span><span class="p">]:</span>
4663
+ <a id="__codelineno-0-91" name="__codelineno-0-91"></a> <span class="n">val</span> <span class="o">=</span> <span class="bp">self</span>
4664
+ <a id="__codelineno-0-92" name="__codelineno-0-92"></a> <span class="k">for</span> <span class="n">lookup</span> <span class="ow">in</span> <span class="n">lookups</span><span class="p">:</span>
4665
+ <a id="__codelineno-0-93" name="__codelineno-0-93"></a> <span class="n">val</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="n">lookup</span><span class="p">)</span>
4666
+ <a id="__codelineno-0-94" name="__codelineno-0-94"></a> <span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
4667
+ <a id="__codelineno-0-95" name="__codelineno-0-95"></a> <span class="k">break</span>
4668
+ <a id="__codelineno-0-96" name="__codelineno-0-96"></a> <span class="n">vals</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
4669
+ <a id="__codelineno-0-97" name="__codelineno-0-97"></a> <span class="c1"># Strip trailing Nones from vals</span>
4670
+ <a id="__codelineno-0-98" name="__codelineno-0-98"></a> <span class="k">while</span> <span class="n">vals</span> <span class="ow">and</span> <span class="n">vals</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
4671
+ <a id="__codelineno-0-99" name="__codelineno-0-99"></a> <span class="n">vals</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
4672
+ <a id="__codelineno-0-100" name="__codelineno-0-100"></a> <span class="k">return</span> <span class="n">vals</span>
4673
+ <a id="__codelineno-0-101" name="__codelineno-0-101"></a>
4674
+ <a id="__codelineno-0-102" name="__codelineno-0-102"></a> <span class="nd">@property</span>
4675
+ <a id="__codelineno-0-103" name="__codelineno-0-103"></a> <span class="k">def</span> <span class="nf">natural_key_slug</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
4676
+ <a id="__codelineno-0-104" name="__codelineno-0-104"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4677
+ <a id="__codelineno-0-105" name="__codelineno-0-105"></a><span class="sd"> Automatic &quot;slug&quot; string derived from this model&#39;s natural key, suitable for use in URLs etc.</span>
4678
+ <a id="__codelineno-0-106" name="__codelineno-0-106"></a>
4679
+ <a id="__codelineno-0-107" name="__codelineno-0-107"></a><span class="sd"> A less naïve implementation than django-natural-keys provides by default, based around URL percent-encoding.</span>
4680
+ <a id="__codelineno-0-108" name="__codelineno-0-108"></a><span class="sd"> &quot;&quot;&quot;</span>
4681
+ <a id="__codelineno-0-109" name="__codelineno-0-109"></a> <span class="k">return</span> <span class="n">construct_natural_key_slug</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">natural_key</span><span class="p">())</span>
4682
+ <a id="__codelineno-0-110" name="__codelineno-0-110"></a>
4683
+ <a id="__codelineno-0-111" name="__codelineno-0-111"></a> <span class="nd">@classproperty</span> <span class="c1"># https://github.com/PyCQA/pylint-django/issues/240</span>
4684
+ <a id="__codelineno-0-112" name="__codelineno-0-112"></a> <span class="k">def</span> <span class="nf">natural_key_field_lookups</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span> <span class="c1"># pylint: disable=no-self-argument</span>
4685
+ <a id="__codelineno-0-113" name="__codelineno-0-113"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4686
+ <a id="__codelineno-0-114" name="__codelineno-0-114"></a><span class="sd"> List of lookups (possibly including nested lookups for related models) that make up this model&#39;s natural key.</span>
4687
+ <a id="__codelineno-0-115" name="__codelineno-0-115"></a>
4688
+ <a id="__codelineno-0-116" name="__codelineno-0-116"></a><span class="sd"> BaseModel provides a &quot;smart&quot; implementation that tries to determine this automatically,</span>
4689
+ <a id="__codelineno-0-117" name="__codelineno-0-117"></a><span class="sd"> but you can also explicitly set `natural_key_field_names` on a given model subclass if desired.</span>
4690
+ <a id="__codelineno-0-118" name="__codelineno-0-118"></a>
4691
+ <a id="__codelineno-0-119" name="__codelineno-0-119"></a><span class="sd"> This property is based on a consolidation of `django-natural-keys` `ForeignKeyModel.get_natural_key_info()`,</span>
4692
+ <a id="__codelineno-0-120" name="__codelineno-0-120"></a><span class="sd"> `ForeignKeyModel.get_natural_key_def()`, and `ForeignKeyModel.get_natural_key_fields()`.</span>
4693
+ <a id="__codelineno-0-121" name="__codelineno-0-121"></a>
4694
+ <a id="__codelineno-0-122" name="__codelineno-0-122"></a><span class="sd"> Unlike `get_natural_key_def()`, this doesn&#39;t auto-exclude all AutoField and BigAutoField fields,</span>
4695
+ <a id="__codelineno-0-123" name="__codelineno-0-123"></a><span class="sd"> but instead explicitly discounts the `id` field (only) as a candidate.</span>
4696
+ <a id="__codelineno-0-124" name="__codelineno-0-124"></a><span class="sd"> &quot;&quot;&quot;</span>
4697
+ <a id="__codelineno-0-125" name="__codelineno-0-125"></a> <span class="c1"># First, figure out which local fields comprise the natural key:</span>
4698
+ <a id="__codelineno-0-126" name="__codelineno-0-126"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="p">[]</span>
4699
+ <a id="__codelineno-0-127" name="__codelineno-0-127"></a> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s2">&quot;natural_key_field_names&quot;</span><span class="p">):</span>
4700
+ <a id="__codelineno-0-128" name="__codelineno-0-128"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">natural_key_field_names</span>
4701
+ <a id="__codelineno-0-129" name="__codelineno-0-129"></a> <span class="k">else</span><span class="p">:</span>
4702
+ <a id="__codelineno-0-130" name="__codelineno-0-130"></a> <span class="c1"># Does this model have any new-style UniqueConstraints? If so, pick the first one</span>
4703
+ <a id="__codelineno-0-131" name="__codelineno-0-131"></a> <span class="k">for</span> <span class="n">constraint</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">constraints</span><span class="p">:</span>
4704
+ <a id="__codelineno-0-132" name="__codelineno-0-132"></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">constraint</span><span class="p">,</span> <span class="n">models</span><span class="o">.</span><span class="n">UniqueConstraint</span><span class="p">):</span>
4705
+ <a id="__codelineno-0-133" name="__codelineno-0-133"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="n">constraint</span><span class="o">.</span><span class="n">fields</span>
4706
+ <a id="__codelineno-0-134" name="__codelineno-0-134"></a> <span class="k">break</span>
4707
+ <a id="__codelineno-0-135" name="__codelineno-0-135"></a> <span class="k">else</span><span class="p">:</span>
4708
+ <a id="__codelineno-0-136" name="__codelineno-0-136"></a> <span class="c1"># Else, does this model have any old-style unique_together? If so, pick the first one.</span>
4709
+ <a id="__codelineno-0-137" name="__codelineno-0-137"></a> <span class="k">if</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">unique_together</span><span class="p">:</span>
4710
+ <a id="__codelineno-0-138" name="__codelineno-0-138"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">unique_together</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
4711
+ <a id="__codelineno-0-139" name="__codelineno-0-139"></a> <span class="k">else</span><span class="p">:</span>
4712
+ <a id="__codelineno-0-140" name="__codelineno-0-140"></a> <span class="c1"># Else, do we have any individual unique=True fields? If so, pick the first one.</span>
4713
+ <a id="__codelineno-0-141" name="__codelineno-0-141"></a> <span class="n">unique_fields</span> <span class="o">=</span> <span class="p">[</span><span class="n">field</span> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">fields</span> <span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">unique</span> <span class="ow">and</span> <span class="n">field</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;id&quot;</span><span class="p">]</span>
4714
+ <a id="__codelineno-0-142" name="__codelineno-0-142"></a> <span class="k">if</span> <span class="n">unique_fields</span><span class="p">:</span>
4715
+ <a id="__codelineno-0-143" name="__codelineno-0-143"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="p">(</span><span class="n">unique_fields</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">name</span><span class="p">,)</span>
4716
+ <a id="__codelineno-0-144" name="__codelineno-0-144"></a>
4717
+ <a id="__codelineno-0-145" name="__codelineno-0-145"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">natural_key_field_names</span><span class="p">:</span>
4718
+ <a id="__codelineno-0-146" name="__codelineno-0-146"></a> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
4719
+ <a id="__codelineno-0-147" name="__codelineno-0-147"></a> <span class="sa">f</span><span class="s2">&quot;Unable to identify an intrinsic natural-key definition for </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">. &quot;</span>
4720
+ <a id="__codelineno-0-148" name="__codelineno-0-148"></a> <span class="s2">&quot;If there isn&#39;t at least one UniqueConstraint, unique_together, or field with unique=True, &quot;</span>
4721
+ <a id="__codelineno-0-149" name="__codelineno-0-149"></a> <span class="s2">&quot;you probably need to explicitly declare the &#39;natural_key_field_names&#39; for this model, &quot;</span>
4722
+ <a id="__codelineno-0-150" name="__codelineno-0-150"></a> <span class="s2">&quot;or potentially override the default &#39;natural_key_field_lookups&#39; implementation for this model.&quot;</span>
4723
+ <a id="__codelineno-0-151" name="__codelineno-0-151"></a> <span class="p">)</span>
4724
+ <a id="__codelineno-0-152" name="__codelineno-0-152"></a>
4725
+ <a id="__codelineno-0-153" name="__codelineno-0-153"></a> <span class="c1"># Next, for any natural key fields that have related models, get the natural key for the related model if known</span>
4726
+ <a id="__codelineno-0-154" name="__codelineno-0-154"></a> <span class="n">natural_key_field_lookups</span> <span class="o">=</span> <span class="p">[]</span>
4727
+ <a id="__codelineno-0-155" name="__codelineno-0-155"></a> <span class="k">for</span> <span class="n">field_name</span> <span class="ow">in</span> <span class="n">natural_key_field_names</span><span class="p">:</span>
4728
+ <a id="__codelineno-0-156" name="__codelineno-0-156"></a> <span class="n">field</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">get_field</span><span class="p">(</span><span class="n">field_name</span><span class="p">)</span>
4729
+ <a id="__codelineno-0-157" name="__codelineno-0-157"></a> <span class="k">if</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="s2">&quot;remote_field&quot;</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
4730
+ <a id="__codelineno-0-158" name="__codelineno-0-158"></a> <span class="c1"># Not a related field, so the field name is the field lookup</span>
4731
+ <a id="__codelineno-0-159" name="__codelineno-0-159"></a> <span class="n">natural_key_field_lookups</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">field_name</span><span class="p">)</span>
4732
+ <a id="__codelineno-0-160" name="__codelineno-0-160"></a> <span class="k">continue</span>
4733
+ <a id="__codelineno-0-161" name="__codelineno-0-161"></a>
4734
+ <a id="__codelineno-0-162" name="__codelineno-0-162"></a> <span class="n">related_model</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">remote_field</span><span class="o">.</span><span class="n">model</span>
4735
+ <a id="__codelineno-0-163" name="__codelineno-0-163"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="kc">None</span>
4736
+ <a id="__codelineno-0-164" name="__codelineno-0-164"></a> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">related_model</span><span class="p">,</span> <span class="s2">&quot;natural_key_field_lookups&quot;</span><span class="p">):</span>
4737
+ <a id="__codelineno-0-165" name="__codelineno-0-165"></a> <span class="c1"># TODO: generic handling for self-referential case, as seen in Location</span>
4738
+ <a id="__codelineno-0-166" name="__codelineno-0-166"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="n">related_model</span><span class="o">.</span><span class="n">natural_key_field_lookups</span>
4739
+ <a id="__codelineno-0-167" name="__codelineno-0-167"></a> <span class="k">else</span><span class="p">:</span>
4740
+ <a id="__codelineno-0-168" name="__codelineno-0-168"></a> <span class="c1"># Related model isn&#39;t a Nautobot model and so doesn&#39;t have a `natural_key_field_lookups`.</span>
4741
+ <a id="__codelineno-0-169" name="__codelineno-0-169"></a> <span class="c1"># The common case we&#39;ve encountered so far is the contenttypes.ContentType model:</span>
4742
+ <a id="__codelineno-0-170" name="__codelineno-0-170"></a> <span class="k">if</span> <span class="n">related_model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">app_label</span> <span class="o">==</span> <span class="s2">&quot;contenttypes&quot;</span> <span class="ow">and</span> <span class="n">related_model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">model_name</span> <span class="o">==</span> <span class="s2">&quot;contenttype&quot;</span><span class="p">:</span>
4743
+ <a id="__codelineno-0-171" name="__codelineno-0-171"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;app_label&quot;</span><span class="p">,</span> <span class="s2">&quot;model&quot;</span><span class="p">]</span>
4744
+ <a id="__codelineno-0-172" name="__codelineno-0-172"></a> <span class="c1"># Additional special cases can be added here</span>
4745
+ <a id="__codelineno-0-173" name="__codelineno-0-173"></a>
4746
+ <a id="__codelineno-0-174" name="__codelineno-0-174"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">related_natural_key_field_lookups</span><span class="p">:</span>
4747
+ <a id="__codelineno-0-175" name="__codelineno-0-175"></a> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
4748
+ <a id="__codelineno-0-176" name="__codelineno-0-176"></a> <span class="sa">f</span><span class="s2">&quot;Unable to determine the related natural-key fields for </span><span class="si">{</span><span class="n">related_model</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> &quot;</span>
4749
+ <a id="__codelineno-0-177" name="__codelineno-0-177"></a> <span class="sa">f</span><span class="s2">&quot;(as referenced from </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="n">field_name</span><span class="si">}</span><span class="s2">). If the related model is a non-Nautobot &quot;</span>
4750
+ <a id="__codelineno-0-178" name="__codelineno-0-178"></a> <span class="s2">&quot;model (such as ContentType) then it may be appropriate to add special-case handling for this &quot;</span>
4751
+ <a id="__codelineno-0-179" name="__codelineno-0-179"></a> <span class="s2">&quot;model in BaseModel.natural_key_field_lookups; alternately you may be able to solve this for &quot;</span>
4752
+ <a id="__codelineno-0-180" name="__codelineno-0-180"></a> <span class="sa">f</span><span class="s2">&quot;a single special case by explicitly defining </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.natural_key_field_lookups.&quot;</span>
4753
+ <a id="__codelineno-0-181" name="__codelineno-0-181"></a> <span class="p">)</span>
4754
+ <a id="__codelineno-0-182" name="__codelineno-0-182"></a>
4755
+ <a id="__codelineno-0-183" name="__codelineno-0-183"></a> <span class="k">for</span> <span class="n">field_lookup</span> <span class="ow">in</span> <span class="n">related_natural_key_field_lookups</span><span class="p">:</span>
4756
+ <a id="__codelineno-0-184" name="__codelineno-0-184"></a> <span class="n">natural_key_field_lookups</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">field_name</span><span class="si">}</span><span class="s2">__</span><span class="si">{</span><span class="n">field_lookup</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
4757
+ <a id="__codelineno-0-185" name="__codelineno-0-185"></a>
4758
+ <a id="__codelineno-0-186" name="__codelineno-0-186"></a> <span class="k">return</span> <span class="n">natural_key_field_lookups</span>
4759
+ <a id="__codelineno-0-187" name="__codelineno-0-187"></a>
4760
+ <a id="__codelineno-0-188" name="__codelineno-0-188"></a> <span class="nd">@classmethod</span>
4761
+ <a id="__codelineno-0-189" name="__codelineno-0-189"></a> <span class="k">def</span> <span class="nf">natural_key_args_to_kwargs</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">args</span><span class="p">):</span>
4762
+ <a id="__codelineno-0-190" name="__codelineno-0-190"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4763
+ <a id="__codelineno-0-191" name="__codelineno-0-191"></a><span class="sd"> Helper function to map a list of natural key field values to actual kwargs suitable for lookup and filtering.</span>
4764
+ <a id="__codelineno-0-192" name="__codelineno-0-192"></a>
4765
+ <a id="__codelineno-0-193" name="__codelineno-0-193"></a><span class="sd"> Based on `django-natural-keys` `NaturalKeyQuerySet.natural_key_kwargs()` method.</span>
4766
+ <a id="__codelineno-0-194" name="__codelineno-0-194"></a><span class="sd"> &quot;&quot;&quot;</span>
4767
+ <a id="__codelineno-0-195" name="__codelineno-0-195"></a> <span class="n">args</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
4768
+ <a id="__codelineno-0-196" name="__codelineno-0-196"></a> <span class="n">natural_key_field_lookups</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">natural_key_field_lookups</span>
4769
+ <a id="__codelineno-0-197" name="__codelineno-0-197"></a> <span class="c1"># Because `natural_key` strips trailing `None` from the natural key to handle the variadic-natural-key case,</span>
4770
+ <a id="__codelineno-0-198" name="__codelineno-0-198"></a> <span class="c1"># we may need to add trailing `None` back on to make the number of args match back up.</span>
4771
+ <a id="__codelineno-0-199" name="__codelineno-0-199"></a> <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">):</span>
4772
+ <a id="__codelineno-0-200" name="__codelineno-0-200"></a> <span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
4773
+ <a id="__codelineno-0-201" name="__codelineno-0-201"></a> <span class="c1"># However, if we have *too many* args, that&#39;s just incorrect usage:</span>
4774
+ <a id="__codelineno-0-202" name="__codelineno-0-202"></a> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">):</span>
4775
+ <a id="__codelineno-0-203" name="__codelineno-0-203"></a> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
4776
+ <a id="__codelineno-0-204" name="__codelineno-0-204"></a> <span class="sa">f</span><span class="s2">&quot;Wrong number of natural-key args for </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.natural_key_args_to_kwargs() -- &quot;</span>
4777
+ <a id="__codelineno-0-205" name="__codelineno-0-205"></a> <span class="sa">f</span><span class="s2">&quot;expected no more than </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">)</span><span class="si">}</span><span class="s2"> but got </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span><span class="si">}</span><span class="s2">.&quot;</span>
4778
+ <a id="__codelineno-0-206" name="__codelineno-0-206"></a> <span class="p">)</span>
4779
+ <a id="__codelineno-0-207" name="__codelineno-0-207"></a> <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">,</span> <span class="n">args</span><span class="p">))</span>
3552
4780
  </code></pre></div></td></tr></table></div>
3553
4781
  </details>
3554
4782
 
@@ -3611,6 +4839,71 @@ can be used for the same purpose in most cases.</p>
3611
4839
 
3612
4840
 
3613
4841
 
4842
+ <h3 id="nautobot.core.models.BaseModel.get_absolute_url" class="doc doc-heading">
4843
+ <code class="highlight language-python"><span class="n">get_absolute_url</span><span class="p">(</span><span class="n">api</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span></code>
4844
+
4845
+ <a href="#nautobot.core.models.BaseModel.get_absolute_url" class="headerlink" title="Permanent link">&para;</a></h3>
4846
+
4847
+
4848
+ <div class="doc doc-contents ">
4849
+
4850
+ <p>Return the canonical URL for this object in either the UI or the REST API.</p>
4851
+
4852
+ <details class="quote">
4853
+ <summary>Source code in <code>nautobot/core/models/__init__.py</code></summary>
4854
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-42">42</a></span>
4855
+ <span class="normal"><a href="#__codelineno-0-43">43</a></span>
4856
+ <span class="normal"><a href="#__codelineno-0-44">44</a></span>
4857
+ <span class="normal"><a href="#__codelineno-0-45">45</a></span>
4858
+ <span class="normal"><a href="#__codelineno-0-46">46</a></span>
4859
+ <span class="normal"><a href="#__codelineno-0-47">47</a></span>
4860
+ <span class="normal"><a href="#__codelineno-0-48">48</a></span>
4861
+ <span class="normal"><a href="#__codelineno-0-49">49</a></span>
4862
+ <span class="normal"><a href="#__codelineno-0-50">50</a></span>
4863
+ <span class="normal"><a href="#__codelineno-0-51">51</a></span>
4864
+ <span class="normal"><a href="#__codelineno-0-52">52</a></span>
4865
+ <span class="normal"><a href="#__codelineno-0-53">53</a></span>
4866
+ <span class="normal"><a href="#__codelineno-0-54">54</a></span>
4867
+ <span class="normal"><a href="#__codelineno-0-55">55</a></span>
4868
+ <span class="normal"><a href="#__codelineno-0-56">56</a></span>
4869
+ <span class="normal"><a href="#__codelineno-0-57">57</a></span>
4870
+ <span class="normal"><a href="#__codelineno-0-58">58</a></span>
4871
+ <span class="normal"><a href="#__codelineno-0-59">59</a></span>
4872
+ <span class="normal"><a href="#__codelineno-0-60">60</a></span>
4873
+ <span class="normal"><a href="#__codelineno-0-61">61</a></span>
4874
+ <span class="normal"><a href="#__codelineno-0-62">62</a></span>
4875
+ <span class="normal"><a href="#__codelineno-0-63">63</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-42" name="__codelineno-0-42"></a><span class="k">def</span> <span class="nf">get_absolute_url</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">api</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
4876
+ <a id="__codelineno-0-43" name="__codelineno-0-43"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4877
+ <a id="__codelineno-0-44" name="__codelineno-0-44"></a><span class="sd"> Return the canonical URL for this object in either the UI or the REST API.</span>
4878
+ <a id="__codelineno-0-45" name="__codelineno-0-45"></a><span class="sd"> &quot;&quot;&quot;</span>
4879
+ <a id="__codelineno-0-46" name="__codelineno-0-46"></a>
4880
+ <a id="__codelineno-0-47" name="__codelineno-0-47"></a> <span class="c1"># Iterate the pk-like fields and try to get a URL, or return None.</span>
4881
+ <a id="__codelineno-0-48" name="__codelineno-0-48"></a> <span class="n">fields</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;pk&quot;</span><span class="p">,</span> <span class="s2">&quot;slug&quot;</span><span class="p">]</span> <span class="c1"># TODO: Eventually all PKs</span>
4882
+ <a id="__codelineno-0-49" name="__codelineno-0-49"></a> <span class="n">actions</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;retrieve&quot;</span><span class="p">,</span> <span class="s2">&quot;detail&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">]</span> <span class="c1"># TODO: Eventually all retrieve</span>
4883
+ <a id="__codelineno-0-50" name="__codelineno-0-50"></a>
4884
+ <a id="__codelineno-0-51" name="__codelineno-0-51"></a> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">fields</span><span class="p">:</span>
4885
+ <a id="__codelineno-0-52" name="__codelineno-0-52"></a> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
4886
+ <a id="__codelineno-0-53" name="__codelineno-0-53"></a> <span class="k">continue</span>
4887
+ <a id="__codelineno-0-54" name="__codelineno-0-54"></a>
4888
+ <a id="__codelineno-0-55" name="__codelineno-0-55"></a> <span class="k">for</span> <span class="n">action</span> <span class="ow">in</span> <span class="n">actions</span><span class="p">:</span>
4889
+ <a id="__codelineno-0-56" name="__codelineno-0-56"></a> <span class="n">route</span> <span class="o">=</span> <span class="n">get_route_for_model</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">api</span><span class="o">=</span><span class="n">api</span><span class="p">)</span>
4890
+ <a id="__codelineno-0-57" name="__codelineno-0-57"></a>
4891
+ <a id="__codelineno-0-58" name="__codelineno-0-58"></a> <span class="k">try</span><span class="p">:</span>
4892
+ <a id="__codelineno-0-59" name="__codelineno-0-59"></a> <span class="k">return</span> <span class="n">reverse</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">kwargs</span><span class="o">=</span><span class="p">{</span><span class="n">field</span><span class="p">:</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)})</span>
4893
+ <a id="__codelineno-0-60" name="__codelineno-0-60"></a> <span class="k">except</span> <span class="n">NoReverseMatch</span><span class="p">:</span>
4894
+ <a id="__codelineno-0-61" name="__codelineno-0-61"></a> <span class="k">continue</span>
4895
+ <a id="__codelineno-0-62" name="__codelineno-0-62"></a>
4896
+ <a id="__codelineno-0-63" name="__codelineno-0-63"></a> <span class="k">return</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Cannot find a URL for </span><span class="si">{</span><span class="bp">self</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">app_label</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">model_name</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
4897
+ </code></pre></div></td></tr></table></div>
4898
+ </details>
4899
+ </div>
4900
+
4901
+ </div>
4902
+
4903
+ <div class="doc doc-object doc-function">
4904
+
4905
+
4906
+
3614
4907
  <h3 id="nautobot.core.models.BaseModel.natural_key" class="doc doc-heading">
3615
4908
  <code class="highlight language-python"><span class="n">natural_key</span><span class="p">()</span></code>
3616
4909
 
@@ -3627,43 +4920,43 @@ can be used for the same purpose in most cases.</p>
3627
4920
 
3628
4921
  <details class="quote">
3629
4922
  <summary>Source code in <code>nautobot/core/models/__init__.py</code></summary>
3630
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-57">57</a></span>
3631
- <span class="normal"><a href="#__codelineno-0-58">58</a></span>
3632
- <span class="normal"><a href="#__codelineno-0-59">59</a></span>
3633
- <span class="normal"><a href="#__codelineno-0-60">60</a></span>
3634
- <span class="normal"><a href="#__codelineno-0-61">61</a></span>
3635
- <span class="normal"><a href="#__codelineno-0-62">62</a></span>
3636
- <span class="normal"><a href="#__codelineno-0-63">63</a></span>
3637
- <span class="normal"><a href="#__codelineno-0-64">64</a></span>
3638
- <span class="normal"><a href="#__codelineno-0-65">65</a></span>
3639
- <span class="normal"><a href="#__codelineno-0-66">66</a></span>
3640
- <span class="normal"><a href="#__codelineno-0-67">67</a></span>
3641
- <span class="normal"><a href="#__codelineno-0-68">68</a></span>
3642
- <span class="normal"><a href="#__codelineno-0-69">69</a></span>
3643
- <span class="normal"><a href="#__codelineno-0-70">70</a></span>
3644
- <span class="normal"><a href="#__codelineno-0-71">71</a></span>
3645
- <span class="normal"><a href="#__codelineno-0-72">72</a></span>
3646
- <span class="normal"><a href="#__codelineno-0-73">73</a></span>
3647
- <span class="normal"><a href="#__codelineno-0-74">74</a></span>
3648
- <span class="normal"><a href="#__codelineno-0-75">75</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-57" name="__codelineno-0-57"></a><span class="k">def</span> <span class="nf">natural_key</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">:</span>
3649
- <a id="__codelineno-0-58" name="__codelineno-0-58"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3650
- <a id="__codelineno-0-59" name="__codelineno-0-59"></a><span class="sd"> Smarter default implementation of natural key construction.</span>
3651
- <a id="__codelineno-0-60" name="__codelineno-0-60"></a>
3652
- <a id="__codelineno-0-61" name="__codelineno-0-61"></a><span class="sd"> 1. Handles nullable foreign keys (https://github.com/wq/django-natural-keys/issues/18)</span>
3653
- <a id="__codelineno-0-62" name="__codelineno-0-62"></a><span class="sd"> 2. Handles variadic natural-keys (e.g. Location model - [name, parent__name, parent__parent__name, ...].)</span>
3654
- <a id="__codelineno-0-63" name="__codelineno-0-63"></a><span class="sd"> &quot;&quot;&quot;</span>
3655
- <a id="__codelineno-0-64" name="__codelineno-0-64"></a> <span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
3656
- <a id="__codelineno-0-65" name="__codelineno-0-65"></a> <span class="k">for</span> <span class="n">lookups</span> <span class="ow">in</span> <span class="p">[</span><span class="n">lookup</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;__&quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">lookup</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">natural_key_field_lookups</span><span class="p">]:</span>
3657
- <a id="__codelineno-0-66" name="__codelineno-0-66"></a> <span class="n">val</span> <span class="o">=</span> <span class="bp">self</span>
3658
- <a id="__codelineno-0-67" name="__codelineno-0-67"></a> <span class="k">for</span> <span class="n">lookup</span> <span class="ow">in</span> <span class="n">lookups</span><span class="p">:</span>
3659
- <a id="__codelineno-0-68" name="__codelineno-0-68"></a> <span class="n">val</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="n">lookup</span><span class="p">)</span>
3660
- <a id="__codelineno-0-69" name="__codelineno-0-69"></a> <span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
3661
- <a id="__codelineno-0-70" name="__codelineno-0-70"></a> <span class="k">break</span>
3662
- <a id="__codelineno-0-71" name="__codelineno-0-71"></a> <span class="n">vals</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
3663
- <a id="__codelineno-0-72" name="__codelineno-0-72"></a> <span class="c1"># Strip trailing Nones from vals</span>
3664
- <a id="__codelineno-0-73" name="__codelineno-0-73"></a> <span class="k">while</span> <span class="n">vals</span> <span class="ow">and</span> <span class="n">vals</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
3665
- <a id="__codelineno-0-74" name="__codelineno-0-74"></a> <span class="n">vals</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
3666
- <a id="__codelineno-0-75" name="__codelineno-0-75"></a> <span class="k">return</span> <span class="n">vals</span>
4923
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-82"> 82</a></span>
4924
+ <span class="normal"><a href="#__codelineno-0-83"> 83</a></span>
4925
+ <span class="normal"><a href="#__codelineno-0-84"> 84</a></span>
4926
+ <span class="normal"><a href="#__codelineno-0-85"> 85</a></span>
4927
+ <span class="normal"><a href="#__codelineno-0-86"> 86</a></span>
4928
+ <span class="normal"><a href="#__codelineno-0-87"> 87</a></span>
4929
+ <span class="normal"><a href="#__codelineno-0-88"> 88</a></span>
4930
+ <span class="normal"><a href="#__codelineno-0-89"> 89</a></span>
4931
+ <span class="normal"><a href="#__codelineno-0-90"> 90</a></span>
4932
+ <span class="normal"><a href="#__codelineno-0-91"> 91</a></span>
4933
+ <span class="normal"><a href="#__codelineno-0-92"> 92</a></span>
4934
+ <span class="normal"><a href="#__codelineno-0-93"> 93</a></span>
4935
+ <span class="normal"><a href="#__codelineno-0-94"> 94</a></span>
4936
+ <span class="normal"><a href="#__codelineno-0-95"> 95</a></span>
4937
+ <span class="normal"><a href="#__codelineno-0-96"> 96</a></span>
4938
+ <span class="normal"><a href="#__codelineno-0-97"> 97</a></span>
4939
+ <span class="normal"><a href="#__codelineno-0-98"> 98</a></span>
4940
+ <span class="normal"><a href="#__codelineno-0-99"> 99</a></span>
4941
+ <span class="normal"><a href="#__codelineno-0-100">100</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-82" name="__codelineno-0-82"></a><span class="k">def</span> <span class="nf">natural_key</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">:</span>
4942
+ <a id="__codelineno-0-83" name="__codelineno-0-83"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4943
+ <a id="__codelineno-0-84" name="__codelineno-0-84"></a><span class="sd"> Smarter default implementation of natural key construction.</span>
4944
+ <a id="__codelineno-0-85" name="__codelineno-0-85"></a>
4945
+ <a id="__codelineno-0-86" name="__codelineno-0-86"></a><span class="sd"> 1. Handles nullable foreign keys (https://github.com/wq/django-natural-keys/issues/18)</span>
4946
+ <a id="__codelineno-0-87" name="__codelineno-0-87"></a><span class="sd"> 2. Handles variadic natural-keys (e.g. Location model - [name, parent__name, parent__parent__name, ...].)</span>
4947
+ <a id="__codelineno-0-88" name="__codelineno-0-88"></a><span class="sd"> &quot;&quot;&quot;</span>
4948
+ <a id="__codelineno-0-89" name="__codelineno-0-89"></a> <span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
4949
+ <a id="__codelineno-0-90" name="__codelineno-0-90"></a> <span class="k">for</span> <span class="n">lookups</span> <span class="ow">in</span> <span class="p">[</span><span class="n">lookup</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;__&quot;</span><span class="p">)</span> <span class="k">for</span> <span class="n">lookup</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">natural_key_field_lookups</span><span class="p">]:</span>
4950
+ <a id="__codelineno-0-91" name="__codelineno-0-91"></a> <span class="n">val</span> <span class="o">=</span> <span class="bp">self</span>
4951
+ <a id="__codelineno-0-92" name="__codelineno-0-92"></a> <span class="k">for</span> <span class="n">lookup</span> <span class="ow">in</span> <span class="n">lookups</span><span class="p">:</span>
4952
+ <a id="__codelineno-0-93" name="__codelineno-0-93"></a> <span class="n">val</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="n">lookup</span><span class="p">)</span>
4953
+ <a id="__codelineno-0-94" name="__codelineno-0-94"></a> <span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
4954
+ <a id="__codelineno-0-95" name="__codelineno-0-95"></a> <span class="k">break</span>
4955
+ <a id="__codelineno-0-96" name="__codelineno-0-96"></a> <span class="n">vals</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
4956
+ <a id="__codelineno-0-97" name="__codelineno-0-97"></a> <span class="c1"># Strip trailing Nones from vals</span>
4957
+ <a id="__codelineno-0-98" name="__codelineno-0-98"></a> <span class="k">while</span> <span class="n">vals</span> <span class="ow">and</span> <span class="n">vals</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
4958
+ <a id="__codelineno-0-99" name="__codelineno-0-99"></a> <span class="n">vals</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
4959
+ <a id="__codelineno-0-100" name="__codelineno-0-100"></a> <span class="k">return</span> <span class="n">vals</span>
3667
4960
  </code></pre></div></td></tr></table></div>
3668
4961
  </details>
3669
4962
  </div>
@@ -3691,45 +4984,45 @@ can be used for the same purpose in most cases.</p>
3691
4984
 
3692
4985
  <details class="quote">
3693
4986
  <summary>Source code in <code>nautobot/core/models/__init__.py</code></summary>
3694
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-163">163</a></span>
3695
- <span class="normal"><a href="#__codelineno-0-164">164</a></span>
3696
- <span class="normal"><a href="#__codelineno-0-165">165</a></span>
3697
- <span class="normal"><a href="#__codelineno-0-166">166</a></span>
3698
- <span class="normal"><a href="#__codelineno-0-167">167</a></span>
3699
- <span class="normal"><a href="#__codelineno-0-168">168</a></span>
3700
- <span class="normal"><a href="#__codelineno-0-169">169</a></span>
3701
- <span class="normal"><a href="#__codelineno-0-170">170</a></span>
3702
- <span class="normal"><a href="#__codelineno-0-171">171</a></span>
3703
- <span class="normal"><a href="#__codelineno-0-172">172</a></span>
3704
- <span class="normal"><a href="#__codelineno-0-173">173</a></span>
3705
- <span class="normal"><a href="#__codelineno-0-174">174</a></span>
3706
- <span class="normal"><a href="#__codelineno-0-175">175</a></span>
3707
- <span class="normal"><a href="#__codelineno-0-176">176</a></span>
3708
- <span class="normal"><a href="#__codelineno-0-177">177</a></span>
3709
- <span class="normal"><a href="#__codelineno-0-178">178</a></span>
3710
- <span class="normal"><a href="#__codelineno-0-179">179</a></span>
3711
- <span class="normal"><a href="#__codelineno-0-180">180</a></span>
3712
- <span class="normal"><a href="#__codelineno-0-181">181</a></span>
3713
- <span class="normal"><a href="#__codelineno-0-182">182</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-163" name="__codelineno-0-163"></a><span class="nd">@classmethod</span>
3714
- <a id="__codelineno-0-164" name="__codelineno-0-164"></a><span class="k">def</span> <span class="nf">natural_key_args_to_kwargs</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">args</span><span class="p">):</span>
3715
- <a id="__codelineno-0-165" name="__codelineno-0-165"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3716
- <a id="__codelineno-0-166" name="__codelineno-0-166"></a><span class="sd"> Helper function to map a list of natural key field values to actual kwargs suitable for lookup and filtering.</span>
3717
- <a id="__codelineno-0-167" name="__codelineno-0-167"></a>
3718
- <a id="__codelineno-0-168" name="__codelineno-0-168"></a><span class="sd"> Based on `django-natural-keys` `NaturalKeyQuerySet.natural_key_kwargs()` method.</span>
3719
- <a id="__codelineno-0-169" name="__codelineno-0-169"></a><span class="sd"> &quot;&quot;&quot;</span>
3720
- <a id="__codelineno-0-170" name="__codelineno-0-170"></a> <span class="n">args</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
3721
- <a id="__codelineno-0-171" name="__codelineno-0-171"></a> <span class="n">natural_key_field_lookups</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">natural_key_field_lookups</span>
3722
- <a id="__codelineno-0-172" name="__codelineno-0-172"></a> <span class="c1"># Because `natural_key` strips trailing `None` from the natural key to handle the variadic-natural-key case,</span>
3723
- <a id="__codelineno-0-173" name="__codelineno-0-173"></a> <span class="c1"># we may need to add trailing `None` back on to make the number of args match back up.</span>
3724
- <a id="__codelineno-0-174" name="__codelineno-0-174"></a> <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">):</span>
3725
- <a id="__codelineno-0-175" name="__codelineno-0-175"></a> <span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
3726
- <a id="__codelineno-0-176" name="__codelineno-0-176"></a> <span class="c1"># However, if we have *too many* args, that&#39;s just incorrect usage:</span>
3727
- <a id="__codelineno-0-177" name="__codelineno-0-177"></a> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">):</span>
3728
- <a id="__codelineno-0-178" name="__codelineno-0-178"></a> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
3729
- <a id="__codelineno-0-179" name="__codelineno-0-179"></a> <span class="sa">f</span><span class="s2">&quot;Wrong number of natural-key args for </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.natural_key_args_to_kwargs() -- &quot;</span>
3730
- <a id="__codelineno-0-180" name="__codelineno-0-180"></a> <span class="sa">f</span><span class="s2">&quot;expected no more than </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">)</span><span class="si">}</span><span class="s2"> but got </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span><span class="si">}</span><span class="s2">.&quot;</span>
3731
- <a id="__codelineno-0-181" name="__codelineno-0-181"></a> <span class="p">)</span>
3732
- <a id="__codelineno-0-182" name="__codelineno-0-182"></a> <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">,</span> <span class="n">args</span><span class="p">))</span>
4987
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-188">188</a></span>
4988
+ <span class="normal"><a href="#__codelineno-0-189">189</a></span>
4989
+ <span class="normal"><a href="#__codelineno-0-190">190</a></span>
4990
+ <span class="normal"><a href="#__codelineno-0-191">191</a></span>
4991
+ <span class="normal"><a href="#__codelineno-0-192">192</a></span>
4992
+ <span class="normal"><a href="#__codelineno-0-193">193</a></span>
4993
+ <span class="normal"><a href="#__codelineno-0-194">194</a></span>
4994
+ <span class="normal"><a href="#__codelineno-0-195">195</a></span>
4995
+ <span class="normal"><a href="#__codelineno-0-196">196</a></span>
4996
+ <span class="normal"><a href="#__codelineno-0-197">197</a></span>
4997
+ <span class="normal"><a href="#__codelineno-0-198">198</a></span>
4998
+ <span class="normal"><a href="#__codelineno-0-199">199</a></span>
4999
+ <span class="normal"><a href="#__codelineno-0-200">200</a></span>
5000
+ <span class="normal"><a href="#__codelineno-0-201">201</a></span>
5001
+ <span class="normal"><a href="#__codelineno-0-202">202</a></span>
5002
+ <span class="normal"><a href="#__codelineno-0-203">203</a></span>
5003
+ <span class="normal"><a href="#__codelineno-0-204">204</a></span>
5004
+ <span class="normal"><a href="#__codelineno-0-205">205</a></span>
5005
+ <span class="normal"><a href="#__codelineno-0-206">206</a></span>
5006
+ <span class="normal"><a href="#__codelineno-0-207">207</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-188" name="__codelineno-0-188"></a><span class="nd">@classmethod</span>
5007
+ <a id="__codelineno-0-189" name="__codelineno-0-189"></a><span class="k">def</span> <span class="nf">natural_key_args_to_kwargs</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">args</span><span class="p">):</span>
5008
+ <a id="__codelineno-0-190" name="__codelineno-0-190"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5009
+ <a id="__codelineno-0-191" name="__codelineno-0-191"></a><span class="sd"> Helper function to map a list of natural key field values to actual kwargs suitable for lookup and filtering.</span>
5010
+ <a id="__codelineno-0-192" name="__codelineno-0-192"></a>
5011
+ <a id="__codelineno-0-193" name="__codelineno-0-193"></a><span class="sd"> Based on `django-natural-keys` `NaturalKeyQuerySet.natural_key_kwargs()` method.</span>
5012
+ <a id="__codelineno-0-194" name="__codelineno-0-194"></a><span class="sd"> &quot;&quot;&quot;</span>
5013
+ <a id="__codelineno-0-195" name="__codelineno-0-195"></a> <span class="n">args</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
5014
+ <a id="__codelineno-0-196" name="__codelineno-0-196"></a> <span class="n">natural_key_field_lookups</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">natural_key_field_lookups</span>
5015
+ <a id="__codelineno-0-197" name="__codelineno-0-197"></a> <span class="c1"># Because `natural_key` strips trailing `None` from the natural key to handle the variadic-natural-key case,</span>
5016
+ <a id="__codelineno-0-198" name="__codelineno-0-198"></a> <span class="c1"># we may need to add trailing `None` back on to make the number of args match back up.</span>
5017
+ <a id="__codelineno-0-199" name="__codelineno-0-199"></a> <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">):</span>
5018
+ <a id="__codelineno-0-200" name="__codelineno-0-200"></a> <span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
5019
+ <a id="__codelineno-0-201" name="__codelineno-0-201"></a> <span class="c1"># However, if we have *too many* args, that&#39;s just incorrect usage:</span>
5020
+ <a id="__codelineno-0-202" name="__codelineno-0-202"></a> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">):</span>
5021
+ <a id="__codelineno-0-203" name="__codelineno-0-203"></a> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
5022
+ <a id="__codelineno-0-204" name="__codelineno-0-204"></a> <span class="sa">f</span><span class="s2">&quot;Wrong number of natural-key args for </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.natural_key_args_to_kwargs() -- &quot;</span>
5023
+ <a id="__codelineno-0-205" name="__codelineno-0-205"></a> <span class="sa">f</span><span class="s2">&quot;expected no more than </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">)</span><span class="si">}</span><span class="s2"> but got </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span><span class="si">}</span><span class="s2">.&quot;</span>
5024
+ <a id="__codelineno-0-206" name="__codelineno-0-206"></a> <span class="p">)</span>
5025
+ <a id="__codelineno-0-207" name="__codelineno-0-207"></a> <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">natural_key_field_lookups</span><span class="p">,</span> <span class="n">args</span><span class="p">))</span>
3733
5026
  </code></pre></div></td></tr></table></div>
3734
5027
  </details>
3735
5028
  </div>
@@ -3758,32 +5051,7 @@ but instead explicitly discounts the <code>id</code> field (only) as a candidate
3758
5051
 
3759
5052
  <details class="quote">
3760
5053
  <summary>Source code in <code>nautobot/core/models/__init__.py</code></summary>
3761
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-86"> 86</a></span>
3762
- <span class="normal"><a href="#__codelineno-0-87"> 87</a></span>
3763
- <span class="normal"><a href="#__codelineno-0-88"> 88</a></span>
3764
- <span class="normal"><a href="#__codelineno-0-89"> 89</a></span>
3765
- <span class="normal"><a href="#__codelineno-0-90"> 90</a></span>
3766
- <span class="normal"><a href="#__codelineno-0-91"> 91</a></span>
3767
- <span class="normal"><a href="#__codelineno-0-92"> 92</a></span>
3768
- <span class="normal"><a href="#__codelineno-0-93"> 93</a></span>
3769
- <span class="normal"><a href="#__codelineno-0-94"> 94</a></span>
3770
- <span class="normal"><a href="#__codelineno-0-95"> 95</a></span>
3771
- <span class="normal"><a href="#__codelineno-0-96"> 96</a></span>
3772
- <span class="normal"><a href="#__codelineno-0-97"> 97</a></span>
3773
- <span class="normal"><a href="#__codelineno-0-98"> 98</a></span>
3774
- <span class="normal"><a href="#__codelineno-0-99"> 99</a></span>
3775
- <span class="normal"><a href="#__codelineno-0-100">100</a></span>
3776
- <span class="normal"><a href="#__codelineno-0-101">101</a></span>
3777
- <span class="normal"><a href="#__codelineno-0-102">102</a></span>
3778
- <span class="normal"><a href="#__codelineno-0-103">103</a></span>
3779
- <span class="normal"><a href="#__codelineno-0-104">104</a></span>
3780
- <span class="normal"><a href="#__codelineno-0-105">105</a></span>
3781
- <span class="normal"><a href="#__codelineno-0-106">106</a></span>
3782
- <span class="normal"><a href="#__codelineno-0-107">107</a></span>
3783
- <span class="normal"><a href="#__codelineno-0-108">108</a></span>
3784
- <span class="normal"><a href="#__codelineno-0-109">109</a></span>
3785
- <span class="normal"><a href="#__codelineno-0-110">110</a></span>
3786
- <span class="normal"><a href="#__codelineno-0-111">111</a></span>
5054
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-111">111</a></span>
3787
5055
  <span class="normal"><a href="#__codelineno-0-112">112</a></span>
3788
5056
  <span class="normal"><a href="#__codelineno-0-113">113</a></span>
3789
5057
  <span class="normal"><a href="#__codelineno-0-114">114</a></span>
@@ -3833,82 +5101,107 @@ but instead explicitly discounts the <code>id</code> field (only) as a candidate
3833
5101
  <span class="normal"><a href="#__codelineno-0-158">158</a></span>
3834
5102
  <span class="normal"><a href="#__codelineno-0-159">159</a></span>
3835
5103
  <span class="normal"><a href="#__codelineno-0-160">160</a></span>
3836
- <span class="normal"><a href="#__codelineno-0-161">161</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-86" name="__codelineno-0-86"></a><span class="nd">@classproperty</span> <span class="c1"># https://github.com/PyCQA/pylint-django/issues/240</span>
3837
- <a id="__codelineno-0-87" name="__codelineno-0-87"></a><span class="k">def</span> <span class="nf">natural_key_field_lookups</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span> <span class="c1"># pylint: disable=no-self-argument</span>
3838
- <a id="__codelineno-0-88" name="__codelineno-0-88"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3839
- <a id="__codelineno-0-89" name="__codelineno-0-89"></a><span class="sd"> List of lookups (possibly including nested lookups for related models) that make up this model&#39;s natural key.</span>
3840
- <a id="__codelineno-0-90" name="__codelineno-0-90"></a>
3841
- <a id="__codelineno-0-91" name="__codelineno-0-91"></a><span class="sd"> BaseModel provides a &quot;smart&quot; implementation that tries to determine this automatically,</span>
3842
- <a id="__codelineno-0-92" name="__codelineno-0-92"></a><span class="sd"> but you can also explicitly set `natural_key_field_names` on a given model subclass if desired.</span>
3843
- <a id="__codelineno-0-93" name="__codelineno-0-93"></a>
3844
- <a id="__codelineno-0-94" name="__codelineno-0-94"></a><span class="sd"> This property is based on a consolidation of `django-natural-keys` `ForeignKeyModel.get_natural_key_info()`,</span>
3845
- <a id="__codelineno-0-95" name="__codelineno-0-95"></a><span class="sd"> `ForeignKeyModel.get_natural_key_def()`, and `ForeignKeyModel.get_natural_key_fields()`.</span>
3846
- <a id="__codelineno-0-96" name="__codelineno-0-96"></a>
3847
- <a id="__codelineno-0-97" name="__codelineno-0-97"></a><span class="sd"> Unlike `get_natural_key_def()`, this doesn&#39;t auto-exclude all AutoField and BigAutoField fields,</span>
3848
- <a id="__codelineno-0-98" name="__codelineno-0-98"></a><span class="sd"> but instead explicitly discounts the `id` field (only) as a candidate.</span>
3849
- <a id="__codelineno-0-99" name="__codelineno-0-99"></a><span class="sd"> &quot;&quot;&quot;</span>
3850
- <a id="__codelineno-0-100" name="__codelineno-0-100"></a> <span class="c1"># First, figure out which local fields comprise the natural key:</span>
3851
- <a id="__codelineno-0-101" name="__codelineno-0-101"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="p">[]</span>
3852
- <a id="__codelineno-0-102" name="__codelineno-0-102"></a> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s2">&quot;natural_key_field_names&quot;</span><span class="p">):</span>
3853
- <a id="__codelineno-0-103" name="__codelineno-0-103"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">natural_key_field_names</span>
3854
- <a id="__codelineno-0-104" name="__codelineno-0-104"></a> <span class="k">else</span><span class="p">:</span>
3855
- <a id="__codelineno-0-105" name="__codelineno-0-105"></a> <span class="c1"># Does this model have any new-style UniqueConstraints? If so, pick the first one</span>
3856
- <a id="__codelineno-0-106" name="__codelineno-0-106"></a> <span class="k">for</span> <span class="n">constraint</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">constraints</span><span class="p">:</span>
3857
- <a id="__codelineno-0-107" name="__codelineno-0-107"></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">constraint</span><span class="p">,</span> <span class="n">models</span><span class="o">.</span><span class="n">UniqueConstraint</span><span class="p">):</span>
3858
- <a id="__codelineno-0-108" name="__codelineno-0-108"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="n">constraint</span><span class="o">.</span><span class="n">fields</span>
3859
- <a id="__codelineno-0-109" name="__codelineno-0-109"></a> <span class="k">break</span>
3860
- <a id="__codelineno-0-110" name="__codelineno-0-110"></a> <span class="k">else</span><span class="p">:</span>
3861
- <a id="__codelineno-0-111" name="__codelineno-0-111"></a> <span class="c1"># Else, does this model have any old-style unique_together? If so, pick the first one.</span>
3862
- <a id="__codelineno-0-112" name="__codelineno-0-112"></a> <span class="k">if</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">unique_together</span><span class="p">:</span>
3863
- <a id="__codelineno-0-113" name="__codelineno-0-113"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">unique_together</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
3864
- <a id="__codelineno-0-114" name="__codelineno-0-114"></a> <span class="k">else</span><span class="p">:</span>
3865
- <a id="__codelineno-0-115" name="__codelineno-0-115"></a> <span class="c1"># Else, do we have any individual unique=True fields? If so, pick the first one.</span>
3866
- <a id="__codelineno-0-116" name="__codelineno-0-116"></a> <span class="n">unique_fields</span> <span class="o">=</span> <span class="p">[</span><span class="n">field</span> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">fields</span> <span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">unique</span> <span class="ow">and</span> <span class="n">field</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;id&quot;</span><span class="p">]</span>
3867
- <a id="__codelineno-0-117" name="__codelineno-0-117"></a> <span class="k">if</span> <span class="n">unique_fields</span><span class="p">:</span>
3868
- <a id="__codelineno-0-118" name="__codelineno-0-118"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="p">(</span><span class="n">unique_fields</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">name</span><span class="p">,)</span>
3869
- <a id="__codelineno-0-119" name="__codelineno-0-119"></a>
3870
- <a id="__codelineno-0-120" name="__codelineno-0-120"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">natural_key_field_names</span><span class="p">:</span>
3871
- <a id="__codelineno-0-121" name="__codelineno-0-121"></a> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
3872
- <a id="__codelineno-0-122" name="__codelineno-0-122"></a> <span class="sa">f</span><span class="s2">&quot;Unable to identify an intrinsic natural-key definition for </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">. &quot;</span>
3873
- <a id="__codelineno-0-123" name="__codelineno-0-123"></a> <span class="s2">&quot;If there isn&#39;t at least one UniqueConstraint, unique_together, or field with unique=True, &quot;</span>
3874
- <a id="__codelineno-0-124" name="__codelineno-0-124"></a> <span class="s2">&quot;you probably need to explicitly declare the &#39;natural_key_field_names&#39; for this model, &quot;</span>
3875
- <a id="__codelineno-0-125" name="__codelineno-0-125"></a> <span class="s2">&quot;or potentially override the default &#39;natural_key_field_lookups&#39; implementation for this model.&quot;</span>
3876
- <a id="__codelineno-0-126" name="__codelineno-0-126"></a> <span class="p">)</span>
3877
- <a id="__codelineno-0-127" name="__codelineno-0-127"></a>
3878
- <a id="__codelineno-0-128" name="__codelineno-0-128"></a> <span class="c1"># Next, for any natural key fields that have related models, get the natural key for the related model if known</span>
3879
- <a id="__codelineno-0-129" name="__codelineno-0-129"></a> <span class="n">natural_key_field_lookups</span> <span class="o">=</span> <span class="p">[]</span>
3880
- <a id="__codelineno-0-130" name="__codelineno-0-130"></a> <span class="k">for</span> <span class="n">field_name</span> <span class="ow">in</span> <span class="n">natural_key_field_names</span><span class="p">:</span>
3881
- <a id="__codelineno-0-131" name="__codelineno-0-131"></a> <span class="n">field</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">get_field</span><span class="p">(</span><span class="n">field_name</span><span class="p">)</span>
3882
- <a id="__codelineno-0-132" name="__codelineno-0-132"></a> <span class="k">if</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="s2">&quot;remote_field&quot;</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
3883
- <a id="__codelineno-0-133" name="__codelineno-0-133"></a> <span class="c1"># Not a related field, so the field name is the field lookup</span>
3884
- <a id="__codelineno-0-134" name="__codelineno-0-134"></a> <span class="n">natural_key_field_lookups</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">field_name</span><span class="p">)</span>
3885
- <a id="__codelineno-0-135" name="__codelineno-0-135"></a> <span class="k">continue</span>
3886
- <a id="__codelineno-0-136" name="__codelineno-0-136"></a>
3887
- <a id="__codelineno-0-137" name="__codelineno-0-137"></a> <span class="n">related_model</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">remote_field</span><span class="o">.</span><span class="n">model</span>
3888
- <a id="__codelineno-0-138" name="__codelineno-0-138"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="kc">None</span>
3889
- <a id="__codelineno-0-139" name="__codelineno-0-139"></a> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">related_model</span><span class="p">,</span> <span class="s2">&quot;natural_key_field_lookups&quot;</span><span class="p">):</span>
3890
- <a id="__codelineno-0-140" name="__codelineno-0-140"></a> <span class="c1"># TODO: generic handling for self-referential case, as seen in Location</span>
3891
- <a id="__codelineno-0-141" name="__codelineno-0-141"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="n">related_model</span><span class="o">.</span><span class="n">natural_key_field_lookups</span>
3892
- <a id="__codelineno-0-142" name="__codelineno-0-142"></a> <span class="k">else</span><span class="p">:</span>
3893
- <a id="__codelineno-0-143" name="__codelineno-0-143"></a> <span class="c1"># Related model isn&#39;t a Nautobot model and so doesn&#39;t have a `natural_key_field_lookups`.</span>
3894
- <a id="__codelineno-0-144" name="__codelineno-0-144"></a> <span class="c1"># The common case we&#39;ve encountered so far is the contenttypes.ContentType model:</span>
3895
- <a id="__codelineno-0-145" name="__codelineno-0-145"></a> <span class="k">if</span> <span class="n">related_model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">app_label</span> <span class="o">==</span> <span class="s2">&quot;contenttypes&quot;</span> <span class="ow">and</span> <span class="n">related_model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">model_name</span> <span class="o">==</span> <span class="s2">&quot;contenttype&quot;</span><span class="p">:</span>
3896
- <a id="__codelineno-0-146" name="__codelineno-0-146"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;app_label&quot;</span><span class="p">,</span> <span class="s2">&quot;model&quot;</span><span class="p">]</span>
3897
- <a id="__codelineno-0-147" name="__codelineno-0-147"></a> <span class="c1"># Additional special cases can be added here</span>
3898
- <a id="__codelineno-0-148" name="__codelineno-0-148"></a>
3899
- <a id="__codelineno-0-149" name="__codelineno-0-149"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">related_natural_key_field_lookups</span><span class="p">:</span>
3900
- <a id="__codelineno-0-150" name="__codelineno-0-150"></a> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
3901
- <a id="__codelineno-0-151" name="__codelineno-0-151"></a> <span class="sa">f</span><span class="s2">&quot;Unable to determine the related natural-key fields for </span><span class="si">{</span><span class="n">related_model</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> &quot;</span>
3902
- <a id="__codelineno-0-152" name="__codelineno-0-152"></a> <span class="sa">f</span><span class="s2">&quot;(as referenced from </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="n">field_name</span><span class="si">}</span><span class="s2">). If the related model is a non-Nautobot &quot;</span>
3903
- <a id="__codelineno-0-153" name="__codelineno-0-153"></a> <span class="s2">&quot;model (such as ContentType) then it may be appropriate to add special-case handling for this &quot;</span>
3904
- <a id="__codelineno-0-154" name="__codelineno-0-154"></a> <span class="s2">&quot;model in BaseModel.natural_key_field_lookups; alternately you may be able to solve this for &quot;</span>
3905
- <a id="__codelineno-0-155" name="__codelineno-0-155"></a> <span class="sa">f</span><span class="s2">&quot;a single special case by explicitly defining </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.natural_key_field_lookups.&quot;</span>
3906
- <a id="__codelineno-0-156" name="__codelineno-0-156"></a> <span class="p">)</span>
3907
- <a id="__codelineno-0-157" name="__codelineno-0-157"></a>
3908
- <a id="__codelineno-0-158" name="__codelineno-0-158"></a> <span class="k">for</span> <span class="n">field_lookup</span> <span class="ow">in</span> <span class="n">related_natural_key_field_lookups</span><span class="p">:</span>
3909
- <a id="__codelineno-0-159" name="__codelineno-0-159"></a> <span class="n">natural_key_field_lookups</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">field_name</span><span class="si">}</span><span class="s2">__</span><span class="si">{</span><span class="n">field_lookup</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
3910
- <a id="__codelineno-0-160" name="__codelineno-0-160"></a>
3911
- <a id="__codelineno-0-161" name="__codelineno-0-161"></a> <span class="k">return</span> <span class="n">natural_key_field_lookups</span>
5104
+ <span class="normal"><a href="#__codelineno-0-161">161</a></span>
5105
+ <span class="normal"><a href="#__codelineno-0-162">162</a></span>
5106
+ <span class="normal"><a href="#__codelineno-0-163">163</a></span>
5107
+ <span class="normal"><a href="#__codelineno-0-164">164</a></span>
5108
+ <span class="normal"><a href="#__codelineno-0-165">165</a></span>
5109
+ <span class="normal"><a href="#__codelineno-0-166">166</a></span>
5110
+ <span class="normal"><a href="#__codelineno-0-167">167</a></span>
5111
+ <span class="normal"><a href="#__codelineno-0-168">168</a></span>
5112
+ <span class="normal"><a href="#__codelineno-0-169">169</a></span>
5113
+ <span class="normal"><a href="#__codelineno-0-170">170</a></span>
5114
+ <span class="normal"><a href="#__codelineno-0-171">171</a></span>
5115
+ <span class="normal"><a href="#__codelineno-0-172">172</a></span>
5116
+ <span class="normal"><a href="#__codelineno-0-173">173</a></span>
5117
+ <span class="normal"><a href="#__codelineno-0-174">174</a></span>
5118
+ <span class="normal"><a href="#__codelineno-0-175">175</a></span>
5119
+ <span class="normal"><a href="#__codelineno-0-176">176</a></span>
5120
+ <span class="normal"><a href="#__codelineno-0-177">177</a></span>
5121
+ <span class="normal"><a href="#__codelineno-0-178">178</a></span>
5122
+ <span class="normal"><a href="#__codelineno-0-179">179</a></span>
5123
+ <span class="normal"><a href="#__codelineno-0-180">180</a></span>
5124
+ <span class="normal"><a href="#__codelineno-0-181">181</a></span>
5125
+ <span class="normal"><a href="#__codelineno-0-182">182</a></span>
5126
+ <span class="normal"><a href="#__codelineno-0-183">183</a></span>
5127
+ <span class="normal"><a href="#__codelineno-0-184">184</a></span>
5128
+ <span class="normal"><a href="#__codelineno-0-185">185</a></span>
5129
+ <span class="normal"><a href="#__codelineno-0-186">186</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-111" name="__codelineno-0-111"></a><span class="nd">@classproperty</span> <span class="c1"># https://github.com/PyCQA/pylint-django/issues/240</span>
5130
+ <a id="__codelineno-0-112" name="__codelineno-0-112"></a><span class="k">def</span> <span class="nf">natural_key_field_lookups</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span> <span class="c1"># pylint: disable=no-self-argument</span>
5131
+ <a id="__codelineno-0-113" name="__codelineno-0-113"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5132
+ <a id="__codelineno-0-114" name="__codelineno-0-114"></a><span class="sd"> List of lookups (possibly including nested lookups for related models) that make up this model&#39;s natural key.</span>
5133
+ <a id="__codelineno-0-115" name="__codelineno-0-115"></a>
5134
+ <a id="__codelineno-0-116" name="__codelineno-0-116"></a><span class="sd"> BaseModel provides a &quot;smart&quot; implementation that tries to determine this automatically,</span>
5135
+ <a id="__codelineno-0-117" name="__codelineno-0-117"></a><span class="sd"> but you can also explicitly set `natural_key_field_names` on a given model subclass if desired.</span>
5136
+ <a id="__codelineno-0-118" name="__codelineno-0-118"></a>
5137
+ <a id="__codelineno-0-119" name="__codelineno-0-119"></a><span class="sd"> This property is based on a consolidation of `django-natural-keys` `ForeignKeyModel.get_natural_key_info()`,</span>
5138
+ <a id="__codelineno-0-120" name="__codelineno-0-120"></a><span class="sd"> `ForeignKeyModel.get_natural_key_def()`, and `ForeignKeyModel.get_natural_key_fields()`.</span>
5139
+ <a id="__codelineno-0-121" name="__codelineno-0-121"></a>
5140
+ <a id="__codelineno-0-122" name="__codelineno-0-122"></a><span class="sd"> Unlike `get_natural_key_def()`, this doesn&#39;t auto-exclude all AutoField and BigAutoField fields,</span>
5141
+ <a id="__codelineno-0-123" name="__codelineno-0-123"></a><span class="sd"> but instead explicitly discounts the `id` field (only) as a candidate.</span>
5142
+ <a id="__codelineno-0-124" name="__codelineno-0-124"></a><span class="sd"> &quot;&quot;&quot;</span>
5143
+ <a id="__codelineno-0-125" name="__codelineno-0-125"></a> <span class="c1"># First, figure out which local fields comprise the natural key:</span>
5144
+ <a id="__codelineno-0-126" name="__codelineno-0-126"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="p">[]</span>
5145
+ <a id="__codelineno-0-127" name="__codelineno-0-127"></a> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s2">&quot;natural_key_field_names&quot;</span><span class="p">):</span>
5146
+ <a id="__codelineno-0-128" name="__codelineno-0-128"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">natural_key_field_names</span>
5147
+ <a id="__codelineno-0-129" name="__codelineno-0-129"></a> <span class="k">else</span><span class="p">:</span>
5148
+ <a id="__codelineno-0-130" name="__codelineno-0-130"></a> <span class="c1"># Does this model have any new-style UniqueConstraints? If so, pick the first one</span>
5149
+ <a id="__codelineno-0-131" name="__codelineno-0-131"></a> <span class="k">for</span> <span class="n">constraint</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">constraints</span><span class="p">:</span>
5150
+ <a id="__codelineno-0-132" name="__codelineno-0-132"></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">constraint</span><span class="p">,</span> <span class="n">models</span><span class="o">.</span><span class="n">UniqueConstraint</span><span class="p">):</span>
5151
+ <a id="__codelineno-0-133" name="__codelineno-0-133"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="n">constraint</span><span class="o">.</span><span class="n">fields</span>
5152
+ <a id="__codelineno-0-134" name="__codelineno-0-134"></a> <span class="k">break</span>
5153
+ <a id="__codelineno-0-135" name="__codelineno-0-135"></a> <span class="k">else</span><span class="p">:</span>
5154
+ <a id="__codelineno-0-136" name="__codelineno-0-136"></a> <span class="c1"># Else, does this model have any old-style unique_together? If so, pick the first one.</span>
5155
+ <a id="__codelineno-0-137" name="__codelineno-0-137"></a> <span class="k">if</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">unique_together</span><span class="p">:</span>
5156
+ <a id="__codelineno-0-138" name="__codelineno-0-138"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">unique_together</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
5157
+ <a id="__codelineno-0-139" name="__codelineno-0-139"></a> <span class="k">else</span><span class="p">:</span>
5158
+ <a id="__codelineno-0-140" name="__codelineno-0-140"></a> <span class="c1"># Else, do we have any individual unique=True fields? If so, pick the first one.</span>
5159
+ <a id="__codelineno-0-141" name="__codelineno-0-141"></a> <span class="n">unique_fields</span> <span class="o">=</span> <span class="p">[</span><span class="n">field</span> <span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">fields</span> <span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">unique</span> <span class="ow">and</span> <span class="n">field</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;id&quot;</span><span class="p">]</span>
5160
+ <a id="__codelineno-0-142" name="__codelineno-0-142"></a> <span class="k">if</span> <span class="n">unique_fields</span><span class="p">:</span>
5161
+ <a id="__codelineno-0-143" name="__codelineno-0-143"></a> <span class="n">natural_key_field_names</span> <span class="o">=</span> <span class="p">(</span><span class="n">unique_fields</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">name</span><span class="p">,)</span>
5162
+ <a id="__codelineno-0-144" name="__codelineno-0-144"></a>
5163
+ <a id="__codelineno-0-145" name="__codelineno-0-145"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">natural_key_field_names</span><span class="p">:</span>
5164
+ <a id="__codelineno-0-146" name="__codelineno-0-146"></a> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
5165
+ <a id="__codelineno-0-147" name="__codelineno-0-147"></a> <span class="sa">f</span><span class="s2">&quot;Unable to identify an intrinsic natural-key definition for </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">. &quot;</span>
5166
+ <a id="__codelineno-0-148" name="__codelineno-0-148"></a> <span class="s2">&quot;If there isn&#39;t at least one UniqueConstraint, unique_together, or field with unique=True, &quot;</span>
5167
+ <a id="__codelineno-0-149" name="__codelineno-0-149"></a> <span class="s2">&quot;you probably need to explicitly declare the &#39;natural_key_field_names&#39; for this model, &quot;</span>
5168
+ <a id="__codelineno-0-150" name="__codelineno-0-150"></a> <span class="s2">&quot;or potentially override the default &#39;natural_key_field_lookups&#39; implementation for this model.&quot;</span>
5169
+ <a id="__codelineno-0-151" name="__codelineno-0-151"></a> <span class="p">)</span>
5170
+ <a id="__codelineno-0-152" name="__codelineno-0-152"></a>
5171
+ <a id="__codelineno-0-153" name="__codelineno-0-153"></a> <span class="c1"># Next, for any natural key fields that have related models, get the natural key for the related model if known</span>
5172
+ <a id="__codelineno-0-154" name="__codelineno-0-154"></a> <span class="n">natural_key_field_lookups</span> <span class="o">=</span> <span class="p">[]</span>
5173
+ <a id="__codelineno-0-155" name="__codelineno-0-155"></a> <span class="k">for</span> <span class="n">field_name</span> <span class="ow">in</span> <span class="n">natural_key_field_names</span><span class="p">:</span>
5174
+ <a id="__codelineno-0-156" name="__codelineno-0-156"></a> <span class="n">field</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">get_field</span><span class="p">(</span><span class="n">field_name</span><span class="p">)</span>
5175
+ <a id="__codelineno-0-157" name="__codelineno-0-157"></a> <span class="k">if</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="s2">&quot;remote_field&quot;</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
5176
+ <a id="__codelineno-0-158" name="__codelineno-0-158"></a> <span class="c1"># Not a related field, so the field name is the field lookup</span>
5177
+ <a id="__codelineno-0-159" name="__codelineno-0-159"></a> <span class="n">natural_key_field_lookups</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">field_name</span><span class="p">)</span>
5178
+ <a id="__codelineno-0-160" name="__codelineno-0-160"></a> <span class="k">continue</span>
5179
+ <a id="__codelineno-0-161" name="__codelineno-0-161"></a>
5180
+ <a id="__codelineno-0-162" name="__codelineno-0-162"></a> <span class="n">related_model</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">remote_field</span><span class="o">.</span><span class="n">model</span>
5181
+ <a id="__codelineno-0-163" name="__codelineno-0-163"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="kc">None</span>
5182
+ <a id="__codelineno-0-164" name="__codelineno-0-164"></a> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">related_model</span><span class="p">,</span> <span class="s2">&quot;natural_key_field_lookups&quot;</span><span class="p">):</span>
5183
+ <a id="__codelineno-0-165" name="__codelineno-0-165"></a> <span class="c1"># TODO: generic handling for self-referential case, as seen in Location</span>
5184
+ <a id="__codelineno-0-166" name="__codelineno-0-166"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="n">related_model</span><span class="o">.</span><span class="n">natural_key_field_lookups</span>
5185
+ <a id="__codelineno-0-167" name="__codelineno-0-167"></a> <span class="k">else</span><span class="p">:</span>
5186
+ <a id="__codelineno-0-168" name="__codelineno-0-168"></a> <span class="c1"># Related model isn&#39;t a Nautobot model and so doesn&#39;t have a `natural_key_field_lookups`.</span>
5187
+ <a id="__codelineno-0-169" name="__codelineno-0-169"></a> <span class="c1"># The common case we&#39;ve encountered so far is the contenttypes.ContentType model:</span>
5188
+ <a id="__codelineno-0-170" name="__codelineno-0-170"></a> <span class="k">if</span> <span class="n">related_model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">app_label</span> <span class="o">==</span> <span class="s2">&quot;contenttypes&quot;</span> <span class="ow">and</span> <span class="n">related_model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">model_name</span> <span class="o">==</span> <span class="s2">&quot;contenttype&quot;</span><span class="p">:</span>
5189
+ <a id="__codelineno-0-171" name="__codelineno-0-171"></a> <span class="n">related_natural_key_field_lookups</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;app_label&quot;</span><span class="p">,</span> <span class="s2">&quot;model&quot;</span><span class="p">]</span>
5190
+ <a id="__codelineno-0-172" name="__codelineno-0-172"></a> <span class="c1"># Additional special cases can be added here</span>
5191
+ <a id="__codelineno-0-173" name="__codelineno-0-173"></a>
5192
+ <a id="__codelineno-0-174" name="__codelineno-0-174"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">related_natural_key_field_lookups</span><span class="p">:</span>
5193
+ <a id="__codelineno-0-175" name="__codelineno-0-175"></a> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
5194
+ <a id="__codelineno-0-176" name="__codelineno-0-176"></a> <span class="sa">f</span><span class="s2">&quot;Unable to determine the related natural-key fields for </span><span class="si">{</span><span class="n">related_model</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> &quot;</span>
5195
+ <a id="__codelineno-0-177" name="__codelineno-0-177"></a> <span class="sa">f</span><span class="s2">&quot;(as referenced from </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="n">field_name</span><span class="si">}</span><span class="s2">). If the related model is a non-Nautobot &quot;</span>
5196
+ <a id="__codelineno-0-178" name="__codelineno-0-178"></a> <span class="s2">&quot;model (such as ContentType) then it may be appropriate to add special-case handling for this &quot;</span>
5197
+ <a id="__codelineno-0-179" name="__codelineno-0-179"></a> <span class="s2">&quot;model in BaseModel.natural_key_field_lookups; alternately you may be able to solve this for &quot;</span>
5198
+ <a id="__codelineno-0-180" name="__codelineno-0-180"></a> <span class="sa">f</span><span class="s2">&quot;a single special case by explicitly defining </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">.natural_key_field_lookups.&quot;</span>
5199
+ <a id="__codelineno-0-181" name="__codelineno-0-181"></a> <span class="p">)</span>
5200
+ <a id="__codelineno-0-182" name="__codelineno-0-182"></a>
5201
+ <a id="__codelineno-0-183" name="__codelineno-0-183"></a> <span class="k">for</span> <span class="n">field_lookup</span> <span class="ow">in</span> <span class="n">related_natural_key_field_lookups</span><span class="p">:</span>
5202
+ <a id="__codelineno-0-184" name="__codelineno-0-184"></a> <span class="n">natural_key_field_lookups</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">field_name</span><span class="si">}</span><span class="s2">__</span><span class="si">{</span><span class="n">field_lookup</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
5203
+ <a id="__codelineno-0-185" name="__codelineno-0-185"></a>
5204
+ <a id="__codelineno-0-186" name="__codelineno-0-186"></a> <span class="k">return</span> <span class="n">natural_key_field_lookups</span>
3912
5205
  </code></pre></div></td></tr></table></div>
3913
5206
  </details>
3914
5207
  </div>
@@ -3932,36 +5225,36 @@ but instead explicitly discounts the <code>id</code> field (only) as a candidate
3932
5225
  which in effect enforces model validation prior to saving the instance, without having
3933
5226
  to manually make these calls seperately. This is a slight departure from Django norms,
3934
5227
  but is intended to offer an optional, simplified interface for performing this common
3935
- workflow. The intended use is for user defined Jobs and scripts run via the <code>nautobot-server nbshell</code>
5228
+ workflow. The intended use is for user defined Jobs run via the <code>nautobot-server nbshell</code>
3936
5229
  command.</p>
3937
5230
 
3938
5231
  <details class="quote">
3939
5232
  <summary>Source code in <code>nautobot/core/models/__init__.py</code></summary>
3940
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-43">43</a></span>
3941
- <span class="normal"><a href="#__codelineno-0-44">44</a></span>
3942
- <span class="normal"><a href="#__codelineno-0-45">45</a></span>
3943
- <span class="normal"><a href="#__codelineno-0-46">46</a></span>
3944
- <span class="normal"><a href="#__codelineno-0-47">47</a></span>
3945
- <span class="normal"><a href="#__codelineno-0-48">48</a></span>
3946
- <span class="normal"><a href="#__codelineno-0-49">49</a></span>
3947
- <span class="normal"><a href="#__codelineno-0-50">50</a></span>
3948
- <span class="normal"><a href="#__codelineno-0-51">51</a></span>
3949
- <span class="normal"><a href="#__codelineno-0-52">52</a></span>
3950
- <span class="normal"><a href="#__codelineno-0-53">53</a></span>
3951
- <span class="normal"><a href="#__codelineno-0-54">54</a></span>
3952
- <span class="normal"><a href="#__codelineno-0-55">55</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-43" name="__codelineno-0-43"></a><span class="k">def</span> <span class="nf">validated_save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
3953
- <a id="__codelineno-0-44" name="__codelineno-0-44"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
3954
- <a id="__codelineno-0-45" name="__codelineno-0-45"></a><span class="sd"> Perform model validation during instance save.</span>
3955
- <a id="__codelineno-0-46" name="__codelineno-0-46"></a>
3956
- <a id="__codelineno-0-47" name="__codelineno-0-47"></a><span class="sd"> This is a convenience method that first calls `self.full_clean()` and then `self.save()`</span>
3957
- <a id="__codelineno-0-48" name="__codelineno-0-48"></a><span class="sd"> which in effect enforces model validation prior to saving the instance, without having</span>
3958
- <a id="__codelineno-0-49" name="__codelineno-0-49"></a><span class="sd"> to manually make these calls seperately. This is a slight departure from Django norms,</span>
3959
- <a id="__codelineno-0-50" name="__codelineno-0-50"></a><span class="sd"> but is intended to offer an optional, simplified interface for performing this common</span>
3960
- <a id="__codelineno-0-51" name="__codelineno-0-51"></a><span class="sd"> workflow. The intended use is for user defined Jobs and scripts run via the `nautobot-server nbshell`</span>
3961
- <a id="__codelineno-0-52" name="__codelineno-0-52"></a><span class="sd"> command.</span>
3962
- <a id="__codelineno-0-53" name="__codelineno-0-53"></a><span class="sd"> &quot;&quot;&quot;</span>
3963
- <a id="__codelineno-0-54" name="__codelineno-0-54"></a> <span class="bp">self</span><span class="o">.</span><span class="n">full_clean</span><span class="p">()</span>
3964
- <a id="__codelineno-0-55" name="__codelineno-0-55"></a> <span class="bp">self</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
5233
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-68">68</a></span>
5234
+ <span class="normal"><a href="#__codelineno-0-69">69</a></span>
5235
+ <span class="normal"><a href="#__codelineno-0-70">70</a></span>
5236
+ <span class="normal"><a href="#__codelineno-0-71">71</a></span>
5237
+ <span class="normal"><a href="#__codelineno-0-72">72</a></span>
5238
+ <span class="normal"><a href="#__codelineno-0-73">73</a></span>
5239
+ <span class="normal"><a href="#__codelineno-0-74">74</a></span>
5240
+ <span class="normal"><a href="#__codelineno-0-75">75</a></span>
5241
+ <span class="normal"><a href="#__codelineno-0-76">76</a></span>
5242
+ <span class="normal"><a href="#__codelineno-0-77">77</a></span>
5243
+ <span class="normal"><a href="#__codelineno-0-78">78</a></span>
5244
+ <span class="normal"><a href="#__codelineno-0-79">79</a></span>
5245
+ <span class="normal"><a href="#__codelineno-0-80">80</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-68" name="__codelineno-0-68"></a><span class="k">def</span> <span class="nf">validated_save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
5246
+ <a id="__codelineno-0-69" name="__codelineno-0-69"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5247
+ <a id="__codelineno-0-70" name="__codelineno-0-70"></a><span class="sd"> Perform model validation during instance save.</span>
5248
+ <a id="__codelineno-0-71" name="__codelineno-0-71"></a>
5249
+ <a id="__codelineno-0-72" name="__codelineno-0-72"></a><span class="sd"> This is a convenience method that first calls `self.full_clean()` and then `self.save()`</span>
5250
+ <a id="__codelineno-0-73" name="__codelineno-0-73"></a><span class="sd"> which in effect enforces model validation prior to saving the instance, without having</span>
5251
+ <a id="__codelineno-0-74" name="__codelineno-0-74"></a><span class="sd"> to manually make these calls seperately. This is a slight departure from Django norms,</span>
5252
+ <a id="__codelineno-0-75" name="__codelineno-0-75"></a><span class="sd"> but is intended to offer an optional, simplified interface for performing this common</span>
5253
+ <a id="__codelineno-0-76" name="__codelineno-0-76"></a><span class="sd"> workflow. The intended use is for user defined Jobs run via the `nautobot-server nbshell`</span>
5254
+ <a id="__codelineno-0-77" name="__codelineno-0-77"></a><span class="sd"> command.</span>
5255
+ <a id="__codelineno-0-78" name="__codelineno-0-78"></a><span class="sd"> &quot;&quot;&quot;</span>
5256
+ <a id="__codelineno-0-79" name="__codelineno-0-79"></a> <span class="bp">self</span><span class="o">.</span><span class="n">full_clean</span><span class="p">()</span>
5257
+ <a id="__codelineno-0-80" name="__codelineno-0-80"></a> <span class="bp">self</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
3965
5258
  </code></pre></div></td></tr></table></div>
3966
5259
  </details>
3967
5260
  </div>
@@ -4000,67 +5293,67 @@ should be set as a string in the form <code>&lt;app_label&gt;.&lt;model_name&gt;
4000
5293
 
4001
5294
  <details class="quote">
4002
5295
  <summary>Source code in <code>nautobot/extras/plugins/__init__.py</code></summary>
4003
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-645">645</a></span>
4004
- <span class="normal"><a href="#__codelineno-0-646">646</a></span>
4005
- <span class="normal"><a href="#__codelineno-0-647">647</a></span>
4006
- <span class="normal"><a href="#__codelineno-0-648">648</a></span>
4007
- <span class="normal"><a href="#__codelineno-0-649">649</a></span>
4008
- <span class="normal"><a href="#__codelineno-0-650">650</a></span>
4009
- <span class="normal"><a href="#__codelineno-0-651">651</a></span>
4010
- <span class="normal"><a href="#__codelineno-0-652">652</a></span>
4011
- <span class="normal"><a href="#__codelineno-0-653">653</a></span>
4012
- <span class="normal"><a href="#__codelineno-0-654">654</a></span>
4013
- <span class="normal"><a href="#__codelineno-0-655">655</a></span>
4014
- <span class="normal"><a href="#__codelineno-0-656">656</a></span>
4015
- <span class="normal"><a href="#__codelineno-0-657">657</a></span>
4016
- <span class="normal"><a href="#__codelineno-0-658">658</a></span>
4017
- <span class="normal"><a href="#__codelineno-0-659">659</a></span>
4018
- <span class="normal"><a href="#__codelineno-0-660">660</a></span>
4019
- <span class="normal"><a href="#__codelineno-0-661">661</a></span>
4020
- <span class="normal"><a href="#__codelineno-0-662">662</a></span>
4021
- <span class="normal"><a href="#__codelineno-0-663">663</a></span>
4022
- <span class="normal"><a href="#__codelineno-0-664">664</a></span>
4023
- <span class="normal"><a href="#__codelineno-0-665">665</a></span>
4024
- <span class="normal"><a href="#__codelineno-0-666">666</a></span>
4025
- <span class="normal"><a href="#__codelineno-0-667">667</a></span>
4026
- <span class="normal"><a href="#__codelineno-0-668">668</a></span>
4027
- <span class="normal"><a href="#__codelineno-0-669">669</a></span>
4028
- <span class="normal"><a href="#__codelineno-0-670">670</a></span>
4029
- <span class="normal"><a href="#__codelineno-0-671">671</a></span>
4030
- <span class="normal"><a href="#__codelineno-0-672">672</a></span>
4031
- <span class="normal"><a href="#__codelineno-0-673">673</a></span>
4032
- <span class="normal"><a href="#__codelineno-0-674">674</a></span>
4033
- <span class="normal"><a href="#__codelineno-0-675">675</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-645" name="__codelineno-0-645"></a><span class="k">class</span> <span class="nc">CustomValidator</span><span class="p">:</span>
4034
- <a id="__codelineno-0-646" name="__codelineno-0-646"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4035
- <a id="__codelineno-0-647" name="__codelineno-0-647"></a><span class="sd"> This class is used to register plugin custom model validators which act on specified models. It contains the clean</span>
4036
- <a id="__codelineno-0-648" name="__codelineno-0-648"></a><span class="sd"> method which is overridden by plugin authors to execute custom validation logic. Plugin authors must raise</span>
4037
- <a id="__codelineno-0-649" name="__codelineno-0-649"></a><span class="sd"> ValidationError within this method to trigger validation error messages which are propagated to the user.</span>
4038
- <a id="__codelineno-0-650" name="__codelineno-0-650"></a><span class="sd"> A convenience method `validation_error(&lt;message&gt;)` may be used for this purpose.</span>
4039
- <a id="__codelineno-0-651" name="__codelineno-0-651"></a>
4040
- <a id="__codelineno-0-652" name="__codelineno-0-652"></a><span class="sd"> The `model` attribute on the class defines the model to which this validator is registered. It</span>
4041
- <a id="__codelineno-0-653" name="__codelineno-0-653"></a><span class="sd"> should be set as a string in the form `&lt;app_label&gt;.&lt;model_name&gt;`.</span>
4042
- <a id="__codelineno-0-654" name="__codelineno-0-654"></a><span class="sd"> &quot;&quot;&quot;</span>
4043
- <a id="__codelineno-0-655" name="__codelineno-0-655"></a>
4044
- <a id="__codelineno-0-656" name="__codelineno-0-656"></a> <span class="n">model</span> <span class="o">=</span> <span class="kc">None</span>
4045
- <a id="__codelineno-0-657" name="__codelineno-0-657"></a>
4046
- <a id="__codelineno-0-658" name="__codelineno-0-658"></a> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
4047
- <a id="__codelineno-0-659" name="__codelineno-0-659"></a> <span class="bp">self</span><span class="o">.</span><span class="n">context</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;object&quot;</span><span class="p">:</span> <span class="n">obj</span><span class="p">}</span>
4048
- <a id="__codelineno-0-660" name="__codelineno-0-660"></a>
4049
- <a id="__codelineno-0-661" name="__codelineno-0-661"></a> <span class="k">def</span> <span class="nf">validation_error</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
4050
- <a id="__codelineno-0-662" name="__codelineno-0-662"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4051
- <a id="__codelineno-0-663" name="__codelineno-0-663"></a><span class="sd"> Convenience method for raising `django.core.exceptions.ValidationError` which is required in order to</span>
4052
- <a id="__codelineno-0-664" name="__codelineno-0-664"></a><span class="sd"> trigger validation error messages which are propagated to the user.</span>
4053
- <a id="__codelineno-0-665" name="__codelineno-0-665"></a><span class="sd"> &quot;&quot;&quot;</span>
4054
- <a id="__codelineno-0-666" name="__codelineno-0-666"></a> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
4055
- <a id="__codelineno-0-667" name="__codelineno-0-667"></a>
4056
- <a id="__codelineno-0-668" name="__codelineno-0-668"></a> <span class="k">def</span> <span class="nf">clean</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
4057
- <a id="__codelineno-0-669" name="__codelineno-0-669"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4058
- <a id="__codelineno-0-670" name="__codelineno-0-670"></a><span class="sd"> Implement custom model validation in the standard Django clean method pattern. The model instance is accessed</span>
4059
- <a id="__codelineno-0-671" name="__codelineno-0-671"></a><span class="sd"> with the `object` key within `self.context`, e.g. `self.context[&#39;object&#39;]`. ValidationError must be raised to</span>
4060
- <a id="__codelineno-0-672" name="__codelineno-0-672"></a><span class="sd"> prevent saving model instance changes, and propagate messages to the user. For convenience,</span>
4061
- <a id="__codelineno-0-673" name="__codelineno-0-673"></a><span class="sd"> `self.validation_error(&lt;message&gt;)` may be called to raise a ValidationError.</span>
4062
- <a id="__codelineno-0-674" name="__codelineno-0-674"></a><span class="sd"> &quot;&quot;&quot;</span>
4063
- <a id="__codelineno-0-675" name="__codelineno-0-675"></a> <span class="k">raise</span> <span class="ne">NotImplementedError</span>
5296
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-528">528</a></span>
5297
+ <span class="normal"><a href="#__codelineno-0-529">529</a></span>
5298
+ <span class="normal"><a href="#__codelineno-0-530">530</a></span>
5299
+ <span class="normal"><a href="#__codelineno-0-531">531</a></span>
5300
+ <span class="normal"><a href="#__codelineno-0-532">532</a></span>
5301
+ <span class="normal"><a href="#__codelineno-0-533">533</a></span>
5302
+ <span class="normal"><a href="#__codelineno-0-534">534</a></span>
5303
+ <span class="normal"><a href="#__codelineno-0-535">535</a></span>
5304
+ <span class="normal"><a href="#__codelineno-0-536">536</a></span>
5305
+ <span class="normal"><a href="#__codelineno-0-537">537</a></span>
5306
+ <span class="normal"><a href="#__codelineno-0-538">538</a></span>
5307
+ <span class="normal"><a href="#__codelineno-0-539">539</a></span>
5308
+ <span class="normal"><a href="#__codelineno-0-540">540</a></span>
5309
+ <span class="normal"><a href="#__codelineno-0-541">541</a></span>
5310
+ <span class="normal"><a href="#__codelineno-0-542">542</a></span>
5311
+ <span class="normal"><a href="#__codelineno-0-543">543</a></span>
5312
+ <span class="normal"><a href="#__codelineno-0-544">544</a></span>
5313
+ <span class="normal"><a href="#__codelineno-0-545">545</a></span>
5314
+ <span class="normal"><a href="#__codelineno-0-546">546</a></span>
5315
+ <span class="normal"><a href="#__codelineno-0-547">547</a></span>
5316
+ <span class="normal"><a href="#__codelineno-0-548">548</a></span>
5317
+ <span class="normal"><a href="#__codelineno-0-549">549</a></span>
5318
+ <span class="normal"><a href="#__codelineno-0-550">550</a></span>
5319
+ <span class="normal"><a href="#__codelineno-0-551">551</a></span>
5320
+ <span class="normal"><a href="#__codelineno-0-552">552</a></span>
5321
+ <span class="normal"><a href="#__codelineno-0-553">553</a></span>
5322
+ <span class="normal"><a href="#__codelineno-0-554">554</a></span>
5323
+ <span class="normal"><a href="#__codelineno-0-555">555</a></span>
5324
+ <span class="normal"><a href="#__codelineno-0-556">556</a></span>
5325
+ <span class="normal"><a href="#__codelineno-0-557">557</a></span>
5326
+ <span class="normal"><a href="#__codelineno-0-558">558</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-528" name="__codelineno-0-528"></a><span class="k">class</span> <span class="nc">CustomValidator</span><span class="p">:</span>
5327
+ <a id="__codelineno-0-529" name="__codelineno-0-529"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5328
+ <a id="__codelineno-0-530" name="__codelineno-0-530"></a><span class="sd"> This class is used to register plugin custom model validators which act on specified models. It contains the clean</span>
5329
+ <a id="__codelineno-0-531" name="__codelineno-0-531"></a><span class="sd"> method which is overridden by plugin authors to execute custom validation logic. Plugin authors must raise</span>
5330
+ <a id="__codelineno-0-532" name="__codelineno-0-532"></a><span class="sd"> ValidationError within this method to trigger validation error messages which are propagated to the user.</span>
5331
+ <a id="__codelineno-0-533" name="__codelineno-0-533"></a><span class="sd"> A convenience method `validation_error(&lt;message&gt;)` may be used for this purpose.</span>
5332
+ <a id="__codelineno-0-534" name="__codelineno-0-534"></a>
5333
+ <a id="__codelineno-0-535" name="__codelineno-0-535"></a><span class="sd"> The `model` attribute on the class defines the model to which this validator is registered. It</span>
5334
+ <a id="__codelineno-0-536" name="__codelineno-0-536"></a><span class="sd"> should be set as a string in the form `&lt;app_label&gt;.&lt;model_name&gt;`.</span>
5335
+ <a id="__codelineno-0-537" name="__codelineno-0-537"></a><span class="sd"> &quot;&quot;&quot;</span>
5336
+ <a id="__codelineno-0-538" name="__codelineno-0-538"></a>
5337
+ <a id="__codelineno-0-539" name="__codelineno-0-539"></a> <span class="n">model</span> <span class="o">=</span> <span class="kc">None</span>
5338
+ <a id="__codelineno-0-540" name="__codelineno-0-540"></a>
5339
+ <a id="__codelineno-0-541" name="__codelineno-0-541"></a> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
5340
+ <a id="__codelineno-0-542" name="__codelineno-0-542"></a> <span class="bp">self</span><span class="o">.</span><span class="n">context</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;object&quot;</span><span class="p">:</span> <span class="n">obj</span><span class="p">}</span>
5341
+ <a id="__codelineno-0-543" name="__codelineno-0-543"></a>
5342
+ <a id="__codelineno-0-544" name="__codelineno-0-544"></a> <span class="k">def</span> <span class="nf">validation_error</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
5343
+ <a id="__codelineno-0-545" name="__codelineno-0-545"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5344
+ <a id="__codelineno-0-546" name="__codelineno-0-546"></a><span class="sd"> Convenience method for raising `django.core.exceptions.ValidationError` which is required in order to</span>
5345
+ <a id="__codelineno-0-547" name="__codelineno-0-547"></a><span class="sd"> trigger validation error messages which are propagated to the user.</span>
5346
+ <a id="__codelineno-0-548" name="__codelineno-0-548"></a><span class="sd"> &quot;&quot;&quot;</span>
5347
+ <a id="__codelineno-0-549" name="__codelineno-0-549"></a> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
5348
+ <a id="__codelineno-0-550" name="__codelineno-0-550"></a>
5349
+ <a id="__codelineno-0-551" name="__codelineno-0-551"></a> <span class="k">def</span> <span class="nf">clean</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
5350
+ <a id="__codelineno-0-552" name="__codelineno-0-552"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5351
+ <a id="__codelineno-0-553" name="__codelineno-0-553"></a><span class="sd"> Implement custom model validation in the standard Django clean method pattern. The model instance is accessed</span>
5352
+ <a id="__codelineno-0-554" name="__codelineno-0-554"></a><span class="sd"> with the `object` key within `self.context`, e.g. `self.context[&#39;object&#39;]`. ValidationError must be raised to</span>
5353
+ <a id="__codelineno-0-555" name="__codelineno-0-555"></a><span class="sd"> prevent saving model instance changes, and propagate messages to the user. For convenience,</span>
5354
+ <a id="__codelineno-0-556" name="__codelineno-0-556"></a><span class="sd"> `self.validation_error(&lt;message&gt;)` may be called to raise a ValidationError.</span>
5355
+ <a id="__codelineno-0-557" name="__codelineno-0-557"></a><span class="sd"> &quot;&quot;&quot;</span>
5356
+ <a id="__codelineno-0-558" name="__codelineno-0-558"></a> <span class="k">raise</span> <span class="ne">NotImplementedError</span>
4064
5357
  </code></pre></div></td></tr></table></div>
4065
5358
  </details>
4066
5359
 
@@ -4095,21 +5388,21 @@ prevent saving model instance changes, and propagate messages to the user. For c
4095
5388
 
4096
5389
  <details class="quote">
4097
5390
  <summary>Source code in <code>nautobot/extras/plugins/__init__.py</code></summary>
4098
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-668">668</a></span>
4099
- <span class="normal"><a href="#__codelineno-0-669">669</a></span>
4100
- <span class="normal"><a href="#__codelineno-0-670">670</a></span>
4101
- <span class="normal"><a href="#__codelineno-0-671">671</a></span>
4102
- <span class="normal"><a href="#__codelineno-0-672">672</a></span>
4103
- <span class="normal"><a href="#__codelineno-0-673">673</a></span>
4104
- <span class="normal"><a href="#__codelineno-0-674">674</a></span>
4105
- <span class="normal"><a href="#__codelineno-0-675">675</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-668" name="__codelineno-0-668"></a><span class="k">def</span> <span class="nf">clean</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
4106
- <a id="__codelineno-0-669" name="__codelineno-0-669"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4107
- <a id="__codelineno-0-670" name="__codelineno-0-670"></a><span class="sd"> Implement custom model validation in the standard Django clean method pattern. The model instance is accessed</span>
4108
- <a id="__codelineno-0-671" name="__codelineno-0-671"></a><span class="sd"> with the `object` key within `self.context`, e.g. `self.context[&#39;object&#39;]`. ValidationError must be raised to</span>
4109
- <a id="__codelineno-0-672" name="__codelineno-0-672"></a><span class="sd"> prevent saving model instance changes, and propagate messages to the user. For convenience,</span>
4110
- <a id="__codelineno-0-673" name="__codelineno-0-673"></a><span class="sd"> `self.validation_error(&lt;message&gt;)` may be called to raise a ValidationError.</span>
4111
- <a id="__codelineno-0-674" name="__codelineno-0-674"></a><span class="sd"> &quot;&quot;&quot;</span>
4112
- <a id="__codelineno-0-675" name="__codelineno-0-675"></a> <span class="k">raise</span> <span class="ne">NotImplementedError</span>
5391
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-551">551</a></span>
5392
+ <span class="normal"><a href="#__codelineno-0-552">552</a></span>
5393
+ <span class="normal"><a href="#__codelineno-0-553">553</a></span>
5394
+ <span class="normal"><a href="#__codelineno-0-554">554</a></span>
5395
+ <span class="normal"><a href="#__codelineno-0-555">555</a></span>
5396
+ <span class="normal"><a href="#__codelineno-0-556">556</a></span>
5397
+ <span class="normal"><a href="#__codelineno-0-557">557</a></span>
5398
+ <span class="normal"><a href="#__codelineno-0-558">558</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-551" name="__codelineno-0-551"></a><span class="k">def</span> <span class="nf">clean</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
5399
+ <a id="__codelineno-0-552" name="__codelineno-0-552"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5400
+ <a id="__codelineno-0-553" name="__codelineno-0-553"></a><span class="sd"> Implement custom model validation in the standard Django clean method pattern. The model instance is accessed</span>
5401
+ <a id="__codelineno-0-554" name="__codelineno-0-554"></a><span class="sd"> with the `object` key within `self.context`, e.g. `self.context[&#39;object&#39;]`. ValidationError must be raised to</span>
5402
+ <a id="__codelineno-0-555" name="__codelineno-0-555"></a><span class="sd"> prevent saving model instance changes, and propagate messages to the user. For convenience,</span>
5403
+ <a id="__codelineno-0-556" name="__codelineno-0-556"></a><span class="sd"> `self.validation_error(&lt;message&gt;)` may be called to raise a ValidationError.</span>
5404
+ <a id="__codelineno-0-557" name="__codelineno-0-557"></a><span class="sd"> &quot;&quot;&quot;</span>
5405
+ <a id="__codelineno-0-558" name="__codelineno-0-558"></a> <span class="k">raise</span> <span class="ne">NotImplementedError</span>
4113
5406
  </code></pre></div></td></tr></table></div>
4114
5407
  </details>
4115
5408
  </div>
@@ -4133,17 +5426,17 @@ trigger validation error messages which are propagated to the user.</p>
4133
5426
 
4134
5427
  <details class="quote">
4135
5428
  <summary>Source code in <code>nautobot/extras/plugins/__init__.py</code></summary>
4136
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-661">661</a></span>
4137
- <span class="normal"><a href="#__codelineno-0-662">662</a></span>
4138
- <span class="normal"><a href="#__codelineno-0-663">663</a></span>
4139
- <span class="normal"><a href="#__codelineno-0-664">664</a></span>
4140
- <span class="normal"><a href="#__codelineno-0-665">665</a></span>
4141
- <span class="normal"><a href="#__codelineno-0-666">666</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-661" name="__codelineno-0-661"></a><span class="k">def</span> <span class="nf">validation_error</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
4142
- <a id="__codelineno-0-662" name="__codelineno-0-662"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4143
- <a id="__codelineno-0-663" name="__codelineno-0-663"></a><span class="sd"> Convenience method for raising `django.core.exceptions.ValidationError` which is required in order to</span>
4144
- <a id="__codelineno-0-664" name="__codelineno-0-664"></a><span class="sd"> trigger validation error messages which are propagated to the user.</span>
4145
- <a id="__codelineno-0-665" name="__codelineno-0-665"></a><span class="sd"> &quot;&quot;&quot;</span>
4146
- <a id="__codelineno-0-666" name="__codelineno-0-666"></a> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
5429
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-544">544</a></span>
5430
+ <span class="normal"><a href="#__codelineno-0-545">545</a></span>
5431
+ <span class="normal"><a href="#__codelineno-0-546">546</a></span>
5432
+ <span class="normal"><a href="#__codelineno-0-547">547</a></span>
5433
+ <span class="normal"><a href="#__codelineno-0-548">548</a></span>
5434
+ <span class="normal"><a href="#__codelineno-0-549">549</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-544" name="__codelineno-0-544"></a><span class="k">def</span> <span class="nf">validation_error</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
5435
+ <a id="__codelineno-0-545" name="__codelineno-0-545"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5436
+ <a id="__codelineno-0-546" name="__codelineno-0-546"></a><span class="sd"> Convenience method for raising `django.core.exceptions.ValidationError` which is required in order to</span>
5437
+ <a id="__codelineno-0-547" name="__codelineno-0-547"></a><span class="sd"> trigger validation error messages which are propagated to the user.</span>
5438
+ <a id="__codelineno-0-548" name="__codelineno-0-548"></a><span class="sd"> &quot;&quot;&quot;</span>
5439
+ <a id="__codelineno-0-549" name="__codelineno-0-549"></a> <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
4147
5440
  </code></pre></div></td></tr></table></div>
4148
5441
  </details>
4149
5442
  </div>
@@ -4184,7 +5477,9 @@ Device Role, Rack Group, Status, Manufacturer, and Platform.</p>
4184
5477
 
4185
5478
  <details class="quote">
4186
5479
  <summary>Source code in <code>nautobot/core/models/generics.py</code></summary>
4187
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-16">16</a></span>
5480
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-14">14</a></span>
5481
+ <span class="normal"><a href="#__codelineno-0-15">15</a></span>
5482
+ <span class="normal"><a href="#__codelineno-0-16">16</a></span>
4188
5483
  <span class="normal"><a href="#__codelineno-0-17">17</a></span>
4189
5484
  <span class="normal"><a href="#__codelineno-0-18">18</a></span>
4190
5485
  <span class="normal"><a href="#__codelineno-0-19">19</a></span>
@@ -4196,23 +5491,21 @@ Device Role, Rack Group, Status, Manufacturer, and Platform.</p>
4196
5491
  <span class="normal"><a href="#__codelineno-0-25">25</a></span>
4197
5492
  <span class="normal"><a href="#__codelineno-0-26">26</a></span>
4198
5493
  <span class="normal"><a href="#__codelineno-0-27">27</a></span>
4199
- <span class="normal"><a href="#__codelineno-0-28">28</a></span>
4200
- <span class="normal"><a href="#__codelineno-0-29">29</a></span>
4201
- <span class="normal"><a href="#__codelineno-0-30">30</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-16" name="__codelineno-0-16"></a><span class="k">class</span> <span class="nc">OrganizationalModel</span><span class="p">(</span>
4202
- <a id="__codelineno-0-17" name="__codelineno-0-17"></a> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">ChangeLoggedModel</span><span class="p">,</span> <span class="n">CustomFieldModel</span><span class="p">,</span> <span class="n">RelationshipModel</span><span class="p">,</span> <span class="n">DynamicGroupMixin</span><span class="p">,</span> <span class="n">NotesMixin</span>
4203
- <a id="__codelineno-0-18" name="__codelineno-0-18"></a><span class="p">):</span>
4204
- <a id="__codelineno-0-19" name="__codelineno-0-19"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4205
- <a id="__codelineno-0-20" name="__codelineno-0-20"></a><span class="sd"> Base abstract model for all organizational models.</span>
4206
- <a id="__codelineno-0-21" name="__codelineno-0-21"></a>
4207
- <a id="__codelineno-0-22" name="__codelineno-0-22"></a><span class="sd"> Organizational models aid the primary models by building structured relationships</span>
4208
- <a id="__codelineno-0-23" name="__codelineno-0-23"></a><span class="sd"> and logical groups, or categorizations. Organizational models do not typically</span>
4209
- <a id="__codelineno-0-24" name="__codelineno-0-24"></a><span class="sd"> represent concrete networking resources or assets, but rather they enable user</span>
4210
- <a id="__codelineno-0-25" name="__codelineno-0-25"></a><span class="sd"> specific use cases and metadata about network resources. Examples include</span>
4211
- <a id="__codelineno-0-26" name="__codelineno-0-26"></a><span class="sd"> Device Role, Rack Group, Status, Manufacturer, and Platform.</span>
4212
- <a id="__codelineno-0-27" name="__codelineno-0-27"></a><span class="sd"> &quot;&quot;&quot;</span>
4213
- <a id="__codelineno-0-28" name="__codelineno-0-28"></a>
4214
- <a id="__codelineno-0-29" name="__codelineno-0-29"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
4215
- <a id="__codelineno-0-30" name="__codelineno-0-30"></a> <span class="n">abstract</span> <span class="o">=</span> <span class="kc">True</span>
5494
+ <span class="normal"><a href="#__codelineno-0-28">28</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-14" name="__codelineno-0-14"></a><span class="k">class</span> <span class="nc">OrganizationalModel</span><span class="p">(</span>
5495
+ <a id="__codelineno-0-15" name="__codelineno-0-15"></a> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">ChangeLoggedModel</span><span class="p">,</span> <span class="n">CustomFieldModel</span><span class="p">,</span> <span class="n">RelationshipModel</span><span class="p">,</span> <span class="n">DynamicGroupMixin</span><span class="p">,</span> <span class="n">NotesMixin</span>
5496
+ <a id="__codelineno-0-16" name="__codelineno-0-16"></a><span class="p">):</span>
5497
+ <a id="__codelineno-0-17" name="__codelineno-0-17"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5498
+ <a id="__codelineno-0-18" name="__codelineno-0-18"></a><span class="sd"> Base abstract model for all organizational models.</span>
5499
+ <a id="__codelineno-0-19" name="__codelineno-0-19"></a>
5500
+ <a id="__codelineno-0-20" name="__codelineno-0-20"></a><span class="sd"> Organizational models aid the primary models by building structured relationships</span>
5501
+ <a id="__codelineno-0-21" name="__codelineno-0-21"></a><span class="sd"> and logical groups, or categorizations. Organizational models do not typically</span>
5502
+ <a id="__codelineno-0-22" name="__codelineno-0-22"></a><span class="sd"> represent concrete networking resources or assets, but rather they enable user</span>
5503
+ <a id="__codelineno-0-23" name="__codelineno-0-23"></a><span class="sd"> specific use cases and metadata about network resources. Examples include</span>
5504
+ <a id="__codelineno-0-24" name="__codelineno-0-24"></a><span class="sd"> Device Role, Rack Group, Status, Manufacturer, and Platform.</span>
5505
+ <a id="__codelineno-0-25" name="__codelineno-0-25"></a><span class="sd"> &quot;&quot;&quot;</span>
5506
+ <a id="__codelineno-0-26" name="__codelineno-0-26"></a>
5507
+ <a id="__codelineno-0-27" name="__codelineno-0-27"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
5508
+ <a id="__codelineno-0-28" name="__codelineno-0-28"></a> <span class="n">abstract</span> <span class="o">=</span> <span class="kc">True</span>
4216
5509
  </code></pre></div></td></tr></table></div>
4217
5510
  </details>
4218
5511
 
@@ -4261,33 +5554,33 @@ tangible or logical resources on the network, or within the organization.</p>
4261
5554
 
4262
5555
  <details class="quote">
4263
5556
  <summary>Source code in <code>nautobot/core/models/generics.py</code></summary>
4264
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-71">71</a></span>
4265
- <span class="normal"><a href="#__codelineno-0-72">72</a></span>
4266
- <span class="normal"><a href="#__codelineno-0-73">73</a></span>
4267
- <span class="normal"><a href="#__codelineno-0-74">74</a></span>
4268
- <span class="normal"><a href="#__codelineno-0-75">75</a></span>
4269
- <span class="normal"><a href="#__codelineno-0-76">76</a></span>
4270
- <span class="normal"><a href="#__codelineno-0-77">77</a></span>
4271
- <span class="normal"><a href="#__codelineno-0-78">78</a></span>
4272
- <span class="normal"><a href="#__codelineno-0-79">79</a></span>
4273
- <span class="normal"><a href="#__codelineno-0-80">80</a></span>
4274
- <span class="normal"><a href="#__codelineno-0-81">81</a></span>
4275
- <span class="normal"><a href="#__codelineno-0-82">82</a></span>
4276
- <span class="normal"><a href="#__codelineno-0-83">83</a></span>
4277
- <span class="normal"><a href="#__codelineno-0-84">84</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-71" name="__codelineno-0-71"></a><span class="k">class</span> <span class="nc">PrimaryModel</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">,</span> <span class="n">ChangeLoggedModel</span><span class="p">,</span> <span class="n">CustomFieldModel</span><span class="p">,</span> <span class="n">RelationshipModel</span><span class="p">,</span> <span class="n">DynamicGroupMixin</span><span class="p">,</span> <span class="n">NotesMixin</span><span class="p">):</span>
4278
- <a id="__codelineno-0-72" name="__codelineno-0-72"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4279
- <a id="__codelineno-0-73" name="__codelineno-0-73"></a><span class="sd"> Base abstract model for all primary models.</span>
4280
- <a id="__codelineno-0-74" name="__codelineno-0-74"></a>
4281
- <a id="__codelineno-0-75" name="__codelineno-0-75"></a><span class="sd"> A primary model is one which is materialistically relevant to the network datamodel.</span>
4282
- <a id="__codelineno-0-76" name="__codelineno-0-76"></a><span class="sd"> Such models form the basis of major elements of the data model, like Device,</span>
4283
- <a id="__codelineno-0-77" name="__codelineno-0-77"></a><span class="sd"> IP Address, Site, VLAN, Virtual Machine, etc. Primary models usually represent</span>
4284
- <a id="__codelineno-0-78" name="__codelineno-0-78"></a><span class="sd"> tangible or logical resources on the network, or within the organization.</span>
4285
- <a id="__codelineno-0-79" name="__codelineno-0-79"></a><span class="sd"> &quot;&quot;&quot;</span>
4286
- <a id="__codelineno-0-80" name="__codelineno-0-80"></a>
4287
- <a id="__codelineno-0-81" name="__codelineno-0-81"></a> <span class="n">tags</span> <span class="o">=</span> <span class="n">TaggableManager</span><span class="p">(</span><span class="n">through</span><span class="o">=</span><span class="n">TaggedItem</span><span class="p">,</span> <span class="n">manager</span><span class="o">=</span><span class="n">_NautobotTaggableManager</span><span class="p">,</span> <span class="n">ordering</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">])</span>
4288
- <a id="__codelineno-0-82" name="__codelineno-0-82"></a>
4289
- <a id="__codelineno-0-83" name="__codelineno-0-83"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
4290
- <a id="__codelineno-0-84" name="__codelineno-0-84"></a> <span class="n">abstract</span> <span class="o">=</span> <span class="kc">True</span>
5557
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-31">31</a></span>
5558
+ <span class="normal"><a href="#__codelineno-0-32">32</a></span>
5559
+ <span class="normal"><a href="#__codelineno-0-33">33</a></span>
5560
+ <span class="normal"><a href="#__codelineno-0-34">34</a></span>
5561
+ <span class="normal"><a href="#__codelineno-0-35">35</a></span>
5562
+ <span class="normal"><a href="#__codelineno-0-36">36</a></span>
5563
+ <span class="normal"><a href="#__codelineno-0-37">37</a></span>
5564
+ <span class="normal"><a href="#__codelineno-0-38">38</a></span>
5565
+ <span class="normal"><a href="#__codelineno-0-39">39</a></span>
5566
+ <span class="normal"><a href="#__codelineno-0-40">40</a></span>
5567
+ <span class="normal"><a href="#__codelineno-0-41">41</a></span>
5568
+ <span class="normal"><a href="#__codelineno-0-42">42</a></span>
5569
+ <span class="normal"><a href="#__codelineno-0-43">43</a></span>
5570
+ <span class="normal"><a href="#__codelineno-0-44">44</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-31" name="__codelineno-0-31"></a><span class="k">class</span> <span class="nc">PrimaryModel</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">,</span> <span class="n">ChangeLoggedModel</span><span class="p">,</span> <span class="n">CustomFieldModel</span><span class="p">,</span> <span class="n">RelationshipModel</span><span class="p">,</span> <span class="n">DynamicGroupMixin</span><span class="p">,</span> <span class="n">NotesMixin</span><span class="p">):</span>
5571
+ <a id="__codelineno-0-32" name="__codelineno-0-32"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5572
+ <a id="__codelineno-0-33" name="__codelineno-0-33"></a><span class="sd"> Base abstract model for all primary models.</span>
5573
+ <a id="__codelineno-0-34" name="__codelineno-0-34"></a>
5574
+ <a id="__codelineno-0-35" name="__codelineno-0-35"></a><span class="sd"> A primary model is one which is materialistically relevant to the network datamodel.</span>
5575
+ <a id="__codelineno-0-36" name="__codelineno-0-36"></a><span class="sd"> Such models form the basis of major elements of the data model, like Device,</span>
5576
+ <a id="__codelineno-0-37" name="__codelineno-0-37"></a><span class="sd"> IP Address, Site, VLAN, Virtual Machine, etc. Primary models usually represent</span>
5577
+ <a id="__codelineno-0-38" name="__codelineno-0-38"></a><span class="sd"> tangible or logical resources on the network, or within the organization.</span>
5578
+ <a id="__codelineno-0-39" name="__codelineno-0-39"></a><span class="sd"> &quot;&quot;&quot;</span>
5579
+ <a id="__codelineno-0-40" name="__codelineno-0-40"></a>
5580
+ <a id="__codelineno-0-41" name="__codelineno-0-41"></a> <span class="n">tags</span> <span class="o">=</span> <span class="n">TagsField</span><span class="p">()</span>
5581
+ <a id="__codelineno-0-42" name="__codelineno-0-42"></a>
5582
+ <a id="__codelineno-0-43" name="__codelineno-0-43"></a> <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
5583
+ <a id="__codelineno-0-44" name="__codelineno-0-44"></a> <span class="n">abstract</span> <span class="o">=</span> <span class="kc">True</span>
4291
5584
  </code></pre></div></td></tr></table></div>
4292
5585
  </details>
4293
5586
 
@@ -4338,8 +5631,7 @@ tangible or logical resources on the network, or within the organization.</p>
4338
5631
 
4339
5632
  <details class="quote">
4340
5633
  <summary>Source code in <code>nautobot/extras/models/statuses.py</code></summary>
4341
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-36"> 36</a></span>
4342
- <span class="normal"><a href="#__codelineno-0-37"> 37</a></span>
5634
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-37"> 37</a></span>
4343
5635
  <span class="normal"><a href="#__codelineno-0-38"> 38</a></span>
4344
5636
  <span class="normal"><a href="#__codelineno-0-39"> 39</a></span>
4345
5637
  <span class="normal"><a href="#__codelineno-0-40"> 40</a></span>
@@ -4402,75 +5694,70 @@ tangible or logical resources on the network, or within the organization.</p>
4402
5694
  <span class="normal"><a href="#__codelineno-0-97"> 97</a></span>
4403
5695
  <span class="normal"><a href="#__codelineno-0-98"> 98</a></span>
4404
5696
  <span class="normal"><a href="#__codelineno-0-99"> 99</a></span>
4405
- <span class="normal"><a href="#__codelineno-0-100">100</a></span>
4406
- <span class="normal"><a href="#__codelineno-0-101">101</a></span>
4407
- <span class="normal"><a href="#__codelineno-0-102">102</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-36" name="__codelineno-0-36"></a><span class="k">class</span> <span class="nc">StatusField</span><span class="p">(</span><span class="n">ForeignKeyLimitedByContentTypes</span><span class="p">):</span>
4408
- <a id="__codelineno-0-37" name="__codelineno-0-37"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4409
- <a id="__codelineno-0-38" name="__codelineno-0-38"></a><span class="sd"> Model database field that automatically limits custom choices.</span>
4410
- <a id="__codelineno-0-39" name="__codelineno-0-39"></a>
4411
- <a id="__codelineno-0-40" name="__codelineno-0-40"></a><span class="sd"> The limit_choices_to for the field are automatically derived from:</span>
4412
- <a id="__codelineno-0-41" name="__codelineno-0-41"></a>
4413
- <a id="__codelineno-0-42" name="__codelineno-0-42"></a><span class="sd"> - the content-type to which the field is attached (e.g. `dcim.device`)</span>
4414
- <a id="__codelineno-0-43" name="__codelineno-0-43"></a><span class="sd"> &quot;&quot;&quot;</span>
4415
- <a id="__codelineno-0-44" name="__codelineno-0-44"></a>
4416
- <a id="__codelineno-0-45" name="__codelineno-0-45"></a> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</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>
4417
- <a id="__codelineno-0-46" name="__codelineno-0-46"></a> <span class="n">kwargs</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s2">&quot;to&quot;</span><span class="p">,</span> <span class="n">Status</span><span class="p">)</span>
4418
- <a id="__codelineno-0-47" name="__codelineno-0-47"></a> <span class="n">kwargs</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s2">&quot;on_delete&quot;</span><span class="p">,</span> <span class="n">models</span><span class="o">.</span><span class="n">PROTECT</span><span class="p">)</span>
4419
- <a id="__codelineno-0-48" name="__codelineno-0-48"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</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>
4420
- <a id="__codelineno-0-49" name="__codelineno-0-49"></a>
4421
- <a id="__codelineno-0-50" name="__codelineno-0-50"></a> <span class="k">def</span> <span class="nf">get_limit_choices_to</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
4422
- <a id="__codelineno-0-51" name="__codelineno-0-51"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">&quot;content_types&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="bp">self</span><span class="o">.</span><span class="n">model</span><span class="p">)}</span>
4423
- <a id="__codelineno-0-52" name="__codelineno-0-52"></a>
4424
- <a id="__codelineno-0-53" name="__codelineno-0-53"></a> <span class="k">def</span> <span class="nf">contribute_to_class</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="bp">cls</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>
4425
- <a id="__codelineno-0-54" name="__codelineno-0-54"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4426
- <a id="__codelineno-0-55" name="__codelineno-0-55"></a><span class="sd"> Overload default so that we can assert that `.get_FOO_display` is</span>
4427
- <a id="__codelineno-0-56" name="__codelineno-0-56"></a><span class="sd"> attached to any model that is using a `StatusField`.</span>
4428
- <a id="__codelineno-0-57" name="__codelineno-0-57"></a>
4429
- <a id="__codelineno-0-58" name="__codelineno-0-58"></a><span class="sd"> Using `.contribute_to_class()` is how field objects get added to the model</span>
4430
- <a id="__codelineno-0-59" name="__codelineno-0-59"></a><span class="sd"> at during the instance preparation. This is also where any custom model</span>
4431
- <a id="__codelineno-0-60" name="__codelineno-0-60"></a><span class="sd"> methods are hooked in. So in short this method asserts that any time a</span>
4432
- <a id="__codelineno-0-61" name="__codelineno-0-61"></a><span class="sd"> `StatusField` is added to a model, that model also gets a</span>
4433
- <a id="__codelineno-0-62" name="__codelineno-0-62"></a><span class="sd"> `.get_status_display()` and a `.get_status_color()` method without</span>
4434
- <a id="__codelineno-0-63" name="__codelineno-0-63"></a><span class="sd"> having to define it on the model yourself.</span>
4435
- <a id="__codelineno-0-64" name="__codelineno-0-64"></a><span class="sd"> &quot;&quot;&quot;</span>
4436
- <a id="__codelineno-0-65" name="__codelineno-0-65"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">contribute_to_class</span><span class="p">(</span><span class="bp">cls</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>
4437
- <a id="__codelineno-0-66" name="__codelineno-0-66"></a>
4438
- <a id="__codelineno-0-67" name="__codelineno-0-67"></a> <span class="k">def</span> <span class="nf">_get_FIELD_display</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
4439
- <a id="__codelineno-0-68" name="__codelineno-0-68"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4440
- <a id="__codelineno-0-69" name="__codelineno-0-69"></a><span class="sd"> Closure to replace default model method of the same name.</span>
4441
- <a id="__codelineno-0-70" name="__codelineno-0-70"></a>
4442
- <a id="__codelineno-0-71" name="__codelineno-0-71"></a><span class="sd"> Cargo-culted from `django.db.models.base.Model._get_FIELD_display`</span>
4443
- <a id="__codelineno-0-72" name="__codelineno-0-72"></a><span class="sd"> &quot;&quot;&quot;</span>
4444
- <a id="__codelineno-0-73" name="__codelineno-0-73"></a> <span class="n">choices</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">get_choices</span><span class="p">()</span>
4445
- <a id="__codelineno-0-74" name="__codelineno-0-74"></a> <span class="n">value</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">attname</span><span class="p">)</span>
4446
- <a id="__codelineno-0-75" name="__codelineno-0-75"></a> <span class="n">choices_dict</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">make_hashable</span><span class="p">(</span><span class="n">choices</span><span class="p">))</span>
4447
- <a id="__codelineno-0-76" name="__codelineno-0-76"></a> <span class="c1"># force_str() to coerce lazy strings.</span>
4448
- <a id="__codelineno-0-77" name="__codelineno-0-77"></a> <span class="k">return</span> <span class="n">force_str</span><span class="p">(</span><span class="n">choices_dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">make_hashable</span><span class="p">(</span><span class="n">value</span><span class="p">),</span> <span class="n">value</span><span class="p">),</span> <span class="n">strings_only</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
4449
- <a id="__codelineno-0-78" name="__codelineno-0-78"></a>
4450
- <a id="__codelineno-0-79" name="__codelineno-0-79"></a> <span class="c1"># Install `.get_FOO_display()` onto the model using our own version.</span>
4451
- <a id="__codelineno-0-80" name="__codelineno-0-80"></a> <span class="k">if</span> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_display&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">:</span>
4452
- <a id="__codelineno-0-81" name="__codelineno-0-81"></a> <span class="nb">setattr</span><span class="p">(</span>
4453
- <a id="__codelineno-0-82" name="__codelineno-0-82"></a> <span class="bp">cls</span><span class="p">,</span>
4454
- <a id="__codelineno-0-83" name="__codelineno-0-83"></a> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_display&quot;</span><span class="p">,</span>
4455
- <a id="__codelineno-0-84" name="__codelineno-0-84"></a> <span class="n">partialmethod</span><span class="p">(</span><span class="n">_get_FIELD_display</span><span class="p">,</span> <span class="n">field</span><span class="o">=</span><span class="bp">self</span><span class="p">),</span>
4456
- <a id="__codelineno-0-85" name="__codelineno-0-85"></a> <span class="p">)</span>
4457
- <a id="__codelineno-0-86" name="__codelineno-0-86"></a>
4458
- <a id="__codelineno-0-87" name="__codelineno-0-87"></a> <span class="k">def</span> <span class="nf">_get_FIELD_color</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
4459
- <a id="__codelineno-0-88" name="__codelineno-0-88"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4460
- <a id="__codelineno-0-89" name="__codelineno-0-89"></a><span class="sd"> Return `self.FOO.color` (where FOO is field name).</span>
4461
- <a id="__codelineno-0-90" name="__codelineno-0-90"></a>
4462
- <a id="__codelineno-0-91" name="__codelineno-0-91"></a><span class="sd"> I am added to the model via `StatusField.contribute_to_class()`.</span>
4463
- <a id="__codelineno-0-92" name="__codelineno-0-92"></a><span class="sd"> &quot;&quot;&quot;</span>
4464
- <a id="__codelineno-0-93" name="__codelineno-0-93"></a> <span class="n">field_method</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
4465
- <a id="__codelineno-0-94" name="__codelineno-0-94"></a> <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">field_method</span><span class="p">,</span> <span class="s2">&quot;color&quot;</span><span class="p">)</span>
4466
- <a id="__codelineno-0-95" name="__codelineno-0-95"></a>
4467
- <a id="__codelineno-0-96" name="__codelineno-0-96"></a> <span class="c1"># Install `.get_FOO_color()` onto the model using our own version.</span>
4468
- <a id="__codelineno-0-97" name="__codelineno-0-97"></a> <span class="k">if</span> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_color&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">:</span>
4469
- <a id="__codelineno-0-98" name="__codelineno-0-98"></a> <span class="nb">setattr</span><span class="p">(</span>
4470
- <a id="__codelineno-0-99" name="__codelineno-0-99"></a> <span class="bp">cls</span><span class="p">,</span>
4471
- <a id="__codelineno-0-100" name="__codelineno-0-100"></a> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_color&quot;</span><span class="p">,</span>
4472
- <a id="__codelineno-0-101" name="__codelineno-0-101"></a> <span class="n">partialmethod</span><span class="p">(</span><span class="n">_get_FIELD_color</span><span class="p">,</span> <span class="n">field</span><span class="o">=</span><span class="bp">self</span><span class="p">),</span>
4473
- <a id="__codelineno-0-102" name="__codelineno-0-102"></a> <span class="p">)</span>
5697
+ <span class="normal"><a href="#__codelineno-0-100">100</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-37" name="__codelineno-0-37"></a><span class="k">class</span> <span class="nc">StatusField</span><span class="p">(</span><span class="n">ForeignKeyLimitedByContentTypes</span><span class="p">):</span>
5698
+ <a id="__codelineno-0-38" name="__codelineno-0-38"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5699
+ <a id="__codelineno-0-39" name="__codelineno-0-39"></a><span class="sd"> Model database field that automatically limits custom choices.</span>
5700
+ <a id="__codelineno-0-40" name="__codelineno-0-40"></a>
5701
+ <a id="__codelineno-0-41" name="__codelineno-0-41"></a><span class="sd"> The limit_choices_to for the field are automatically derived from:</span>
5702
+ <a id="__codelineno-0-42" name="__codelineno-0-42"></a>
5703
+ <a id="__codelineno-0-43" name="__codelineno-0-43"></a><span class="sd"> - the content-type to which the field is attached (e.g. `dcim.device`)</span>
5704
+ <a id="__codelineno-0-44" name="__codelineno-0-44"></a><span class="sd"> &quot;&quot;&quot;</span>
5705
+ <a id="__codelineno-0-45" name="__codelineno-0-45"></a>
5706
+ <a id="__codelineno-0-46" name="__codelineno-0-46"></a> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</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>
5707
+ <a id="__codelineno-0-47" name="__codelineno-0-47"></a> <span class="n">kwargs</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s2">&quot;to&quot;</span><span class="p">,</span> <span class="n">Status</span><span class="p">)</span>
5708
+ <a id="__codelineno-0-48" name="__codelineno-0-48"></a> <span class="n">kwargs</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s2">&quot;on_delete&quot;</span><span class="p">,</span> <span class="n">models</span><span class="o">.</span><span class="n">PROTECT</span><span class="p">)</span>
5709
+ <a id="__codelineno-0-49" name="__codelineno-0-49"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</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>
5710
+ <a id="__codelineno-0-50" name="__codelineno-0-50"></a>
5711
+ <a id="__codelineno-0-51" name="__codelineno-0-51"></a> <span class="k">def</span> <span class="nf">contribute_to_class</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="bp">cls</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>
5712
+ <a id="__codelineno-0-52" name="__codelineno-0-52"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5713
+ <a id="__codelineno-0-53" name="__codelineno-0-53"></a><span class="sd"> Overload default so that we can assert that `.get_FOO_display` is</span>
5714
+ <a id="__codelineno-0-54" name="__codelineno-0-54"></a><span class="sd"> attached to any model that is using a `StatusField`.</span>
5715
+ <a id="__codelineno-0-55" name="__codelineno-0-55"></a>
5716
+ <a id="__codelineno-0-56" name="__codelineno-0-56"></a><span class="sd"> Using `.contribute_to_class()` is how field objects get added to the model</span>
5717
+ <a id="__codelineno-0-57" name="__codelineno-0-57"></a><span class="sd"> at during the instance preparation. This is also where any custom model</span>
5718
+ <a id="__codelineno-0-58" name="__codelineno-0-58"></a><span class="sd"> methods are hooked in. So in short this method asserts that any time a</span>
5719
+ <a id="__codelineno-0-59" name="__codelineno-0-59"></a><span class="sd"> `StatusField` is added to a model, that model also gets a</span>
5720
+ <a id="__codelineno-0-60" name="__codelineno-0-60"></a><span class="sd"> `.get_status_display()` and a `.get_status_color()` method without</span>
5721
+ <a id="__codelineno-0-61" name="__codelineno-0-61"></a><span class="sd"> having to define it on the model yourself.</span>
5722
+ <a id="__codelineno-0-62" name="__codelineno-0-62"></a><span class="sd"> &quot;&quot;&quot;</span>
5723
+ <a id="__codelineno-0-63" name="__codelineno-0-63"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">contribute_to_class</span><span class="p">(</span><span class="bp">cls</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>
5724
+ <a id="__codelineno-0-64" name="__codelineno-0-64"></a>
5725
+ <a id="__codelineno-0-65" name="__codelineno-0-65"></a> <span class="k">def</span> <span class="nf">_get_FIELD_display</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
5726
+ <a id="__codelineno-0-66" name="__codelineno-0-66"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5727
+ <a id="__codelineno-0-67" name="__codelineno-0-67"></a><span class="sd"> Closure to replace default model method of the same name.</span>
5728
+ <a id="__codelineno-0-68" name="__codelineno-0-68"></a>
5729
+ <a id="__codelineno-0-69" name="__codelineno-0-69"></a><span class="sd"> Cargo-culted from `django.db.models.base.Model._get_FIELD_display`</span>
5730
+ <a id="__codelineno-0-70" name="__codelineno-0-70"></a><span class="sd"> &quot;&quot;&quot;</span>
5731
+ <a id="__codelineno-0-71" name="__codelineno-0-71"></a> <span class="n">choices</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">get_choices</span><span class="p">()</span>
5732
+ <a id="__codelineno-0-72" name="__codelineno-0-72"></a> <span class="n">value</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">attname</span><span class="p">)</span>
5733
+ <a id="__codelineno-0-73" name="__codelineno-0-73"></a> <span class="n">choices_dict</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">make_hashable</span><span class="p">(</span><span class="n">choices</span><span class="p">))</span>
5734
+ <a id="__codelineno-0-74" name="__codelineno-0-74"></a> <span class="c1"># force_str() to coerce lazy strings.</span>
5735
+ <a id="__codelineno-0-75" name="__codelineno-0-75"></a> <span class="k">return</span> <span class="n">force_str</span><span class="p">(</span><span class="n">choices_dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">make_hashable</span><span class="p">(</span><span class="n">value</span><span class="p">),</span> <span class="n">value</span><span class="p">),</span> <span class="n">strings_only</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
5736
+ <a id="__codelineno-0-76" name="__codelineno-0-76"></a>
5737
+ <a id="__codelineno-0-77" name="__codelineno-0-77"></a> <span class="c1"># Install `.get_FOO_display()` onto the model using our own version.</span>
5738
+ <a id="__codelineno-0-78" name="__codelineno-0-78"></a> <span class="k">if</span> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_display&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">:</span>
5739
+ <a id="__codelineno-0-79" name="__codelineno-0-79"></a> <span class="nb">setattr</span><span class="p">(</span>
5740
+ <a id="__codelineno-0-80" name="__codelineno-0-80"></a> <span class="bp">cls</span><span class="p">,</span>
5741
+ <a id="__codelineno-0-81" name="__codelineno-0-81"></a> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_display&quot;</span><span class="p">,</span>
5742
+ <a id="__codelineno-0-82" name="__codelineno-0-82"></a> <span class="n">partialmethod</span><span class="p">(</span><span class="n">_get_FIELD_display</span><span class="p">,</span> <span class="n">field</span><span class="o">=</span><span class="bp">self</span><span class="p">),</span>
5743
+ <a id="__codelineno-0-83" name="__codelineno-0-83"></a> <span class="p">)</span>
5744
+ <a id="__codelineno-0-84" name="__codelineno-0-84"></a>
5745
+ <a id="__codelineno-0-85" name="__codelineno-0-85"></a> <span class="k">def</span> <span class="nf">_get_FIELD_color</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
5746
+ <a id="__codelineno-0-86" name="__codelineno-0-86"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5747
+ <a id="__codelineno-0-87" name="__codelineno-0-87"></a><span class="sd"> Return `self.FOO.color` (where FOO is field name).</span>
5748
+ <a id="__codelineno-0-88" name="__codelineno-0-88"></a>
5749
+ <a id="__codelineno-0-89" name="__codelineno-0-89"></a><span class="sd"> I am added to the model via `StatusField.contribute_to_class()`.</span>
5750
+ <a id="__codelineno-0-90" name="__codelineno-0-90"></a><span class="sd"> &quot;&quot;&quot;</span>
5751
+ <a id="__codelineno-0-91" name="__codelineno-0-91"></a> <span class="n">field_method</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
5752
+ <a id="__codelineno-0-92" name="__codelineno-0-92"></a> <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">field_method</span><span class="p">,</span> <span class="s2">&quot;color&quot;</span><span class="p">)</span>
5753
+ <a id="__codelineno-0-93" name="__codelineno-0-93"></a>
5754
+ <a id="__codelineno-0-94" name="__codelineno-0-94"></a> <span class="c1"># Install `.get_FOO_color()` onto the model using our own version.</span>
5755
+ <a id="__codelineno-0-95" name="__codelineno-0-95"></a> <span class="k">if</span> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_color&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">:</span>
5756
+ <a id="__codelineno-0-96" name="__codelineno-0-96"></a> <span class="nb">setattr</span><span class="p">(</span>
5757
+ <a id="__codelineno-0-97" name="__codelineno-0-97"></a> <span class="bp">cls</span><span class="p">,</span>
5758
+ <a id="__codelineno-0-98" name="__codelineno-0-98"></a> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_color&quot;</span><span class="p">,</span>
5759
+ <a id="__codelineno-0-99" name="__codelineno-0-99"></a> <span class="n">partialmethod</span><span class="p">(</span><span class="n">_get_FIELD_color</span><span class="p">,</span> <span class="n">field</span><span class="o">=</span><span class="bp">self</span><span class="p">),</span>
5760
+ <a id="__codelineno-0-100" name="__codelineno-0-100"></a> <span class="p">)</span>
4474
5761
  </code></pre></div></td></tr></table></div>
4475
5762
  </details>
4476
5763
 
@@ -4509,7 +5796,9 @@ having to define it on the model yourself.</p>
4509
5796
 
4510
5797
  <details class="quote">
4511
5798
  <summary>Source code in <code>nautobot/extras/models/statuses.py</code></summary>
4512
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-53"> 53</a></span>
5799
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-51"> 51</a></span>
5800
+ <span class="normal"><a href="#__codelineno-0-52"> 52</a></span>
5801
+ <span class="normal"><a href="#__codelineno-0-53"> 53</a></span>
4513
5802
  <span class="normal"><a href="#__codelineno-0-54"> 54</a></span>
4514
5803
  <span class="normal"><a href="#__codelineno-0-55"> 55</a></span>
4515
5804
  <span class="normal"><a href="#__codelineno-0-56"> 56</a></span>
@@ -4556,58 +5845,56 @@ having to define it on the model yourself.</p>
4556
5845
  <span class="normal"><a href="#__codelineno-0-97"> 97</a></span>
4557
5846
  <span class="normal"><a href="#__codelineno-0-98"> 98</a></span>
4558
5847
  <span class="normal"><a href="#__codelineno-0-99"> 99</a></span>
4559
- <span class="normal"><a href="#__codelineno-0-100">100</a></span>
4560
- <span class="normal"><a href="#__codelineno-0-101">101</a></span>
4561
- <span class="normal"><a href="#__codelineno-0-102">102</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-53" name="__codelineno-0-53"></a><span class="k">def</span> <span class="nf">contribute_to_class</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="bp">cls</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>
4562
- <a id="__codelineno-0-54" name="__codelineno-0-54"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4563
- <a id="__codelineno-0-55" name="__codelineno-0-55"></a><span class="sd"> Overload default so that we can assert that `.get_FOO_display` is</span>
4564
- <a id="__codelineno-0-56" name="__codelineno-0-56"></a><span class="sd"> attached to any model that is using a `StatusField`.</span>
4565
- <a id="__codelineno-0-57" name="__codelineno-0-57"></a>
4566
- <a id="__codelineno-0-58" name="__codelineno-0-58"></a><span class="sd"> Using `.contribute_to_class()` is how field objects get added to the model</span>
4567
- <a id="__codelineno-0-59" name="__codelineno-0-59"></a><span class="sd"> at during the instance preparation. This is also where any custom model</span>
4568
- <a id="__codelineno-0-60" name="__codelineno-0-60"></a><span class="sd"> methods are hooked in. So in short this method asserts that any time a</span>
4569
- <a id="__codelineno-0-61" name="__codelineno-0-61"></a><span class="sd"> `StatusField` is added to a model, that model also gets a</span>
4570
- <a id="__codelineno-0-62" name="__codelineno-0-62"></a><span class="sd"> `.get_status_display()` and a `.get_status_color()` method without</span>
4571
- <a id="__codelineno-0-63" name="__codelineno-0-63"></a><span class="sd"> having to define it on the model yourself.</span>
4572
- <a id="__codelineno-0-64" name="__codelineno-0-64"></a><span class="sd"> &quot;&quot;&quot;</span>
4573
- <a id="__codelineno-0-65" name="__codelineno-0-65"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">contribute_to_class</span><span class="p">(</span><span class="bp">cls</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>
4574
- <a id="__codelineno-0-66" name="__codelineno-0-66"></a>
4575
- <a id="__codelineno-0-67" name="__codelineno-0-67"></a> <span class="k">def</span> <span class="nf">_get_FIELD_display</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
4576
- <a id="__codelineno-0-68" name="__codelineno-0-68"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4577
- <a id="__codelineno-0-69" name="__codelineno-0-69"></a><span class="sd"> Closure to replace default model method of the same name.</span>
4578
- <a id="__codelineno-0-70" name="__codelineno-0-70"></a>
4579
- <a id="__codelineno-0-71" name="__codelineno-0-71"></a><span class="sd"> Cargo-culted from `django.db.models.base.Model._get_FIELD_display`</span>
4580
- <a id="__codelineno-0-72" name="__codelineno-0-72"></a><span class="sd"> &quot;&quot;&quot;</span>
4581
- <a id="__codelineno-0-73" name="__codelineno-0-73"></a> <span class="n">choices</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">get_choices</span><span class="p">()</span>
4582
- <a id="__codelineno-0-74" name="__codelineno-0-74"></a> <span class="n">value</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">attname</span><span class="p">)</span>
4583
- <a id="__codelineno-0-75" name="__codelineno-0-75"></a> <span class="n">choices_dict</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">make_hashable</span><span class="p">(</span><span class="n">choices</span><span class="p">))</span>
4584
- <a id="__codelineno-0-76" name="__codelineno-0-76"></a> <span class="c1"># force_str() to coerce lazy strings.</span>
4585
- <a id="__codelineno-0-77" name="__codelineno-0-77"></a> <span class="k">return</span> <span class="n">force_str</span><span class="p">(</span><span class="n">choices_dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">make_hashable</span><span class="p">(</span><span class="n">value</span><span class="p">),</span> <span class="n">value</span><span class="p">),</span> <span class="n">strings_only</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
4586
- <a id="__codelineno-0-78" name="__codelineno-0-78"></a>
4587
- <a id="__codelineno-0-79" name="__codelineno-0-79"></a> <span class="c1"># Install `.get_FOO_display()` onto the model using our own version.</span>
4588
- <a id="__codelineno-0-80" name="__codelineno-0-80"></a> <span class="k">if</span> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_display&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">:</span>
4589
- <a id="__codelineno-0-81" name="__codelineno-0-81"></a> <span class="nb">setattr</span><span class="p">(</span>
4590
- <a id="__codelineno-0-82" name="__codelineno-0-82"></a> <span class="bp">cls</span><span class="p">,</span>
4591
- <a id="__codelineno-0-83" name="__codelineno-0-83"></a> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_display&quot;</span><span class="p">,</span>
4592
- <a id="__codelineno-0-84" name="__codelineno-0-84"></a> <span class="n">partialmethod</span><span class="p">(</span><span class="n">_get_FIELD_display</span><span class="p">,</span> <span class="n">field</span><span class="o">=</span><span class="bp">self</span><span class="p">),</span>
4593
- <a id="__codelineno-0-85" name="__codelineno-0-85"></a> <span class="p">)</span>
4594
- <a id="__codelineno-0-86" name="__codelineno-0-86"></a>
4595
- <a id="__codelineno-0-87" name="__codelineno-0-87"></a> <span class="k">def</span> <span class="nf">_get_FIELD_color</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
4596
- <a id="__codelineno-0-88" name="__codelineno-0-88"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
4597
- <a id="__codelineno-0-89" name="__codelineno-0-89"></a><span class="sd"> Return `self.FOO.color` (where FOO is field name).</span>
4598
- <a id="__codelineno-0-90" name="__codelineno-0-90"></a>
4599
- <a id="__codelineno-0-91" name="__codelineno-0-91"></a><span class="sd"> I am added to the model via `StatusField.contribute_to_class()`.</span>
4600
- <a id="__codelineno-0-92" name="__codelineno-0-92"></a><span class="sd"> &quot;&quot;&quot;</span>
4601
- <a id="__codelineno-0-93" name="__codelineno-0-93"></a> <span class="n">field_method</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
4602
- <a id="__codelineno-0-94" name="__codelineno-0-94"></a> <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">field_method</span><span class="p">,</span> <span class="s2">&quot;color&quot;</span><span class="p">)</span>
4603
- <a id="__codelineno-0-95" name="__codelineno-0-95"></a>
4604
- <a id="__codelineno-0-96" name="__codelineno-0-96"></a> <span class="c1"># Install `.get_FOO_color()` onto the model using our own version.</span>
4605
- <a id="__codelineno-0-97" name="__codelineno-0-97"></a> <span class="k">if</span> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_color&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">:</span>
4606
- <a id="__codelineno-0-98" name="__codelineno-0-98"></a> <span class="nb">setattr</span><span class="p">(</span>
4607
- <a id="__codelineno-0-99" name="__codelineno-0-99"></a> <span class="bp">cls</span><span class="p">,</span>
4608
- <a id="__codelineno-0-100" name="__codelineno-0-100"></a> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_color&quot;</span><span class="p">,</span>
4609
- <a id="__codelineno-0-101" name="__codelineno-0-101"></a> <span class="n">partialmethod</span><span class="p">(</span><span class="n">_get_FIELD_color</span><span class="p">,</span> <span class="n">field</span><span class="o">=</span><span class="bp">self</span><span class="p">),</span>
4610
- <a id="__codelineno-0-102" name="__codelineno-0-102"></a> <span class="p">)</span>
5848
+ <span class="normal"><a href="#__codelineno-0-100">100</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-51" name="__codelineno-0-51"></a><span class="k">def</span> <span class="nf">contribute_to_class</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="bp">cls</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>
5849
+ <a id="__codelineno-0-52" name="__codelineno-0-52"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5850
+ <a id="__codelineno-0-53" name="__codelineno-0-53"></a><span class="sd"> Overload default so that we can assert that `.get_FOO_display` is</span>
5851
+ <a id="__codelineno-0-54" name="__codelineno-0-54"></a><span class="sd"> attached to any model that is using a `StatusField`.</span>
5852
+ <a id="__codelineno-0-55" name="__codelineno-0-55"></a>
5853
+ <a id="__codelineno-0-56" name="__codelineno-0-56"></a><span class="sd"> Using `.contribute_to_class()` is how field objects get added to the model</span>
5854
+ <a id="__codelineno-0-57" name="__codelineno-0-57"></a><span class="sd"> at during the instance preparation. This is also where any custom model</span>
5855
+ <a id="__codelineno-0-58" name="__codelineno-0-58"></a><span class="sd"> methods are hooked in. So in short this method asserts that any time a</span>
5856
+ <a id="__codelineno-0-59" name="__codelineno-0-59"></a><span class="sd"> `StatusField` is added to a model, that model also gets a</span>
5857
+ <a id="__codelineno-0-60" name="__codelineno-0-60"></a><span class="sd"> `.get_status_display()` and a `.get_status_color()` method without</span>
5858
+ <a id="__codelineno-0-61" name="__codelineno-0-61"></a><span class="sd"> having to define it on the model yourself.</span>
5859
+ <a id="__codelineno-0-62" name="__codelineno-0-62"></a><span class="sd"> &quot;&quot;&quot;</span>
5860
+ <a id="__codelineno-0-63" name="__codelineno-0-63"></a> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">contribute_to_class</span><span class="p">(</span><span class="bp">cls</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>
5861
+ <a id="__codelineno-0-64" name="__codelineno-0-64"></a>
5862
+ <a id="__codelineno-0-65" name="__codelineno-0-65"></a> <span class="k">def</span> <span class="nf">_get_FIELD_display</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
5863
+ <a id="__codelineno-0-66" name="__codelineno-0-66"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5864
+ <a id="__codelineno-0-67" name="__codelineno-0-67"></a><span class="sd"> Closure to replace default model method of the same name.</span>
5865
+ <a id="__codelineno-0-68" name="__codelineno-0-68"></a>
5866
+ <a id="__codelineno-0-69" name="__codelineno-0-69"></a><span class="sd"> Cargo-culted from `django.db.models.base.Model._get_FIELD_display`</span>
5867
+ <a id="__codelineno-0-70" name="__codelineno-0-70"></a><span class="sd"> &quot;&quot;&quot;</span>
5868
+ <a id="__codelineno-0-71" name="__codelineno-0-71"></a> <span class="n">choices</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">get_choices</span><span class="p">()</span>
5869
+ <a id="__codelineno-0-72" name="__codelineno-0-72"></a> <span class="n">value</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">attname</span><span class="p">)</span>
5870
+ <a id="__codelineno-0-73" name="__codelineno-0-73"></a> <span class="n">choices_dict</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">make_hashable</span><span class="p">(</span><span class="n">choices</span><span class="p">))</span>
5871
+ <a id="__codelineno-0-74" name="__codelineno-0-74"></a> <span class="c1"># force_str() to coerce lazy strings.</span>
5872
+ <a id="__codelineno-0-75" name="__codelineno-0-75"></a> <span class="k">return</span> <span class="n">force_str</span><span class="p">(</span><span class="n">choices_dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">make_hashable</span><span class="p">(</span><span class="n">value</span><span class="p">),</span> <span class="n">value</span><span class="p">),</span> <span class="n">strings_only</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
5873
+ <a id="__codelineno-0-76" name="__codelineno-0-76"></a>
5874
+ <a id="__codelineno-0-77" name="__codelineno-0-77"></a> <span class="c1"># Install `.get_FOO_display()` onto the model using our own version.</span>
5875
+ <a id="__codelineno-0-78" name="__codelineno-0-78"></a> <span class="k">if</span> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_display&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">:</span>
5876
+ <a id="__codelineno-0-79" name="__codelineno-0-79"></a> <span class="nb">setattr</span><span class="p">(</span>
5877
+ <a id="__codelineno-0-80" name="__codelineno-0-80"></a> <span class="bp">cls</span><span class="p">,</span>
5878
+ <a id="__codelineno-0-81" name="__codelineno-0-81"></a> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_display&quot;</span><span class="p">,</span>
5879
+ <a id="__codelineno-0-82" name="__codelineno-0-82"></a> <span class="n">partialmethod</span><span class="p">(</span><span class="n">_get_FIELD_display</span><span class="p">,</span> <span class="n">field</span><span class="o">=</span><span class="bp">self</span><span class="p">),</span>
5880
+ <a id="__codelineno-0-83" name="__codelineno-0-83"></a> <span class="p">)</span>
5881
+ <a id="__codelineno-0-84" name="__codelineno-0-84"></a>
5882
+ <a id="__codelineno-0-85" name="__codelineno-0-85"></a> <span class="k">def</span> <span class="nf">_get_FIELD_color</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">):</span>
5883
+ <a id="__codelineno-0-86" name="__codelineno-0-86"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5884
+ <a id="__codelineno-0-87" name="__codelineno-0-87"></a><span class="sd"> Return `self.FOO.color` (where FOO is field name).</span>
5885
+ <a id="__codelineno-0-88" name="__codelineno-0-88"></a>
5886
+ <a id="__codelineno-0-89" name="__codelineno-0-89"></a><span class="sd"> I am added to the model via `StatusField.contribute_to_class()`.</span>
5887
+ <a id="__codelineno-0-90" name="__codelineno-0-90"></a><span class="sd"> &quot;&quot;&quot;</span>
5888
+ <a id="__codelineno-0-91" name="__codelineno-0-91"></a> <span class="n">field_method</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
5889
+ <a id="__codelineno-0-92" name="__codelineno-0-92"></a> <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">field_method</span><span class="p">,</span> <span class="s2">&quot;color&quot;</span><span class="p">)</span>
5890
+ <a id="__codelineno-0-93" name="__codelineno-0-93"></a>
5891
+ <a id="__codelineno-0-94" name="__codelineno-0-94"></a> <span class="c1"># Install `.get_FOO_color()` onto the model using our own version.</span>
5892
+ <a id="__codelineno-0-95" name="__codelineno-0-95"></a> <span class="k">if</span> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_color&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">:</span>
5893
+ <a id="__codelineno-0-96" name="__codelineno-0-96"></a> <span class="nb">setattr</span><span class="p">(</span>
5894
+ <a id="__codelineno-0-97" name="__codelineno-0-97"></a> <span class="bp">cls</span><span class="p">,</span>
5895
+ <a id="__codelineno-0-98" name="__codelineno-0-98"></a> <span class="sa">f</span><span class="s2">&quot;get_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">_color&quot;</span><span class="p">,</span>
5896
+ <a id="__codelineno-0-99" name="__codelineno-0-99"></a> <span class="n">partialmethod</span><span class="p">(</span><span class="n">_get_FIELD_color</span><span class="p">,</span> <span class="n">field</span><span class="o">=</span><span class="bp">self</span><span class="p">),</span>
5897
+ <a id="__codelineno-0-100" name="__codelineno-0-100"></a> <span class="p">)</span>
4611
5898
  </code></pre></div></td></tr></table></div>
4612
5899
  </details>
4613
5900
  </div>
@@ -5031,7 +6318,8 @@ having to define it on the model yourself.</p>
5031
6318
 
5032
6319
  <details class="quote">
5033
6320
  <summary>Source code in <code>nautobot/extras/utils.py</code></summary>
5034
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-206">206</a></span>
6321
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-205">205</a></span>
6322
+ <span class="normal"><a href="#__codelineno-0-206">206</a></span>
5035
6323
  <span class="normal"><a href="#__codelineno-0-207">207</a></span>
5036
6324
  <span class="normal"><a href="#__codelineno-0-208">208</a></span>
5037
6325
  <span class="normal"><a href="#__codelineno-0-209">209</a></span>
@@ -5047,25 +6335,24 @@ having to define it on the model yourself.</p>
5047
6335
  <span class="normal"><a href="#__codelineno-0-219">219</a></span>
5048
6336
  <span class="normal"><a href="#__codelineno-0-220">220</a></span>
5049
6337
  <span class="normal"><a href="#__codelineno-0-221">221</a></span>
5050
- <span class="normal"><a href="#__codelineno-0-222">222</a></span>
5051
- <span class="normal"><a href="#__codelineno-0-223">223</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-206" name="__codelineno-0-206"></a><span class="k">def</span> <span class="nf">extras_features</span><span class="p">(</span><span class="o">*</span><span class="n">features</span><span class="p">):</span>
5052
- <a id="__codelineno-0-207" name="__codelineno-0-207"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5053
- <a id="__codelineno-0-208" name="__codelineno-0-208"></a><span class="sd"> Decorator used to register extras provided features to a model</span>
5054
- <a id="__codelineno-0-209" name="__codelineno-0-209"></a><span class="sd"> &quot;&quot;&quot;</span>
5055
- <a id="__codelineno-0-210" name="__codelineno-0-210"></a>
5056
- <a id="__codelineno-0-211" name="__codelineno-0-211"></a> <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">model_class</span><span class="p">):</span>
5057
- <a id="__codelineno-0-212" name="__codelineno-0-212"></a> <span class="c1"># Initialize the model_features store if not already defined</span>
5058
- <a id="__codelineno-0-213" name="__codelineno-0-213"></a> <span class="k">if</span> <span class="s2">&quot;model_features&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">registry</span><span class="p">:</span>
5059
- <a id="__codelineno-0-214" name="__codelineno-0-214"></a> <span class="n">registry</span><span class="p">[</span><span class="s2">&quot;model_features&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="n">f</span><span class="p">:</span> <span class="n">collections</span><span class="o">.</span><span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">EXTRAS_FEATURES</span><span class="p">}</span>
5060
- <a id="__codelineno-0-215" name="__codelineno-0-215"></a> <span class="k">for</span> <span class="n">feature</span> <span class="ow">in</span> <span class="n">features</span><span class="p">:</span>
5061
- <a id="__codelineno-0-216" name="__codelineno-0-216"></a> <span class="k">if</span> <span class="n">feature</span> <span class="ow">in</span> <span class="n">EXTRAS_FEATURES</span><span class="p">:</span>
5062
- <a id="__codelineno-0-217" name="__codelineno-0-217"></a> <span class="n">app_label</span><span class="p">,</span> <span class="n">model_name</span> <span class="o">=</span> <span class="n">model_class</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">label_lower</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)</span>
5063
- <a id="__codelineno-0-218" name="__codelineno-0-218"></a> <span class="n">registry</span><span class="p">[</span><span class="s2">&quot;model_features&quot;</span><span class="p">][</span><span class="n">feature</span><span class="p">][</span><span class="n">app_label</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">model_name</span><span class="p">)</span>
5064
- <a id="__codelineno-0-219" name="__codelineno-0-219"></a> <span class="k">else</span><span class="p">:</span>
5065
- <a id="__codelineno-0-220" name="__codelineno-0-220"></a> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">feature</span><span class="si">}</span><span class="s2"> is not a valid extras feature!&quot;</span><span class="p">)</span>
5066
- <a id="__codelineno-0-221" name="__codelineno-0-221"></a> <span class="k">return</span> <span class="n">model_class</span>
5067
- <a id="__codelineno-0-222" name="__codelineno-0-222"></a>
5068
- <a id="__codelineno-0-223" name="__codelineno-0-223"></a> <span class="k">return</span> <span class="n">wrapper</span>
6338
+ <span class="normal"><a href="#__codelineno-0-222">222</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-205" name="__codelineno-0-205"></a><span class="k">def</span> <span class="nf">extras_features</span><span class="p">(</span><span class="o">*</span><span class="n">features</span><span class="p">):</span>
6339
+ <a id="__codelineno-0-206" name="__codelineno-0-206"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
6340
+ <a id="__codelineno-0-207" name="__codelineno-0-207"></a><span class="sd"> Decorator used to register extras provided features to a model</span>
6341
+ <a id="__codelineno-0-208" name="__codelineno-0-208"></a><span class="sd"> &quot;&quot;&quot;</span>
6342
+ <a id="__codelineno-0-209" name="__codelineno-0-209"></a>
6343
+ <a id="__codelineno-0-210" name="__codelineno-0-210"></a> <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">model_class</span><span class="p">):</span>
6344
+ <a id="__codelineno-0-211" name="__codelineno-0-211"></a> <span class="c1"># Initialize the model_features store if not already defined</span>
6345
+ <a id="__codelineno-0-212" name="__codelineno-0-212"></a> <span class="k">if</span> <span class="s2">&quot;model_features&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">registry</span><span class="p">:</span>
6346
+ <a id="__codelineno-0-213" name="__codelineno-0-213"></a> <span class="n">registry</span><span class="p">[</span><span class="s2">&quot;model_features&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="n">f</span><span class="p">:</span> <span class="n">collections</span><span class="o">.</span><span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">EXTRAS_FEATURES</span><span class="p">}</span>
6347
+ <a id="__codelineno-0-214" name="__codelineno-0-214"></a> <span class="k">for</span> <span class="n">feature</span> <span class="ow">in</span> <span class="n">features</span><span class="p">:</span>
6348
+ <a id="__codelineno-0-215" name="__codelineno-0-215"></a> <span class="k">if</span> <span class="n">feature</span> <span class="ow">in</span> <span class="n">EXTRAS_FEATURES</span><span class="p">:</span>
6349
+ <a id="__codelineno-0-216" name="__codelineno-0-216"></a> <span class="n">app_label</span><span class="p">,</span> <span class="n">model_name</span> <span class="o">=</span> <span class="n">model_class</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">label_lower</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)</span>
6350
+ <a id="__codelineno-0-217" name="__codelineno-0-217"></a> <span class="n">registry</span><span class="p">[</span><span class="s2">&quot;model_features&quot;</span><span class="p">][</span><span class="n">feature</span><span class="p">][</span><span class="n">app_label</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">model_name</span><span class="p">)</span>
6351
+ <a id="__codelineno-0-218" name="__codelineno-0-218"></a> <span class="k">else</span><span class="p">:</span>
6352
+ <a id="__codelineno-0-219" name="__codelineno-0-219"></a> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">feature</span><span class="si">}</span><span class="s2"> is not a valid extras feature!&quot;</span><span class="p">)</span>
6353
+ <a id="__codelineno-0-220" name="__codelineno-0-220"></a> <span class="k">return</span> <span class="n">model_class</span>
6354
+ <a id="__codelineno-0-221" name="__codelineno-0-221"></a>
6355
+ <a id="__codelineno-0-222" name="__codelineno-0-222"></a> <span class="k">return</span> <span class="n">wrapper</span>
5069
6356
  </code></pre></div></td></tr></table></div>
5070
6357
  </details>
5071
6358
  </div>