nautobot 3.0.0a2__py3-none-any.whl → 3.0.0a3__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 (420) hide show
  1. nautobot/apps/choices.py +0 -2
  2. nautobot/apps/filters.py +7 -9
  3. nautobot/apps/models.py +2 -2
  4. nautobot/apps/ui.py +9 -1
  5. nautobot/circuits/filters.py +3 -2
  6. nautobot/circuits/navigation.py +3 -2
  7. nautobot/circuits/templates/circuits/circuit.html +1 -1
  8. nautobot/circuits/templates/circuits/circuit_create.html +3 -3
  9. nautobot/circuits/templates/circuits/circuittermination.html +1 -1
  10. nautobot/circuits/templates/circuits/circuittermination_create.html +9 -24
  11. nautobot/circuits/templates/circuits/circuittype.html +1 -1
  12. nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +6 -6
  13. nautobot/circuits/templates/circuits/inc/speed_widget.html +12 -12
  14. nautobot/circuits/templates/circuits/providernetwork.html +1 -1
  15. nautobot/circuits/tests/integration/test_circuit.py +10 -13
  16. nautobot/cloud/filters.py +1 -1
  17. nautobot/cloud/navigation.py +3 -2
  18. nautobot/core/api/schema.py +1 -1
  19. nautobot/core/api/serializers.py +6 -1
  20. nautobot/core/api/urls.py +1 -0
  21. nautobot/core/api/views.py +8 -0
  22. nautobot/core/apps/__init__.py +11 -10
  23. nautobot/core/celery/__init__.py +3 -5
  24. nautobot/core/checks.py +46 -0
  25. nautobot/core/cli/bootstrap_v3_to_v5.py +70 -1
  26. nautobot/core/cli/migrate_deprecated_templates.py +200 -0
  27. nautobot/core/constants.py +3 -0
  28. nautobot/core/context_processors.py +9 -1
  29. nautobot/core/forms/forms.py +1 -1
  30. nautobot/core/jobs/__init__.py +6 -3
  31. nautobot/core/jobs/groups.py +31 -1
  32. nautobot/core/management/commands/generate_test_data.py +28 -9
  33. nautobot/core/models/generics.py +9 -1
  34. nautobot/core/models/tree_queries.py +10 -5
  35. nautobot/core/settings.py +18 -12
  36. nautobot/core/settings.yaml +13 -7
  37. nautobot/core/signals.py +12 -1
  38. nautobot/core/tables.py +13 -6
  39. nautobot/core/templates/40x.html +1 -1
  40. nautobot/core/templates/500.html +2 -2
  41. nautobot/core/templates/admin/config/config.html +12 -12
  42. nautobot/core/templates/admin/index.html +3 -3
  43. nautobot/core/templates/buttons/export.html +1 -1
  44. nautobot/core/templates/components/button/dropdown.html +5 -3
  45. nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
  46. nautobot/core/templates/components/panel/panel.html +3 -3
  47. nautobot/core/templates/components/tab/content_wrapper.html +2 -3
  48. nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +1 -1
  49. nautobot/core/templates/echarts/echarts.html +1 -1
  50. nautobot/core/templates/generic/object_bulk_add_component.html +2 -1
  51. nautobot/core/templates/generic/object_bulk_create.html +4 -3
  52. nautobot/core/templates/generic/object_bulk_destroy.html +3 -3
  53. nautobot/core/templates/generic/object_bulk_remove.html +2 -2
  54. nautobot/core/templates/generic/object_bulk_update.html +5 -4
  55. nautobot/core/templates/generic/object_create.html +5 -4
  56. nautobot/core/templates/generic/object_import.html +2 -1
  57. nautobot/core/templates/generic/object_list.html +12 -4
  58. nautobot/core/templates/generic/object_notes.html +5 -3
  59. nautobot/core/templates/generic/object_retrieve.html +2 -3
  60. nautobot/core/templates/graphene/graphiql.html +7 -7
  61. nautobot/core/templates/home.html +1 -1
  62. nautobot/core/templates/import_success.html +2 -1
  63. nautobot/core/templates/inc/computed_fields/panel_data.html +1 -1
  64. nautobot/core/templates/inc/created_updated.html +7 -3
  65. nautobot/core/templates/inc/custom_fields/panel_data.html +1 -1
  66. nautobot/core/templates/inc/form_static_field.html +6 -0
  67. nautobot/core/templates/inc/header.html +1 -1
  68. nautobot/core/templates/inc/image_attachments.html +2 -1
  69. nautobot/core/templates/inc/nav_menu.html +2 -1
  70. nautobot/core/templates/inc/search_panel.html +4 -4
  71. nautobot/core/templates/login.html +4 -2
  72. nautobot/core/templates/nautobot_config.py.j2 +6 -5
  73. nautobot/core/templates/redoc_ui.html +7 -0
  74. nautobot/core/templates/search.html +1 -1
  75. nautobot/core/templates/swagger_ui.html +17 -3
  76. nautobot/core/templates/system_jobs/import_objects.html +1 -2
  77. nautobot/core/templates/utilities/confirmation_form.html +2 -2
  78. nautobot/core/templates/utilities/obj_table.html +10 -2
  79. nautobot/core/templates/utilities/render_field.html +7 -7
  80. nautobot/core/templates/utilities/render_jinja2.html +2 -2
  81. nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +4 -4
  82. nautobot/core/templates/utilities/theme_preview.html +16 -3
  83. nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
  84. nautobot/core/templatetags/helpers.py +52 -6
  85. nautobot/core/testing/api.py +68 -9
  86. nautobot/core/testing/filters.py +0 -23
  87. nautobot/core/testing/integration.py +23 -10
  88. nautobot/core/testing/mixins.py +2 -0
  89. nautobot/core/testing/views.py +4 -0
  90. nautobot/core/tests/integration/test_app_home.py +34 -30
  91. nautobot/core/tests/integration/test_app_navbar.py +3 -0
  92. nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
  93. nautobot/core/tests/runner.py +9 -1
  94. nautobot/core/tests/test_api.py +5 -3
  95. nautobot/core/tests/test_breadcrumbs.py +6 -7
  96. nautobot/core/tests/test_checks.py +28 -0
  97. nautobot/core/tests/test_cli.py +40 -0
  98. nautobot/core/tests/test_config.py +2 -1
  99. nautobot/core/tests/test_forms.py +55 -13
  100. nautobot/core/tests/test_jobs.py +75 -1
  101. nautobot/core/tests/test_nautobot_server.py +2 -0
  102. nautobot/core/tests/test_navigations.py +76 -1
  103. nautobot/core/tests/test_patch_social_django.py +42 -0
  104. nautobot/core/tests/test_tables.py +3 -1
  105. nautobot/core/tests/test_templatetags_helpers.py +53 -13
  106. nautobot/core/tests/test_templatetags_ui_framework.py +4 -4
  107. nautobot/core/tests/test_tree_queries.py +14 -1
  108. nautobot/core/tests/test_ui.py +1 -1
  109. nautobot/core/tests/test_utils.py +31 -4
  110. nautobot/core/tests/test_views.py +159 -31
  111. nautobot/core/ui/breadcrumbs.py +2 -12
  112. nautobot/core/ui/choices.py +142 -10
  113. nautobot/core/ui/constants.py +76 -12
  114. nautobot/core/ui/object_detail.py +92 -12
  115. nautobot/core/urls.py +12 -1
  116. nautobot/core/utils/cache.py +2 -1
  117. nautobot/core/utils/filtering.py +17 -17
  118. nautobot/core/utils/lookup.py +3 -8
  119. nautobot/core/utils/module_loading.py +21 -0
  120. nautobot/core/utils/patch_social_django.py +128 -0
  121. nautobot/core/views/__init__.py +38 -1
  122. nautobot/core/views/generic.py +3 -3
  123. nautobot/core/views/mixins.py +15 -3
  124. nautobot/core/views/renderers.py +2 -0
  125. nautobot/core/views/viewsets.py +2 -1
  126. nautobot/data_validation/apps.py +1 -5
  127. nautobot/data_validation/custom_validators.py +4 -4
  128. nautobot/data_validation/filters.py +1 -1
  129. nautobot/data_validation/forms.py +40 -0
  130. nautobot/data_validation/migrations/0001_initial.py +0 -7
  131. nautobot/data_validation/migrations/0002_data_migration_from_app.py +0 -12
  132. nautobot/data_validation/models.py +16 -7
  133. nautobot/data_validation/navigation.py +8 -1
  134. nautobot/data_validation/tables.py +12 -5
  135. nautobot/data_validation/templates/data_validation/datacompliance_tab.html +1 -0
  136. nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
  137. nautobot/data_validation/tests/__init__.py +2 -2
  138. nautobot/data_validation/tests/migrations/test_migrations.py +83 -3
  139. nautobot/data_validation/tests/test_data_compliance_rules.py +12 -7
  140. nautobot/data_validation/tests/test_filters.py +8 -6
  141. nautobot/data_validation/tests/test_models.py +15 -0
  142. nautobot/data_validation/tests/test_views.py +190 -32
  143. nautobot/data_validation/urls.py +2 -5
  144. nautobot/data_validation/views.py +73 -40
  145. nautobot/dcim/api/serializers.py +0 -13
  146. nautobot/dcim/apps.py +4 -0
  147. nautobot/dcim/choices.py +16 -0
  148. nautobot/dcim/custom_validators.py +84 -0
  149. nautobot/dcim/filter_mixins.py +353 -4
  150. nautobot/dcim/{filters/__init__.py → filters.py} +2 -35
  151. nautobot/dcim/forms.py +1 -1
  152. nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
  153. nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
  154. nautobot/dcim/models/device_components.py +81 -68
  155. nautobot/dcim/models/devices.py +13 -16
  156. nautobot/dcim/navigation.py +7 -6
  157. nautobot/dcim/tables/devices.py +3 -0
  158. nautobot/dcim/tables/template_code.py +14 -14
  159. nautobot/dcim/templates/dcim/cable.html +2 -61
  160. nautobot/dcim/templates/dcim/cable_connect.html +28 -112
  161. nautobot/dcim/templates/dcim/cable_edit.html +2 -5
  162. nautobot/dcim/templates/dcim/cable_retrieve.html +61 -0
  163. nautobot/dcim/templates/dcim/cable_trace.html +1 -3
  164. nautobot/dcim/templates/dcim/cable_update.html +5 -0
  165. nautobot/dcim/templates/dcim/consoleport.html +6 -5
  166. nautobot/dcim/templates/dcim/consoleserverport.html +6 -5
  167. nautobot/dcim/templates/dcim/device/config.html +2 -2
  168. nautobot/dcim/templates/dcim/device/consoleports.html +1 -1
  169. nautobot/dcim/templates/dcim/device/consoleserverports.html +1 -1
  170. nautobot/dcim/templates/dcim/device/devicebays.html +1 -1
  171. nautobot/dcim/templates/dcim/device/frontports.html +1 -1
  172. nautobot/dcim/templates/dcim/device/interfaces.html +1 -1
  173. nautobot/dcim/templates/dcim/device/inventory.html +1 -1
  174. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +1 -1
  175. nautobot/dcim/templates/dcim/device/modulebays.html +1 -1
  176. nautobot/dcim/templates/dcim/device/poweroutlets.html +1 -1
  177. nautobot/dcim/templates/dcim/device/powerports.html +1 -1
  178. nautobot/dcim/templates/dcim/device/rearports.html +1 -1
  179. nautobot/dcim/templates/dcim/device/status.html +8 -8
  180. nautobot/dcim/templates/dcim/device/wireless.html +1 -1
  181. nautobot/dcim/templates/dcim/device.html +1 -1
  182. nautobot/dcim/templates/dcim/device_component_add.html +2 -2
  183. nautobot/dcim/templates/dcim/device_create.html +5 -3
  184. nautobot/dcim/templates/dcim/device_interface_delete.html +1 -1
  185. nautobot/dcim/templates/dcim/device_list.html +73 -10
  186. nautobot/dcim/templates/dcim/devicebay_populate.html +2 -2
  187. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  188. nautobot/dcim/templates/dcim/devicetype_component_add.html +2 -2
  189. nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +14 -0
  190. nautobot/dcim/templates/dcim/frontport.html +9 -8
  191. nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
  192. nautobot/dcim/templates/dcim/interface.html +26 -6
  193. nautobot/dcim/templates/dcim/interface_bulk_delete.html +1 -1
  194. nautobot/dcim/templates/dcim/inventoryitem_add.html +3 -1
  195. nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
  196. nautobot/dcim/templates/dcim/inventoryitem_edit.html +3 -1
  197. nautobot/dcim/templates/dcim/location_retrieve.html +1 -242
  198. nautobot/dcim/templates/dcim/module/base.html +49 -9
  199. nautobot/dcim/templates/dcim/module_list.html +57 -8
  200. nautobot/dcim/templates/dcim/modulefamily_retrieve.html +1 -1
  201. nautobot/dcim/templates/dcim/moduletype_retrieve.html +49 -9
  202. nautobot/dcim/templates/dcim/platform_create.html +1 -1
  203. nautobot/dcim/templates/dcim/powerfeed.html +1 -1
  204. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  205. nautobot/dcim/templates/dcim/powerport.html +5 -4
  206. nautobot/dcim/templates/dcim/rack_elevation_list.html +16 -4
  207. nautobot/dcim/templates/dcim/rack_retrieve.html +33 -15
  208. nautobot/dcim/templates/dcim/rearport.html +7 -6
  209. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  210. nautobot/dcim/templates/dcim/virtualchassis_add_member.html +16 -14
  211. nautobot/dcim/templates/dcim/virtualchassis_update.html +14 -6
  212. nautobot/dcim/tests/integration/test_controller.py +1 -0
  213. nautobot/dcim/tests/test_api.py +8 -0
  214. nautobot/dcim/tests/test_custom_validators.py +229 -0
  215. nautobot/dcim/tests/test_filters.py +12 -6
  216. nautobot/dcim/tests/test_models.py +63 -4
  217. nautobot/dcim/tests/test_views.py +63 -22
  218. nautobot/dcim/urls.py +64 -21
  219. nautobot/dcim/utils.py +3 -3
  220. nautobot/dcim/views.py +547 -273
  221. nautobot/extras/api/views.py +9 -1
  222. nautobot/extras/choices.py +2 -13
  223. nautobot/extras/{filters/mixins.py → filter_mixins.py} +1 -1
  224. nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +42 -6
  225. nautobot/extras/{filters/__init__.py → filters.py} +14 -46
  226. nautobot/extras/forms/forms.py +5 -13
  227. nautobot/extras/forms/mixins.py +0 -41
  228. nautobot/extras/management/__init__.py +9 -0
  229. nautobot/extras/migrations/0127_approval_workflow_models.py +6 -6
  230. nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
  231. nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
  232. nautobot/extras/models/__init__.py +1 -2
  233. nautobot/extras/models/approvals.py +22 -13
  234. nautobot/extras/models/contacts.py +2 -0
  235. nautobot/extras/models/groups.py +44 -5
  236. nautobot/extras/models/jobs.py +59 -1
  237. nautobot/extras/models/mixins.py +28 -0
  238. nautobot/extras/models/models.py +13 -0
  239. nautobot/extras/models/secrets.py +1 -0
  240. nautobot/extras/models/statuses.py +0 -15
  241. nautobot/extras/navigation.py +13 -9
  242. nautobot/extras/plugins/__init__.py +33 -55
  243. nautobot/extras/plugins/tables.py +3 -3
  244. nautobot/extras/plugins/urls.py +2 -21
  245. nautobot/extras/plugins/utils.py +1 -33
  246. nautobot/extras/plugins/views.py +0 -4
  247. nautobot/extras/signals.py +20 -19
  248. nautobot/extras/tables.py +52 -68
  249. nautobot/extras/templates/extras/approval_dashboard.html +7 -5
  250. nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +4 -2
  251. nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +20 -12
  252. nautobot/extras/templates/extras/computedfield.html +1 -1
  253. nautobot/extras/templates/extras/configcontext.html +1 -1
  254. nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
  255. nautobot/extras/templates/extras/customfield.html +1 -1
  256. nautobot/extras/templates/extras/dynamicgroup_retrieve.html +11 -5
  257. nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
  258. nautobot/extras/templates/extras/gitrepository_result.html +0 -2
  259. nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -96
  260. nautobot/extras/templates/extras/inc/approval_buttons_column.html +20 -6
  261. nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +8 -7
  262. nautobot/extras/templates/extras/inc/configcontext_format.html +10 -3
  263. nautobot/extras/templates/extras/inc/graphqlquery_execute.html +71 -0
  264. nautobot/extras/templates/extras/inc/job_tiles.html +15 -3
  265. nautobot/extras/templates/extras/inc/json_format.html +10 -3
  266. nautobot/extras/templates/extras/inc/overridable_field.html +13 -12
  267. nautobot/extras/templates/extras/job.html +29 -12
  268. nautobot/extras/templates/extras/job_bulk_edit.html +18 -0
  269. nautobot/extras/templates/extras/job_edit.html +52 -46
  270. nautobot/extras/templates/extras/job_list.html +29 -25
  271. nautobot/extras/templates/extras/marketplace.html +5 -9
  272. nautobot/extras/templates/extras/object_configcontext.html +1 -1
  273. nautobot/extras/templates/extras/object_dynamicgroups.html +2 -2
  274. nautobot/extras/templates/extras/objectchange_retrieve.html +19 -37
  275. nautobot/extras/templates/extras/plugin_detail.html +26 -21
  276. nautobot/extras/templates/extras/plugins_list.html +16 -26
  277. nautobot/extras/templates/extras/role_retrieve.html +64 -0
  278. nautobot/extras/templates/extras/scheduledjob.html +4 -2
  279. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  280. nautobot/extras/templates/extras/tag.html +1 -1
  281. nautobot/extras/templatetags/custom_links.py +12 -12
  282. nautobot/extras/templatetags/job_buttons.py +14 -12
  283. nautobot/extras/test_jobs/invalid_import.py +9 -0
  284. nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
  285. nautobot/extras/test_jobs/missing_import.py +11 -0
  286. nautobot/extras/tests/integration/test_configcontextschema.py +27 -26
  287. nautobot/extras/tests/integration/test_customfields.py +8 -7
  288. nautobot/extras/tests/integration/test_dynamicgroups.py +5 -1
  289. nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
  290. nautobot/extras/tests/integration/test_plugins.py +18 -6
  291. nautobot/extras/tests/test_api.py +27 -18
  292. nautobot/extras/tests/test_approvals.py +38 -38
  293. nautobot/extras/tests/test_changelog.py +35 -3
  294. nautobot/extras/tests/test_customfields.py +22 -13
  295. nautobot/extras/tests/test_customfields_filters.py +479 -0
  296. nautobot/extras/tests/test_dynamicgroups.py +39 -1
  297. nautobot/extras/tests/test_filters.py +21 -19
  298. nautobot/extras/tests/test_forms.py +18 -21
  299. nautobot/extras/tests/test_jobs.py +25 -4
  300. nautobot/extras/tests/test_migrations.py +1 -0
  301. nautobot/extras/tests/test_models.py +13 -31
  302. nautobot/extras/tests/test_plugins.py +36 -10
  303. nautobot/extras/tests/test_views.py +31 -30
  304. nautobot/extras/views.py +81 -19
  305. nautobot/ipam/factory.py +7 -0
  306. nautobot/ipam/filter_mixins.py +38 -0
  307. nautobot/ipam/filters.py +27 -38
  308. nautobot/ipam/formfields.py +1 -1
  309. nautobot/ipam/forms.py +6 -3
  310. nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
  311. nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
  312. nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
  313. nautobot/ipam/models.py +29 -2
  314. nautobot/ipam/navigation.py +3 -2
  315. nautobot/ipam/signals.py +71 -0
  316. nautobot/ipam/tables.py +13 -6
  317. nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
  318. nautobot/ipam/templates/ipam/inc/vlangroup_header.html +1 -0
  319. nautobot/ipam/templates/ipam/ipaddress.html +14 -0
  320. nautobot/ipam/templates/ipam/ipaddress_merge.html +3 -3
  321. nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +1 -0
  322. nautobot/ipam/templates/ipam/namespace_update.html +15 -0
  323. nautobot/ipam/templates/ipam/prefix_delete.html +1 -1
  324. nautobot/ipam/templates/ipam/prefix_list.html +14 -13
  325. nautobot/ipam/templates/ipam/service.html +1 -1
  326. nautobot/ipam/templates/ipam/vlan.html +1 -1
  327. nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
  328. nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
  329. nautobot/ipam/tests/migration/test_migrations.py +89 -0
  330. nautobot/ipam/tests/test_api.py +13 -6
  331. nautobot/ipam/tests/test_filters.py +10 -0
  332. nautobot/ipam/tests/test_forms.py +1 -1
  333. nautobot/ipam/tests/test_models.py +43 -1
  334. nautobot/ipam/tests/test_tables.py +1 -2
  335. nautobot/ipam/tests/test_utils.py +1 -1
  336. nautobot/ipam/tests/test_views.py +13 -14
  337. nautobot/ipam/ui.py +0 -17
  338. nautobot/ipam/utils/migrations.py +16 -2
  339. nautobot/ipam/utils/testing.py +9 -3
  340. nautobot/ipam/views.py +46 -6
  341. nautobot/project-static/dist/css/nautobot.css +1 -1
  342. nautobot/project-static/dist/css/nautobot.css.map +1 -1
  343. nautobot/project-static/dist/js/nautobot.js +1 -1
  344. nautobot/project-static/dist/js/nautobot.js.map +1 -1
  345. nautobot/project-static/js/cabletrace.js +1 -1
  346. nautobot/project-static/js/interface_filtering.js +20 -16
  347. nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
  348. nautobot/project-static/nautobot-icons/cloud.svg +1 -1
  349. nautobot/project-static/nautobot-icons/control-panel.svg +1 -1
  350. nautobot/project-static/nautobot-icons/device-lifecycle.svg +1 -1
  351. nautobot/project-static/nautobot-icons/elements.svg +1 -1
  352. nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
  353. nautobot/project-static/nautobot-icons/hammer.svg +1 -1
  354. nautobot/project-static/nautobot-icons/organization.svg +3 -0
  355. nautobot/project-static/nautobot-icons/secrets.svg +1 -1
  356. nautobot/project-static/nautobot-icons/security.svg +3 -0
  357. nautobot/project-static/nautobot-icons/server.svg +1 -1
  358. nautobot/project-static/nautobot-icons/star-filled.svg +1 -1
  359. nautobot/project-static/nautobot-icons/star.svg +1 -1
  360. nautobot/tenancy/api/serializers.py +1 -0
  361. nautobot/tenancy/api/views.py +2 -1
  362. nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
  363. nautobot/tenancy/navigation.py +3 -1
  364. nautobot/tenancy/tests/test_filters.py +0 -2
  365. nautobot/tenancy/views.py +2 -1
  366. nautobot/ui/src/js/collapse.js +3 -3
  367. nautobot/ui/src/js/nautobot.js +16 -0
  368. nautobot/ui/src/scss/colors.scss +1 -1
  369. nautobot/ui/src/scss/nautobot.scss +61 -28
  370. nautobot/users/templates/users/profile.html +45 -12
  371. nautobot/users/templates/users/sessionkey_delete.html +1 -1
  372. nautobot/users/tests/test_api.py +4 -0
  373. nautobot/users/views.py +4 -2
  374. nautobot/virtualization/models.py +1 -68
  375. nautobot/virtualization/navigation.py +3 -2
  376. nautobot/virtualization/templates/virtualization/virtual_machine_vminterface_delete.html +1 -1
  377. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  378. nautobot/virtualization/templates/virtualization/virtualmachine_list.html +2 -2
  379. nautobot/virtualization/templates/virtualization/virtualmachine_update.html +3 -1
  380. nautobot/virtualization/tests/test_api.py +3 -0
  381. nautobot/virtualization/tests/test_models.py +44 -4
  382. nautobot/vpn/__init__.py +0 -0
  383. nautobot/vpn/api/serializers.py +113 -0
  384. nautobot/vpn/api/urls.py +19 -0
  385. nautobot/vpn/api/views.py +70 -0
  386. nautobot/vpn/apps.py +8 -0
  387. nautobot/vpn/choices.py +171 -0
  388. nautobot/vpn/factory.py +209 -0
  389. nautobot/vpn/filters.py +233 -0
  390. nautobot/vpn/forms.py +486 -0
  391. nautobot/vpn/homepage.py +19 -0
  392. nautobot/vpn/migrations/0001_initial.py +541 -0
  393. nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
  394. nautobot/vpn/migrations/__init__.py +0 -0
  395. nautobot/vpn/models.py +527 -0
  396. nautobot/vpn/navigation.py +98 -0
  397. nautobot/vpn/tables.py +380 -0
  398. nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
  399. nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
  400. nautobot/vpn/tests/__init__.py +0 -0
  401. nautobot/vpn/tests/test_api.py +341 -0
  402. nautobot/vpn/tests/test_filters.py +139 -0
  403. nautobot/vpn/tests/test_forms.py +294 -0
  404. nautobot/vpn/tests/test_models.py +97 -0
  405. nautobot/vpn/tests/test_views.py +281 -0
  406. nautobot/vpn/urls.py +16 -0
  407. nautobot/vpn/views.py +437 -0
  408. nautobot/wireless/navigation.py +3 -2
  409. nautobot/wireless/tests/integration/test_radio_profile.py +1 -5
  410. nautobot/wireless/tests/test_api.py +1 -1
  411. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +14 -14
  412. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +417 -366
  413. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
  414. nautobot/data_validation/template_content.py +0 -42
  415. nautobot/dcim/filters/mixins.py +0 -354
  416. nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
  417. /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
  418. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
  419. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
  420. {nautobot-3.0.0a2.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
@@ -6,7 +6,7 @@ from django.contrib.auth import get_user_model
6
6
  from django.contrib.auth.models import Group
7
7
  from django.contrib.contenttypes.models import ContentType
8
8
  from django.core.files.uploadedfile import SimpleUploadedFile
9
- from django.test import override_settings, RequestFactory
9
+ from django.test import override_settings, RequestFactory, tag
10
10
  from django.utils.timezone import now
11
11
 
12
12
  from nautobot.core.jobs import BulkDeleteObjects
@@ -165,35 +165,35 @@ class ApprovalWorkflowTestMixin:
165
165
  cls.approval_workflow_1_definition = ApprovalWorkflowDefinition.objects.create(
166
166
  name="Test Approval Workflow 1 Definition",
167
167
  model_content_type=cls.scheduledjob_ct,
168
- priority=1,
168
+ weight=1,
169
169
  )
170
170
  cls.approval_workflow_2_definition = ApprovalWorkflowDefinition.objects.create(
171
171
  name="Test Approval Workflow 2 Definition",
172
172
  model_content_type=cls.scheduledjob_ct,
173
173
  model_constraints={"name": "Bulk Delete Objects"},
174
- priority=2,
174
+ weight=2,
175
175
  )
176
176
  cls.approval_workflow_3_definition = ApprovalWorkflowDefinition.objects.create(
177
177
  name="Test Approval Workflow 3 Definition",
178
178
  model_content_type=cls.scheduledjob_ct,
179
179
  model_constraints={"name": "Bulk Delete Objects"},
180
- priority=3,
180
+ weight=3,
181
181
  )
182
182
  cls.approval_workflow_4_definition = ApprovalWorkflowDefinition.objects.create(
183
183
  name="Test Approval Workflow 4 Definition",
184
184
  model_content_type=cls.scheduledjob_ct,
185
185
  model_constraints={"name": "Bulk Delete Objects"},
186
- priority=4,
186
+ weight=4,
187
187
  )
188
188
  cls.approval_workflow_5_definition = ApprovalWorkflowDefinition.objects.create(
189
189
  name="Test Approval Workflow 5 Definition",
190
190
  model_content_type=cls.scheduledjob_ct,
191
191
  model_constraints={"name": "Bulk Delete Objects"},
192
- priority=5,
192
+ weight=5,
193
193
  )
194
194
  cls.approval_workflow_1_stage_1_definition = ApprovalWorkflowStageDefinition.objects.create(
195
195
  approval_workflow_definition=cls.approval_workflow_1_definition,
196
- weight=100,
196
+ sequence=100,
197
197
  name="Test Approval Workflow 1 Stage 1 Definition",
198
198
  min_approvers=2,
199
199
  denial_message="Stage 1 Denial Message",
@@ -201,7 +201,7 @@ class ApprovalWorkflowTestMixin:
201
201
  )
202
202
  cls.approval_workflow_1_stage_2_definition = ApprovalWorkflowStageDefinition.objects.create(
203
203
  approval_workflow_definition=cls.approval_workflow_1_definition,
204
- weight=200,
204
+ sequence=200,
205
205
  name="Test Approval Workflow 1 Stage 2 Definition",
206
206
  min_approvers=2,
207
207
  denial_message="Stage 2 Denial Message",
@@ -209,7 +209,7 @@ class ApprovalWorkflowTestMixin:
209
209
  )
210
210
  cls.approval_workflow_1_stage_3_definition = ApprovalWorkflowStageDefinition.objects.create(
211
211
  approval_workflow_definition=cls.approval_workflow_1_definition,
212
- weight=300,
212
+ sequence=300,
213
213
  name="Test Approval Workflow 1 Stage 3 Definition",
214
214
  min_approvers=3,
215
215
  denial_message="Stage 3 Denial Message",
@@ -217,7 +217,7 @@ class ApprovalWorkflowTestMixin:
217
217
  )
218
218
  cls.approval_workflow_1_stage_4_definition = ApprovalWorkflowStageDefinition.objects.create(
219
219
  approval_workflow_definition=cls.approval_workflow_1_definition,
220
- weight=400,
220
+ sequence=400,
221
221
  name="Test Approval Workflow 1 Stage 4 Definition",
222
222
  min_approvers=5,
223
223
  denial_message="Stage 4 Denial Message",
@@ -225,7 +225,7 @@ class ApprovalWorkflowTestMixin:
225
225
  )
226
226
  cls.approval_workflow_1_stage_5_definition = ApprovalWorkflowStageDefinition.objects.create(
227
227
  approval_workflow_definition=cls.approval_workflow_1_definition,
228
- weight=500,
228
+ sequence=500,
229
229
  name="Test Approval Workflow 1 Stage 5 Definition",
230
230
  min_approvers=2,
231
231
  denial_message="Stage 5 Denial Message",
@@ -233,7 +233,7 @@ class ApprovalWorkflowTestMixin:
233
233
  )
234
234
  cls.approval_workflow_1_stage_6_definition = ApprovalWorkflowStageDefinition.objects.create(
235
235
  approval_workflow_definition=cls.approval_workflow_1_definition,
236
- weight=600,
236
+ sequence=600,
237
237
  name="Test Approval Workflow 1 Stage 6 Definition",
238
238
  min_approvers=2,
239
239
  denial_message="Stage 6 Denial Message",
@@ -241,7 +241,7 @@ class ApprovalWorkflowTestMixin:
241
241
  )
242
242
  cls.approval_workflow_2_stage_1_definition = ApprovalWorkflowStageDefinition.objects.create(
243
243
  approval_workflow_definition=cls.approval_workflow_2_definition,
244
- weight=100,
244
+ sequence=100,
245
245
  name="Test Approval Workflow 2 Stage 1 Definition",
246
246
  min_approvers=2,
247
247
  denial_message="Stage 1 Denial Message",
@@ -249,7 +249,7 @@ class ApprovalWorkflowTestMixin:
249
249
  )
250
250
  cls.approval_workflow_2_stage_2_definition = ApprovalWorkflowStageDefinition.objects.create(
251
251
  approval_workflow_definition=cls.approval_workflow_2_definition,
252
- weight=200,
252
+ sequence=200,
253
253
  name="Test Approval Workflow 2 Stage 2 Definition",
254
254
  min_approvers=2,
255
255
  denial_message="Stage 2 Denial Message",
@@ -257,7 +257,7 @@ class ApprovalWorkflowTestMixin:
257
257
  )
258
258
  cls.approval_workflow_2_stage_3_definition = ApprovalWorkflowStageDefinition.objects.create(
259
259
  approval_workflow_definition=cls.approval_workflow_2_definition,
260
- weight=300,
260
+ sequence=300,
261
261
  name="Test Approval Workflow 2 Stage 3 Definition",
262
262
  min_approvers=2,
263
263
  denial_message="Stage 3 Denial Message",
@@ -265,7 +265,7 @@ class ApprovalWorkflowTestMixin:
265
265
  )
266
266
  cls.approval_workflow_3_stage_1_definition = ApprovalWorkflowStageDefinition.objects.create(
267
267
  approval_workflow_definition=cls.approval_workflow_3_definition,
268
- weight=100,
268
+ sequence=100,
269
269
  name="Test Approval Workflow 3 Stage 1 Definition",
270
270
  min_approvers=2,
271
271
  denial_message="Stage 1 Denial Message",
@@ -273,7 +273,7 @@ class ApprovalWorkflowTestMixin:
273
273
  )
274
274
  cls.approval_workflow_3_stage_2_definition = ApprovalWorkflowStageDefinition.objects.create(
275
275
  approval_workflow_definition=cls.approval_workflow_3_definition,
276
- weight=200,
276
+ sequence=200,
277
277
  name="Test Approval Workflow 3 Stage 2 Definition",
278
278
  min_approvers=2,
279
279
  denial_message="Stage 2 Denial Message",
@@ -281,7 +281,7 @@ class ApprovalWorkflowTestMixin:
281
281
  )
282
282
  cls.approval_workflow_3_stage_3_definition = ApprovalWorkflowStageDefinition.objects.create(
283
283
  approval_workflow_definition=cls.approval_workflow_3_definition,
284
- weight=300,
284
+ sequence=300,
285
285
  name="Test Approval Workflow 3 Stage 3 Definition",
286
286
  min_approvers=2,
287
287
  denial_message="Stage 3 Denial Message",
@@ -442,7 +442,7 @@ class ApprovalWorkflowStageDefinitionFilterTestCase(ApprovalWorkflowTestMixin, F
442
442
  ("created",),
443
443
  ("last_updated",),
444
444
  ("approval_workflow_definition",),
445
- ("weight",),
445
+ ("sequence",),
446
446
  ("name",),
447
447
  ("min_approvers",),
448
448
  ("denial_message",),
@@ -1331,6 +1331,7 @@ class JobFilterSetTestCase(FilterTestCases.FilterTestCase):
1331
1331
  params = {"dryrun_default": True}
1332
1332
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
1333
1333
 
1334
+ @tag("example_app")
1334
1335
  def test_hidden(self):
1335
1336
  params = {"hidden": True}
1336
1337
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
@@ -1341,6 +1342,7 @@ class JobFilterSetTestCase(FilterTestCases.FilterTestCase):
1341
1342
  self.filterset(params, self.queryset).qs, self.queryset.filter(read_only=True)
1342
1343
  )
1343
1344
 
1345
+ @tag("example_app")
1344
1346
  def test_is_job_hook_receiver(self):
1345
1347
  params = {"is_job_hook_receiver": True}
1346
1348
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
@@ -1,4 +1,5 @@
1
1
  import json
2
+ from unittest import skip
2
3
  import warnings
3
4
 
4
5
  from django.contrib.auth import get_user_model
@@ -14,14 +15,9 @@ from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipTypeChoi
14
15
  from nautobot.extras.forms import (
15
16
  ConfigContextFilterForm,
16
17
  ConfigContextForm,
17
- CustomFieldModelBulkEditFormMixin,
18
18
  CustomFieldModelFormMixin,
19
19
  JobButtonForm,
20
20
  JobHookForm,
21
- RelationshipModelFormMixin,
22
- StatusModelBulkEditFormMixin,
23
- StatusModelFilterFormMixin,
24
- TagsBulkEditFormMixin,
25
21
  WebhookForm,
26
22
  )
27
23
  from nautobot.extras.models import (
@@ -1095,29 +1091,30 @@ class WebhookFormTestCase(TestCase):
1095
1091
  )
1096
1092
 
1097
1093
 
1094
+ @skip(reason="Skipping until we have items that need to be deprecated again.")
1098
1095
  class DeprecatedAliasesTestCase(TestCase):
1099
1096
  """Test that deprecated class names still exist, but report a DeprecationWarning when used."""
1100
1097
 
1101
1098
  def test_deprecated_form_mixin_classes(self):
1102
1099
  # Importing these mixin classes doesn't directly warn, but subclassing them does.
1103
- from nautobot.extras.forms import CustomFieldBulkCreateForm
1104
- from nautobot.extras.forms.mixins import (
1105
- AddRemoveTagsForm,
1106
- CustomFieldBulkEditForm,
1107
- CustomFieldModelForm,
1108
- RelationshipModelForm,
1109
- StatusBulkEditFormMixin,
1110
- StatusFilterFormMixin,
1111
- )
1100
+ # from nautobot.extras.forms import CustomFieldBulkCreateForm
1101
+ # from nautobot.extras.forms.mixins import (
1102
+ # AddRemoveTagsForm,
1103
+ # CustomFieldBulkEditForm,
1104
+ # CustomFieldModelForm,
1105
+ # RelationshipModelForm,
1106
+ # StatusBulkEditFormMixin,
1107
+ # StatusFilterFormMixin,
1108
+ # )
1112
1109
 
1113
1110
  for deprecated_form_class, replacement_form_class in (
1114
- (AddRemoveTagsForm, TagsBulkEditFormMixin),
1115
- (CustomFieldBulkEditForm, CustomFieldModelBulkEditFormMixin),
1116
- (CustomFieldBulkCreateForm, CustomFieldModelBulkEditFormMixin),
1117
- (CustomFieldModelForm, CustomFieldModelFormMixin),
1118
- (RelationshipModelForm, RelationshipModelFormMixin),
1119
- (StatusBulkEditFormMixin, StatusModelBulkEditFormMixin),
1120
- (StatusFilterFormMixin, StatusModelFilterFormMixin),
1111
+ # (AddRemoveTagsForm, TagsBulkEditFormMixin),
1112
+ # (CustomFieldBulkEditForm, CustomFieldModelBulkEditFormMixin),
1113
+ # (CustomFieldBulkCreateForm, CustomFieldModelBulkEditFormMixin),
1114
+ # (CustomFieldModelForm, CustomFieldModelFormMixin),
1115
+ # (RelationshipModelForm, RelationshipModelFormMixin),
1116
+ # (StatusBulkEditFormMixin, StatusModelBulkEditFormMixin),
1117
+ # (StatusFilterFormMixin, StatusModelFilterFormMixin),
1121
1118
  ):
1122
1119
  with self.subTest(msg=f"Replace {deprecated_form_class.__name__} with {replacement_form_class.__name__}"):
1123
1120
  # Subclassing the deprecated class should raise a DeprecationWarning
@@ -16,7 +16,7 @@ from django.core.exceptions import ValidationError
16
16
  from django.core.files.uploadedfile import SimpleUploadedFile
17
17
  from django.core.management import call_command
18
18
  from django.core.management.base import CommandError
19
- from django.test import override_settings
19
+ from django.test import override_settings, tag
20
20
  from django.test.client import RequestFactory
21
21
  from django.utils import timezone
22
22
 
@@ -286,6 +286,7 @@ register_jobs(BadJob)
286
286
  # Clean up back to normal behavior
287
287
  get_jobs(reload=True)
288
288
 
289
+ @tag("example_app")
289
290
  def test_app_dynamic_jobs(self):
290
291
  """
291
292
  Test that get_job() correctly reloads dynamic jobs when `NautobotAppConfig.provides_dynamic_jobs` is True.
@@ -645,6 +646,26 @@ class JobTransactionTest(TransactionTestCase):
645
646
  self.assertFalse(logs.filter(message="I should NOT be logged to the database").exists())
646
647
  self.assertTrue(logs.filter(message="I should be logged to the database").exists())
647
648
 
649
+ def test_log_counts_by_level(self):
650
+ """
651
+ Test that related JobLogEntry counts are stored for JobResult list summary.
652
+ """
653
+ module = "log_counts_by_level"
654
+ name = "TestLogCountsByLevel"
655
+ job_result = create_job_result_and_run_job(module, name)
656
+
657
+ self.assertGreater(job_result.job_log_entries.count(), 0)
658
+ self.assertIsNotNone(job_result.debug_log_count)
659
+ self.assertEqual(job_result.debug_log_count, 0)
660
+ self.assertIsNotNone(job_result.success_log_count)
661
+ self.assertEqual(job_result.success_log_count, 1)
662
+ self.assertIsNotNone(job_result.info_log_count)
663
+ self.assertEqual(job_result.info_log_count, 3)
664
+ self.assertIsNotNone(job_result.warning_log_count)
665
+ self.assertEqual(job_result.warning_log_count, 2)
666
+ self.assertIsNotNone(job_result.error_log_count)
667
+ self.assertEqual(job_result.error_log_count, 3)
668
+
648
669
  def test_object_vars(self):
649
670
  """
650
671
  Test that Object variable fields behave as expected.
@@ -1261,10 +1282,10 @@ class JobHookTransactionTest(TransactionTestCase): # TODO: BaseModelTestCase mi
1261
1282
  status = models.Status.objects.get_for_model(Location).first()
1262
1283
  loc = Location.objects.create(name="Test Job Hook Location 1", location_type=self.location_type, status=status)
1263
1284
  models.ObjectChange.objects.all().delete()
1264
- tag = models.Tag.objects.create(name="A Test Tag")
1265
- tag.content_types.add(ContentType.objects.get_for_model(Location))
1285
+ tag_instance = models.Tag.objects.create(name="A Test Tag")
1286
+ tag_instance.content_types.add(ContentType.objects.get_for_model(Location))
1266
1287
  with web_request_context(user=self.user):
1267
- loc.tags.add(tag)
1288
+ loc.tags.add(tag_instance)
1268
1289
  job_result = models.JobResult.objects.filter(job_model=self.job_model).first()
1269
1290
  self.assertIsNotNone(job_result)
1270
1291
  expected_log_messages = [
@@ -118,6 +118,7 @@ class CustomFieldDataMigrationTest(NautobotDataMigrationTest):
118
118
  self.assertEqual(cf_1.key, "a123_main_ave")
119
119
  self.assertEqual(cf_2.key, "a_456_main_ave")
120
120
 
121
+ @tag("example_app")
121
122
  @skip("Something bad is happening with the test data, suspecting bad merge")
122
123
  def test_custom_field_data_populated_correctly(self):
123
124
  location_0 = self.location.objects.get(name="Test Location 1")
@@ -4,7 +4,6 @@ import shutil
4
4
  import tempfile
5
5
  from unittest import expectedFailure, mock
6
6
  import uuid
7
- import warnings
8
7
  from zoneinfo import ZoneInfo
9
8
 
10
9
  from django.conf import settings
@@ -15,8 +14,7 @@ from django.core.exceptions import ValidationError
15
14
  from django.core.files.uploadedfile import SimpleUploadedFile
16
15
  from django.db.models import ProtectedError
17
16
  from django.db.utils import IntegrityError
18
- from django.test import override_settings
19
- from django.test.utils import isolate_apps
17
+ from django.test import override_settings, tag
20
18
  from django.utils.timezone import get_default_timezone, now
21
19
  from django_celery_beat.tzcrontab import TzAwareCrontab
22
20
  from git import GitCommandError
@@ -90,7 +88,6 @@ from nautobot.extras.models import (
90
88
  Team,
91
89
  Webhook,
92
90
  )
93
- from nautobot.extras.models.statuses import StatusModel
94
91
  from nautobot.extras.registry import registry
95
92
  from nautobot.extras.secrets.exceptions import SecretParametersError, SecretProviderError, SecretValueNotFoundError
96
93
  from nautobot.extras.tests.git_helper import create_and_populate_git_repository
@@ -103,8 +100,6 @@ from nautobot.virtualization.models import (
103
100
  VirtualMachine,
104
101
  )
105
102
 
106
- from example_app.jobs import ExampleJob
107
-
108
103
  User = get_user_model()
109
104
 
110
105
 
@@ -146,12 +141,12 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
146
141
  cls.approval_workflow_definition = ApprovalWorkflowDefinition.objects.create(
147
142
  name="Approval Workflow with Three Stages",
148
143
  model_content_type=cls.scheduledjob_ct,
149
- priority=1,
144
+ weight=1,
150
145
  )
151
146
  # Create three stages of the Approval Workflow Definition
152
147
  cls.approval_workflow_stage_definition_1 = ApprovalWorkflowStageDefinition.objects.create(
153
148
  approval_workflow_definition=cls.approval_workflow_definition,
154
- weight=100,
149
+ sequence=100,
155
150
  name="Approval Workflow Stage Definition 1",
156
151
  min_approvers=1,
157
152
  denial_message="Stage 1 Denial Message",
@@ -159,7 +154,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
159
154
  )
160
155
  cls.approval_workflow_stage_definition_2 = ApprovalWorkflowStageDefinition.objects.create(
161
156
  approval_workflow_definition=cls.approval_workflow_definition,
162
- weight=200,
157
+ sequence=200,
163
158
  name="Approval Workflow Stage Definition 2",
164
159
  min_approvers=2,
165
160
  denial_message="Stage 2 Denial Message",
@@ -167,7 +162,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
167
162
  )
168
163
  cls.approval_workflow_stage_definition_3 = ApprovalWorkflowStageDefinition.objects.create(
169
164
  approval_workflow_definition=cls.approval_workflow_definition,
170
- weight=300,
165
+ sequence=300,
171
166
  name="Approval Workflow Stage Definition 3",
172
167
  min_approvers=2,
173
168
  denial_message="Stage 3 Denial Message",
@@ -228,7 +223,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
228
223
  """
229
224
  Test the active_stage property of the ApprovalWorkflow model.
230
225
  """
231
- # Test that the active stage is the one with the lowest weight when all stages are pending
226
+ # Test that the active stage is the one with the lowest sequence when all stages are pending
232
227
  self.assertEqual(self.approval_workflow.active_stage, self.approval_workflow_stage_1)
233
228
  # Test that the active stage is the second stage when the first stage is approved
234
229
  self.approval_workflow_stage_1_response_1.state = ApprovalWorkflowStateChoices.APPROVED
@@ -323,7 +318,7 @@ class ApprovalWorkflowTest(ModelTestCases.BaseModelTestCase):
323
318
  approval_workflow_definition = ApprovalWorkflowDefinition.objects.create(
324
319
  name="Scheduled Job Approval Workflow",
325
320
  model_content_type=scheduled_job_ct,
326
- priority=2,
321
+ weight=2,
327
322
  )
328
323
  approval_workflow = ApprovalWorkflow(
329
324
  approval_workflow_definition=approval_workflow_definition,
@@ -1961,6 +1956,7 @@ class GitRepositoryTest(ModelTestCases.BaseModelTestCase):
1961
1956
  shutil.rmtree(self.tempdir.name, ignore_errors=True)
1962
1957
 
1963
1958
 
1959
+ @tag("example_app")
1964
1960
  class JobModelTest(ModelTestCases.BaseModelTestCase):
1965
1961
  """
1966
1962
  Tests for the `Job` model class.
@@ -1976,6 +1972,8 @@ class JobModelTest(ModelTestCases.BaseModelTestCase):
1976
1972
  cls.app_job = JobModel.objects.get(job_class_name="ExampleJob")
1977
1973
 
1978
1974
  def test_job_class(self):
1975
+ from example_app.jobs import ExampleJob
1976
+
1979
1977
  self.assertIsNotNone(self.local_job.job_class)
1980
1978
  self.assertEqual(self.local_job.job_class.description, "Validate job import")
1981
1979
 
@@ -3495,31 +3493,15 @@ class StatusTest(ModelTestCases.BaseModelTestCase):
3495
3493
  self.status.save()
3496
3494
  self.assertEqual(str(self.status), test)
3497
3495
 
3498
- @isolate_apps("nautobot.extras.tests")
3499
- def test_deprecated_mixin_class(self):
3500
- """Test that inheriting from StatusModel raises a DeprecationWarning."""
3501
- with warnings.catch_warnings(record=True) as warn_list:
3502
- warnings.simplefilter("always")
3503
-
3504
- class MyModel(StatusModel): # pylint: disable=unused-variable
3505
- pass
3506
-
3507
- self.assertEqual(len(warn_list), 1)
3508
- warning = warn_list[0]
3509
- self.assertTrue(issubclass(warning.category, DeprecationWarning))
3510
- self.assertIn("StatusModel is deprecated", str(warning))
3511
- self.assertIn("Instead of deriving MyModel from StatusModel", str(warning))
3512
- self.assertIn("please directly declare `status = StatusField(...)` on your model instead", str(warning))
3513
-
3514
3496
 
3515
3497
  class TagTest(ModelTestCases.BaseModelTestCase):
3516
3498
  model = Tag
3517
3499
 
3518
3500
  def test_create_tag_unicode(self):
3519
- tag = Tag(name="Testing Unicode: 台灣")
3520
- tag.save()
3501
+ instance = Tag(name="Testing Unicode: 台灣")
3502
+ instance.save()
3521
3503
 
3522
- self.assertEqual(tag.name, "Testing Unicode: 台灣")
3504
+ self.assertEqual(instance.name, "Testing Unicode: 台灣")
3523
3505
 
3524
3506
 
3525
3507
  class JobLogEntryTest(TestCase): # TODO: change to BaseModelTestCase
@@ -5,7 +5,7 @@ from django.conf import settings
5
5
  from django.contrib.contenttypes.models import ContentType
6
6
  from django.core.exceptions import ValidationError
7
7
  from django.template import engines
8
- from django.test import override_settings
8
+ from django.test import override_settings, tag
9
9
  from django.urls import NoReverseMatch, reverse
10
10
  import django_tables2 as tables
11
11
  import netaddr
@@ -32,11 +32,8 @@ from nautobot.tenancy.forms import TenantFilterForm
32
32
  from nautobot.tenancy.models import Tenant, TenantGroup
33
33
  from nautobot.users.models import ObjectPermission
34
34
 
35
- from example_app import config as example_config
36
- from example_app.datasources import refresh_git_text_files
37
- from example_app.models import ExampleModel
38
-
39
35
 
36
+ @tag("example_app")
40
37
  class AppTest(TestCase):
41
38
  def test_config(self):
42
39
  self.assertIn(
@@ -46,6 +43,8 @@ class AppTest(TestCase):
46
43
 
47
44
  def test_models(self):
48
45
  # Test saving an instance
46
+ from example_app.models import ExampleModel
47
+
49
48
  instance = ExampleModel(name="Instance 1", number=100)
50
49
  instance.save()
51
50
  self.assertIsNotNone(instance.pk)
@@ -134,6 +133,8 @@ class AppTest(TestCase):
134
133
  """
135
134
  Check that App DatasourceContents are registered.
136
135
  """
136
+ from example_app.datasources import refresh_git_text_files
137
+
137
138
  registered_datasources = registry.get("datasource_contents", {}).get("extras.gitrepository", [])
138
139
 
139
140
  app_datasource = DatasourceContent(
@@ -158,6 +159,8 @@ class AppTest(TestCase):
158
159
  """
159
160
  Check that App middleware is registered.
160
161
  """
162
+ from example_app import config as example_config
163
+
161
164
  self.assertIn(
162
165
  "example_app.middleware.ExampleMiddleware",
163
166
  settings.MIDDLEWARE,
@@ -175,6 +178,8 @@ class AppTest(TestCase):
175
178
  """
176
179
  Check enforcement of minimum Nautobot version.
177
180
  """
181
+ from example_app import config as example_config
182
+
178
183
  with self.assertRaises(PluginImproperlyConfigured):
179
184
  example_config.validate({}, "0.8")
180
185
 
@@ -182,6 +187,8 @@ class AppTest(TestCase):
182
187
  """
183
188
  Check enforcement of maximum Nautobot version.
184
189
  """
190
+ from example_app import config as example_config
191
+
185
192
  with self.assertRaises(PluginImproperlyConfigured):
186
193
  example_config.validate({}, "10.0")
187
194
 
@@ -189,6 +196,7 @@ class AppTest(TestCase):
189
196
  """
190
197
  Validate enforcement of required settings.
191
198
  """
199
+ from example_app import config as example_config
192
200
 
193
201
  class ExampleConfigWithRequiredSettings(example_config):
194
202
  required_settings = ["foo"]
@@ -210,6 +218,7 @@ class AppTest(TestCase):
210
218
  """
211
219
  Validate population of default config settings.
212
220
  """
221
+ from example_app import config as example_config
213
222
 
214
223
  class ExampleConfigWithDefaultSettings(example_config):
215
224
  default_settings = {
@@ -239,6 +248,8 @@ class AppTest(TestCase):
239
248
  """
240
249
  Validate that App installed Django apps and dependencies are are registered.
241
250
  """
251
+ from example_app import config as example_config
252
+
242
253
  self.assertIn(
243
254
  "example_app.ExampleAppConfig",
244
255
  settings.INSTALLED_APPS,
@@ -283,11 +294,13 @@ class AppTest(TestCase):
283
294
  self.assertEqual(secret.get_value(obj=secret), secret.parameters["constant"])
284
295
 
285
296
 
297
+ @tag("example_app")
286
298
  class AppGenericViewTest(ViewTestCases.PrimaryObjectViewTestCase):
287
- model = ExampleModel
288
-
289
299
  @classmethod
290
300
  def setUpTestData(cls):
301
+ from example_app.models import ExampleModel
302
+
303
+ cls.model = ExampleModel
291
304
  # Example objects to test.
292
305
  ExampleModel.objects.create(name="Example 1", number=1)
293
306
  ExampleModel.objects.create(name="Example 2", number=2)
@@ -311,6 +324,7 @@ class AppGenericViewTest(ViewTestCases.PrimaryObjectViewTestCase):
311
324
  }
312
325
 
313
326
 
327
+ @tag("example_app")
314
328
  class AppListViewTest(TestCase):
315
329
  def test_list_plugins_anonymous(self):
316
330
  # Make the request as an unauthenticated user
@@ -382,6 +396,7 @@ class AppListViewTest(TestCase):
382
396
  )
383
397
 
384
398
 
399
+ @tag("example_app")
385
400
  class PluginDetailViewTest(TestCase):
386
401
  def test_view_detail_anonymous(self):
387
402
  # Make the request as an unauthenticated user
@@ -413,8 +428,8 @@ class MarketplaceViewTest(TestCase):
413
428
  self.assertHttpStatus(response, 200)
414
429
 
415
430
 
431
+ @tag("example_app")
416
432
  class AppAPITest(APIViewTestCases.APIViewTestCase):
417
- model = ExampleModel
418
433
  bulk_update_data = {
419
434
  "number": 2600,
420
435
  }
@@ -436,6 +451,9 @@ class AppAPITest(APIViewTestCases.APIViewTestCase):
436
451
 
437
452
  @classmethod
438
453
  def setUpTestData(cls):
454
+ from example_app.models import ExampleModel
455
+
456
+ cls.model = ExampleModel
439
457
  # Example objects to test.
440
458
  ExampleModel.objects.create(name="Example 1", number=1)
441
459
  ExampleModel.objects.create(name="Example 2", number=2)
@@ -443,6 +461,8 @@ class AppAPITest(APIViewTestCases.APIViewTestCase):
443
461
 
444
462
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
445
463
  def test_api_urls(self):
464
+ from example_app.models import ExampleModel
465
+
446
466
  # Test list URL resolution
447
467
  list_url = reverse("plugins-api:example_app-api:examplemodel-list")
448
468
  self.assertEqual(list_url, self._get_list_url())
@@ -509,6 +529,7 @@ class TestUserContextCustomValidator(CustomValidator):
509
529
  self.validation_error(f"TestUserContextCustomValidator: user is {self.context['user']}")
510
530
 
511
531
 
532
+ @tag("example_app")
512
533
  class AppCustomValidationTest(TestCase):
513
534
  def setUp(self):
514
535
  # When creating a fresh test DB, wrapping model clean methods fails, which is normal.
@@ -571,13 +592,15 @@ class AppCustomValidationTest(TestCase):
571
592
  registry["plugin_custom_validators"]["dcim.locationtype"] = before
572
593
 
573
594
 
595
+ @tag("example_app")
574
596
  class ExampleModelCustomActionViewTest(TestCase):
575
597
  """Test for custom action view `all_names` added to Example App"""
576
598
 
577
- model = ExampleModel
578
-
579
599
  @classmethod
580
600
  def setUpTestData(cls):
601
+ from example_app.models import ExampleModel
602
+
603
+ cls.model = ExampleModel
581
604
  ExampleModel.objects.create(name="Example 1", number=100)
582
605
  ExampleModel.objects.create(name="Example 2", number=200)
583
606
  ExampleModel.objects.create(name="Example 3", number=300)
@@ -633,6 +656,7 @@ class ExampleModelCustomActionViewTest(TestCase):
633
656
  self.assertNotIn(example_model.name, response_body, msg=response_body)
634
657
 
635
658
 
659
+ @tag("example_app")
636
660
  class FilterExtensionTest(TestCase):
637
661
  """
638
662
  Tests for adding filter extensions
@@ -882,6 +906,7 @@ class LoadPluginTest(TestCase):
882
906
  load_plugin(app_name, settings)
883
907
 
884
908
 
909
+ @tag("example_app", "example_app_with_view_override")
885
910
  class TestAppCoreViewOverrides(TestCase):
886
911
  """
887
912
  Validate that overridden core views work as expected.
@@ -916,6 +941,7 @@ class TestAppCoreViewOverrides(TestCase):
916
941
  )
917
942
 
918
943
 
944
+ @tag("example_app")
919
945
  class PluginTemplateExtensionsTest(TestCase):
920
946
  """
921
947
  Test that registered TemplateExtensions inject content as expected