nautobot 2.2.8__py3-none-any.whl → 2.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nautobot might be problematic. Click here for more details.

Files changed (704) hide show
  1. nautobot/apps/forms.py +4 -0
  2. nautobot/apps/models.py +10 -1
  3. nautobot/circuits/__init__.py +0 -1
  4. nautobot/circuits/apps.py +1 -0
  5. nautobot/circuits/factory.py +15 -3
  6. nautobot/circuits/filters.py +13 -0
  7. nautobot/circuits/forms.py +13 -0
  8. nautobot/circuits/migrations/0021_alter_circuit_status_alter_circuittermination__path.py +32 -0
  9. nautobot/circuits/migrations/0022_circuittermination_cloud_network.py +25 -0
  10. nautobot/circuits/models.py +16 -3
  11. nautobot/circuits/tables.py +16 -2
  12. nautobot/circuits/templates/circuits/circuittermination_create.html +10 -2
  13. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +6 -0
  14. nautobot/circuits/templates/circuits/inc/circuit_termination.html +6 -1
  15. nautobot/circuits/tests/test_api.py +7 -5
  16. nautobot/circuits/tests/test_filters.py +12 -5
  17. nautobot/circuits/tests/test_models.py +33 -2
  18. nautobot/circuits/views.py +2 -3
  19. nautobot/cloud/__init__.py +0 -0
  20. nautobot/cloud/api/__init__.py +0 -0
  21. nautobot/cloud/api/serializers.py +54 -0
  22. nautobot/cloud/api/urls.py +16 -0
  23. nautobot/cloud/api/views.py +48 -0
  24. nautobot/cloud/apps.py +13 -0
  25. nautobot/cloud/factory.py +113 -0
  26. nautobot/cloud/filters.py +187 -0
  27. nautobot/cloud/forms.py +339 -0
  28. nautobot/cloud/homepage.py +43 -0
  29. nautobot/cloud/migrations/0001_initial.py +304 -0
  30. nautobot/cloud/migrations/__init__.py +0 -0
  31. nautobot/cloud/models.py +246 -0
  32. nautobot/cloud/navigation.py +85 -0
  33. nautobot/cloud/tables.py +157 -0
  34. nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +43 -0
  35. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +122 -0
  36. nautobot/cloud/templates/cloud/cloudnetwork_update.html +33 -0
  37. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +111 -0
  38. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +69 -0
  39. nautobot/cloud/templates/cloud/cloudservice_update.html +25 -0
  40. nautobot/cloud/tests/__init__.py +0 -0
  41. nautobot/cloud/tests/test_api.py +248 -0
  42. nautobot/cloud/tests/test_filters.py +125 -0
  43. nautobot/cloud/tests/test_models.py +43 -0
  44. nautobot/cloud/tests/test_views.py +153 -0
  45. nautobot/cloud/urls.py +14 -0
  46. nautobot/cloud/views.py +181 -0
  47. nautobot/core/__init__.py +0 -3
  48. nautobot/core/api/metadata.py +1 -0
  49. nautobot/core/api/parsers.py +7 -1
  50. nautobot/core/api/urls.py +1 -0
  51. nautobot/core/api/utils.py +1 -0
  52. nautobot/core/api/views.py +4 -0
  53. nautobot/core/apps/__init__.py +6 -3
  54. nautobot/core/constants.py +8 -0
  55. nautobot/core/factory.py +32 -1
  56. nautobot/core/filters.py +110 -14
  57. nautobot/core/forms/fields.py +10 -4
  58. nautobot/core/forms/forms.py +11 -3
  59. nautobot/core/forms/widgets.py +18 -1
  60. nautobot/core/graphql/generators.py +2 -2
  61. nautobot/core/graphql/schema.py +28 -7
  62. nautobot/core/jobs/__init__.py +20 -3
  63. nautobot/core/jobs/cleanup.py +100 -0
  64. nautobot/core/jobs/groups.py +38 -0
  65. nautobot/core/management/commands/generate_test_data.py +116 -3
  66. nautobot/core/models/__init__.py +34 -9
  67. nautobot/core/models/generics.py +19 -3
  68. nautobot/core/models/name_color_content_types.py +7 -28
  69. nautobot/core/models/querysets.py +4 -3
  70. nautobot/core/models/tree_queries.py +1 -1
  71. nautobot/core/models/utils.py +21 -5
  72. nautobot/core/settings.py +15 -19
  73. nautobot/core/settings.yaml +48 -13
  74. nautobot/core/settings_funcs.py +103 -0
  75. nautobot/core/tables.py +130 -56
  76. nautobot/core/templates/admin/search_form.html +1 -1
  77. nautobot/core/templates/buttons/add.html +11 -3
  78. nautobot/core/templates/buttons/consolidated_bulk_action_buttons.html +13 -0
  79. nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +13 -0
  80. nautobot/core/templates/buttons/export.html +101 -53
  81. nautobot/core/templates/buttons/job_import.html +11 -3
  82. nautobot/core/templates/generic/object_bulk_destroy.html +3 -1
  83. nautobot/core/templates/generic/object_bulk_update.html +3 -1
  84. nautobot/core/templates/generic/object_changelog.html +0 -9
  85. nautobot/core/templates/generic/object_list.html +156 -17
  86. nautobot/core/templates/generic/object_retrieve.html +80 -16
  87. nautobot/core/templates/inc/extras_features_edit_form_fields.html +8 -0
  88. nautobot/core/templates/inc/javascript.html +2 -0
  89. nautobot/core/templates/inc/media.html +2 -2
  90. nautobot/core/templates/inc/nav_menu.html +1 -0
  91. nautobot/core/templates/inc/paginator.html +7 -7
  92. nautobot/core/templates/inc/search_panel.html +2 -2
  93. nautobot/core/templates/inc/table.html +2 -2
  94. nautobot/core/templates/nautobot_config.py.j2 +28 -8
  95. nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +37 -0
  96. nautobot/core/templates/utilities/templatetags/filter_form_modal.html +2 -2
  97. nautobot/core/templates/utilities/templatetags/saved_view_modal.html +38 -0
  98. nautobot/core/templates/utilities/theme_preview.html +25 -8
  99. nautobot/core/templates/utilities/worker_status.html +152 -0
  100. nautobot/core/templatetags/buttons.py +335 -38
  101. nautobot/core/templatetags/form_helpers.py +1 -1
  102. nautobot/core/templatetags/helpers.py +181 -11
  103. nautobot/core/testing/api.py +5 -4
  104. nautobot/core/testing/filters.py +63 -14
  105. nautobot/core/testing/mixins.py +46 -0
  106. nautobot/core/testing/models.py +22 -0
  107. nautobot/core/testing/schema.py +4 -8
  108. nautobot/core/testing/views.py +31 -14
  109. nautobot/core/tests/integration/test_general_functionality.py +1 -1
  110. nautobot/core/tests/integration/test_import_objects_ui.py +1 -0
  111. nautobot/core/tests/integration/test_swagger.py +1 -1
  112. nautobot/core/tests/nautobot_config.py +0 -1
  113. nautobot/core/tests/runner.py +2 -2
  114. nautobot/core/tests/test_api.py +1 -0
  115. nautobot/core/tests/test_authentication.py +7 -2
  116. nautobot/core/tests/test_filters.py +11 -9
  117. nautobot/core/tests/test_forms.py +9 -0
  118. nautobot/core/tests/test_graphql.py +27 -16
  119. nautobot/core/tests/test_jobs.py +204 -2
  120. nautobot/core/tests/test_tables.py +3 -1
  121. nautobot/core/tests/test_templatetags_helpers.py +12 -5
  122. nautobot/core/tests/test_templatetags_netutils.py +3 -3
  123. nautobot/core/tests/test_utils.py +31 -20
  124. nautobot/core/tests/test_views.py +6 -6
  125. nautobot/core/urls.py +8 -3
  126. nautobot/core/utils/deprecation.py +29 -0
  127. nautobot/core/utils/filtering.py +12 -9
  128. nautobot/core/utils/lookup.py +37 -2
  129. nautobot/core/utils/requests.py +4 -1
  130. nautobot/core/views/__init__.py +137 -24
  131. nautobot/core/views/generic.py +119 -67
  132. nautobot/core/views/mixins.py +105 -36
  133. nautobot/core/views/paginator.py +9 -3
  134. nautobot/core/views/renderers.py +121 -56
  135. nautobot/core/views/utils.py +81 -1
  136. nautobot/dcim/__init__.py +0 -1
  137. nautobot/dcim/api/serializers.py +180 -44
  138. nautobot/dcim/api/urls.py +7 -3
  139. nautobot/dcim/api/views.py +53 -7
  140. nautobot/dcim/apps.py +3 -0
  141. nautobot/dcim/choices.py +25 -0
  142. nautobot/dcim/constants.py +7 -0
  143. nautobot/dcim/factory.py +252 -18
  144. nautobot/dcim/filters/__init__.py +373 -193
  145. nautobot/dcim/filters/mixins.py +274 -1
  146. nautobot/dcim/forms.py +834 -121
  147. nautobot/dcim/graphql/types.py +2 -2
  148. nautobot/dcim/homepage.py +1 -1
  149. nautobot/dcim/migrations/0059_add_role_field_to_interface_models.py +27 -0
  150. nautobot/dcim/migrations/0060_alter_cable_status_alter_consoleport__path_and_more.py +303 -0
  151. nautobot/dcim/migrations/0061_module_models.py +862 -0
  152. nautobot/dcim/migrations/0062_module_data_migration.py +25 -0
  153. nautobot/dcim/models/__init__.py +8 -0
  154. nautobot/dcim/models/cables.py +15 -0
  155. nautobot/dcim/models/device_component_templates.py +207 -53
  156. nautobot/dcim/models/device_components.py +282 -99
  157. nautobot/dcim/models/devices.py +472 -13
  158. nautobot/dcim/models/racks.py +0 -1
  159. nautobot/dcim/navigation.py +47 -0
  160. nautobot/dcim/signals.py +3 -3
  161. nautobot/dcim/tables/__init__.py +35 -23
  162. nautobot/dcim/tables/devices.py +248 -47
  163. nautobot/dcim/tables/devicetypes.py +65 -9
  164. nautobot/dcim/tables/racks.py +5 -1
  165. nautobot/dcim/tables/template_code.py +46 -26
  166. nautobot/dcim/templates/dcim/cable_connect.html +76 -3
  167. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -5
  168. nautobot/dcim/templates/dcim/device/base.html +14 -6
  169. nautobot/dcim/templates/dcim/device/consoleports.html +2 -3
  170. nautobot/dcim/templates/dcim/device/consoleserverports.html +2 -3
  171. nautobot/dcim/templates/dcim/device/devicebays.html +6 -7
  172. nautobot/dcim/templates/dcim/device/frontports.html +2 -3
  173. nautobot/dcim/templates/dcim/device/interfaces.html +2 -3
  174. nautobot/dcim/templates/dcim/device/inventory.html +2 -3
  175. nautobot/dcim/templates/dcim/device/modulebays.html +49 -0
  176. nautobot/dcim/templates/dcim/device/poweroutlets.html +2 -3
  177. nautobot/dcim/templates/dcim/device/powerports.html +2 -3
  178. nautobot/dcim/templates/dcim/device/rearports.html +2 -3
  179. nautobot/dcim/templates/dcim/device.html +45 -1
  180. nautobot/dcim/templates/dcim/device_component.html +13 -5
  181. nautobot/dcim/templates/dcim/device_list.html +2 -1
  182. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +6 -0
  183. nautobot/dcim/templates/dcim/devicetype.html +99 -98
  184. nautobot/dcim/templates/dcim/devicetype_list.html +8 -16
  185. nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
  186. nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +39 -0
  187. nautobot/dcim/templates/dcim/interface.html +17 -2
  188. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -5
  189. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  190. nautobot/dcim/templates/dcim/manufacturer.html +24 -0
  191. nautobot/dcim/templates/dcim/module/base.html +97 -0
  192. nautobot/dcim/templates/dcim/module_bulk_destroy.html +5 -0
  193. nautobot/dcim/templates/dcim/module_consoleports.html +53 -0
  194. nautobot/dcim/templates/dcim/module_consoleserverports.html +53 -0
  195. nautobot/dcim/templates/dcim/module_destroy.html +5 -0
  196. nautobot/dcim/templates/dcim/module_frontports.html +53 -0
  197. nautobot/dcim/templates/dcim/module_interfaces.html +57 -0
  198. nautobot/dcim/templates/dcim/module_list.html +20 -0
  199. nautobot/dcim/templates/dcim/module_modulebays.html +49 -0
  200. nautobot/dcim/templates/dcim/module_poweroutlets.html +53 -0
  201. nautobot/dcim/templates/dcim/module_powerports.html +53 -0
  202. nautobot/dcim/templates/dcim/module_rearports.html +53 -0
  203. nautobot/dcim/templates/dcim/module_retrieve.html +63 -0
  204. nautobot/dcim/templates/dcim/module_update.html +71 -0
  205. nautobot/dcim/templates/dcim/modulebay_bulk_destroy.html +5 -0
  206. nautobot/dcim/templates/dcim/modulebay_destroy.html +8 -0
  207. nautobot/dcim/templates/dcim/modulebay_retrieve.html +101 -0
  208. nautobot/dcim/templates/dcim/moduletype_list.html +11 -0
  209. nautobot/dcim/templates/dcim/moduletype_retrieve.html +159 -0
  210. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -5
  211. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +65 -19
  212. nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
  213. nautobot/dcim/tests/test_api.py +693 -208
  214. nautobot/dcim/tests/test_filters.py +843 -217
  215. nautobot/dcim/tests/test_models.py +1103 -8
  216. nautobot/dcim/tests/test_views.py +1525 -343
  217. nautobot/dcim/urls.py +17 -2
  218. nautobot/dcim/utils.py +2 -3
  219. nautobot/dcim/views.py +1109 -113
  220. nautobot/extras/__init__.py +0 -1
  221. nautobot/extras/api/serializers.py +115 -3
  222. nautobot/extras/api/urls.py +12 -0
  223. nautobot/extras/api/views.py +73 -59
  224. nautobot/extras/apps.py +2 -2
  225. nautobot/extras/choices.py +43 -0
  226. nautobot/extras/context_managers.py +13 -8
  227. nautobot/extras/datasources/git.py +2 -0
  228. nautobot/extras/factory.py +460 -9
  229. nautobot/extras/filters/__init__.py +174 -3
  230. nautobot/extras/filters/mixins.py +46 -43
  231. nautobot/extras/forms/base.py +24 -5
  232. nautobot/extras/forms/forms.py +227 -8
  233. nautobot/extras/forms/mixins.py +93 -0
  234. nautobot/extras/graphql/types.py +23 -10
  235. nautobot/extras/homepage.py +26 -3
  236. nautobot/extras/jobs.py +2 -2
  237. nautobot/extras/management/__init__.py +1 -0
  238. nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +1 -16
  239. nautobot/extras/migrations/0021_customfield_changelog_data.py +1 -0
  240. nautobot/extras/migrations/0109_dynamicgroup_group_type_dynamicgroup_tags_and_more.py +108 -0
  241. nautobot/extras/migrations/0110_alter_configcontext_cluster_groups_and_more.py +111 -0
  242. nautobot/extras/migrations/0111_metadata.py +162 -0
  243. nautobot/extras/migrations/0112_dynamic_group_group_type_data_migration.py +28 -0
  244. nautobot/extras/migrations/0113_saved_views.py +77 -0
  245. nautobot/extras/models/__init__.py +15 -1
  246. nautobot/extras/models/change_logging.py +3 -3
  247. nautobot/extras/models/contacts.py +4 -0
  248. nautobot/extras/models/customfields.py +18 -3
  249. nautobot/extras/models/groups.py +389 -225
  250. nautobot/extras/models/jobs.py +87 -3
  251. nautobot/extras/models/metadata.py +441 -0
  252. nautobot/extras/models/mixins.py +72 -62
  253. nautobot/extras/models/models.py +118 -9
  254. nautobot/extras/models/relationships.py +9 -2
  255. nautobot/extras/models/tags.py +13 -2
  256. nautobot/extras/navigation.py +57 -0
  257. nautobot/extras/plugins/__init__.py +3 -1
  258. nautobot/extras/querysets.py +30 -66
  259. nautobot/extras/signals.py +109 -101
  260. nautobot/extras/tables.py +201 -17
  261. nautobot/extras/templates/extras/dynamicgroup.html +44 -15
  262. nautobot/extras/templates/extras/dynamicgroup_edit.html +2 -0
  263. nautobot/extras/templates/extras/job.html +1 -1
  264. nautobot/extras/templates/extras/job_detail.html +11 -0
  265. nautobot/extras/templates/extras/jobresult.html +61 -74
  266. nautobot/extras/templates/extras/metadatatype_create.html +89 -0
  267. nautobot/extras/templates/extras/metadatatype_retrieve.html +67 -0
  268. nautobot/extras/templates/extras/object_dynamicgroups.html +7 -0
  269. nautobot/extras/templates/extras/objectchange_list.html +0 -12
  270. nautobot/extras/templates/extras/plugins_list.html +1 -3
  271. nautobot/extras/templates/extras/role_retrieve.html +48 -0
  272. nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +20 -0
  273. nautobot/extras/tests/integration/test_customfields.py +1 -0
  274. nautobot/extras/tests/test_api.py +509 -23
  275. nautobot/extras/tests/test_changelog.py +20 -9
  276. nautobot/extras/tests/test_context_managers.py +22 -15
  277. nautobot/extras/tests/test_datasources.py +13 -1
  278. nautobot/extras/tests/test_dynamicgroups.py +201 -171
  279. nautobot/extras/tests/test_filters.py +211 -12
  280. nautobot/extras/tests/test_jobs.py +6 -6
  281. nautobot/extras/tests/test_models.py +501 -4
  282. nautobot/extras/tests/test_relationships.py +1 -0
  283. nautobot/extras/tests/test_views.py +586 -8
  284. nautobot/extras/tests/test_webhooks.py +1 -1
  285. nautobot/extras/urls.py +5 -0
  286. nautobot/extras/utils.py +85 -16
  287. nautobot/extras/views.py +562 -122
  288. nautobot/ipam/__init__.py +0 -1
  289. nautobot/ipam/apps.py +1 -0
  290. nautobot/ipam/factory.py +17 -19
  291. nautobot/ipam/filters.py +13 -0
  292. nautobot/ipam/forms.py +8 -4
  293. nautobot/ipam/graphql/types.py +2 -2
  294. nautobot/ipam/migrations/0047_alter_ipaddress_role_alter_ipaddress_status_and_more.py +59 -0
  295. nautobot/ipam/models.py +20 -20
  296. nautobot/ipam/querysets.py +1 -1
  297. nautobot/ipam/signals.py +4 -2
  298. nautobot/ipam/tables.py +5 -0
  299. nautobot/ipam/templates/ipam/ipaddress_interfaces.html +1 -1
  300. nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +1 -1
  301. nautobot/ipam/templates/ipam/prefix.html +1 -0
  302. nautobot/ipam/tests/test_api.py +37 -18
  303. nautobot/ipam/tests/test_filters.py +26 -2
  304. nautobot/ipam/tests/test_models.py +9 -2
  305. nautobot/ipam/tests/test_querysets.py +1 -1
  306. nautobot/ipam/tests/test_views.py +3 -2
  307. nautobot/ipam/urls.py +2 -2
  308. nautobot/ipam/views.py +20 -34
  309. nautobot/project-static/css/base.css +21 -0
  310. nautobot/project-static/css/dark.css +11 -0
  311. nautobot/project-static/docs/404.html +894 -90
  312. nautobot/project-static/docs/apps/index.html +894 -90
  313. nautobot/project-static/docs/apps/nautobot-apps.html +894 -90
  314. nautobot/project-static/docs/assets/_mkdocstrings.css +5 -0
  315. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +1 -0
  316. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
  317. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +921 -122
  318. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +906 -103
  319. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1620 -905
  320. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +937 -146
  321. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +979 -190
  322. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +903 -101
  323. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +899 -95
  324. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +993 -195
  325. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +976 -133
  326. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +1080 -274
  327. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +1244 -336
  328. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +1729 -877
  329. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +1166 -383
  330. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2090 -1376
  331. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2248 -1424
  332. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +914 -113
  333. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +965 -165
  334. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1012 -225
  335. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +1915 -1279
  336. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +1848 -1104
  337. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +906 -103
  338. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2335 -1701
  339. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +1804 -1026
  340. nautobot/project-static/docs/development/apps/api/configuration-view.html +894 -90
  341. nautobot/project-static/docs/development/apps/api/database-backend-config.html +894 -90
  342. nautobot/project-static/docs/development/apps/api/models/django-admin.html +894 -90
  343. nautobot/project-static/docs/development/apps/api/models/global-search.html +894 -90
  344. nautobot/project-static/docs/development/apps/api/models/graphql.html +894 -90
  345. nautobot/project-static/docs/development/apps/api/models/index.html +944 -92
  346. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +894 -90
  347. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +894 -90
  348. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +894 -90
  349. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +894 -90
  350. nautobot/project-static/docs/development/apps/api/platform-features/index.html +894 -90
  351. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +894 -90
  352. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +894 -90
  353. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +894 -90
  354. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +894 -90
  355. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +894 -90
  356. nautobot/project-static/docs/development/apps/api/prometheus.html +894 -90
  357. nautobot/project-static/docs/development/apps/api/setup.html +894 -90
  358. nautobot/project-static/docs/development/apps/api/testing.html +894 -90
  359. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +894 -90
  360. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +894 -90
  361. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +894 -90
  362. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +894 -90
  363. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +894 -90
  364. nautobot/project-static/docs/development/apps/api/views/base-template.html +894 -90
  365. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +894 -90
  366. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +894 -90
  367. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +894 -90
  368. nautobot/project-static/docs/development/apps/api/views/index.html +894 -90
  369. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +894 -90
  370. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +894 -90
  371. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +894 -90
  372. nautobot/project-static/docs/development/apps/api/views/notes.html +894 -90
  373. nautobot/project-static/docs/development/apps/api/views/rest-api.html +894 -90
  374. nautobot/project-static/docs/development/apps/api/views/urls.html +894 -90
  375. nautobot/project-static/docs/development/apps/index.html +894 -90
  376. nautobot/project-static/docs/development/apps/migration/code-updates.html +894 -90
  377. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +894 -90
  378. nautobot/project-static/docs/development/apps/migration/from-v1.html +894 -90
  379. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +894 -90
  380. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +894 -90
  381. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +894 -90
  382. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +894 -90
  383. nautobot/project-static/docs/development/apps/porting-from-netbox.html +894 -90
  384. nautobot/project-static/docs/development/core/application-registry.html +894 -90
  385. nautobot/project-static/docs/development/core/best-practices.html +895 -90
  386. nautobot/project-static/docs/development/core/bootstrap-ui.html +894 -90
  387. nautobot/project-static/docs/development/core/caching.html +894 -90
  388. nautobot/project-static/docs/development/core/controllers.html +894 -90
  389. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +894 -90
  390. nautobot/project-static/docs/development/core/generic-views.html +894 -90
  391. nautobot/project-static/docs/development/core/getting-started.html +894 -90
  392. nautobot/project-static/docs/development/core/homepage.html +894 -90
  393. nautobot/project-static/docs/development/core/index.html +905 -90
  394. nautobot/project-static/docs/development/core/model-checklist.html +903 -91
  395. nautobot/project-static/docs/development/core/model-features.html +894 -90
  396. nautobot/project-static/docs/development/core/natural-keys.html +894 -90
  397. nautobot/project-static/docs/development/core/navigation-menu.html +894 -90
  398. nautobot/project-static/docs/development/core/release-checklist.html +897 -93
  399. nautobot/project-static/docs/development/core/role-internals.html +894 -90
  400. nautobot/project-static/docs/development/core/settings.html +894 -90
  401. nautobot/project-static/docs/development/core/style-guide.html +895 -91
  402. nautobot/project-static/docs/development/core/templates.html +906 -91
  403. nautobot/project-static/docs/development/core/testing.html +894 -90
  404. nautobot/project-static/docs/development/core/user-preferences.html +894 -90
  405. nautobot/project-static/docs/development/index.html +894 -90
  406. nautobot/project-static/docs/development/jobs/index.html +1271 -453
  407. nautobot/project-static/docs/development/jobs/migration/from-v1.html +894 -90
  408. nautobot/project-static/docs/index.html +9032 -13
  409. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_dark.png +0 -0
  410. nautobot/project-static/docs/media/models/cloud_aws_direct_connect_light.png +0 -0
  411. nautobot/project-static/docs/models/cloud/cloudaccount.html +15 -0
  412. nautobot/project-static/docs/models/cloud/cloudnetwork.html +15 -0
  413. nautobot/project-static/docs/models/cloud/cloudnetworkprefixassignment.html +15 -0
  414. nautobot/project-static/docs/models/cloud/cloudresourcetype.html +15 -0
  415. nautobot/project-static/docs/models/cloud/cloudservice.html +15 -0
  416. nautobot/project-static/docs/models/cloud/cloudservicenetworkassignment.html +15 -0
  417. nautobot/project-static/docs/models/dcim/module.html +15 -0
  418. nautobot/project-static/docs/models/dcim/modulebay.html +15 -0
  419. nautobot/project-static/docs/models/dcim/modulebaytemplate.html +15 -0
  420. nautobot/project-static/docs/models/dcim/moduletype.html +15 -0
  421. nautobot/project-static/docs/models/extras/metadatachoice.html +15 -0
  422. nautobot/project-static/docs/models/extras/metadatatype.html +15 -0
  423. nautobot/project-static/docs/models/extras/objectmetadata.html +15 -0
  424. nautobot/project-static/docs/models/extras/role.html +15 -0
  425. nautobot/project-static/docs/models/extras/savedview.html +15 -0
  426. nautobot/project-static/docs/models/extras/staticgroupassociation.html +15 -0
  427. nautobot/project-static/docs/models/extras/status.html +15 -0
  428. nautobot/project-static/docs/objects.inv +0 -0
  429. nautobot/project-static/docs/overview/application_stack.html +902 -91
  430. nautobot/project-static/docs/overview/design_philosophy.html +896 -92
  431. nautobot/project-static/docs/overview/index.html +13 -8228
  432. nautobot/project-static/docs/release-notes/index.html +1131 -94
  433. nautobot/project-static/docs/release-notes/version-1.0.html +894 -90
  434. nautobot/project-static/docs/release-notes/version-1.1.html +894 -90
  435. nautobot/project-static/docs/release-notes/version-1.2.html +894 -90
  436. nautobot/project-static/docs/release-notes/version-1.3.html +894 -90
  437. nautobot/project-static/docs/release-notes/version-1.4.html +894 -90
  438. nautobot/project-static/docs/release-notes/version-1.5.html +895 -91
  439. nautobot/project-static/docs/release-notes/version-1.6.html +895 -91
  440. nautobot/project-static/docs/release-notes/version-2.0.html +894 -90
  441. nautobot/project-static/docs/release-notes/version-2.1.html +894 -90
  442. nautobot/project-static/docs/release-notes/version-2.2.html +1137 -196
  443. nautobot/project-static/docs/release-notes/version-2.3.html +9954 -0
  444. nautobot/project-static/docs/requirements.txt +5 -5
  445. nautobot/project-static/docs/search/search_index.json +1 -1
  446. nautobot/project-static/docs/sitemap.xml +335 -260
  447. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  448. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +894 -90
  449. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +894 -90
  450. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +894 -90
  451. nautobot/project-static/docs/user-guide/administration/configuration/index.html +894 -90
  452. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +1025 -175
  453. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +894 -90
  454. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +894 -90
  455. nautobot/project-static/docs/user-guide/administration/guides/caching.html +894 -90
  456. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +902 -90
  457. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +894 -90
  458. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +894 -90
  459. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +894 -90
  460. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +894 -90
  461. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +894 -90
  462. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +894 -90
  463. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +894 -90
  464. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +894 -90
  465. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +946 -155
  466. nautobot/project-static/docs/user-guide/administration/installation/index.html +903 -95
  467. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +936 -124
  468. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +956 -159
  469. nautobot/project-static/docs/user-guide/administration/installation/services.html +915 -114
  470. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +910 -101
  471. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +894 -90
  472. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +894 -90
  473. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +894 -90
  474. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +894 -90
  475. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +977 -121
  476. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +894 -90
  477. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +894 -90
  478. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +894 -90
  479. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +894 -90
  480. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +894 -90
  481. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +894 -90
  482. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +894 -90
  483. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +894 -90
  484. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +894 -90
  485. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +894 -90
  486. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +894 -90
  487. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +895 -91
  488. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +894 -90
  489. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +898 -90
  490. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +897 -93
  491. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +8984 -0
  492. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +8828 -0
  493. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +8829 -0
  494. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +8828 -0
  495. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +8829 -0
  496. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +8833 -0
  497. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +8828 -0
  498. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +908 -104
  499. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +925 -107
  500. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +925 -107
  501. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +920 -102
  502. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +925 -107
  503. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +908 -104
  504. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +908 -104
  505. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +915 -107
  506. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +922 -118
  507. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +923 -119
  508. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +920 -116
  509. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +908 -104
  510. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +916 -107
  511. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +928 -110
  512. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +938 -120
  513. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +930 -108
  514. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +908 -104
  515. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +939 -121
  516. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +930 -112
  517. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +920 -116
  518. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +923 -119
  519. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +925 -117
  520. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +8828 -0
  521. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +8846 -0
  522. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +8843 -0
  523. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +8823 -0
  524. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +918 -114
  525. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +908 -104
  526. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +942 -85
  527. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +926 -108
  528. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +908 -104
  529. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +945 -88
  530. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +923 -105
  531. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +931 -127
  532. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +920 -116
  533. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +908 -104
  534. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +924 -106
  535. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +926 -108
  536. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +908 -104
  537. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +908 -104
  538. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +908 -104
  539. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +938 -90
  540. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +894 -90
  541. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +899 -91
  542. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +899 -91
  543. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +894 -90
  544. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +894 -90
  545. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +894 -90
  546. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +894 -90
  547. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +894 -90
  548. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +894 -90
  549. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +894 -90
  550. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +894 -90
  551. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +894 -90
  552. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +894 -90
  553. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +903 -98
  554. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +894 -90
  555. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +894 -90
  556. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +894 -90
  557. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +894 -90
  558. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +894 -90
  559. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +899 -91
  560. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +894 -90
  561. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +894 -90
  562. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +894 -90
  563. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +894 -90
  564. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +894 -90
  565. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +894 -90
  566. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +894 -90
  567. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +894 -90
  568. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +894 -90
  569. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +894 -90
  570. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +894 -90
  571. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +894 -90
  572. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +894 -90
  573. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/clear-view-button.png +0 -0
  574. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/cleared-view.png +0 -0
  575. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/config-table-columns-to-locations.png +0 -0
  576. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/configure-button.png +0 -0
  577. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/create-saved-view-success.png +0 -0
  578. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/current-saved-view-drop-down-menu.png +0 -0
  579. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/default-location-list-view.png +0 -0
  580. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/dropdown-button-after-new-saved-view.png +0 -0
  581. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-application-to-locations.png +0 -0
  582. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/filter-button.png +0 -0
  583. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/global-default-location-list-view.png +0 -0
  584. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/location-list-view-with-saved-views.png +0 -0
  585. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/navigation-menu.png +0 -0
  586. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-as-new-view-drop-down.png +0 -0
  587. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/save-view-modal.png +0 -0
  588. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-buttons.png +0 -0
  589. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-success.png +0 -0
  590. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view-unchecked.png +0 -0
  591. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-admin-edit-view.png +0 -0
  592. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-different-user.png +0 -0
  593. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/saved-view-modal-unchecked.png +0 -0
  594. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-button.png +0 -0
  595. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/set-as-my-default-success.png +0 -0
  596. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/unsaved-saved-view.png +0 -0
  597. nautobot/project-static/docs/user-guide/feature-guides/images/saved-views/updated-saved-view.png +0 -0
  598. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +894 -90
  599. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +894 -90
  600. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +894 -90
  601. nautobot/project-static/docs/user-guide/index.html +894 -90
  602. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +894 -90
  603. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +894 -90
  604. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +894 -90
  605. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +894 -90
  606. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +1260 -787
  607. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +897 -93
  608. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +894 -90
  609. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +894 -90
  610. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +894 -90
  611. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +894 -90
  612. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +894 -90
  613. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +894 -90
  614. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +894 -90
  615. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +894 -90
  616. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +894 -90
  617. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +898 -90
  618. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +894 -90
  619. nautobot/project-static/docs/user-guide/platform-functionality/note.html +897 -93
  620. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +9061 -0
  621. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +897 -93
  622. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +894 -90
  623. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +894 -90
  624. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +894 -90
  625. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +894 -90
  626. nautobot/project-static/docs/user-guide/platform-functionality/role.html +897 -93
  627. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9137 -0
  628. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +897 -93
  629. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +8933 -0
  630. nautobot/project-static/docs/user-guide/platform-functionality/status.html +894 -90
  631. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +894 -90
  632. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +952 -123
  633. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +894 -90
  634. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +894 -90
  635. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +894 -90
  636. nautobot/project-static/js/forms.js +71 -0
  637. nautobot/project-static/js/table_sorting_indicator.js +46 -0
  638. nautobot/project-static/js/tableconfig.js +6 -1
  639. nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +3 -0
  640. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.eot +0 -0
  641. nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/fonts/materialdesignicons-webfont.ttf +0 -0
  642. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff +0 -0
  643. nautobot/project-static/materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 +0 -0
  644. nautobot/tenancy/__init__.py +0 -1
  645. nautobot/tenancy/apps.py +1 -0
  646. nautobot/tenancy/factory.py +3 -2
  647. nautobot/tenancy/filters/__init__.py +1 -0
  648. nautobot/tenancy/forms.py +1 -1
  649. nautobot/tenancy/templates/tenancy/tenant.html +24 -20
  650. nautobot/tenancy/views.py +11 -10
  651. nautobot/users/__init__.py +0 -1
  652. nautobot/users/api/serializers.py +1 -1
  653. nautobot/users/api/views.py +4 -2
  654. nautobot/users/apps.py +3 -2
  655. nautobot/users/factory.py +3 -3
  656. nautobot/users/migrations/0010_user_default_saved_views.py +20 -0
  657. nautobot/users/models.py +12 -0
  658. nautobot/users/tests/test_filters.py +6 -3
  659. nautobot/users/urls.py +8 -0
  660. nautobot/virtualization/__init__.py +0 -1
  661. nautobot/virtualization/apps.py +1 -0
  662. nautobot/virtualization/filters.py +6 -1
  663. nautobot/virtualization/forms.py +11 -3
  664. nautobot/virtualization/graphql/types.py +2 -2
  665. nautobot/virtualization/migrations/0029_add_role_field_to_interface_models.py +27 -0
  666. nautobot/virtualization/migrations/0030_alter_virtualmachine_local_config_context_data_owner_content_type_and_more.py +67 -0
  667. nautobot/virtualization/models.py +0 -2
  668. nautobot/virtualization/tables.py +12 -8
  669. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  670. nautobot/virtualization/templates/virtualization/vminterface.html +7 -1
  671. nautobot/virtualization/templates/virtualization/vminterface_edit.html +1 -0
  672. nautobot/virtualization/tests/test_api.py +9 -4
  673. nautobot/virtualization/tests/test_filters.py +22 -0
  674. nautobot/virtualization/tests/test_models.py +7 -3
  675. nautobot/virtualization/tests/test_views.py +19 -3
  676. nautobot/virtualization/urls.py +2 -2
  677. nautobot/virtualization/views.py +10 -32
  678. {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/METADATA +21 -19
  679. {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/RECORD +684 -564
  680. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css +0 -1
  681. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css.map +0 -1
  682. nautobot/project-static/materialdesignicons-6.5.95/.github/ISSUE_TEMPLATE.md +0 -3
  683. nautobot/project-static/materialdesignicons-6.5.95/README.md +0 -25
  684. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css +0 -26654
  685. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.css.map +0 -16
  686. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css +0 -3
  687. nautobot/project-static/materialdesignicons-6.5.95/css/materialdesignicons.min.css.map +0 -16
  688. nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff +0 -0
  689. nautobot/project-static/materialdesignicons-6.5.95/fonts/materialdesignicons-webfont.woff2 +0 -0
  690. nautobot/project-static/materialdesignicons-6.5.95/package.json +0 -28
  691. nautobot/project-static/materialdesignicons-6.5.95/preview.html +0 -717
  692. nautobot/project-static/materialdesignicons-6.5.95/scss/_animated.scss +0 -27
  693. nautobot/project-static/materialdesignicons-6.5.95/scss/_core.scss +0 -10
  694. nautobot/project-static/materialdesignicons-6.5.95/scss/_extras.scss +0 -65
  695. nautobot/project-static/materialdesignicons-6.5.95/scss/_functions.scss +0 -20
  696. nautobot/project-static/materialdesignicons-6.5.95/scss/_icons.scss +0 -10
  697. nautobot/project-static/materialdesignicons-6.5.95/scss/_path.scss +0 -10
  698. nautobot/project-static/materialdesignicons-6.5.95/scss/_variables.scss +0 -6606
  699. nautobot/project-static/materialdesignicons-6.5.95/scss/materialdesignicons.scss +0 -8
  700. /nautobot/project-static/{materialdesignicons-6.5.95 → materialdesignicons-7.4.47}/LICENSE +0 -0
  701. {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/LICENSE.txt +0 -0
  702. {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/NOTICE +0 -0
  703. {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/WHEEL +0 -0
  704. {nautobot-2.2.8.dist-info → nautobot-2.3.0.dist-info}/entry_points.txt +0 -0
@@ -2,6 +2,7 @@ import logging
2
2
 
3
3
  from django.contrib import messages
4
4
  from django.contrib.auth.mixins import AccessMixin
5
+ from django.contrib.auth.models import AnonymousUser
5
6
  from django.contrib.contenttypes.models import ContentType
6
7
  from django.core.exceptions import (
7
8
  FieldDoesNotExist,
@@ -10,7 +11,7 @@ from django.core.exceptions import (
10
11
  ValidationError,
11
12
  )
12
13
  from django.db import transaction
13
- from django.db.models import ManyToManyField, ProtectedError
14
+ from django.db.models import ManyToManyField, ProtectedError, Q
14
15
  from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
15
16
  from django.http import HttpResponse
16
17
  from django.shortcuts import get_object_or_404, redirect
@@ -37,7 +38,7 @@ from nautobot.core.forms import (
37
38
  restrict_form_fields,
38
39
  )
39
40
  from nautobot.core.utils import lookup, permissions
40
- from nautobot.core.utils.requests import get_filterable_params_from_filter_params
41
+ from nautobot.core.utils.requests import get_filterable_params_from_filter_params, normalize_querydict
41
42
  from nautobot.core.views.renderers import NautobotHTMLRenderer
42
43
  from nautobot.core.views.utils import (
43
44
  get_csv_form_fields_from_serializer_class,
@@ -47,9 +48,9 @@ from nautobot.core.views.utils import (
47
48
  )
48
49
  from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
49
50
  from nautobot.extras.forms import NoteForm
50
- from nautobot.extras.models import ExportTemplate
51
+ from nautobot.extras.models import ExportTemplate, SavedView, UserSavedViewAssociation
51
52
  from nautobot.extras.tables import NoteTable, ObjectChangeTable
52
- from nautobot.extras.utils import bulk_delete_with_bulk_change_logging, remove_prefix_from_cf_key
53
+ from nautobot.extras.utils import bulk_delete_with_bulk_change_logging, get_base_template, remove_prefix_from_cf_key
53
54
 
54
55
  PERMISSIONS_ACTION_MAP = {
55
56
  "list": "view",
@@ -59,6 +60,7 @@ PERMISSIONS_ACTION_MAP = {
59
60
  "update": "change",
60
61
  "bulk_create": "add", # 3.0 TODO: remove, replaced by system Job
61
62
  "bulk_destroy": "delete",
63
+ "bulk_rename": "change",
62
64
  "bulk_update": "change",
63
65
  "changelog": "view",
64
66
  "notes": "view",
@@ -459,8 +461,11 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
459
461
 
460
462
  def get_filter_params(self, request):
461
463
  """Helper function - take request.GET and discard any parameters that are not used for queryset filtering."""
462
- filter_params = request.GET.copy()
463
- return get_filterable_params_from_filter_params(filter_params, self.non_filter_params, self.filterset_class())
464
+ params = request.GET.copy()
465
+ filter_params = get_filterable_params_from_filter_params(params, self.non_filter_params, self.filterset_class())
466
+ if params.get("saved_view") and not filter_params and not params.get("all_filters_removed"):
467
+ return SavedView.objects.get(pk=params.get("saved_view")).config.get("filter_params", {})
468
+ return filter_params
464
469
 
465
470
  def get_queryset(self):
466
471
  """
@@ -586,6 +591,9 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
586
591
  queryset = self.get_queryset()
587
592
  return queryset.all()
588
593
 
594
+ def extra_post_save_action(self, obj, form):
595
+ """Extra actions after a form is saved"""
596
+
589
597
 
590
598
  class ObjectDetailViewMixin(NautobotViewSetMixin, mixins.RetrieveModelMixin):
591
599
  """
@@ -596,11 +604,7 @@ class ObjectDetailViewMixin(NautobotViewSetMixin, mixins.RetrieveModelMixin):
596
604
  """
597
605
  Retrieve a model instance.
598
606
  """
599
- instance = self.get_object()
600
- serializer = self.get_serializer(instance)
601
-
602
- context = serializer.data
603
- context["use_new_ui"] = True
607
+ context = {"use_new_ui": True}
604
608
  return Response(context)
605
609
 
606
610
 
@@ -618,6 +622,10 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
618
622
  "page", # used by django-tables2.RequestConfig
619
623
  "per_page", # used by get_paginate_count
620
624
  "sort", # table sorting
625
+ "saved_view", # saved_view indicator pk or composite keys
626
+ "table_changes_pending", # indicator for if there is any table changes not applied to the saved view
627
+ "all_filters_removed", # indicator for if all filters have been removed from the saved view
628
+ "clear_view", # indicator for if the clear view button is clicked or not
621
629
  )
622
630
 
623
631
  def filter_queryset(self, queryset):
@@ -684,13 +692,43 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
684
692
  List the model instances.
685
693
  """
686
694
  context = {"use_new_ui": True}
695
+ queryset = self.get_queryset()
696
+ clear_view = request.GET.get("clear_view", False)
687
697
  if "export" in request.GET: # 3.0 TODO: remove, irrelevant after #4746
688
- queryset = self.get_queryset()
689
698
  model = queryset.model
690
699
  content_type = ContentType.objects.get_for_model(model)
691
700
  response = self.check_for_export(request, model, content_type)
692
701
  if response is not None:
693
702
  return response
703
+
704
+ # If the user clicks on the clear view button, we do not check for global or user defaults
705
+ if not clear_view and not request.GET.get("saved_view"):
706
+ # Check if there is a default for this view for this specific user
707
+ app_label, model_name = queryset.model._meta.label.split(".")
708
+ view_name = f"{app_label}:{model_name.lower()}_list"
709
+ user = request.user
710
+ if not isinstance(user, AnonymousUser):
711
+ try:
712
+ user_default_saved_view_pk = UserSavedViewAssociation.objects.get(
713
+ user=user, view_name=view_name
714
+ ).saved_view.pk
715
+ # Saved view should either belong to the user or be public
716
+ SavedView.objects.get(
717
+ Q(pk=user_default_saved_view_pk),
718
+ Q(owner=user) | Q(is_shared=True),
719
+ )
720
+ sv_url = reverse("extras:savedview", kwargs={"pk": user_default_saved_view_pk})
721
+ return redirect(sv_url)
722
+ except ObjectDoesNotExist:
723
+ pass
724
+
725
+ # Check if there is a global default for this view
726
+ try:
727
+ global_saved_view = SavedView.objects.get(view=view_name, is_global_default=True)
728
+ return redirect(reverse("extras:savedview", kwargs={"pk": global_saved_view.pk}))
729
+ except ObjectDoesNotExist:
730
+ pass
731
+
694
732
  return Response(context)
695
733
 
696
734
 
@@ -707,8 +745,8 @@ class ObjectDestroyViewMixin(NautobotViewSetMixin, mixins.DestroyModelMixin):
707
745
  queryset = self.get_queryset()
708
746
  try:
709
747
  with transaction.atomic():
710
- obj.delete()
711
748
  msg = f"Deleted {queryset.model._meta.verbose_name} {obj}"
749
+ obj.delete()
712
750
  self.logger.info(msg)
713
751
  messages.success(request, msg)
714
752
  self.success_url = self.get_return_url(request, obj)
@@ -734,7 +772,7 @@ class ObjectDestroyViewMixin(NautobotViewSetMixin, mixins.DestroyModelMixin):
734
772
  """
735
773
  self.obj = self.get_object()
736
774
  form_class = self.get_form_class()
737
- form = form_class(request.POST)
775
+ form = form_class(request.POST, initial=normalize_querydict(request.GET, form_class=form_class))
738
776
  if form.is_valid():
739
777
  return self.form_valid(form)
740
778
  else:
@@ -746,6 +784,18 @@ class ObjectEditViewMixin(NautobotViewSetMixin, mixins.CreateModelMixin, mixins.
746
784
  UI mixin to create or update a model instance.
747
785
  """
748
786
 
787
+ def extra_message_context(self, obj):
788
+ """
789
+ Context variables for this extra message.
790
+ """
791
+ return {}
792
+
793
+ def extra_message(self, **kwargs):
794
+ """
795
+ Append extra message at the end of create or update success message.
796
+ """
797
+ return ""
798
+
749
799
  def _process_create_or_update_form(self, form):
750
800
  """
751
801
  Helper method to create or update an object after the form is validated successfully.
@@ -765,9 +815,14 @@ class ObjectEditViewMixin(NautobotViewSetMixin, mixins.CreateModelMixin, mixins.
765
815
  msg = f'{"Created" if object_created else "Modified"} {queryset.model._meta.verbose_name}'
766
816
  self.logger.info(f"{msg} {obj} (PK: {obj.pk})")
767
817
  try:
768
- msg = format_html('{} <a href="{}">{}</a>', msg, obj.get_absolute_url(), obj)
818
+ msg = format_html(
819
+ '{} <a href="{}">{}</a>' + self.extra_message(**self.extra_message_context(obj)),
820
+ msg,
821
+ obj.get_absolute_url(),
822
+ obj,
823
+ )
769
824
  except AttributeError:
770
- msg = format_html("{} {}", msg, obj)
825
+ msg = format_html("{} {}" + self.extra_message(**self.extra_message_context(obj)), msg, obj)
771
826
  messages.success(request, msg)
772
827
  if "_addanother" in request.POST:
773
828
  # If the object has clone_fields, pre-populate a new instance of the form
@@ -800,7 +855,12 @@ class ObjectEditViewMixin(NautobotViewSetMixin, mixins.CreateModelMixin, mixins.
800
855
  """
801
856
  self.obj = self.get_object()
802
857
  form_class = self.get_form_class()
803
- form = form_class(data=request.POST, files=request.FILES, instance=self.obj)
858
+ form = form_class(
859
+ data=request.POST,
860
+ files=request.FILES,
861
+ initial=normalize_querydict(request.GET, form_class=form_class),
862
+ instance=self.obj,
863
+ )
804
864
  restrict_form_fields(form, request.user)
805
865
  if form.is_valid():
806
866
  return self.form_valid(form)
@@ -825,7 +885,12 @@ class ObjectEditViewMixin(NautobotViewSetMixin, mixins.CreateModelMixin, mixins.
825
885
  """
826
886
  self.obj = self.get_object()
827
887
  form_class = self.get_form_class()
828
- form = form_class(data=request.POST, files=request.FILES, instance=self.obj)
888
+ form = form_class(
889
+ data=request.POST,
890
+ files=request.FILES,
891
+ initial=normalize_querydict(request.GET, form_class=form_class),
892
+ instance=self.obj,
893
+ )
829
894
  restrict_form_fields(form, request.user)
830
895
  if form.is_valid():
831
896
  return self.form_valid(form)
@@ -878,21 +943,18 @@ class ObjectBulkDestroyViewMixin(NautobotViewSetMixin, BulkDestroyModelMixin):
878
943
  model = queryset.model
879
944
  # Are we deleting *all* objects in the queryset or just a selected subset?
880
945
  if request.POST.get("_all"):
881
- filter_params = self.get_filter_params(request)
882
- if not filter_params:
883
- self.pk_list = list(model.objects.only("pk").all().values_list("pk", flat=True))
884
- elif self.filterset_class is None:
885
- raise NotImplementedError("filterset_class must be defined to use _all")
886
- else:
946
+ if self.filterset_class is not None:
887
947
  self.pk_list = list(
888
- self.filterset_class(filter_params, model.objects.only("pk")).qs.values_list("pk", flat=True)
948
+ self.filterset_class(request.GET, model.objects.only("pk")).qs.values_list("pk", flat=True)
889
949
  )
950
+ else:
951
+ self.pk_list = list(model.objects.all().values_list("pk", flat=True))
890
952
  else:
891
953
  self.pk_list = list(request.POST.getlist("pk"))
892
954
  form_class = self.get_form_class(**kwargs)
893
955
  data = {}
894
956
  if "_confirm" in request.POST:
895
- form = form_class(request.POST)
957
+ form = form_class(request.POST, initial=normalize_querydict(request.GET, form_class=form_class))
896
958
  if form.is_valid():
897
959
  return self.form_valid(form)
898
960
  else:
@@ -953,7 +1015,11 @@ class ObjectBulkCreateViewMixin(NautobotViewSetMixin): # 3.0 TODO: remove, unus
953
1015
 
954
1016
  def perform_bulk_create(self, request):
955
1017
  form_class = self.get_form_class()
956
- form = form_class(request.POST, request.FILES)
1018
+ form = form_class(
1019
+ data=request.POST,
1020
+ files=request.FILES,
1021
+ initial=normalize_querydict(request.GET, form_class=form_class),
1022
+ )
957
1023
  if form.is_valid():
958
1024
  return self.form_valid(form)
959
1025
  else:
@@ -1031,6 +1097,8 @@ class ObjectBulkUpdateViewMixin(NautobotViewSetMixin, BulkUpdateModelMixin):
1031
1097
  if hasattr(form, "save_note") and callable(form.save_note):
1032
1098
  form.save_note(instance=obj, user=request.user)
1033
1099
 
1100
+ self.extra_post_save_action(obj, form)
1101
+
1034
1102
  # Enforce object-level permissions
1035
1103
  if queryset.filter(pk__in=[obj.pk for obj in updated_objects]).count() != len(updated_objects):
1036
1104
  raise ObjectDoesNotExist
@@ -1059,15 +1127,12 @@ class ObjectBulkUpdateViewMixin(NautobotViewSetMixin, BulkUpdateModelMixin):
1059
1127
 
1060
1128
  # If we are editing *all* objects in the queryset, replace the PK list with all matched objects.
1061
1129
  if request.POST.get("_all"):
1062
- filter_params = self.get_filter_params(request)
1063
- if not filter_params:
1064
- self.pk_list = list(model.objects.only("pk").all().values_list("pk", flat=True))
1065
- elif self.filterset_class is None:
1066
- raise NotImplementedError("filterset_class must be defined to use _all")
1067
- else:
1130
+ if self.filterset_class is not None:
1068
1131
  self.pk_list = list(
1069
- self.filterset_class(filter_params, model.objects.only("pk")).qs.values_list("pk", flat=True)
1132
+ self.filterset_class(request.GET, model.objects.only("pk")).qs.values_list("pk", flat=True)
1070
1133
  )
1134
+ else:
1135
+ self.pk_list = list(model.objects.all().values_list("pk", flat=True))
1071
1136
  else:
1072
1137
  self.pk_list = list(request.POST.getlist("pk"))
1073
1138
  data = {}
@@ -1101,8 +1166,10 @@ class ObjectChangeLogViewMixin(NautobotViewSetMixin):
1101
1166
 
1102
1167
  @drf_action(detail=True)
1103
1168
  def changelog(self, request, *args, **kwargs):
1169
+ model = self.get_queryset().model
1104
1170
  data = {
1105
- "base_template": self.base_template,
1171
+ "base_template": get_base_template(self.base_template, model),
1172
+ "active_tab": "changelog",
1106
1173
  }
1107
1174
  return Response(data)
1108
1175
 
@@ -1116,7 +1183,9 @@ class ObjectNotesViewMixin(NautobotViewSetMixin):
1116
1183
 
1117
1184
  @drf_action(detail=True)
1118
1185
  def notes(self, request, *args, **kwargs):
1186
+ model = self.get_queryset().model
1119
1187
  data = {
1120
- "base_template": self.base_template,
1188
+ "base_template": get_base_template(self.base_template, model),
1189
+ "active_tab": "notes",
1121
1190
  }
1122
1191
  return Response(data)
@@ -41,13 +41,14 @@ class EnhancedPage(Page):
41
41
  return page_list
42
42
 
43
43
 
44
- def get_paginate_count(request):
44
+ def get_paginate_count(request, saved_view=None):
45
45
  """
46
46
  Determine the length of a page, using the following in order:
47
47
 
48
48
  1. per_page URL query parameter
49
- 2. Saved user preference
50
- 3. PAGINATE_COUNT global setting.
49
+ 2. Saved view config
50
+ 3. Saved user preference
51
+ 4. PAGINATE_COUNT global setting.
51
52
  """
52
53
  if "per_page" in request.GET:
53
54
  try:
@@ -58,6 +59,11 @@ def get_paginate_count(request):
58
59
  except ValueError:
59
60
  pass
60
61
 
62
+ if saved_view is not None:
63
+ per_page = saved_view.config.get("pagination_count", None)
64
+ if per_page:
65
+ return per_page
66
+
61
67
  if request.user.is_authenticated:
62
68
  return request.user.get_config("pagination.per_page", config.get_settings_or_config("PAGINATE_COUNT"))
63
69
  return config.get_settings_or_config("PAGINATE_COUNT")
@@ -2,7 +2,9 @@ import logging
2
2
 
3
3
  from django.contrib import messages
4
4
  from django.contrib.contenttypes.models import ContentType
5
+ from django.core.exceptions import ObjectDoesNotExist
5
6
  from django.db.models import Q
7
+ from django.template import engines, loader
6
8
  from django_tables2 import RequestConfig
7
9
  from rest_framework import renderers
8
10
 
@@ -14,17 +16,20 @@ from nautobot.core.forms import (
14
16
  from nautobot.core.forms.forms import DynamicFilterFormSet
15
17
  from nautobot.core.templatetags.helpers import bettertitle, validated_viewname
16
18
  from nautobot.core.utils.config import get_settings_or_config
17
- from nautobot.core.utils.lookup import get_created_and_last_updated_usernames_for_model
18
19
  from nautobot.core.utils.permissions import get_permission_for_model
19
20
  from nautobot.core.utils.requests import (
20
21
  convert_querydict_to_factory_formset_acceptable_querydict,
21
22
  normalize_querydict,
22
23
  )
23
24
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
24
- from nautobot.core.views.utils import check_filter_for_display, get_csv_form_fields_from_serializer_class
25
+ from nautobot.core.views.utils import (
26
+ check_filter_for_display,
27
+ common_detail_view_context,
28
+ get_csv_form_fields_from_serializer_class,
29
+ view_changes_not_saved,
30
+ )
31
+ from nautobot.extras.models import SavedView
25
32
  from nautobot.extras.models.change_logging import ObjectChange
26
- from nautobot.extras.tables import AssociatedContactsTable
27
- from nautobot.extras.utils import get_base_template
28
33
 
29
34
 
30
35
  class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
@@ -34,6 +39,9 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
34
39
 
35
40
  # Log error messages within NautobotHTMLRenderer
36
41
  logger = logging.getLogger(__name__)
42
+ saved_view = None
43
+
44
+ exception_template_names = ["%(status_code)s.html"]
37
45
 
38
46
  def get_dynamic_filter_form(self, view, request, *args, filterset_class=None, **kwargs):
39
47
  """
@@ -44,7 +52,9 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
44
52
  filterset = None
45
53
  if filterset_class:
46
54
  filterset = filterset_class()
47
- factory_formset_params = convert_querydict_to_factory_formset_acceptable_querydict(request.GET, filterset)
55
+ factory_formset_params = convert_querydict_to_factory_formset_acceptable_querydict(
56
+ view.filter_params if view.filter_params is not None else request.GET, filterset
57
+ )
48
58
  return DynamicFilterFormSet(filterset=filterset, data=factory_formset_params)
49
59
 
50
60
  def construct_user_permissions(self, request, model):
@@ -64,14 +74,32 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
64
74
  """
65
75
  table_class = view.get_table_class()
66
76
  request = kwargs.get("request", view.request)
77
+ saved_view_pk = request.GET.get("saved_view", None)
78
+ table_changes_pending = request.GET.get("table_changes_pending", False)
67
79
  queryset = view.alter_queryset(request)
68
80
 
69
81
  if view.action in ["list", "notes", "changelog"]:
70
82
  if view.action == "list":
71
83
  permissions = kwargs.get("permissions", {})
72
- if view.request.GET.getlist("sort"):
84
+ self.saved_view = None
85
+ if saved_view_pk is not None:
86
+ try:
87
+ # We are not using .restrict(request.user, "view") here
88
+ # User should be able to see any saved view that he has the list view access to.
89
+ self.saved_view = SavedView.objects.get(pk=saved_view_pk)
90
+ except ObjectDoesNotExist:
91
+ pass
92
+ if view.request.GET.getlist("sort") or (
93
+ self.saved_view is not None and self.saved_view.config.get("sort_order")
94
+ ):
73
95
  view.hide_hierarchy_ui = True # hide tree hierarchy if custom sort is used
74
- table = table_class(queryset, user=request.user, hide_hierarchy_ui=view.hide_hierarchy_ui)
96
+ table = table_class(
97
+ queryset,
98
+ table_changes_pending=table_changes_pending,
99
+ saved_view=self.saved_view,
100
+ user=request.user,
101
+ hide_hierarchy_ui=view.hide_hierarchy_ui,
102
+ )
75
103
  if "pk" in table.base_columns and (permissions["change"] or permissions["delete"]):
76
104
  table.columns.show("pk")
77
105
  elif view.action == "notes":
@@ -93,7 +121,7 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
93
121
  # Apply the request context
94
122
  paginate = {
95
123
  "paginator_class": EnhancedPaginator,
96
- "per_page": get_paginate_count(request),
124
+ "per_page": get_paginate_count(request, self.saved_view),
97
125
  }
98
126
  max_page_size = get_settings_or_config("MAX_PAGE_SIZE")
99
127
  if max_page_size and paginate["per_page"] > max_page_size:
@@ -129,6 +157,30 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
129
157
  messages.error(request, f"Missing views for action(s) {', '.join(invalid_actions)}")
130
158
  return valid_actions
131
159
 
160
+ def get_template_context(self, data, renderer_context):
161
+ # borrowed from rest_framework's TemplateHTMLRenderer - should our html renderer be based on that class?
162
+ response = renderer_context["response"]
163
+ if response.exception:
164
+ data["status_code"] = response.status_code
165
+ return data
166
+
167
+ def get_exception_template(self, response):
168
+ # borrowed from rest_framework's TemplateHTMLRenderer - remove if switching base class
169
+ template_names = [name % {"status_code": response.status_code} for name in self.exception_template_names]
170
+
171
+ try:
172
+ # Try to find an appropriate error template
173
+ return self.resolve_template(template_names)
174
+ except Exception:
175
+ # Fall back to using eg '404 Not Found'
176
+ body = f"{response.status_code} {response.status_text.title()}"
177
+ template = engines["django"].from_string(body)
178
+ return template
179
+
180
+ def resolve_template(self, template_names):
181
+ # borrowed from rest_framework's TemplateHTMLRenderer - remove if switching base class
182
+ return loader.select_template(template_names)
183
+
132
184
  def get_context(self, data, accepted_media_type, renderer_context):
133
185
  """
134
186
  Override get_context() from BrowsableAPIRenderer to obtain the context data we need to render our templates.
@@ -157,7 +209,7 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
157
209
  display_filter_params = []
158
210
  # Compile a dictionary indicating which permissions are available to the current user for this model
159
211
  permissions = self.construct_user_permissions(request, model)
160
- if view.action in ["create", "retrieve", "update", "destroy", "changelog", "notes"]:
212
+ if view.detail or view.action == "create":
161
213
  instance = view.get_object()
162
214
  return_url = view.get_return_url(request, instance)
163
215
  else:
@@ -179,9 +231,9 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
179
231
  for field_name, values in view.filter_params.items()
180
232
  ]
181
233
  if view.filterset_form_class is not None:
182
- filter_form = view.filterset_form_class(request.GET, label_suffix="")
234
+ filter_form = view.filterset_form_class(view.filter_params, label_suffix="")
183
235
  table = self.construct_table(view, request=request, permissions=permissions)
184
- search_form = SearchForm(data=request.GET)
236
+ search_form = SearchForm(data=view.filter_params)
185
237
  elif view.action == "destroy":
186
238
  form = form_class(initial=request.GET)
187
239
  elif view.action in ["create", "update"]:
@@ -238,52 +290,56 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
238
290
  "verbose_name_plural": queryset.model._meta.verbose_name_plural,
239
291
  }
240
292
  if view.action == "retrieve":
241
- created_by, last_updated_by = get_created_and_last_updated_usernames_for_model(instance)
293
+ context.update(common_detail_view_context(request, instance))
294
+ elif view.action == "list":
295
+ # Construct valid actions for list view.
296
+ valid_actions = self.validate_action_buttons(view, request)
297
+ # Query SavedViews for dropdown button
298
+ list_url = validated_viewname(model, "list")
299
+ saved_views = None
300
+ if model.is_saved_view_model:
301
+ # We are not using .restrict(request.user, "view") here
302
+ # User should be able to see any saved view that he has the list view access to.
303
+ if request.user.has_perms(["extras.view_savedview"]):
304
+ saved_views = SavedView.objects.filter(view=list_url).order_by("name").only("pk", "name")
305
+ else:
306
+ shared_saved_views = (
307
+ SavedView.objects.filter(view=list_url, is_shared=True).order_by("name").only("pk", "name")
308
+ )
309
+ user_owned_saved_views = (
310
+ SavedView.objects.filter(view=list_url, owner=request.user).order_by("name").only("pk", "name")
311
+ )
312
+ saved_views = shared_saved_views | user_owned_saved_views
242
313
 
243
- context["created_by"] = created_by
244
- context["last_updated_by"] = last_updated_by
245
- if instance.is_contact_associable_model:
246
- paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
247
- associations = instance.associated_contacts.restrict(request.user, "view").order_by("role__name")
248
- associations_table = AssociatedContactsTable(associations, orderable=False)
249
- RequestConfig(request, paginate).configure(associations_table)
250
- associations_table.columns.show("pk")
251
- context["associated_contacts_table"] = associations_table
252
- else:
253
- context["associated_contacts_table"] = None
254
- context.update(view.get_extra_context(request, instance))
255
- else:
256
- if view.action == "list":
257
- # Construct valid actions for list view.
258
- valid_actions = self.validate_action_buttons(view, request)
259
- context.update(
260
- {
261
- "action_buttons": valid_actions,
262
- "list_url": validated_viewname(model, "list"),
263
- "title": bettertitle(model._meta.verbose_name_plural),
264
- }
265
- )
266
- elif view.action in ["create", "update"]:
267
- context.update(
268
- {
269
- "editing": instance.present_in_database,
270
- }
271
- )
272
- elif view.action == "bulk_create": # 3.0 TODO: remove, replaced by ImportObjects system Job
273
- context.update(
274
- {
275
- "active_tab": view.bulk_create_active_tab if view.bulk_create_active_tab else "csv-data",
276
- "fields": get_csv_form_fields_from_serializer_class(view.serializer_class),
277
- }
278
- )
279
- elif view.action in ["changelog", "notes"]:
280
- context.update(
281
- {
282
- "base_template": get_base_template(data.get("base_template"), model),
283
- "active_tab": view.action,
284
- }
285
- )
286
- context.update(view.get_extra_context(request, instance=None))
314
+ new_changes_not_applied = view_changes_not_saved(request, view, self.saved_view)
315
+ context.update(
316
+ {
317
+ "model": model,
318
+ "current_saved_view": self.saved_view,
319
+ "new_changes_not_applied": new_changes_not_applied,
320
+ "action_buttons": valid_actions,
321
+ "list_url": list_url,
322
+ "saved_views": saved_views,
323
+ "title": bettertitle(model._meta.verbose_name_plural),
324
+ }
325
+ )
326
+ elif view.action in ["create", "update"]:
327
+ context.update(
328
+ {
329
+ "editing": instance.present_in_database,
330
+ }
331
+ )
332
+ elif view.action == "bulk_create": # 3.0 TODO: remove, replaced by ImportObjects system Job
333
+ context.update(
334
+ {
335
+ "active_tab": view.bulk_create_active_tab if view.bulk_create_active_tab else "csv-data",
336
+ "fields": get_csv_form_fields_from_serializer_class(view.serializer_class),
337
+ }
338
+ )
339
+
340
+ # Ensure the proper inheritance of context variables is applied: the view's returned data takes priority over the viewset's get_extra_context
341
+ context.update(view.get_extra_context(request, instance))
342
+ context.update(self.get_template_context(data, renderer_context))
287
343
  return context
288
344
 
289
345
  def render(self, data, accepted_media_type=None, renderer_context=None):
@@ -291,6 +347,15 @@ class NautobotHTMLRenderer(renderers.BrowsableAPIRenderer):
291
347
  Overrode render() from BrowsableAPIRenderer to set self.template with NautobotViewSet's get_template_name() before it is rendered.
292
348
  """
293
349
  view = renderer_context["view"]
350
+ request = renderer_context["request"]
351
+ response = renderer_context["response"]
352
+
353
+ # TODO: borrowed from TemplateHTMLRenderer. Remove when switching base class
354
+ if response.exception:
355
+ template = self.get_exception_template(response)
356
+ context = self.get_context(data, accepted_media_type, renderer_context)
357
+ return template.render(context, request=request)
358
+
294
359
  # Get the corresponding template based on self.action in view.get_template_name() unless it is already specified in the Response() data.
295
360
  # See form_valid() for self.action == "bulk_create".
296
361
  self.template = data.get("template", view.get_template_name())