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
@@ -65,7 +65,10 @@ def convert_querydict_to_factory_formset_acceptable_querydict(request_querydict,
65
65
  # the filterset can handle the QueryDict conversion and we can just pass the QueryDict to the filterset
66
66
  # then use the FilterSet to de-dupe the field names
67
67
  lookup_field = re.sub(r"__\w+", "", filter_field_name)
68
- lookup_value = request_querydict.getlist(filter_field_name)
68
+ if isinstance(request_querydict, QueryDict):
69
+ lookup_value = request_querydict.getlist(filter_field_name)
70
+ else:
71
+ lookup_value = request_querydict.get(filter_field_name)
69
72
 
70
73
  query_dict.setlistdefault(lookup_field_placeholder % num, [lookup_field])
71
74
  query_dict.setlistdefault(lookup_type_placeholder % num, [filter_field_name])
@@ -7,14 +7,15 @@ import time
7
7
  from db_file_storage.views import get_file
8
8
  from django.apps import apps
9
9
  from django.conf import settings
10
+ from django.contrib import messages
10
11
  from django.contrib.auth.decorators import permission_required
11
- from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
12
+ from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin, UserPassesTestMixin
12
13
  from django.contrib.contenttypes.models import ContentType
13
14
  from django.http import HttpResponseForbidden, HttpResponseServerError, JsonResponse
14
15
  from django.shortcuts import get_object_or_404, render
15
16
  from django.template import loader, RequestContext, Template
16
17
  from django.template.exceptions import TemplateDoesNotExist
17
- from django.urls import resolve, reverse
18
+ from django.urls import NoReverseMatch, resolve, reverse
18
19
  from django.utils.encoding import smart_str
19
20
  from django.views.csrf import csrf_failure as _csrf_failure
20
21
  from django.views.decorators.csrf import requires_csrf_token
@@ -37,6 +38,7 @@ from rest_framework.response import Response
37
38
  from rest_framework.versioning import AcceptHeaderVersioning
38
39
  from rest_framework.views import APIView
39
40
 
41
+ from nautobot.core.celery import app
40
42
  from nautobot.core.constants import SEARCH_MAX_RESULTS
41
43
  from nautobot.core.forms import SearchForm
42
44
  from nautobot.core.releases import get_latest_release
@@ -125,6 +127,112 @@ class HomeView(AccessMixin, TemplateView):
125
127
  return self.render_to_response(context)
126
128
 
127
129
 
130
+ class WorkerStatusView(UserPassesTestMixin, TemplateView):
131
+ template_name = "utilities/worker_status.html"
132
+
133
+ def test_func(self):
134
+ return self.request.user.is_staff
135
+
136
+ def get(self, request, *args, **kwargs):
137
+ from nautobot.extras.models import JobResult
138
+ from nautobot.extras.tables import JobResultTable
139
+
140
+ # Sort queues but move the default queue to the front so that it appears first
141
+ def sort_queues(queue):
142
+ return (queue != settings.CELERY_TASK_DEFAULT_QUEUE, queue) # False sorts before true in Python sorted
143
+
144
+ # Sort workers on hostname first, then worker name -- celery worker names are in the form name@hostname (celery@worker.example.com)
145
+ def sort_workers(worker):
146
+ return list(reversed(worker.split("@")))
147
+
148
+ # Use a long timeout to retrieve the initial list of workers
149
+ max_timeout = 60
150
+ timeout = request.GET.get("timeout", "5")
151
+ if not timeout.isdigit():
152
+ timeout = 5
153
+ elif int(timeout) > max_timeout:
154
+ timeout = max_timeout
155
+ else:
156
+ timeout = int(timeout)
157
+ celery_inspect = app.control.inspect(timeout=timeout)
158
+
159
+ # stats() returns a dict of {worker_name: stats_dict}
160
+ worker_stats = celery_inspect.stats()
161
+
162
+ if worker_stats:
163
+ # Set explicit list of workers to speed up subsequent queries
164
+ celery_inspect = app.control.inspect(list(worker_stats.keys()), timeout=5.0)
165
+
166
+ # active() returns a dict of {worker_name: [task_dict, task_dict, ...]}
167
+ active_tasks = celery_inspect.active() or {}
168
+
169
+ # reserved() returns a dict of {worker_name: [task_dict, task_dict, ...]}
170
+ reserved_tasks = celery_inspect.reserved() or {}
171
+
172
+ # active_queues() returns a dict of {worker_name: [queue_dict, queue_dict, ...]}
173
+ active_queues = celery_inspect.active_queues() or {}
174
+ else:
175
+ # No workers were found, default to empty dicts for all commands
176
+ worker_stats = active_tasks = reserved_tasks = active_queues = {}
177
+
178
+ workers = []
179
+ for worker_name in sorted(worker_stats, key=sort_workers):
180
+ worker_details = worker_stats[worker_name]
181
+ active_task_job_results = JobResult.objects.filter(
182
+ id__in=[task["id"] for task in active_tasks.get(worker_name, [])]
183
+ ).only("name", "date_created")
184
+ reserved_task_job_results = JobResult.objects.filter(
185
+ id__in=[task["id"] for task in reserved_tasks.get(worker_name, [])]
186
+ ).only("name", "date_created")
187
+ running_tasks_table = JobResultTable(
188
+ active_task_job_results, exclude=["actions", "job_model", "summary", "user", "status"]
189
+ )
190
+ pending_tasks_table = JobResultTable(
191
+ reserved_task_job_results, exclude=["actions", "job_model", "summary", "user", "status"]
192
+ )
193
+
194
+ # Sort the worker's queue list
195
+ queues = sorted([queue["name"] for queue in active_queues.get(worker_name, [])], key=sort_queues)
196
+
197
+ workers.append(
198
+ {
199
+ "hostname": worker_name,
200
+ "running_tasks_table": running_tasks_table,
201
+ "pending_tasks_table": pending_tasks_table,
202
+ "queues": queues,
203
+ "uptime": worker_details["uptime"],
204
+ }
205
+ )
206
+
207
+ # Count the number of workers per queue
208
+ queue_worker_count = {
209
+ settings.CELERY_TASK_DEFAULT_QUEUE: set(),
210
+ }
211
+ for worker_name, task_queue_list in active_queues.items():
212
+ distinct_queues = {q["name"] for q in task_queue_list}
213
+ for queue in distinct_queues:
214
+ queue_worker_count.setdefault(queue, set())
215
+ queue_worker_count[queue].add(worker_name)
216
+
217
+ # Sort the queue_worker_count dictionary by queue name, then by worker hostname/name
218
+ # Then make the default queue the first entry
219
+ queue_worker_count = {
220
+ queue_name: sorted(queue_worker_count[queue_name], key=sort_workers)
221
+ for queue_name in sorted(queue_worker_count, key=sort_queues)
222
+ }
223
+
224
+ context = {
225
+ "worker_status": {
226
+ "max_timeout": max_timeout,
227
+ "queue_worker_count": queue_worker_count,
228
+ "timeout": timeout,
229
+ "workers": workers,
230
+ },
231
+ }
232
+
233
+ return self.render_to_response(context)
234
+
235
+
128
236
  class ThemePreviewView(LoginRequiredMixin, TemplateView):
129
237
  template_name = "utilities/theme_preview.html"
130
238
 
@@ -132,6 +240,8 @@ class ThemePreviewView(LoginRequiredMixin, TemplateView):
132
240
  return {
133
241
  "content_type": ContentType.objects.get_for_model(Status),
134
242
  "object": Status.objects.first(),
243
+ "verbose_name": Status.objects.all().model._meta.verbose_name,
244
+ "verbose_name_plural": Status.objects.all().model._meta.verbose_name_plural,
135
245
  }
136
246
 
137
247
 
@@ -177,28 +287,31 @@ class SearchView(AccessMixin, View):
177
287
  # corresponding to that URL, and finally the queryset, filterset, and table classes needed
178
288
  # to find and display the model search results.
179
289
  url = get_route_for_model(f"{label}.{modelname}", "list")
180
- view_func = resolve(reverse(url)).func
181
- # For a UIViewSet, view_func.cls gets what we need; for an ObjectListView, view_func.view_class is it.
182
- view_or_viewset = getattr(view_func, "cls", getattr(view_func, "view_class", None))
183
- queryset = view_or_viewset.queryset.restrict(request.user, "view")
184
- # For a UIViewSet, .filterset_class, for an ObjectListView, .filterset.
185
- filterset = getattr(view_or_viewset, "filterset_class", getattr(view_or_viewset, "filterset", None))
186
- # For a UIViewSet, .table_class, for an ObjectListView, .table.
187
- table = getattr(view_or_viewset, "table_class", getattr(view_or_viewset, "table", None))
188
-
189
- # Construct the results table for this object type
190
- filtered_queryset = filterset({"q": form.cleaned_data["q"]}, queryset=queryset).qs
191
- table = table(filtered_queryset, orderable=False)
192
- table.paginate(per_page=SEARCH_MAX_RESULTS)
193
-
194
- if table.page:
195
- results.append(
196
- {
197
- "name": queryset.model._meta.verbose_name_plural,
198
- "table": table,
199
- "url": f"{reverse(url)}?q={form.cleaned_data.get('q')}",
200
- }
201
- )
290
+ try:
291
+ view_func = resolve(reverse(url)).func
292
+ # For UIViewSet, view_func.cls gets what we need; for an ObjectListView, view_func.view_class is it.
293
+ view_or_viewset = getattr(view_func, "cls", getattr(view_func, "view_class", None))
294
+ queryset = view_or_viewset.queryset.restrict(request.user, "view")
295
+ # For a UIViewSet, .filterset_class, for an ObjectListView, .filterset.
296
+ filterset = getattr(view_or_viewset, "filterset_class", getattr(view_or_viewset, "filterset", None))
297
+ # For a UIViewSet, .table_class, for an ObjectListView, .table.
298
+ table = getattr(view_or_viewset, "table_class", getattr(view_or_viewset, "table", None))
299
+
300
+ # Construct the results table for this object type
301
+ filtered_queryset = filterset({"q": form.cleaned_data["q"]}, queryset=queryset).qs
302
+ table = table(filtered_queryset, orderable=False)
303
+ table.paginate(per_page=SEARCH_MAX_RESULTS)
304
+
305
+ if table.page:
306
+ results.append(
307
+ {
308
+ "name": queryset.model._meta.verbose_name_plural,
309
+ "table": table,
310
+ "url": f"{reverse(url)}?q={form.cleaned_data.get('q')}",
311
+ }
312
+ )
313
+ except NoReverseMatch:
314
+ messages.error(request, f'Missing URL "{url}" - unable to show search results for {modelname}.')
202
315
 
203
316
  return render(
204
317
  request,
@@ -5,6 +5,7 @@ import re
5
5
  from django.conf import settings
6
6
  from django.contrib import messages
7
7
  from django.contrib.auth.mixins import LoginRequiredMixin
8
+ from django.contrib.auth.models import AnonymousUser
8
9
  from django.contrib.contenttypes.models import ContentType
9
10
  from django.core.exceptions import (
10
11
  FieldDoesNotExist,
@@ -12,10 +13,11 @@ from django.core.exceptions import (
12
13
  ValidationError,
13
14
  )
14
15
  from django.db import IntegrityError, transaction
15
- from django.db.models import ManyToManyField, ProtectedError
16
+ from django.db.models import ManyToManyField, ProtectedError, Q
16
17
  from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
17
- from django.http import HttpResponse, JsonResponse
18
+ from django.http import HttpResponse
18
19
  from django.shortcuts import get_object_or_404, redirect, render
20
+ from django.urls import reverse
19
21
  from django.utils.encoding import iri_to_uri
20
22
  from django.utils.html import format_html
21
23
  from django.utils.http import url_has_allowed_host_and_scheme
@@ -38,7 +40,6 @@ from nautobot.core.forms import (
38
40
  from nautobot.core.forms.forms import DynamicFilterFormSet
39
41
  from nautobot.core.templatetags.helpers import bettertitle, validated_viewname
40
42
  from nautobot.core.utils.config import get_settings_or_config
41
- from nautobot.core.utils.lookup import get_created_and_last_updated_usernames_for_model
42
43
  from nautobot.core.utils.permissions import get_permission_for_model
43
44
  from nautobot.core.utils.requests import (
44
45
  convert_querydict_to_factory_formset_acceptable_querydict,
@@ -49,14 +50,15 @@ from nautobot.core.views.mixins import GetReturnURLMixin, ObjectPermissionRequir
49
50
  from nautobot.core.views.paginator import EnhancedPaginator, get_paginate_count
50
51
  from nautobot.core.views.utils import (
51
52
  check_filter_for_display,
53
+ common_detail_view_context,
52
54
  get_csv_form_fields_from_serializer_class,
53
55
  handle_protectederror,
54
56
  import_csv_helper,
55
57
  prepare_cloned_fields,
58
+ view_changes_not_saved,
56
59
  )
57
60
  from nautobot.extras.context_managers import deferred_change_logging_for_bulk_operation
58
- from nautobot.extras.models import ContactAssociation, ExportTemplate
59
- from nautobot.extras.tables import AssociatedContactsTable
61
+ from nautobot.extras.models import ExportTemplate, SavedView, UserSavedViewAssociation
60
62
  from nautobot.extras.utils import bulk_delete_with_bulk_change_logging, remove_prefix_from_cf_key
61
63
 
62
64
 
@@ -111,53 +113,17 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
111
113
  Generic GET handler for accessing an object.
112
114
  """
113
115
  instance = get_object_or_404(self.queryset, **kwargs)
114
- # Get the ObjectChange records to populate the advanced tab information
115
- created_by, last_updated_by = get_created_and_last_updated_usernames_for_model(instance)
116
-
117
- # TODO: this feels inelegant - should the tabs lookup be a dedicated endpoint rather than piggybacking
118
- # on the object-retrieve endpoint?
119
- # TODO: similar functionality probably needed in NautobotUIViewSet as well, not currently present
120
- if request.GET.get("viewconfig", None) == "true":
121
- # TODO: we shouldn't be importing a private-named function from another module. Should it be renamed?
122
- from nautobot.extras.templatetags.plugins import _get_registered_content
123
-
124
- temp_fake_context = {
125
- "object": instance,
126
- "request": request,
127
- "settings": {},
128
- "csrf_token": "",
129
- "perms": {},
130
- }
116
+ content_type = ContentType.objects.get_for_model(self.queryset.model)
117
+ context = {
118
+ "object": instance,
119
+ "content_type": content_type,
120
+ "verbose_name": self.queryset.model._meta.verbose_name,
121
+ "verbose_name_plural": self.queryset.model._meta.verbose_name_plural,
122
+ **common_detail_view_context(request, instance),
123
+ **self.get_extra_context(request, instance),
124
+ }
131
125
 
132
- plugin_tabs = _get_registered_content(instance, "detail_tabs", temp_fake_context, return_html=False)
133
- resp = {"tabs": plugin_tabs}
134
- return JsonResponse(resp)
135
- else:
136
- content_type = ContentType.objects.get_for_model(self.queryset.model)
137
- context = {
138
- "object": instance,
139
- "content_type": content_type,
140
- "verbose_name": self.queryset.model._meta.verbose_name,
141
- "verbose_name_plural": self.queryset.model._meta.verbose_name_plural,
142
- "created_by": created_by,
143
- "last_updated_by": last_updated_by,
144
- **self.get_extra_context(request, instance),
145
- }
146
- if instance.is_contact_associable_model:
147
- paginate = {"paginator_class": EnhancedPaginator, "per_page": get_paginate_count(request)}
148
- associations = (
149
- ContactAssociation.objects.filter(
150
- associated_object_id=instance.id,
151
- associated_object_type=content_type,
152
- )
153
- .restrict(request.user, "view")
154
- .order_by("role__name")
155
- )
156
- associations_table = AssociatedContactsTable(associations, orderable=False)
157
- RequestConfig(request, paginate).configure(associations_table)
158
- associations_table.columns.show("pk")
159
- context["associated_contacts_table"] = associations_table
160
- return render(request, self.get_template_name(), context)
126
+ return render(request, self.get_template_name(), context)
161
127
 
162
128
 
163
129
  class ObjectListView(ObjectPermissionRequiredMixin, View):
@@ -184,12 +150,19 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
184
150
  "page", # used by django-tables2.RequestConfig
185
151
  "per_page", # used by get_paginate_count
186
152
  "sort", # table sorting
153
+ "saved_view", # saved_view indicator pk or composite keys
154
+ "table_changes_pending", # indicator for if there is any table changes not applied to the saved view
155
+ "all_filters_removed", # indicator for if all filters have been removed from the saved view
156
+ "clear_view", # indicator for if the clear view button is clicked or not
187
157
  )
188
158
 
189
159
  def get_filter_params(self, request):
190
160
  """Helper function - take request.GET and discard any parameters that are not used for queryset filtering."""
191
- filter_params = request.GET.copy()
192
- return get_filterable_params_from_filter_params(filter_params, self.non_filter_params, self.filterset())
161
+ params = request.GET.copy()
162
+ filter_params = get_filterable_params_from_filter_params(params, self.non_filter_params, self.filterset())
163
+ if params.get("saved_view") and not filter_params and not params.get("all_filters_removed"):
164
+ return SavedView.objects.get(pk=params.get("saved_view")).config.get("filter_params", {})
165
+ return filter_params
193
166
 
194
167
  def get_required_permission(self):
195
168
  return get_permission_for_model(self.queryset.model, "view")
@@ -225,10 +198,41 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
225
198
  model = self.queryset.model
226
199
  content_type = ContentType.objects.get_for_model(model)
227
200
 
201
+ filter_params = request.GET
202
+ user = request.user
228
203
  display_filter_params = []
229
204
  dynamic_filter_form = None
230
205
  filter_form = None
231
206
  hide_hierarchy_ui = False
207
+ clear_view = request.GET.get("clear_view", False)
208
+
209
+ # If the user clicks on the clear view button, we do not check for global or user defaults
210
+ if not clear_view and not request.GET.get("saved_view"):
211
+ # Check if there is a default for this view for this specific user
212
+ app_label, model_name = model._meta.label.split(".")
213
+ view_name = f"{app_label}:{model_name.lower()}_list"
214
+
215
+ if not isinstance(user, AnonymousUser):
216
+ try:
217
+ user_default_saved_view_pk = UserSavedViewAssociation.objects.get(
218
+ user=user, view_name=view_name
219
+ ).saved_view.pk
220
+ # Saved view should either belong to the user or be public
221
+ SavedView.objects.get(
222
+ Q(pk=user_default_saved_view_pk),
223
+ Q(owner=user) | Q(is_shared=True),
224
+ )
225
+ sv_url = reverse("extras:savedview", kwargs={"pk": user_default_saved_view_pk})
226
+ return redirect(sv_url)
227
+ except ObjectDoesNotExist:
228
+ pass
229
+
230
+ # Check if there is a global default for this view
231
+ try:
232
+ global_saved_view = SavedView.objects.get(view=view_name, is_global_default=True)
233
+ return redirect(reverse("extras:savedview", kwargs={"pk": global_saved_view.pk}))
234
+ except ObjectDoesNotExist:
235
+ pass
232
236
 
233
237
  if self.filterset:
234
238
  filter_params = self.get_filter_params(request)
@@ -254,7 +258,7 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
254
258
 
255
259
  if request.GET:
256
260
  factory_formset_params = convert_querydict_to_factory_formset_acceptable_querydict(
257
- request.GET, filterset
261
+ filter_params, filterset
258
262
  )
259
263
  dynamic_filter_form = DynamicFilterFormSet(filterset=filterset, data=factory_formset_params)
260
264
  else:
@@ -296,18 +300,50 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
296
300
 
297
301
  table = None
298
302
  table_config_form = None
303
+ current_saved_view = None
304
+ current_saved_view_pk = self.request.GET.get("saved_view", None)
305
+ list_url = validated_viewname(model, "list")
306
+ # We are not using .restrict(request.user, "view") here
307
+ # User should be able to see any saved view that he has the list view access to.
308
+ if user.has_perms(["extras.view_savedview"]):
309
+ saved_views = SavedView.objects.filter(view=list_url).order_by("name").only("pk", "name")
310
+ else:
311
+ shared_saved_views = (
312
+ SavedView.objects.filter(view=list_url, is_shared=True).order_by("name").only("pk", "name")
313
+ )
314
+ user_owned_saved_views = (
315
+ SavedView.objects.filter(view=list_url, owner=user).order_by("name").only("pk", "name")
316
+ )
317
+ saved_views = shared_saved_views | user_owned_saved_views
299
318
  if self.table:
300
319
  # Construct the objects table
301
- if self.request.GET.getlist("sort"):
320
+ if current_saved_view_pk:
321
+ try:
322
+ # We are not using .restrict(request.user, "view") here
323
+ # User should be able to see any saved view that he has the list view access to.
324
+ current_saved_view = SavedView.objects.get(view=list_url, pk=current_saved_view_pk)
325
+ except ObjectDoesNotExist:
326
+ messages.error(request, f"Saved view {current_saved_view_pk} not found")
327
+ if self.request.GET.getlist("sort") or (
328
+ current_saved_view is not None and current_saved_view.config.get("sort_order")
329
+ ):
302
330
  hide_hierarchy_ui = True # hide tree hierarchy if custom sort is used
303
- table = self.table(self.queryset, user=request.user, hide_hierarchy_ui=hide_hierarchy_ui)
331
+ table_changes_pending = self.request.GET.get("table_changes_pending", False)
332
+
333
+ table = self.table(
334
+ self.queryset,
335
+ table_changes_pending=table_changes_pending,
336
+ saved_view=current_saved_view,
337
+ user=request.user,
338
+ hide_hierarchy_ui=hide_hierarchy_ui,
339
+ )
304
340
  if "pk" in table.base_columns and (permissions["change"] or permissions["delete"]):
305
341
  table.columns.show("pk")
306
342
 
307
343
  # Apply the request context
308
344
  paginate = {
309
345
  "paginator_class": EnhancedPaginator,
310
- "per_page": get_paginate_count(request),
346
+ "per_page": get_paginate_count(request, current_saved_view),
311
347
  }
312
348
  RequestConfig(request, paginate).configure(table)
313
349
  table_config_form = TableConfigForm(table=table)
@@ -320,10 +356,13 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
320
356
 
321
357
  # For the search form field, use a custom placeholder.
322
358
  q_placeholder = "Search " + bettertitle(model._meta.verbose_name_plural)
323
- search_form = SearchForm(data=request.GET, q_placeholder=q_placeholder)
359
+ search_form = SearchForm(data=filter_params, q_placeholder=q_placeholder)
324
360
 
325
361
  valid_actions = self.validate_action_buttons(request)
326
362
 
363
+ # Query SavedViews for dropdown button
364
+ new_changes_not_applied = view_changes_not_saved(request, self, current_saved_view)
365
+
327
366
  context = {
328
367
  "content_type": content_type,
329
368
  "table": table,
@@ -334,8 +373,12 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
334
373
  "filter_form": filter_form,
335
374
  "dynamic_filter_form": dynamic_filter_form,
336
375
  "search_form": search_form,
337
- "list_url": validated_viewname(model, "list"),
376
+ "list_url": list_url,
338
377
  "title": bettertitle(model._meta.verbose_name_plural),
378
+ "new_changes_not_applied": new_changes_not_applied,
379
+ "current_saved_view": current_saved_view,
380
+ "saved_views": saved_views,
381
+ "model": model,
339
382
  }
340
383
 
341
384
  # `extra_context()` would require `request` access, however `request` parameter cannot simply be
@@ -439,7 +482,12 @@ class ObjectEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
439
482
  def post(self, request, *args, **kwargs):
440
483
  logger = logging.getLogger(__name__ + ".ObjectEditView")
441
484
  obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
442
- form = self.model_form(data=request.POST, files=request.FILES, instance=obj)
485
+ form = self.model_form(
486
+ data=request.POST,
487
+ files=request.FILES,
488
+ initial=normalize_querydict(request.GET, form_class=self.model_form),
489
+ instance=obj,
490
+ )
443
491
  restrict_form_fields(form, request.user)
444
492
 
445
493
  if form.is_valid():
@@ -538,6 +586,7 @@ class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
538
586
 
539
587
  if form.is_valid():
540
588
  logger.debug("Form validation was successful")
589
+ msg = f"Deleted {self.queryset.model._meta.verbose_name} {obj}"
541
590
 
542
591
  try:
543
592
  obj.delete()
@@ -546,7 +595,6 @@ class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
546
595
  handle_protectederror([obj], request, e)
547
596
  return redirect(obj.get_absolute_url())
548
597
 
549
- msg = f"Deleted {self.queryset.model._meta.verbose_name} {obj}"
550
598
  logger.info(msg)
551
599
  messages.success(request, msg)
552
600
 
@@ -752,7 +800,9 @@ class ObjectImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
752
800
 
753
801
  related_obj_pks = []
754
802
  for i, rel_obj_data in enumerate(data.get(field_name, [])):
755
- f = related_object_form(obj, rel_obj_data)
803
+ # add parent object key to related object data
804
+ rel_obj_data[obj._meta.verbose_name.replace(" ", "_")] = str(obj.pk)
805
+ f = related_object_form(rel_obj_data)
756
806
 
757
807
  for subfield_name, field in f.fields.items():
758
808
  if subfield_name not in rel_obj_data and hasattr(field, "initial"):
@@ -1210,7 +1260,7 @@ class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
1210
1260
  Delete objects in bulk.
1211
1261
 
1212
1262
  queryset: Custom queryset to use when retrieving objects (e.g. to select related objects)
1213
- filter: FilterSet to apply when deleting by QuerySet
1263
+ filterset: FilterSet to apply when deleting by QuerySet
1214
1264
  table: The table used to display devices being deleted
1215
1265
  form: The form class used to delete objects in bulk
1216
1266
  template_name: The name of the template
@@ -1336,7 +1386,7 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
1336
1386
  return get_permission_for_model(self.queryset.model, "add")
1337
1387
 
1338
1388
  def get(self, request):
1339
- form = self.form(initial=request.GET)
1389
+ form = self.form(initial=normalize_querydict(request.GET, form_class=self.form))
1340
1390
  model_form = self.model_form(request.GET)
1341
1391
 
1342
1392
  return render(
@@ -1352,8 +1402,8 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
1352
1402
 
1353
1403
  def post(self, request):
1354
1404
  logger = logging.getLogger(__name__ + ".ComponentCreateView")
1355
- form = self.form(request.POST, initial=request.GET)
1356
- model_form = self.model_form(request.POST)
1405
+ form = self.form(request.POST, initial=normalize_querydict(request.GET, form_class=self.form))
1406
+ model_form = self.model_form(request.POST, initial=normalize_querydict(request.GET, form_class=self.model_form))
1357
1407
 
1358
1408
  if form.is_valid():
1359
1409
  new_components = []
@@ -1368,7 +1418,9 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
1368
1418
  data["label"] = label
1369
1419
  if hasattr(form, "get_iterative_data"):
1370
1420
  data.update(form.get_iterative_data(i))
1371
- component_form = self.model_form(data)
1421
+ component_form = self.model_form(
1422
+ data, initial=normalize_querydict(request.GET, form_class=self.model_form)
1423
+ )
1372
1424
 
1373
1425
  if component_form.is_valid():
1374
1426
  new_components.append(component_form)