nautobot 2.0.0a2__py3-none-any.whl → 2.0.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1029) hide show
  1. nautobot/__init__.py +1 -5
  2. nautobot/apps/api.py +6 -8
  3. nautobot/apps/forms.py +0 -2
  4. nautobot/apps/ui.py +0 -8
  5. nautobot/circuits/api/serializers.py +9 -119
  6. nautobot/circuits/api/urls.py +1 -1
  7. nautobot/circuits/api/views.py +0 -1
  8. nautobot/circuits/choices.py +0 -2
  9. nautobot/circuits/filters.py +7 -6
  10. nautobot/circuits/forms.py +3 -73
  11. nautobot/circuits/migrations/0001_initial_part_1.py +0 -1
  12. nautobot/circuits/migrations/0002_initial_part_2.py +0 -1
  13. nautobot/circuits/migrations/0003_auto_slug.py +0 -1
  14. nautobot/circuits/migrations/0004_increase_provider_account_length.py +0 -1
  15. nautobot/circuits/migrations/0005_providernetwork.py +0 -1
  16. nautobot/circuits/migrations/0006_cache_circuit_terminations.py +0 -1
  17. nautobot/circuits/migrations/0007_circuitterminations_primary_model.py +0 -1
  18. nautobot/circuits/migrations/0008_add_natural_indexing.py +0 -1
  19. nautobot/circuits/migrations/0009_circuittermination_location.py +0 -1
  20. nautobot/circuits/migrations/0010_rename_foreign_keys_and_related_names.py +0 -1
  21. nautobot/circuits/migrations/0011_remove_site_foreign_key_from_circuit_termination_class.py +0 -1
  22. nautobot/circuits/migrations/0012_created_datetime.py +0 -1
  23. nautobot/circuits/migrations/0013_alter_circuittermination__path.py +0 -1
  24. nautobot/circuits/migrations/0014_related_name_changes.py +1 -2
  25. nautobot/circuits/migrations/0015_remove_circuittype_provider_slug.py +20 -0
  26. nautobot/circuits/migrations/0016_tagsfield.py +34 -0
  27. nautobot/circuits/migrations/0017_fixup_null_statuses.py +22 -0
  28. nautobot/circuits/migrations/0018_status_nonnullable.py +22 -0
  29. nautobot/circuits/models.py +3 -93
  30. nautobot/circuits/navigation.py +14 -69
  31. nautobot/circuits/signals.py +0 -2
  32. nautobot/circuits/tables.py +42 -5
  33. nautobot/circuits/templates/circuits/circuit_retrieve.html +1 -1
  34. nautobot/circuits/templates/circuits/circuittermination_retrieve.html +1 -1
  35. nautobot/circuits/templates/circuits/circuittype_retrieve.html +1 -1
  36. nautobot/circuits/templates/circuits/provider_create.html +0 -1
  37. nautobot/circuits/templates/circuits/provider_retrieve.html +1 -1
  38. nautobot/circuits/tests/integration/test_relationships.py +13 -16
  39. nautobot/circuits/tests/test_api.py +13 -43
  40. nautobot/circuits/tests/test_filters.py +20 -15
  41. nautobot/circuits/tests/test_models.py +7 -3
  42. nautobot/circuits/tests/test_views.py +57 -67
  43. nautobot/circuits/views.py +18 -9
  44. nautobot/core/api/__init__.py +8 -2
  45. nautobot/core/api/authentication.py +0 -3
  46. nautobot/core/api/fields.py +15 -6
  47. nautobot/core/api/filter_backends.py +3 -2
  48. nautobot/core/api/metadata.py +237 -30
  49. nautobot/core/api/mixins.py +94 -0
  50. nautobot/core/api/pagination.py +3 -3
  51. nautobot/core/api/parsers.py +154 -0
  52. nautobot/core/api/renderers.py +153 -2
  53. nautobot/core/api/schema.py +47 -3
  54. nautobot/core/api/serializers.py +377 -37
  55. nautobot/core/api/urls.py +11 -3
  56. nautobot/core/api/utils.py +174 -2
  57. nautobot/core/api/versioning.py +32 -10
  58. nautobot/core/api/views.py +266 -75
  59. nautobot/core/apps/__init__.py +138 -221
  60. nautobot/core/celery/__init__.py +112 -41
  61. nautobot/core/celery/backends.py +19 -13
  62. nautobot/core/celery/control.py +46 -0
  63. nautobot/core/celery/encoders.py +53 -0
  64. nautobot/core/celery/log.py +38 -0
  65. nautobot/core/celery/schedulers.py +23 -4
  66. nautobot/core/celery/task.py +1 -16
  67. nautobot/core/checks.py +0 -27
  68. nautobot/core/choices.py +21 -113
  69. nautobot/core/{cli.py → cli/__init__.py} +1 -2
  70. nautobot/core/cli/__main__.py +3 -0
  71. nautobot/core/constants.py +25 -43
  72. nautobot/core/context_processors.py +12 -0
  73. nautobot/core/filters.py +2 -2
  74. nautobot/core/forms/__init__.py +0 -4
  75. nautobot/core/forms/fields.py +39 -68
  76. nautobot/core/forms/forms.py +27 -27
  77. nautobot/core/forms/utils.py +7 -59
  78. nautobot/core/forms/widgets.py +0 -1
  79. nautobot/core/graphql/__init__.py +2 -2
  80. nautobot/core/graphql/schema.py +4 -27
  81. nautobot/core/jobs/__init__.py +75 -0
  82. nautobot/core/management/commands/build_ui.py +255 -0
  83. nautobot/core/management/commands/celery.py +0 -1
  84. nautobot/core/management/commands/generate_test_data.py +18 -13
  85. nautobot/core/management/commands/post_upgrade.py +24 -24
  86. nautobot/core/management/commands/validate_models.py +0 -1
  87. nautobot/core/middleware.py +0 -1
  88. nautobot/core/models/__init__.py +26 -1
  89. nautobot/core/models/fields.py +24 -5
  90. nautobot/core/models/generics.py +2 -46
  91. nautobot/core/models/managers.py +5 -0
  92. nautobot/core/models/name_color_content_types.py +1 -19
  93. nautobot/core/models/tree_queries.py +14 -4
  94. nautobot/core/models/utils.py +9 -10
  95. nautobot/core/models/validators.py +17 -8
  96. nautobot/core/releases.py +8 -10
  97. nautobot/core/settings.py +81 -53
  98. nautobot/core/tables.py +5 -5
  99. nautobot/core/tasks.py +4 -7
  100. nautobot/core/templates/base.html +1 -49
  101. nautobot/core/templates/base_django.html +49 -0
  102. nautobot/core/templates/base_react.html +55 -0
  103. nautobot/core/templates/buttons/export.html +6 -4
  104. nautobot/core/templates/generic/object_bulk_create.html +10 -21
  105. nautobot/core/templates/generic/object_list.html +4 -1
  106. nautobot/core/templates/generic/object_retrieve_plugin_full_width.html +3 -0
  107. nautobot/core/templates/inc/footer.html +1 -0
  108. nautobot/core/templates/inc/javascript.html +0 -14
  109. nautobot/core/templates/inc/nav_menu.html +28 -33
  110. nautobot/core/templates/inc/object_details_advanced_panel.html +13 -0
  111. nautobot/core/templates/inc/relationships_table_rows.html +2 -2
  112. nautobot/core/templates/nautobot_config.py.j2 +8 -25
  113. nautobot/core/templates/plugin_template/__init__.py-tpl +1 -2
  114. nautobot/core/templates/rest_framework/api.html +8 -0
  115. nautobot/core/templatetags/buttons.py +32 -29
  116. nautobot/core/templatetags/helpers.py +1 -1
  117. nautobot/core/testing/__init__.py +47 -44
  118. nautobot/core/testing/api.py +365 -47
  119. nautobot/core/testing/filters.py +12 -7
  120. nautobot/core/testing/integration.py +1 -1
  121. nautobot/core/testing/migrations.py +2 -0
  122. nautobot/core/testing/mixins.py +22 -12
  123. nautobot/core/testing/schema.py +2 -1
  124. nautobot/core/testing/views.py +28 -51
  125. nautobot/core/tests/integration/test_filters.py +17 -8
  126. nautobot/core/tests/integration/test_navbar.py +11 -34
  127. nautobot/core/tests/integration/test_plugin_navbar.py +9 -103
  128. nautobot/core/tests/nautobot_config.py +2 -3
  129. nautobot/core/tests/runner.py +0 -1
  130. nautobot/core/tests/test_api.py +290 -24
  131. nautobot/core/tests/test_authentication.py +57 -14
  132. nautobot/core/tests/test_checks.py +0 -7
  133. nautobot/core/tests/test_choices.py +0 -1
  134. nautobot/core/tests/test_filters.py +117 -110
  135. nautobot/core/tests/test_forms.py +47 -110
  136. nautobot/core/tests/test_graphql.py +158 -135
  137. nautobot/core/tests/test_logging.py +4 -1
  138. nautobot/core/tests/test_managers.py +3 -5
  139. nautobot/core/tests/test_models.py +2 -0
  140. nautobot/core/tests/test_ordering.py +0 -2
  141. nautobot/core/tests/test_paginator.py +3 -1
  142. nautobot/core/tests/test_releases.py +12 -12
  143. nautobot/core/tests/test_templatetags_helpers.py +7 -4
  144. nautobot/core/tests/test_utils.py +112 -78
  145. nautobot/core/tests/test_views.py +12 -17
  146. nautobot/core/tests/test_views_utils.py +6 -9
  147. nautobot/core/utils/data.py +17 -0
  148. nautobot/core/utils/deprecation.py +13 -20
  149. nautobot/core/utils/filtering.py +53 -9
  150. nautobot/core/utils/git.py +12 -4
  151. nautobot/core/utils/lookup.py +3 -1
  152. nautobot/core/utils/requests.py +23 -116
  153. nautobot/core/views/__init__.py +1 -2
  154. nautobot/core/views/generic.py +131 -119
  155. nautobot/core/views/mixins.py +53 -62
  156. nautobot/core/views/paginator.py +0 -1
  157. nautobot/core/views/renderers.py +14 -12
  158. nautobot/core/views/utils.py +87 -4
  159. nautobot/dcim/api/serializers.py +160 -672
  160. nautobot/dcim/api/urls.py +1 -1
  161. nautobot/dcim/api/views.py +7 -46
  162. nautobot/dcim/choices.py +2 -25
  163. nautobot/dcim/elevations.py +0 -1
  164. nautobot/dcim/factory.py +15 -4
  165. nautobot/dcim/filters/__init__.py +42 -13
  166. nautobot/dcim/form_mixins.py +1 -27
  167. nautobot/dcim/forms.py +58 -797
  168. nautobot/dcim/management/commands/trace_paths.py +0 -1
  169. nautobot/dcim/migrations/0001_initial_part_1.py +0 -1
  170. nautobot/dcim/migrations/0002_initial_part_2.py +0 -1
  171. nautobot/dcim/migrations/0003_initial_part_3.py +0 -1
  172. nautobot/dcim/migrations/0004_initial_part_4.py +0 -1
  173. nautobot/dcim/migrations/0005_device_local_context_schema.py +0 -1
  174. nautobot/dcim/migrations/0006_auto_slug.py +0 -1
  175. nautobot/dcim/migrations/0007_device_secrets_group.py +0 -1
  176. nautobot/dcim/migrations/0008_increase_all_serial_lengths.py +0 -1
  177. nautobot/dcim/migrations/0009_add_natural_indexing.py +0 -1
  178. nautobot/dcim/migrations/0010_interface_status.py +0 -1
  179. nautobot/dcim/migrations/0011_interface_status_data_migration.py +0 -1
  180. nautobot/dcim/migrations/0012_interface_parent_bridge.py +0 -1
  181. nautobot/dcim/migrations/0013_location_location_type.py +0 -1
  182. nautobot/dcim/migrations/0014_location_status_data_migration.py +0 -1
  183. nautobot/dcim/migrations/0015_device_components__changeloggedmodel.py +0 -1
  184. nautobot/dcim/migrations/0016_device_components__timestamp_data_migration.py +0 -1
  185. nautobot/dcim/migrations/0017_locationtype_nestable.py +0 -1
  186. nautobot/dcim/migrations/0018_device_redundancy_group.py +0 -1
  187. nautobot/dcim/migrations/0019_device_redundancy_group_data_migration.py +0 -1
  188. nautobot/dcim/migrations/0020_move_site_fields_to_location_model.py +0 -1
  189. nautobot/dcim/migrations/0021_mptt_to_tree_queries.py +0 -1
  190. nautobot/dcim/migrations/0022_interface_mac_address_data_migration.py +0 -1
  191. nautobot/dcim/migrations/0023_alter_interface_mac_address.py +0 -1
  192. nautobot/dcim/migrations/0024_alter_device_and_rack_role_add_new_role.py +2 -2
  193. nautobot/dcim/migrations/0025_device_and_rack_roles_data_migrations.py +19 -14
  194. nautobot/dcim/migrations/0026_rename_device_and_rack_role.py +0 -1
  195. nautobot/dcim/migrations/0027_remove_device_role_and_rack_role.py +1 -2
  196. nautobot/dcim/migrations/0028_rename_foreignkey_fields.py +1 -2
  197. nautobot/dcim/migrations/0029_add_tree_managers_and_foreign_keys_pre_data_migration.py +0 -1
  198. nautobot/dcim/migrations/0030_migrate_region_and_site_data_to_locations.py +2 -2
  199. nautobot/dcim/migrations/0031_rename_path_end_point_related_name.py +0 -1
  200. nautobot/dcim/migrations/0032_remove_site_foreign_key_from_dcim_models.py +0 -1
  201. nautobot/dcim/migrations/0033_created_datetime.py +0 -1
  202. nautobot/dcim/migrations/0034_fixup_fks_and_related_names.py +0 -1
  203. nautobot/dcim/migrations/0035_related_name_changes.py +1 -2
  204. nautobot/dcim/migrations/0036_remove_region_and_site.py +1 -2
  205. nautobot/dcim/migrations/0037_interface_ip_addresses_m2m.py +0 -1
  206. nautobot/dcim/migrations/0038_alter_location_managers.py +0 -1
  207. nautobot/dcim/migrations/0039_remove_slug.py +24 -0
  208. nautobot/dcim/migrations/0040_tagsfield.py +109 -0
  209. nautobot/dcim/migrations/0041_ipam__namespaces.py +25 -0
  210. nautobot/dcim/migrations/0042_fixup_null_statuses.py +51 -0
  211. nautobot/dcim/migrations/0043_status_nonnullable.py +72 -0
  212. nautobot/dcim/models/cables.py +4 -35
  213. nautobot/dcim/models/device_component_templates.py +7 -2
  214. nautobot/dcim/models/device_components.py +26 -203
  215. nautobot/dcim/models/devices.py +30 -152
  216. nautobot/dcim/models/locations.py +3 -64
  217. nautobot/dcim/models/power.py +3 -51
  218. nautobot/dcim/models/racks.py +7 -86
  219. nautobot/dcim/navigation.py +141 -467
  220. nautobot/dcim/signals.py +0 -2
  221. nautobot/dcim/tables/devices.py +8 -5
  222. nautobot/dcim/tables/devicetypes.py +1 -1
  223. nautobot/dcim/tables/locations.py +2 -2
  224. nautobot/dcim/tables/power.py +2 -2
  225. nautobot/dcim/templates/dcim/console_port_connection_list.html +7 -0
  226. nautobot/dcim/templates/dcim/device.html +15 -4
  227. nautobot/dcim/templates/dcim/device_edit.html +6 -0
  228. nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +0 -1
  229. nautobot/dcim/templates/dcim/devicetype.html +2 -2
  230. nautobot/dcim/templates/dcim/interface.html +4 -0
  231. nautobot/dcim/templates/dcim/interface_connection_list.html +7 -0
  232. nautobot/dcim/templates/dcim/interface_edit.html +1 -0
  233. nautobot/dcim/templates/dcim/location.html +16 -1
  234. nautobot/dcim/templates/dcim/locationtype.html +15 -0
  235. nautobot/dcim/templates/dcim/power_port_connection_list.html +7 -0
  236. nautobot/dcim/templates/dcim/rackgroup.html +0 -12
  237. nautobot/dcim/tests/integration/test_cable_connect_form.py +4 -4
  238. nautobot/dcim/tests/test_api.py +202 -130
  239. nautobot/dcim/tests/test_cablepaths.py +47 -42
  240. nautobot/dcim/tests/test_filters.py +156 -134
  241. nautobot/dcim/tests/test_forms.py +12 -213
  242. nautobot/dcim/tests/test_graphql.py +8 -3
  243. nautobot/dcim/tests/test_migrations.py +6 -11
  244. nautobot/dcim/tests/test_models.py +208 -158
  245. nautobot/dcim/tests/test_natural_ordering.py +12 -14
  246. nautobot/dcim/tests/test_signals.py +7 -4
  247. nautobot/dcim/tests/test_views.py +270 -264
  248. nautobot/dcim/urls.py +21 -26
  249. nautobot/dcim/views.py +14 -156
  250. nautobot/docs/additional-features/caching.md +6 -87
  251. nautobot/docs/additional-features/job-scheduling-and-approvals.md +3 -0
  252. nautobot/docs/additional-features/jobs.md +179 -197
  253. nautobot/docs/administration/nautobot-server.md +9 -24
  254. nautobot/docs/administration/nautobot-shell.md +6 -6
  255. nautobot/docs/administration/replicating-nautobot.md +0 -10
  256. nautobot/docs/configuration/index.md +9 -9
  257. nautobot/docs/configuration/optional-settings.md +32 -61
  258. nautobot/docs/configuration/required-settings.md +11 -52
  259. nautobot/docs/development/application-registry.md +2 -13
  260. nautobot/docs/development/best-practices.md +2 -1
  261. nautobot/docs/development/docker-compose-advanced-use-cases.md +1 -1
  262. nautobot/docs/development/extending-models.md +15 -17
  263. nautobot/docs/development/generic-views.md +0 -2
  264. nautobot/docs/development/getting-started.md +56 -6
  265. nautobot/docs/development/navigation-menu.md +22 -93
  266. nautobot/docs/development/react-ui.md +105 -0
  267. nautobot/docs/development/release-checklist.md +3 -3
  268. nautobot/docs/development/role-internals.md +1 -3
  269. nautobot/docs/development/style-guide.md +6 -4
  270. nautobot/docs/development/templates.md +2 -1
  271. nautobot/docs/docker/index.md +16 -14
  272. nautobot/docs/index.md +7 -3
  273. nautobot/docs/installation/index.md +4 -1
  274. nautobot/docs/installation/migrating-from-netbox.md +12 -43
  275. nautobot/docs/installation/migrating-from-postgresql.md +1 -1
  276. nautobot/docs/installation/nautobot.md +1 -1
  277. nautobot/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  278. nautobot/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  279. nautobot/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  280. nautobot/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  281. nautobot/docs/installation/tables/v2-code-removals.yaml +67 -0
  282. nautobot/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  283. nautobot/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  284. nautobot/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  285. nautobot/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  286. nautobot/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  287. nautobot/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  288. nautobot/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  289. nautobot/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  290. nautobot/docs/installation/upgrading-from-nautobot-v1.md +190 -636
  291. nautobot/docs/installation/upgrading.md +5 -2
  292. nautobot/docs/models/dcim/device.md +3 -0
  293. nautobot/docs/models/dcim/deviceredundancygroup.md +3 -3
  294. nautobot/docs/models/extras/computedfield.md +4 -4
  295. nautobot/docs/models/extras/dynamicgroup.md +9 -9
  296. nautobot/docs/models/extras/gitrepository.md +3 -0
  297. nautobot/docs/models/extras/job.md +1 -0
  298. nautobot/docs/models/extras/jobbutton.md +18 -13
  299. nautobot/docs/models/extras/jobhook.md +7 -4
  300. nautobot/docs/models/extras/jobresult.md +6 -2
  301. nautobot/docs/models/extras/relationship.md +2 -2
  302. nautobot/docs/models/extras/status.md +6 -19
  303. nautobot/docs/models/ipam/ipaddress.md +3 -0
  304. nautobot/docs/models/ipam/vrf.md +0 -3
  305. nautobot/docs/models/virtualization/virtualmachine.md +3 -0
  306. nautobot/docs/plugins/development.md +92 -24
  307. nautobot/docs/release-notes/version-1.5.md +96 -0
  308. nautobot/docs/release-notes/version-2.0.md +216 -0
  309. nautobot/docs/requirements.txt +5 -4
  310. nautobot/docs/rest-api/overview.md +384 -215
  311. nautobot/docs/rest-api/ui-related-endpoints.md +9 -0
  312. nautobot/extras/admin.py +3 -5
  313. nautobot/extras/api/customfields.py +15 -39
  314. nautobot/extras/api/fields.py +0 -11
  315. nautobot/extras/api/mixins.py +45 -0
  316. nautobot/extras/api/relationships.py +63 -159
  317. nautobot/extras/api/serializers.py +165 -706
  318. nautobot/extras/api/urls.py +1 -1
  319. nautobot/extras/api/views.py +295 -282
  320. nautobot/extras/apps.py +4 -7
  321. nautobot/extras/choices.py +11 -22
  322. nautobot/extras/constants.py +9 -3
  323. nautobot/extras/datasources/__init__.py +2 -0
  324. nautobot/extras/datasources/git.py +135 -186
  325. nautobot/extras/datasources/registry.py +25 -35
  326. nautobot/extras/factory.py +1 -3
  327. nautobot/extras/filters/__init__.py +49 -47
  328. nautobot/extras/filters/mixins.py +10 -8
  329. nautobot/extras/forms/forms.py +72 -148
  330. nautobot/extras/forms/mixins.py +34 -57
  331. nautobot/extras/health_checks.py +0 -33
  332. nautobot/extras/jobs.py +387 -566
  333. nautobot/extras/management/__init__.py +55 -48
  334. nautobot/extras/management/commands/renaturalize.py +0 -1
  335. nautobot/extras/management/commands/runjob.py +24 -62
  336. nautobot/extras/management/commands/webhook_receiver.py +0 -1
  337. nautobot/extras/managers.py +30 -7
  338. nautobot/extras/migrations/0001_initial_part_1.py +0 -1
  339. nautobot/extras/migrations/0002_initial_part_2.py +0 -1
  340. nautobot/extras/migrations/0003_initial_part_3.py +0 -1
  341. nautobot/extras/migrations/0004_populate_default_status_records.py +0 -1
  342. nautobot/extras/migrations/0005_configcontext_device_types.py +0 -1
  343. nautobot/extras/migrations/0006_graphqlquery.py +0 -1
  344. nautobot/extras/migrations/0007_configcontextschema.py +0 -1
  345. nautobot/extras/migrations/0008_jobresult__custom_field_data.py +0 -1
  346. nautobot/extras/migrations/0009_computedfield.py +0 -1
  347. nautobot/extras/migrations/0010_change_cf_validation_max_min_field_to_bigint.py +0 -1
  348. nautobot/extras/migrations/0011_fileattachment_fileproxy.py +0 -1
  349. nautobot/extras/migrations/0012_healthchecktestmodel.py +0 -1
  350. nautobot/extras/migrations/0013_default_fallback_value_computedfield.py +0 -1
  351. nautobot/extras/migrations/0014_auto_slug.py +0 -1
  352. nautobot/extras/migrations/0015_scheduled_job.py +0 -1
  353. nautobot/extras/migrations/0016_secret.py +0 -1
  354. nautobot/extras/migrations/0017_joblogentry.py +0 -1
  355. nautobot/extras/migrations/0018_joblog_data_migration.py +0 -2
  356. nautobot/extras/migrations/0019_joblogentry__meta_options__related_name.py +0 -1
  357. nautobot/extras/migrations/0020_customfield_changelog.py +0 -1
  358. nautobot/extras/migrations/0021_customfield_changelog_data.py +0 -1
  359. nautobot/extras/migrations/0022_objectchange_object_datav2.py +0 -1
  360. nautobot/extras/migrations/0023_job_model.py +0 -1
  361. nautobot/extras/migrations/0024_job_data_migration.py +0 -1
  362. nautobot/extras/migrations/0025_add_advanced_ui_boolean_to_customfield_conputedfield_and_relationship.py +0 -1
  363. nautobot/extras/migrations/0026_job_add_gitrepository_fk.py +0 -1
  364. nautobot/extras/migrations/0027_job_gitrepository_data_migration.py +0 -1
  365. nautobot/extras/migrations/0028_job_reduce_source.py +0 -1
  366. nautobot/extras/migrations/0029_dynamicgroup.py +0 -1
  367. nautobot/extras/migrations/0030_webhook_alter_unique_together.py +0 -1
  368. nautobot/extras/migrations/0031_tag_content_types.py +0 -1
  369. nautobot/extras/migrations/0032_tag_content_types_data_migration.py +0 -1
  370. nautobot/extras/migrations/0033_add__optimized_indexing.py +0 -1
  371. nautobot/extras/migrations/0034_alter_fileattachment_mimetype.py +0 -1
  372. nautobot/extras/migrations/0035_scheduledjob_crontab.py +0 -1
  373. nautobot/extras/migrations/0036_job_add_has_sensitive_variables.py +0 -1
  374. nautobot/extras/migrations/0037_configcontextschema__remove_name_unique__create_constraint_unique_name_owner.py +0 -1
  375. nautobot/extras/migrations/0038_configcontext_locations.py +0 -1
  376. nautobot/extras/migrations/0039_objectchange__add_change_context.py +0 -1
  377. nautobot/extras/migrations/0040_dynamicgroup__dynamicgroupmembership.py +0 -1
  378. nautobot/extras/migrations/0041_jobresult_job_kwargs.py +0 -1
  379. nautobot/extras/migrations/0042_job__add_is_job_hook_receiver.py +0 -1
  380. nautobot/extras/migrations/0043_note.py +0 -1
  381. nautobot/extras/migrations/0044_add_job_hook.py +0 -1
  382. nautobot/extras/migrations/0045_add_custom_field_slug.py +0 -1
  383. nautobot/extras/migrations/0046_populate_custom_field_slug_label.py +0 -1
  384. nautobot/extras/migrations/0047_enforce_custom_field_slug.py +0 -1
  385. nautobot/extras/migrations/0048_alter_objectchange_change_context_detail.py +0 -1
  386. nautobot/extras/migrations/0049_alter_tag_slug.py +0 -1
  387. nautobot/extras/migrations/0050_customfield_grouping.py +0 -1
  388. nautobot/extras/migrations/0051_add_job_task_queues.py +0 -1
  389. nautobot/extras/migrations/0052_configcontext_device_redundancy_groups.py +0 -1
  390. nautobot/extras/migrations/0053_relationship_required_on.py +0 -1
  391. nautobot/extras/migrations/0054_scheduledjob_kwargs_request_user_change.py +0 -1
  392. nautobot/extras/migrations/0055_configcontext_dynamic_groups.py +0 -1
  393. nautobot/extras/migrations/0056_objectchange_add_reverse_time_idx.py +0 -1
  394. nautobot/extras/migrations/0057_jobbutton.py +0 -1
  395. nautobot/extras/migrations/0058_jobresult_add_time_status_idxs.py +38 -0
  396. nautobot/extras/migrations/{0058_joblogentry_scheduledjob_webhook_data_migration.py → 0059_joblogentry_scheduledjob_webhook_data_migration.py} +1 -2
  397. nautobot/extras/migrations/{0059_alter_joblogentry_scheduledjob_webhook_fields.py → 0060_alter_joblogentry_scheduledjob_webhook_fields.py} +1 -2
  398. nautobot/extras/migrations/{0060_role_and_alter_status.py → 0061_role_and_alter_status.py} +1 -8
  399. nautobot/extras/migrations/{0061_collect_roles_from_related_apps_roles.py → 0062_collect_roles_from_related_apps_roles.py} +33 -33
  400. nautobot/extras/migrations/{0062_alter_role_options.py → 0063_alter_role_options.py} +1 -2
  401. nautobot/extras/migrations/{0063_alter_configcontext_and_add_new_role.py → 0064_alter_configcontext_and_add_new_role.py} +1 -2
  402. nautobot/extras/migrations/0065_configcontext_data_migrations.py +44 -0
  403. nautobot/extras/migrations/{0065_rename_configcontext_role.py → 0066_rename_configcontext_role.py} +1 -2
  404. nautobot/extras/migrations/{0066_jobresult__add_celery_fields.py → 0067_jobresult__add_celery_fields.py} +36 -3
  405. nautobot/extras/migrations/{0067_created_datetime.py → 0068_created_datetime.py} +1 -2
  406. nautobot/extras/migrations/{0068_remove_site_and_region_attributes_from_config_context.py → 0069_remove_site_and_region_attributes_from_config_context.py} +1 -2
  407. nautobot/extras/migrations/{0069_replace_related_names.py → 0070_replace_related_names.py} +1 -1
  408. nautobot/extras/migrations/{0070_rename_model_fields.py → 0071_rename_model_fields.py} +1 -2
  409. nautobot/extras/migrations/0072_job__unique_name_data_migration.py +86 -0
  410. nautobot/extras/migrations/{0072_job__unique_name.py → 0073_job__unique_name.py} +13 -10
  411. nautobot/extras/migrations/{0073_remove_gitrepository_fields.py → 0074_remove_gitrepository_fields.py} +1 -2
  412. nautobot/extras/migrations/{0074_rename_slug_to_key_for_custom_field.py → 0075_rename_slug_to_key_for_custom_field.py} +1 -1
  413. nautobot/extras/migrations/{0075_migrate_custom_field_data.py → 0076_migrate_custom_field_data.py} +1 -1
  414. nautobot/extras/migrations/{0076_remove_name_field_and_make_label_field_non_nullable.py → 0077_remove_name_field_and_make_label_field_non_nullable.py} +1 -1
  415. nautobot/extras/migrations/0078_remove_slug.py +45 -0
  416. nautobot/extras/migrations/0079_tagsfield.py +28 -0
  417. nautobot/extras/migrations/0080_rename_relationship_slug_to_key.py +17 -0
  418. nautobot/extras/migrations/0081_rename_relationship_name_to_label.py +29 -0
  419. nautobot/extras/migrations/0082_ensure_relationship_keys_are_unique.py +43 -0
  420. nautobot/extras/migrations/0083_rename_computed_field_slug_to_key.py +21 -0
  421. nautobot/extras/migrations/0084_taggeditem_cleanup.py +43 -0
  422. nautobot/extras/migrations/0085_taggeditem_uniqueness.py +22 -0
  423. nautobot/extras/migrations/0086_job__celery_task_fields__dryrun_support.py +81 -0
  424. nautobot/extras/migrations/0087_job__commit_default_data_migration.py +26 -0
  425. nautobot/extras/migrations/0088_joblogentry__log_level_default.py +17 -0
  426. nautobot/extras/migrations/0089_joblogentry__log_level_data_migration.py +34 -0
  427. nautobot/extras/migrations/0090_scheduledjob__data_migration.py +57 -0
  428. nautobot/extras/models/__init__.py +2 -3
  429. nautobot/extras/models/change_logging.py +0 -36
  430. nautobot/extras/models/customfields.py +39 -33
  431. nautobot/extras/models/datasources.py +48 -50
  432. nautobot/extras/models/groups.py +5 -12
  433. nautobot/extras/models/jobs.py +190 -323
  434. nautobot/extras/models/mixins.py +0 -71
  435. nautobot/extras/models/models.py +1 -22
  436. nautobot/extras/models/relationships.py +20 -21
  437. nautobot/extras/models/roles.py +0 -23
  438. nautobot/extras/models/secrets.py +2 -31
  439. nautobot/extras/models/statuses.py +6 -5
  440. nautobot/extras/models/tags.py +2 -17
  441. nautobot/extras/navigation.py +89 -307
  442. nautobot/extras/plugins/__init__.py +3 -121
  443. nautobot/extras/plugins/utils.py +0 -3
  444. nautobot/extras/plugins/validators.py +5 -4
  445. nautobot/extras/plugins/views.py +16 -4
  446. nautobot/extras/querysets.py +1 -7
  447. nautobot/extras/registry.py +3 -0
  448. nautobot/extras/signals.py +26 -60
  449. nautobot/extras/tables.py +42 -49
  450. nautobot/extras/tasks.py +0 -12
  451. nautobot/extras/templates/extras/configcontext.html +1 -1
  452. nautobot/extras/templates/extras/configcontextschema.html +16 -1
  453. nautobot/extras/templates/extras/customfield.html +0 -13
  454. nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -1
  455. nautobot/extras/templates/extras/gitrepository.html +3 -3
  456. nautobot/extras/templates/extras/inc/jobresult.html +10 -0
  457. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  458. nautobot/extras/templates/extras/job.html +35 -25
  459. nautobot/extras/templates/extras/job_approval_request.html +15 -30
  460. nautobot/extras/templates/extras/job_detail.html +13 -31
  461. nautobot/extras/templates/extras/job_edit.html +14 -17
  462. nautobot/extras/templates/extras/jobresult.html +24 -6
  463. nautobot/extras/templates/extras/objectchange_list.html +1 -1
  464. nautobot/extras/templates/extras/scheduledjob.html +2 -2
  465. nautobot/extras/templates/extras/secret.html +28 -0
  466. nautobot/extras/templates/extras/secret_edit.html +0 -1
  467. nautobot/extras/templates/extras/secretsgroup_edit.html +0 -1
  468. nautobot/extras/templatetags/custom_links.py +0 -2
  469. nautobot/extras/templatetags/job_buttons.py +1 -0
  470. nautobot/extras/templatetags/plugins.py +0 -1
  471. nautobot/extras/{tests/example_jobs → test_jobs}/api_test_job.py +13 -6
  472. nautobot/extras/test_jobs/atomic_transaction.py +53 -0
  473. nautobot/extras/test_jobs/dry_run.py +29 -0
  474. nautobot/extras/{tests/example_jobs/test_duplicate_name.py → test_jobs/duplicate_name.py} +4 -0
  475. nautobot/extras/test_jobs/duplicate_name2.py +9 -0
  476. nautobot/extras/test_jobs/fail.py +23 -0
  477. nautobot/extras/{tests/example_jobs/test_field_default.py → test_jobs/field_default.py} +4 -0
  478. nautobot/extras/{tests/example_jobs/test_field_order.py → test_jobs/field_order.py} +4 -0
  479. nautobot/extras/{tests/example_jobs/test_file_upload_fail.py → test_jobs/file_upload_fail.py} +11 -6
  480. nautobot/extras/test_jobs/file_upload_pass.py +25 -0
  481. nautobot/extras/test_jobs/has_sensitive_variables.py +25 -0
  482. nautobot/extras/test_jobs/ipaddress_vars.py +66 -0
  483. nautobot/extras/test_jobs/job_button_receiver.py +28 -0
  484. nautobot/extras/test_jobs/job_hook_receiver.py +29 -0
  485. nautobot/extras/test_jobs/job_variables.py +88 -0
  486. nautobot/extras/test_jobs/location_with_custom_field.py +45 -0
  487. nautobot/extras/test_jobs/log_redaction.py +20 -0
  488. nautobot/extras/test_jobs/log_skip_db_logging.py +17 -0
  489. nautobot/extras/test_jobs/modify_db.py +25 -0
  490. nautobot/extras/{tests/example_jobs/test_no_field_order.py → test_jobs/no_field_order.py} +4 -0
  491. nautobot/extras/test_jobs/object_var_optional.py +21 -0
  492. nautobot/extras/test_jobs/object_var_required.py +21 -0
  493. nautobot/extras/test_jobs/object_vars.py +26 -0
  494. nautobot/extras/test_jobs/pass.py +25 -0
  495. nautobot/extras/test_jobs/profiling.py +32 -0
  496. nautobot/extras/test_jobs/read_only_job.py +15 -0
  497. nautobot/extras/{tests/example_jobs/test_required_args.py → test_jobs/required_args.py} +4 -0
  498. nautobot/extras/{tests/example_jobs/test_soft_time_limit_greater_than_time_limit.py → test_jobs/soft_time_limit_greater_than_time_limit.py} +5 -1
  499. nautobot/extras/{tests/example_jobs/test_task_queues.py → test_jobs/task_queues.py} +5 -1
  500. nautobot/extras/tests/integration/__init__.py +3 -3
  501. nautobot/extras/tests/integration/test_computedfields.py +1 -1
  502. nautobot/extras/tests/integration/test_configcontextschema.py +7 -5
  503. nautobot/extras/tests/integration/test_customfields.py +4 -2
  504. nautobot/extras/tests/integration/test_dynamicgroups.py +2 -2
  505. nautobot/extras/tests/integration/test_jobs.py +25 -27
  506. nautobot/extras/tests/integration/test_notes.py +8 -4
  507. nautobot/extras/tests/integration/test_plugins.py +4 -4
  508. nautobot/extras/tests/integration/test_relationships.py +2 -2
  509. nautobot/extras/tests/test_api.py +371 -381
  510. nautobot/extras/tests/test_changelog.py +17 -16
  511. nautobot/extras/tests/test_context_managers.py +5 -6
  512. nautobot/extras/tests/test_customfields.py +112 -73
  513. nautobot/extras/tests/test_datasources.py +191 -117
  514. nautobot/extras/tests/test_dynamicgroups.py +45 -68
  515. nautobot/extras/tests/test_filters.py +170 -130
  516. nautobot/extras/tests/test_forms.py +107 -109
  517. nautobot/extras/tests/{test_scripts.py → test_job_variables.py} +43 -49
  518. nautobot/extras/tests/test_jobs.py +271 -273
  519. nautobot/extras/tests/test_management.py +3 -6
  520. nautobot/extras/tests/test_migrations.py +5 -3
  521. nautobot/extras/tests/test_models.py +121 -173
  522. nautobot/extras/tests/test_notes.py +0 -1
  523. nautobot/extras/tests/test_plugins.py +55 -89
  524. nautobot/extras/tests/test_relationships.py +174 -130
  525. nautobot/extras/tests/test_tags.py +6 -12
  526. nautobot/extras/tests/test_utils.py +31 -1
  527. nautobot/extras/tests/test_views.py +223 -184
  528. nautobot/extras/tests/test_webhooks.py +16 -15
  529. nautobot/extras/urls.py +69 -69
  530. nautobot/extras/utils.py +137 -163
  531. nautobot/extras/views.py +81 -153
  532. nautobot/ipam/api/fields.py +17 -0
  533. nautobot/ipam/api/serializers.py +77 -164
  534. nautobot/ipam/api/urls.py +4 -1
  535. nautobot/ipam/api/views.py +28 -19
  536. nautobot/ipam/apps.py +1 -0
  537. nautobot/ipam/choices.py +5 -12
  538. nautobot/ipam/constants.py +1 -0
  539. nautobot/ipam/factory.py +41 -30
  540. nautobot/ipam/filters.py +58 -25
  541. nautobot/ipam/forms.py +82 -211
  542. nautobot/ipam/graphql/types.py +0 -9
  543. nautobot/ipam/lookups.py +13 -8
  544. nautobot/ipam/management/commands/__init__.py +0 -0
  545. nautobot/ipam/management/commands/fix_prefix_broadcast.py +17 -0
  546. nautobot/ipam/migrations/0001_initial_part_1.py +0 -1
  547. nautobot/ipam/migrations/0002_initial_part_2.py +0 -1
  548. nautobot/ipam/migrations/0003_remove_max_length.py +0 -1
  549. nautobot/ipam/migrations/0004_fixup_p2p_broadcast.py +0 -1
  550. nautobot/ipam/migrations/0005_auto_slug.py +0 -1
  551. nautobot/ipam/migrations/0006_ipaddress_nat_outside_list.py +0 -1
  552. nautobot/ipam/migrations/0007_add_natural_indexing.py +0 -1
  553. nautobot/ipam/migrations/0008_prefix_vlan_vlangroup_location.py +0 -1
  554. nautobot/ipam/migrations/0009_alter_vlan_name.py +0 -1
  555. nautobot/ipam/migrations/0010_alter_ipam_role_add_new_role.py +1 -2
  556. nautobot/ipam/migrations/0011_migrate_ipam_role_data.py +32 -39
  557. nautobot/ipam/migrations/0012_rename_ipam_roles.py +0 -1
  558. nautobot/ipam/migrations/0013_delete_role.py +0 -1
  559. nautobot/ipam/migrations/0014_rename_foreign_keys_and_related_names.py +0 -1
  560. nautobot/ipam/migrations/0015_prefix_add_type.py +0 -1
  561. nautobot/ipam/migrations/0016_prefix_type_data_migration.py +0 -3
  562. nautobot/ipam/migrations/0017_prefix_remove_is_pool.py +0 -1
  563. nautobot/ipam/migrations/0018_remove_site_foreign_key_from_ipam_models.py +0 -1
  564. nautobot/ipam/migrations/0019_created_datetime.py +0 -1
  565. nautobot/ipam/migrations/0020_related_name_changes.py +1 -2
  566. nautobot/ipam/migrations/0021_prefix_add_rir_and_date_allocated.py +0 -1
  567. nautobot/ipam/migrations/0022_aggregate_to_prefix_data_migration.py +3 -5
  568. nautobot/ipam/migrations/0023_delete_aggregate.py +0 -1
  569. nautobot/ipam/migrations/0024_interface_to_ipaddress_m2m.py +0 -1
  570. nautobot/ipam/migrations/0025_interface_ipaddress_m2m_data_migration.py +0 -1
  571. nautobot/ipam/migrations/0026_ipaddress_remove_assigned_object.py +0 -1
  572. nautobot/ipam/migrations/0027_remove_rir_slug.py +16 -0
  573. nautobot/ipam/migrations/0028_tagsfield.py +44 -0
  574. nautobot/ipam/migrations/0029_ip_address_to_interface_uniqueness_constraints.py +18 -0
  575. nautobot/ipam/migrations/0030_ipam__namespaces.py +231 -0
  576. nautobot/ipam/migrations/0031_ipam__prefix__add_parent.py +58 -0
  577. nautobot/ipam/migrations/0032_ipam__namespaces_finish.py +63 -0
  578. nautobot/ipam/migrations/0033_fixup_null_statuses.py +26 -0
  579. nautobot/ipam/migrations/0034_status_nonnullable.py +36 -0
  580. nautobot/ipam/models.py +579 -368
  581. nautobot/ipam/navigation.py +36 -159
  582. nautobot/ipam/querysets.py +117 -90
  583. nautobot/ipam/signals.py +89 -0
  584. nautobot/ipam/tables.py +86 -28
  585. nautobot/ipam/templates/ipam/ipaddress.html +14 -30
  586. nautobot/ipam/templates/ipam/ipaddress_edit.html +1 -0
  587. nautobot/ipam/templates/ipam/namespace_ipaddresses.html +11 -0
  588. nautobot/ipam/templates/ipam/namespace_prefixes.html +11 -0
  589. nautobot/ipam/templates/ipam/namespace_retrieve.html +42 -0
  590. nautobot/ipam/templates/ipam/namespace_vrfs.html +11 -0
  591. nautobot/ipam/templates/ipam/prefix.html +27 -33
  592. nautobot/ipam/templates/ipam/prefix_edit.html +7 -1
  593. nautobot/ipam/templates/ipam/vlangroup.html +0 -13
  594. nautobot/ipam/templates/ipam/vrf.html +6 -4
  595. nautobot/ipam/templates/ipam/vrf_edit.html +20 -2
  596. nautobot/ipam/tests/integration/test_prefixes.py +4 -27
  597. nautobot/ipam/tests/test_api.py +60 -61
  598. nautobot/ipam/tests/test_filters.py +187 -126
  599. nautobot/ipam/tests/test_forms.py +12 -6
  600. nautobot/ipam/tests/test_graphql.py +8 -6
  601. nautobot/ipam/tests/test_migrations.py +8 -13
  602. nautobot/ipam/tests/test_models.py +426 -274
  603. nautobot/ipam/tests/test_ordering.py +6 -3
  604. nautobot/ipam/tests/test_querysets.py +340 -96
  605. nautobot/ipam/tests/test_views.py +100 -55
  606. nautobot/ipam/urls.py +28 -5
  607. nautobot/ipam/{utils.py → utils/__init__.py} +2 -2
  608. nautobot/ipam/utils/migrations.py +713 -0
  609. nautobot/ipam/views.py +237 -122
  610. nautobot/project-static/docs/404.html +1399 -166
  611. nautobot/project-static/docs/additional-features/caching.html +1416 -320
  612. nautobot/project-static/docs/additional-features/change-logging.html +1389 -190
  613. nautobot/project-static/docs/additional-features/config-contexts.html +1389 -190
  614. nautobot/project-static/docs/additional-features/graphql.html +1389 -190
  615. nautobot/project-static/docs/additional-features/healthcheck.html +1389 -190
  616. nautobot/project-static/docs/additional-features/job-scheduling-and-approvals.html +1393 -190
  617. nautobot/project-static/docs/additional-features/jobs.html +1677 -460
  618. nautobot/project-static/docs/additional-features/napalm.html +1389 -190
  619. nautobot/project-static/docs/additional-features/prometheus-metrics.html +1389 -190
  620. nautobot/project-static/docs/additional-features/template-filters.html +1389 -190
  621. nautobot/project-static/docs/administration/celery-queues.html +1389 -190
  622. nautobot/project-static/docs/administration/nautobot-server.html +1553 -375
  623. nautobot/project-static/docs/administration/nautobot-shell.html +1395 -196
  624. nautobot/project-static/docs/administration/permissions.html +1389 -190
  625. nautobot/project-static/docs/administration/replicating-nautobot.html +1387 -207
  626. nautobot/project-static/docs/apps/index.html +1389 -190
  627. nautobot/project-static/docs/apps/nautobot-apps.html +1387 -175
  628. nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js +29 -0
  629. nautobot/project-static/docs/assets/javascripts/bundle.51198bba.min.js.map +8 -0
  630. nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js → search.208ed371.min.js} +9 -15
  631. nautobot/project-static/docs/assets/javascripts/workers/{search.16e2a7d4.min.js.map → search.208ed371.min.js.map} +4 -4
  632. nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css +1 -0
  633. nautobot/project-static/docs/assets/stylesheets/main.ded33207.min.css.map +1 -0
  634. nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css +1 -0
  635. nautobot/project-static/docs/assets/stylesheets/palette.a0c5b2b5.min.css.map +1 -0
  636. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +1775 -590
  637. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +1389 -190
  638. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +3588 -1922
  639. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +1461 -262
  640. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +1401 -170
  641. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +1396 -191
  642. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +2095 -894
  643. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +2357 -1194
  644. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2258 -940
  645. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +1389 -190
  646. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +1400 -201
  647. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +11068 -7861
  648. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2867 -2224
  649. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +1389 -190
  650. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +2641 -1573
  651. nautobot/project-static/docs/configuration/authentication/ldap.html +1389 -190
  652. nautobot/project-static/docs/configuration/authentication/remote.html +1389 -190
  653. nautobot/project-static/docs/configuration/authentication/sso.html +1389 -190
  654. nautobot/project-static/docs/configuration/index.html +1398 -199
  655. nautobot/project-static/docs/configuration/optional-settings.html +1418 -274
  656. nautobot/project-static/docs/configuration/required-settings.html +1419 -287
  657. nautobot/project-static/docs/core-functionality/circuits.html +1446 -247
  658. nautobot/project-static/docs/core-functionality/device-types.html +1448 -249
  659. nautobot/project-static/docs/core-functionality/devices.html +1452 -249
  660. nautobot/project-static/docs/core-functionality/ipam.html +1452 -253
  661. nautobot/project-static/docs/core-functionality/power.html +1448 -249
  662. nautobot/project-static/docs/core-functionality/secrets.html +1448 -249
  663. nautobot/project-static/docs/core-functionality/services.html +1448 -249
  664. nautobot/project-static/docs/core-functionality/sites-and-racks.html +1448 -249
  665. nautobot/project-static/docs/core-functionality/tenancy.html +1448 -249
  666. nautobot/project-static/docs/core-functionality/virtualization.html +1452 -249
  667. nautobot/project-static/docs/core-functionality/vlans.html +1448 -249
  668. nautobot/project-static/docs/development/application-registry.html +1393 -214
  669. nautobot/project-static/docs/development/best-practices.html +1392 -192
  670. nautobot/project-static/docs/development/docker-compose-advanced-use-cases.html +1390 -191
  671. nautobot/project-static/docs/development/extending-models.html +1443 -257
  672. nautobot/project-static/docs/development/generic-views.html +1403 -174
  673. nautobot/project-static/docs/development/getting-started.html +1568 -262
  674. nautobot/project-static/docs/development/homepage.html +1389 -190
  675. nautobot/project-static/docs/development/index.html +1389 -190
  676. nautobot/project-static/docs/development/model-features.html +1389 -190
  677. nautobot/project-static/docs/development/natural-keys.html +1389 -190
  678. nautobot/project-static/docs/development/navigation-menu.html +1451 -330
  679. nautobot/project-static/docs/development/react-ui.html +4199 -0
  680. nautobot/project-static/docs/development/release-checklist.html +1392 -193
  681. nautobot/project-static/docs/development/role-internals.html +1402 -172
  682. nautobot/project-static/docs/development/style-guide.html +1399 -199
  683. nautobot/project-static/docs/development/templates.html +1391 -191
  684. nautobot/project-static/docs/development/testing.html +1389 -190
  685. nautobot/project-static/docs/development/user-preferences.html +1389 -190
  686. nautobot/project-static/docs/docker/index.html +1408 -206
  687. nautobot/project-static/docs/index.html +1397 -180
  688. nautobot/project-static/docs/installation/centos.html +1401 -170
  689. nautobot/project-static/docs/installation/external-authentication.html +1389 -190
  690. nautobot/project-static/docs/installation/http-server.html +1389 -190
  691. nautobot/project-static/docs/installation/index.html +1394 -191
  692. nautobot/project-static/docs/installation/migrating-from-netbox.html +1452 -305
  693. nautobot/project-static/docs/installation/migrating-from-postgresql.html +1390 -191
  694. nautobot/project-static/docs/installation/nautobot.html +1390 -191
  695. nautobot/project-static/docs/installation/region-and-site-data-migration-guide.html +1389 -190
  696. nautobot/project-static/docs/installation/selinux-troubleshooting.html +1401 -170
  697. nautobot/project-static/docs/installation/services.html +1389 -190
  698. nautobot/project-static/docs/installation/tables/v2-api-behavior-changes.yaml +70 -0
  699. nautobot/project-static/docs/installation/tables/v2-api-removed-fields.yaml +142 -0
  700. nautobot/project-static/docs/installation/tables/v2-api-renamed-fields.yaml +124 -0
  701. nautobot/project-static/docs/installation/tables/v2-code-location-changes.yaml +241 -0
  702. nautobot/project-static/docs/installation/tables/v2-code-removals.yaml +67 -0
  703. nautobot/project-static/docs/installation/tables/v2-database-behavior-changes.yaml +37 -0
  704. nautobot/project-static/docs/installation/tables/v2-database-removed-fields.yaml +166 -0
  705. nautobot/project-static/docs/installation/tables/v2-database-renamed-fields.yaml +340 -0
  706. nautobot/project-static/docs/installation/tables/v2-filters-corrected-fields.yaml +28 -0
  707. nautobot/project-static/docs/installation/tables/v2-filters-enhanced-fields.yaml +241 -0
  708. nautobot/project-static/docs/installation/tables/v2-filters-removed-fields.yaml +553 -0
  709. nautobot/project-static/docs/installation/tables/v2-filters-renamed-fields.yaml +223 -0
  710. nautobot/project-static/docs/installation/tables/v2-logging-renamed-loggers.yaml +23 -0
  711. nautobot/project-static/docs/installation/ubuntu.html +1401 -170
  712. nautobot/project-static/docs/installation/upgrading-from-nautobot-v1.html +4254 -1923
  713. nautobot/project-static/docs/installation/upgrading.html +1395 -192
  714. nautobot/project-static/docs/models/circuits/circuit.html +1427 -174
  715. nautobot/project-static/docs/models/circuits/circuittermination.html +1427 -174
  716. nautobot/project-static/docs/models/circuits/circuittype.html +1427 -174
  717. nautobot/project-static/docs/models/circuits/provider.html +1427 -174
  718. nautobot/project-static/docs/models/circuits/providernetwork.html +1427 -174
  719. nautobot/project-static/docs/models/dcim/cable.html +1458 -174
  720. nautobot/project-static/docs/models/dcim/consoleport.html +1427 -174
  721. nautobot/project-static/docs/models/dcim/consoleporttemplate.html +1427 -174
  722. nautobot/project-static/docs/models/dcim/consoleserverport.html +1427 -174
  723. nautobot/project-static/docs/models/dcim/consoleserverporttemplate.html +1427 -174
  724. nautobot/project-static/docs/models/dcim/device.html +1431 -174
  725. nautobot/project-static/docs/models/dcim/devicebay.html +1427 -174
  726. nautobot/project-static/docs/models/dcim/devicebaytemplate.html +1427 -174
  727. nautobot/project-static/docs/models/dcim/deviceredundancygroup.html +1522 -177
  728. nautobot/project-static/docs/models/dcim/devicetype.html +1427 -174
  729. nautobot/project-static/docs/models/dcim/frontport.html +1427 -174
  730. nautobot/project-static/docs/models/dcim/frontporttemplate.html +1427 -174
  731. nautobot/project-static/docs/models/dcim/interface.html +1427 -174
  732. nautobot/project-static/docs/models/dcim/interfacetemplate.html +1427 -174
  733. nautobot/project-static/docs/models/dcim/inventoryitem.html +1427 -174
  734. nautobot/project-static/docs/models/dcim/location.html +1427 -174
  735. nautobot/project-static/docs/models/dcim/locationtype.html +1427 -174
  736. nautobot/project-static/docs/models/dcim/manufacturer.html +1427 -174
  737. nautobot/project-static/docs/models/dcim/platform.html +1427 -174
  738. nautobot/project-static/docs/models/dcim/powerfeed.html +1425 -172
  739. nautobot/project-static/docs/models/dcim/poweroutlet.html +1427 -174
  740. nautobot/project-static/docs/models/dcim/poweroutlettemplate.html +1427 -174
  741. nautobot/project-static/docs/models/dcim/powerpanel.html +1425 -172
  742. nautobot/project-static/docs/models/dcim/powerport.html +1427 -174
  743. nautobot/project-static/docs/models/dcim/powerporttemplate.html +1427 -174
  744. nautobot/project-static/docs/models/dcim/rack.html +1427 -174
  745. nautobot/project-static/docs/models/dcim/rackgroup.html +1427 -174
  746. nautobot/project-static/docs/models/dcim/rackreservation.html +1427 -174
  747. nautobot/project-static/docs/models/dcim/rearport.html +1427 -174
  748. nautobot/project-static/docs/models/dcim/rearporttemplate.html +1427 -174
  749. nautobot/project-static/docs/models/dcim/region.html +1401 -170
  750. nautobot/project-static/docs/models/dcim/site.html +1401 -170
  751. nautobot/project-static/docs/models/dcim/virtualchassis.html +1425 -172
  752. nautobot/project-static/docs/models/extras/computedfield.html +1393 -194
  753. nautobot/project-static/docs/models/extras/configcontext.html +1465 -174
  754. nautobot/project-static/docs/models/extras/configcontextschema.html +1421 -168
  755. nautobot/project-static/docs/models/extras/customfield.html +1389 -190
  756. nautobot/project-static/docs/models/extras/customlink.html +1389 -190
  757. nautobot/project-static/docs/models/extras/dynamicgroup.html +1398 -199
  758. nautobot/project-static/docs/models/extras/exporttemplate.html +1389 -190
  759. nautobot/project-static/docs/models/extras/gitrepository.html +1393 -190
  760. nautobot/project-static/docs/models/extras/graphqlquery.html +1469 -171
  761. nautobot/project-static/docs/models/extras/imageattachment.html +1434 -181
  762. nautobot/project-static/docs/models/extras/job.html +1411 -157
  763. nautobot/project-static/docs/models/extras/jobbutton.html +1410 -207
  764. nautobot/project-static/docs/models/extras/jobhook.html +1397 -194
  765. nautobot/project-static/docs/models/extras/joblogentry.html +1408 -155
  766. nautobot/project-static/docs/models/extras/jobresult.html +1417 -159
  767. nautobot/project-static/docs/models/extras/note.html +1389 -190
  768. nautobot/project-static/docs/models/extras/relationship.html +1391 -192
  769. nautobot/project-static/docs/models/extras/role.html +1495 -198
  770. nautobot/project-static/docs/models/extras/secret.html +1492 -201
  771. nautobot/project-static/docs/models/extras/secretsgroup.html +1410 -157
  772. nautobot/project-static/docs/models/extras/status.html +1381 -221
  773. nautobot/project-static/docs/models/extras/tag.html +1389 -190
  774. nautobot/project-static/docs/models/extras/webhook.html +1389 -190
  775. nautobot/project-static/docs/models/ipam/ipaddress.html +1488 -200
  776. nautobot/project-static/docs/models/ipam/prefix.html +1410 -157
  777. nautobot/project-static/docs/models/ipam/rir.html +1410 -157
  778. nautobot/project-static/docs/models/ipam/routetarget.html +1410 -157
  779. nautobot/project-static/docs/models/ipam/service.html +1410 -157
  780. nautobot/project-static/docs/models/ipam/vlan.html +1410 -157
  781. nautobot/project-static/docs/models/ipam/vlangroup.html +1410 -157
  782. nautobot/project-static/docs/models/ipam/vrf.html +1410 -161
  783. nautobot/project-static/docs/models/tenancy/tenant.html +1412 -159
  784. nautobot/project-static/docs/models/tenancy/tenantgroup.html +1412 -159
  785. nautobot/project-static/docs/models/users/objectpermission.html +1462 -171
  786. nautobot/project-static/docs/models/users/token.html +1410 -157
  787. nautobot/project-static/docs/models/virtualization/cluster.html +1410 -157
  788. nautobot/project-static/docs/models/virtualization/clustergroup.html +1410 -157
  789. nautobot/project-static/docs/models/virtualization/clustertype.html +1410 -157
  790. nautobot/project-static/docs/models/virtualization/virtualmachine.html +1414 -157
  791. nautobot/project-static/docs/models/virtualization/vminterface.html +1410 -157
  792. nautobot/project-static/docs/objects.inv +0 -0
  793. nautobot/project-static/docs/plugins/development.html +1916 -646
  794. nautobot/project-static/docs/plugins/index.html +1389 -190
  795. nautobot/project-static/docs/plugins/porting-from-netbox.html +1389 -190
  796. nautobot/project-static/docs/release-notes/index.html +1389 -190
  797. nautobot/project-static/docs/release-notes/version-1.0.html +1389 -190
  798. nautobot/project-static/docs/release-notes/version-1.1.html +1389 -190
  799. nautobot/project-static/docs/release-notes/version-1.2.html +1389 -190
  800. nautobot/project-static/docs/release-notes/version-1.3.html +1389 -190
  801. nautobot/project-static/docs/release-notes/version-1.4.html +1389 -190
  802. nautobot/project-static/docs/release-notes/version-1.5.html +2016 -397
  803. nautobot/project-static/docs/release-notes/version-2.0.html +1935 -287
  804. nautobot/project-static/docs/requirements.txt +5 -4
  805. nautobot/project-static/docs/rest-api/authentication.html +1389 -190
  806. nautobot/project-static/docs/rest-api/filtering.html +1389 -190
  807. nautobot/project-static/docs/rest-api/overview.html +2002 -576
  808. nautobot/project-static/docs/rest-api/ui-related-endpoints.html +4057 -0
  809. nautobot/project-static/docs/search/search_index.json +1 -1
  810. nautobot/project-static/docs/sitemap.xml +197 -187
  811. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  812. nautobot/project-static/docs/user-guides/custom-fields.html +1390 -191
  813. nautobot/project-static/docs/user-guides/getting-started/creating-devices.html +1392 -193
  814. nautobot/project-static/docs/user-guides/getting-started/index.html +1388 -189
  815. nautobot/project-static/docs/user-guides/getting-started/interfaces.html +1388 -189
  816. nautobot/project-static/docs/user-guides/getting-started/ipam.html +1386 -187
  817. nautobot/project-static/docs/user-guides/getting-started/platforms.html +1448 -249
  818. nautobot/project-static/docs/user-guides/getting-started/regions.html +1411 -212
  819. nautobot/project-static/docs/user-guides/getting-started/search-bar.html +1395 -196
  820. nautobot/project-static/docs/user-guides/getting-started/tenants.html +1448 -249
  821. nautobot/project-static/docs/user-guides/getting-started/vlans-and-vlan-groups.html +1448 -249
  822. nautobot/project-static/docs/user-guides/git-data-source.html +1405 -206
  823. nautobot/project-static/docs/user-guides/graphql.html +1402 -203
  824. nautobot/project-static/docs/user-guides/relationships.html +1448 -249
  825. nautobot/project-static/docs/user-guides/s3-django-storage.html +1448 -249
  826. nautobot/project-static/js/forms.js +16 -9
  827. nautobot/project-static/js/theme.js +5 -0
  828. nautobot/tenancy/api/serializers.py +4 -34
  829. nautobot/tenancy/api/urls.py +1 -1
  830. nautobot/tenancy/filters/__init__.py +9 -7
  831. nautobot/tenancy/filters/mixins.py +3 -2
  832. nautobot/tenancy/forms.py +3 -36
  833. nautobot/tenancy/migrations/0001_initial.py +0 -1
  834. nautobot/tenancy/migrations/0002_auto_slug.py +0 -1
  835. nautobot/tenancy/migrations/0003_mptt_to_tree_queries.py +0 -1
  836. nautobot/tenancy/migrations/0004_change_tree_manager_on_tree_models.py +0 -1
  837. nautobot/tenancy/migrations/0005_rename_foreign_keys_and_related_names.py +0 -1
  838. nautobot/tenancy/migrations/0006_created_datetime.py +0 -1
  839. nautobot/tenancy/migrations/0007_remove_tenant_tenantgroup_slug.py +20 -0
  840. nautobot/tenancy/migrations/0008_tagsfield.py +19 -0
  841. nautobot/tenancy/models.py +0 -30
  842. nautobot/tenancy/navigation.py +6 -39
  843. nautobot/tenancy/tables.py +4 -4
  844. nautobot/tenancy/templates/tenancy/tenant.html +12 -12
  845. nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -1
  846. nautobot/tenancy/templates/tenancy/tenantgroup.html +1 -1
  847. nautobot/tenancy/tests/test_api.py +1 -12
  848. nautobot/tenancy/tests/test_filters.py +20 -12
  849. nautobot/tenancy/tests/test_views.py +11 -29
  850. nautobot/tenancy/urls.py +10 -10
  851. nautobot/tenancy/views.py +0 -3
  852. nautobot/ui/.eslintignore +6 -0
  853. nautobot/ui/.gitignore +10 -0
  854. nautobot/ui/.prettierignore +9 -0
  855. nautobot/ui/.prettierrc +4 -0
  856. nautobot/ui/README.md +33 -0
  857. nautobot/ui/app_imports.js.j2 +7 -0
  858. nautobot/ui/craco.config.js +46 -0
  859. nautobot/ui/jsconfig-base.json +11 -0
  860. nautobot/ui/jsconfig.json +5 -0
  861. nautobot/ui/lib/nautobot-craco-alias-plugin.js +40 -0
  862. nautobot/ui/package-lock.json +21451 -0
  863. nautobot/ui/package.json +70 -0
  864. nautobot/ui/public/index.html +47 -0
  865. nautobot/ui/public/logo192.png +0 -0
  866. nautobot/ui/public/logo512.png +0 -0
  867. nautobot/ui/public/manifest.json +25 -0
  868. nautobot/ui/public/nautobot_logo.svg +131 -0
  869. nautobot/ui/public/robots.txt +3 -0
  870. nautobot/ui/src/App.js +71 -0
  871. nautobot/ui/src/components/AppFullWidthComponents.js +8 -0
  872. nautobot/ui/src/components/AppTab.js +40 -0
  873. nautobot/ui/src/components/Apps.js +60 -0
  874. nautobot/ui/src/components/HomeChangelogPanel.js +98 -0
  875. nautobot/ui/src/components/HomePanel.js +58 -0
  876. nautobot/ui/src/components/JobHistoryTable.js +78 -0
  877. nautobot/ui/src/components/Layout.js +53 -0
  878. nautobot/ui/src/components/LoadingWidget.js +25 -0
  879. nautobot/ui/src/components/Navbar.js +116 -0
  880. nautobot/ui/src/components/NotificationPopover.js +27 -0
  881. nautobot/ui/src/components/ObjectListTable.js +209 -0
  882. nautobot/ui/src/components/ReferenceDataTag.js +35 -0
  883. nautobot/ui/src/components/RouterButton.js +10 -0
  884. nautobot/ui/src/components/RouterLink.js +10 -0
  885. nautobot/ui/src/components/SidebarNav.js +147 -0
  886. nautobot/ui/src/components/Table.js +48 -0
  887. nautobot/ui/src/components/TableItem.js +71 -0
  888. nautobot/ui/src/components/__tests__/AppFullWidthComponents.test.js +16 -0
  889. nautobot/ui/src/components/__tests__/AppTab.test.js +21 -0
  890. nautobot/ui/src/components/__tests__/Apps.test.js +14 -0
  891. nautobot/ui/src/components/__tests__/Layout.test.js +33 -0
  892. nautobot/ui/src/components/__tests__/Table.test.js +36 -0
  893. nautobot/ui/src/components/__tests__/TableItem.test.js +37 -0
  894. nautobot/ui/src/components/__tests__/paginator.test.js +43 -0
  895. nautobot/ui/src/components/__tests__/paginator_form.test.js +13 -0
  896. nautobot/ui/src/components/pagination.js +93 -0
  897. nautobot/ui/src/components/paginator.js +79 -0
  898. nautobot/ui/src/components/paginator_form.js +43 -0
  899. nautobot/ui/src/components/usePagination.js +57 -0
  900. nautobot/ui/src/constants/apiPath.js +10 -0
  901. nautobot/ui/src/constants/icons.js +15 -0
  902. nautobot/ui/src/constants/size.js +15 -0
  903. nautobot/ui/src/index.js +65 -0
  904. nautobot/ui/src/reportWebVitals.js +15 -0
  905. nautobot/ui/src/router.js +77 -0
  906. nautobot/ui/src/utils/api.js +131 -0
  907. nautobot/ui/src/utils/app-import.js +15 -0
  908. nautobot/ui/src/utils/color.js +15 -0
  909. nautobot/ui/src/utils/date.js +14 -0
  910. nautobot/ui/src/utils/index.js +15 -0
  911. nautobot/ui/src/utils/navigation.js +32 -0
  912. nautobot/ui/src/utils/session.js +64 -0
  913. nautobot/ui/src/utils/store.js +242 -0
  914. nautobot/ui/src/utils/string.js +6 -0
  915. nautobot/ui/src/utils/url.js +4 -0
  916. nautobot/ui/src/views/Home.js +138 -0
  917. nautobot/ui/src/views/InstalledApps.js +80 -0
  918. nautobot/ui/src/views/Login.js +48 -0
  919. nautobot/ui/src/views/Logout.js +20 -0
  920. nautobot/ui/src/views/__tests__/BSCreateViewTemplate.test.js +11 -0
  921. nautobot/ui/src/views/__tests__/BSListViewTemplate.test.js +107 -0
  922. nautobot/ui/src/views/__tests__/Login.test.js +15 -0
  923. nautobot/ui/src/views/generic/GenericView.js +142 -0
  924. nautobot/ui/src/views/generic/ObjectCreate.js +96 -0
  925. nautobot/ui/src/views/generic/ObjectList.js +127 -0
  926. nautobot/ui/src/views/generic/ObjectRetrieve.js +551 -0
  927. nautobot/users/admin.py +1 -1
  928. nautobot/users/api/serializers.py +51 -61
  929. nautobot/users/api/urls.py +1 -1
  930. nautobot/users/api/views.py +53 -2
  931. nautobot/users/migrations/0001_initial.py +0 -1
  932. nautobot/users/migrations/0002_token_ordering_by_created.py +0 -1
  933. nautobot/users/migrations/0003_alter_user_options.py +0 -1
  934. nautobot/users/migrations/0004_alter_user_managers.py +0 -1
  935. nautobot/users/tests/test_api.py +109 -28
  936. nautobot/users/tests/test_filters.py +0 -4
  937. nautobot/users/tests/test_models.py +0 -1
  938. nautobot/users/views.py +0 -7
  939. nautobot/virtualization/api/serializers.py +18 -132
  940. nautobot/virtualization/api/urls.py +1 -1
  941. nautobot/virtualization/api/views.py +1 -22
  942. nautobot/virtualization/choices.py +0 -2
  943. nautobot/virtualization/filters.py +12 -7
  944. nautobot/virtualization/forms.py +21 -117
  945. nautobot/virtualization/migrations/0001_initial.py +0 -1
  946. nautobot/virtualization/migrations/0002_virtualmachine_local_context_schema.py +0 -1
  947. nautobot/virtualization/migrations/0003_vminterface_verbose_name.py +0 -1
  948. nautobot/virtualization/migrations/0004_auto_slug.py +0 -1
  949. nautobot/virtualization/migrations/0005_add_natural_indexing.py +0 -1
  950. nautobot/virtualization/migrations/0006_vminterface_status.py +0 -1
  951. nautobot/virtualization/migrations/0007_vminterface_status_data_migration.py +0 -1
  952. nautobot/virtualization/migrations/0008_vminterface_parent.py +0 -1
  953. nautobot/virtualization/migrations/0009_cluster_location.py +0 -1
  954. nautobot/virtualization/migrations/0010_vminterface_mac_address_data_migration.py +0 -1
  955. nautobot/virtualization/migrations/0011_alter_vminterface_mac_address.py +0 -1
  956. nautobot/virtualization/migrations/0012_alter_virtualmachine_role_add_new_role.py +1 -2
  957. nautobot/virtualization/migrations/0013_migrate_virtualmachine_role_data.py +18 -12
  958. nautobot/virtualization/migrations/0014_rename_virtualmachine_roles.py +0 -1
  959. nautobot/virtualization/migrations/0015_rename_foreignkey_fields.py +1 -2
  960. nautobot/virtualization/migrations/0016_remove_site_foreign_key_from_cluster_class.py +0 -1
  961. nautobot/virtualization/migrations/0017_created_datetime.py +0 -1
  962. nautobot/virtualization/migrations/0018_related_name_changes.py +1 -2
  963. nautobot/virtualization/migrations/0019_vminterface_ip_addresses_m2m.py +0 -1
  964. nautobot/virtualization/migrations/0020_remove_clustergroup_clustertype_slug.py +20 -0
  965. nautobot/virtualization/migrations/0021_tagsfield_and_vminterface_to_primarymodel.py +39 -0
  966. nautobot/virtualization/migrations/0022_vminterface_timestamps_data_migration.py +17 -0
  967. nautobot/virtualization/migrations/0023_ipam__namespaces.py +25 -0
  968. nautobot/virtualization/migrations/0024_fixup_null_statuses.py +25 -0
  969. nautobot/virtualization/migrations/0025_status_nonnullable.py +29 -0
  970. nautobot/virtualization/models.py +39 -131
  971. nautobot/virtualization/navigation.py +18 -99
  972. nautobot/virtualization/tables.py +4 -4
  973. nautobot/virtualization/templates/virtualization/virtualmachine.html +13 -2
  974. nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +6 -0
  975. nautobot/virtualization/tests/test_api.py +42 -52
  976. nautobot/virtualization/tests/test_filters.py +98 -75
  977. nautobot/virtualization/tests/test_models.py +36 -13
  978. nautobot/virtualization/tests/test_views.py +68 -73
  979. nautobot/virtualization/urls.py +10 -10
  980. nautobot/virtualization/views.py +8 -14
  981. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/METADATA +15 -22
  982. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/RECORD +987 -834
  983. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/WHEEL +1 -1
  984. nautobot/circuits/api/nested_serializers.py +0 -69
  985. nautobot/core/templates/plugin_template/navigation.py-tpl +0 -22
  986. nautobot/dcim/api/nested_serializers.py +0 -356
  987. nautobot/dcim/templates/dcim/device_import.html +0 -5
  988. nautobot/dcim/templates/dcim/device_import_child.html +0 -5
  989. nautobot/dcim/templates/dcim/inc/device_import_header.html +0 -4
  990. nautobot/extras/api/nested_serializers.py +0 -353
  991. nautobot/extras/migrations/0064_configcontext_data_migrations.py +0 -42
  992. nautobot/extras/migrations/0071_job__unique_name_data_migration.py +0 -47
  993. nautobot/extras/reports.py +0 -60
  994. nautobot/extras/scripts.py +0 -72
  995. nautobot/extras/tests/example_jobs/script_variables.py +0 -67
  996. nautobot/extras/tests/example_jobs/test_duplicate_name2.py +0 -5
  997. nautobot/extras/tests/example_jobs/test_fail.py +0 -16
  998. nautobot/extras/tests/example_jobs/test_file_upload_pass.py +0 -20
  999. nautobot/extras/tests/example_jobs/test_ipaddress_vars.py +0 -52
  1000. nautobot/extras/tests/example_jobs/test_job_button_receiver.py +0 -21
  1001. nautobot/extras/tests/example_jobs/test_job_hook_receiver.py +0 -20
  1002. nautobot/extras/tests/example_jobs/test_location_with_custom_field.py +0 -35
  1003. nautobot/extras/tests/example_jobs/test_log_redaction.py +0 -14
  1004. nautobot/extras/tests/example_jobs/test_modify_db.py +0 -19
  1005. nautobot/extras/tests/example_jobs/test_object_var_optional.py +0 -14
  1006. nautobot/extras/tests/example_jobs/test_object_var_required.py +0 -14
  1007. nautobot/extras/tests/example_jobs/test_object_vars.py +0 -29
  1008. nautobot/extras/tests/example_jobs/test_pass.py +0 -19
  1009. nautobot/extras/tests/example_jobs/test_read_only_fail.py +0 -24
  1010. nautobot/extras/tests/example_jobs/test_read_only_no_commit_field.py +0 -10
  1011. nautobot/extras/tests/example_jobs/test_read_only_pass.py +0 -22
  1012. nautobot/ipam/api/nested_serializers.py +0 -143
  1013. nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js +0 -29
  1014. nautobot/project-static/docs/assets/javascripts/bundle.5a2dcb6a.min.js.map +0 -8
  1015. nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js +0 -18
  1016. nautobot/project-static/docs/assets/javascripts/extra/bundle.5f09fbc3.min.js.map +0 -8
  1017. nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css +0 -1
  1018. nautobot/project-static/docs/assets/stylesheets/extra.0d2c79a8.min.css.map +0 -1
  1019. nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css +0 -1
  1020. nautobot/project-static/docs/assets/stylesheets/main.975780f9.min.css.map +0 -1
  1021. nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css +0 -1
  1022. nautobot/project-static/docs/assets/stylesheets/palette.2505c338.min.css.map +0 -1
  1023. nautobot/tenancy/api/nested_serializers.py +0 -31
  1024. nautobot/users/api/nested_serializers.py +0 -67
  1025. nautobot/virtualization/api/nested_serializers.py +0 -65
  1026. /nautobot/extras/{tests/example_jobs → test_jobs}/__init__.py +0 -0
  1027. /nautobot/{dcim/models/sites.py → ipam/management/__init__.py} +0 -0
  1028. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/LICENSE.txt +0 -0
  1029. {nautobot-2.0.0a2.dist-info → nautobot-2.0.0b1.dist-info}/entry_points.txt +0 -0
@@ -79,7 +79,7 @@ from nautobot.extras.models import (
79
79
  Webhook,
80
80
  )
81
81
  from nautobot.ipam.filters import VLANFilterSet
82
- from nautobot.ipam.models import IPAddress, VLAN
82
+ from nautobot.ipam.models import IPAddress, VLAN, Namespace, Prefix
83
83
  from nautobot.tenancy.models import Tenant, TenantGroup
84
84
  from nautobot.virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
85
85
 
@@ -95,7 +95,7 @@ class ComputedFieldTestCase(FilterTestCases.FilterTestCase):
95
95
  def setUpTestData(cls):
96
96
  ComputedField.objects.create(
97
97
  content_type=ContentType.objects.get_for_model(Location),
98
- slug="computed_field_one",
98
+ key="computed_field_one",
99
99
  label="Computed Field One",
100
100
  template="{{ obj.name }} is the name of this location.",
101
101
  fallback_value="An error occurred while rendering this template.",
@@ -104,7 +104,7 @@ class ComputedFieldTestCase(FilterTestCases.FilterTestCase):
104
104
  # Field whose template will raise a TemplateError
105
105
  ComputedField.objects.create(
106
106
  content_type=ContentType.objects.get_for_model(Location),
107
- slug="bad_computed_field",
107
+ key="bad_computed_field",
108
108
  label="Bad Computed Field",
109
109
  template="{{ something_that_throws_an_err | not_a_real_filter }} bad data",
110
110
  fallback_value="This template has errored",
@@ -113,7 +113,7 @@ class ComputedFieldTestCase(FilterTestCases.FilterTestCase):
113
113
  # Field whose template will raise a TypeError
114
114
  ComputedField.objects.create(
115
115
  content_type=ContentType.objects.get_for_model(Location),
116
- slug="worse_computed_field",
116
+ key="worse_computed_field",
117
117
  label="Worse Computed Field",
118
118
  template="{{ obj.images | list }}",
119
119
  fallback_value="Another template error",
@@ -121,15 +121,15 @@ class ComputedFieldTestCase(FilterTestCases.FilterTestCase):
121
121
  )
122
122
  ComputedField.objects.create(
123
123
  content_type=ContentType.objects.get_for_model(Device),
124
- slug="device_computed_field",
124
+ key="device_computed_field",
125
125
  label="Device Computed Field",
126
126
  template="Hello, world.",
127
127
  fallback_value="This template has errored",
128
128
  weight=100,
129
129
  )
130
130
 
131
- def test_slug(self):
132
- params = {"slug": ["device_computed_field", "worse_computed_field"]}
131
+ def test_key(self):
132
+ params = {"key": ["device_computed_field", "worse_computed_field"]}
133
133
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
134
134
 
135
135
  def test_content_type(self):
@@ -177,7 +177,7 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
177
177
  device_roles = Role.objects.get_for_model(Device)
178
178
  cls.device_roles = device_roles
179
179
 
180
- manufacturer = Manufacturer.objects.create(name="Manufacturer 1", slug="manufacturer-1")
180
+ manufacturer = Manufacturer.objects.first()
181
181
 
182
182
  device_types = (
183
183
  DeviceType.objects.create(model="Device Type 1", slug="device-type-1", manufacturer=manufacturer),
@@ -186,22 +186,18 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
186
186
  )
187
187
  cls.device_types = device_types
188
188
 
189
- platforms = (
190
- Platform.objects.create(name="Platform 1", slug="platform-1"),
191
- Platform.objects.create(name="Platform 2", slug="platform-2"),
192
- Platform.objects.create(name="Platform 3", slug="platform-3"),
193
- )
189
+ platforms = Platform.objects.all()[:3]
194
190
  cls.platforms = platforms
195
191
 
196
192
  cls.locations = Location.objects.all()[:3]
197
193
 
198
194
  cluster_groups = (
199
- ClusterGroup.objects.create(name="Cluster Group 1", slug="cluster-group-1"),
200
- ClusterGroup.objects.create(name="Cluster Group 2", slug="cluster-group-2"),
201
- ClusterGroup.objects.create(name="Cluster Group 3", slug="cluster-group-3"),
195
+ ClusterGroup.objects.create(name="Cluster Group 1"),
196
+ ClusterGroup.objects.create(name="Cluster Group 2"),
197
+ ClusterGroup.objects.create(name="Cluster Group 3"),
202
198
  )
203
199
 
204
- cluster_type = ClusterType.objects.create(name="Cluster Type 1", slug="cluster-type-1")
200
+ cluster_type = ClusterType.objects.create(name="Cluster Type 1")
205
201
  clusters = (
206
202
  Cluster.objects.create(name="Cluster 1", cluster_type=cluster_type),
207
203
  Cluster.objects.create(name="Cluster 2", cluster_type=cluster_type),
@@ -252,7 +248,7 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
252
248
  def test_role(self):
253
249
  device_role = Role.objects.get_for_model(Device).first()
254
250
  vm_role = Role.objects.get_for_model(VirtualMachine).first()
255
- params = {"role": [device_role.pk, vm_role.slug]}
251
+ params = {"role": [device_role.pk, vm_role.name]}
256
252
  self.assertQuerysetEqualAndNotEmpty(
257
253
  self.filterset(params, self.queryset).qs,
258
254
  self.queryset.filter(roles__in=[vm_role, device_role]).distinct(),
@@ -273,7 +269,7 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
273
269
  platforms = list(self.platforms[:2])
274
270
  filter_params = [
275
271
  {"platform_id": [platforms[0].pk, platforms[1].pk]},
276
- {"platform": [platforms[0].pk, platforms[1].slug]},
272
+ {"platform": [platforms[0].pk, platforms[1].name]},
277
273
  ]
278
274
  for params in filter_params:
279
275
  self.assertQuerysetEqualAndNotEmpty(
@@ -284,7 +280,7 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
284
280
  cluster_groups = list(ClusterGroup.objects.all()[:2])
285
281
  filter_params = [
286
282
  {"cluster_group_id": [cluster_groups[0].pk, cluster_groups[1].pk]},
287
- {"cluster_group": [cluster_groups[0].pk, cluster_groups[1].slug]},
283
+ {"cluster_group": [cluster_groups[0].pk, cluster_groups[1].name]},
288
284
  ]
289
285
  for params in filter_params:
290
286
  self.assertQuerysetEqualAndNotEmpty(
@@ -301,7 +297,7 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
301
297
  tenant_groups = list(self.tenant_groups[:2])
302
298
  filter_params = [
303
299
  {"tenant_group_id": [tenant_groups[0].pk, tenant_groups[1].pk]},
304
- {"tenant_group": [tenant_groups[0].slug, tenant_groups[1].pk]},
300
+ {"tenant_group": [tenant_groups[0].name, tenant_groups[1].pk]},
305
301
  ]
306
302
  for params in filter_params:
307
303
  self.assertQuerysetEqualAndNotEmpty(
@@ -313,7 +309,7 @@ class ConfigContextTestCase(FilterTestCases.FilterTestCase):
313
309
  tenants = list(self.tenants[:2])
314
310
  filter_params = [
315
311
  {"tenant_id": [tenants[0].pk, tenants[1].pk]},
316
- {"tenant": [tenants[0].slug, tenants[1].pk]},
312
+ {"tenant": [tenants[0].name, tenants[1].pk]},
317
313
  ]
318
314
  for params in filter_params:
319
315
  self.assertQuerysetEqualAndNotEmpty(
@@ -511,8 +507,8 @@ class GitRepositoryTestCase(FilterTestCases.FilterTestCase):
511
507
  def setUpTestData(cls):
512
508
  # Create Three GitRepository records
513
509
  secrets_groups = [
514
- SecretsGroup.objects.create(name="Secrets Group 1", slug="secrets-group-1"),
515
- SecretsGroup.objects.create(name="Secrets Group 2", slug="secrets-group-2"),
510
+ SecretsGroup.objects.create(name="Secrets Group 1"),
511
+ SecretsGroup.objects.create(name="Secrets Group 2"),
516
512
  ]
517
513
  cls.secrets_groups = secrets_groups
518
514
  repos = (
@@ -550,7 +546,7 @@ class GitRepositoryTestCase(FilterTestCases.FilterTestCase):
550
546
  ),
551
547
  )
552
548
  for repo in repos:
553
- repo.save(trigger_resync=False)
549
+ repo.save()
554
550
  repos[0].tags.set(Tag.objects.get_for_model(GitRepository))
555
551
  repos[1].tags.set(Tag.objects.get_for_model(GitRepository)[:3])
556
552
 
@@ -579,7 +575,7 @@ class GitRepositoryTestCase(FilterTestCases.FilterTestCase):
579
575
  def test_secrets_group(self):
580
576
  filter_params = [
581
577
  {"secrets_group_id": [self.secrets_groups[0].pk, self.secrets_groups[1].pk]},
582
- {"secrets_group": [self.secrets_groups[0].slug, self.secrets_groups[1].pk]},
578
+ {"secrets_group": [self.secrets_groups[0].name, self.secrets_groups[1].pk]},
583
579
  ]
584
580
  for params in filter_params:
585
581
  self.assertQuerysetEqualAndNotEmpty(
@@ -588,7 +584,7 @@ class GitRepositoryTestCase(FilterTestCases.FilterTestCase):
588
584
  )
589
585
 
590
586
 
591
- class GraphQLTestCase(FilterTestCases.NameSlugFilterTestCase):
587
+ class GraphQLTestCase(FilterTestCases.NameOnlyFilterTestCase):
592
588
  queryset = GraphQLQuery.objects.all()
593
589
  filterset = GraphQLQueryFilterSet
594
590
 
@@ -597,17 +593,14 @@ class GraphQLTestCase(FilterTestCases.NameSlugFilterTestCase):
597
593
  graphqlqueries = (
598
594
  GraphQLQuery(
599
595
  name="graphql-query-1",
600
- slug="graphql-query-1",
601
596
  query="{ query: locations {name} }",
602
597
  ),
603
598
  GraphQLQuery(
604
599
  name="graphql-query-2",
605
- slug="graphql-query-2",
606
600
  query='{ devices(role: "edge") { id, name, device_role { name slug } } }',
607
601
  ),
608
602
  GraphQLQuery(
609
603
  name="graphql-query-3",
610
- slug="graphql-query-3",
611
604
  query="""
612
605
  query ($device: String!) {
613
606
  devices(name: $device) {
@@ -720,9 +713,10 @@ class ImageAttachmentTestCase(FilterTestCases.FilterTestCase):
720
713
 
721
714
  locations = Location.objects.filter(location_type=LocationType.objects.get(name="Campus"))[:2]
722
715
 
716
+ rack_status = Status.objects.get_for_model(Rack).first()
723
717
  racks = (
724
- Rack.objects.create(name="Rack 1", location=locations[0]),
725
- Rack.objects.create(name="Rack 2", location=locations[1]),
718
+ Rack.objects.create(name="Rack 1", location=locations[0], status=rack_status),
719
+ Rack.objects.create(name="Rack 2", location=locations[1], status=rack_status),
726
720
  )
727
721
 
728
722
  ImageAttachment.objects.create(
@@ -774,7 +768,7 @@ class ImageAttachmentTestCase(FilterTestCases.FilterTestCase):
774
768
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
775
769
 
776
770
 
777
- class JobFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
771
+ class JobFilterSetTestCase(FilterTestCases.NameOnlyFilterTestCase):
778
772
  queryset = Job.objects.all()
779
773
  filterset = JobFilterSet
780
774
 
@@ -784,7 +778,7 @@ class JobFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
784
778
  Job.objects.last().tags.set(Tag.objects.get_for_model(Job)[:3])
785
779
 
786
780
  def test_grouping(self):
787
- params = {"grouping": ["test_file_upload_pass", "test_file_upload_fail"]}
781
+ params = {"grouping": ["file_upload_pass", "file_upload_fail"]}
788
782
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
789
783
 
790
784
  def test_installed(self):
@@ -795,8 +789,8 @@ class JobFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
795
789
  params = {"job_class_name": "TestPass", "enabled": False}
796
790
  self.assertTrue(self.filterset(params, self.queryset).qs.exists())
797
791
 
798
- def test_commit_default(self):
799
- params = {"commit_default": False}
792
+ def test_dryrun_default(self):
793
+ params = {"dryrun_default": True}
800
794
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
801
795
 
802
796
  def test_hidden(self):
@@ -805,15 +799,27 @@ class JobFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
805
799
 
806
800
  def test_read_only(self):
807
801
  params = {"read_only": True}
808
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
802
+ self.assertQuerysetEqualAndNotEmpty(
803
+ self.filterset(params, self.queryset).qs, self.queryset.filter(read_only=True)
804
+ )
809
805
 
810
806
  def test_approval_required(self):
811
807
  params = {"approval_required": True}
812
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
808
+ self.assertQuerysetEqualAndNotEmpty(
809
+ self.filterset(params, self.queryset).qs,
810
+ self.queryset.filter(approval_required=True),
811
+ )
813
812
 
814
813
  def test_search(self):
815
814
  params = {"q": "file"}
816
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
815
+ expected_matches = (
816
+ Q(name__icontains="file") # pylint: disable=unsupported-binary-operation
817
+ | Q(grouping__icontains="file")
818
+ | Q(description__icontains="file")
819
+ )
820
+ self.assertQuerysetEqualAndNotEmpty(
821
+ self.filterset(params, self.queryset).qs, self.queryset.filter(expected_matches)
822
+ )
817
823
  value = self.queryset.values_list("pk", flat=True)[0]
818
824
  params = {"q": value}
819
825
  self.assertEqual(self.filterset(params, self.queryset).qs.values_list("pk", flat=True)[0], value)
@@ -836,16 +842,14 @@ class JobResultFilterSetTestCase(FilterTestCases.FilterTestCase):
836
842
  job_model=job,
837
843
  name=job.class_path,
838
844
  user=User.objects.first(),
839
- obj_type=ContentType.objects.get_for_model(Job),
840
845
  status=JobResultStatusChoices.STATUS_STARTED,
841
- task_id=job.pk,
842
846
  )
843
847
 
844
848
  def test_job_model(self):
845
849
  jobs = list(self.jobs[:2])
846
850
  filter_params = [
847
851
  {"job_model_id": [jobs[0].pk, jobs[1].pk]},
848
- {"job_model": [jobs[0].pk, jobs[1].slug]},
852
+ {"job_model": [jobs[0].pk, jobs[1].name]},
849
853
  ]
850
854
  for params in filter_params:
851
855
  self.assertQuerysetEqualAndNotEmpty(
@@ -853,7 +857,7 @@ class JobResultFilterSetTestCase(FilterTestCases.FilterTestCase):
853
857
  )
854
858
 
855
859
 
856
- class JobHookFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
860
+ class JobHookFilterSetTestCase(FilterTestCases.NameOnlyFilterTestCase):
857
861
  queryset = JobHook.objects.all()
858
862
  filterset = JobHookFilterSet
859
863
 
@@ -900,11 +904,7 @@ class JobHookFilterSetTestCase(FilterTestCases.NameSlugFilterTestCase):
900
904
 
901
905
  def test_job(self):
902
906
  jobs = Job.objects.filter(job_class_name__in=["TestJobHookReceiverLog", "TestJobHookReceiverChange"])[:2]
903
- params = {"job": [jobs[0].slug, jobs[1].pk]}
904
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
905
-
906
- def test_slug(self):
907
- params = {"slug": ["jobhook1", "jobhook2"]}
907
+ params = {"job": [jobs[0].name, jobs[1].pk]}
908
908
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
909
909
 
910
910
  def test_type_create(self):
@@ -973,9 +973,9 @@ class JobButtonFilterTestCase(FilterTestCases.FilterTestCase):
973
973
  self.filterset(params, self.queryset).qs, self.queryset.filter(job__pk=job.pk)
974
974
  )
975
975
 
976
- params = {"job": [job.slug]}
976
+ params = {"job": [job.name]}
977
977
  self.assertQuerysetEqualAndNotEmpty(
978
- self.filterset(params, self.queryset).qs, self.queryset.filter(job__slug=job.slug)
978
+ self.filterset(params, self.queryset).qs, self.queryset.filter(job__name=job.name)
979
979
  )
980
980
 
981
981
  def test_weight(self):
@@ -998,13 +998,9 @@ class JobLogEntryTestCase(FilterTestCases.FilterTestCase):
998
998
 
999
999
  @classmethod
1000
1000
  def setUpTestData(cls):
1001
- cls.job_result = JobResult.objects.create(
1002
- name="test",
1003
- task_id=uuid.uuid4(),
1004
- obj_type=ContentType.objects.get_for_model(GitRepository),
1005
- )
1001
+ cls.job_result = JobResult.objects.create(name="test")
1006
1002
 
1007
- for log_level in ("debug", "info", "success", "warning"):
1003
+ for log_level in ("debug", "info", "warning", "error", "critical"):
1008
1004
  JobLogEntry.objects.create(
1009
1005
  log_level=log_level,
1010
1006
  grouping="run",
@@ -1013,24 +1009,36 @@ class JobLogEntryTestCase(FilterTestCases.FilterTestCase):
1013
1009
  )
1014
1010
 
1015
1011
  def test_log_level(self):
1016
- params = {"log_level": ["success"]}
1017
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1012
+ params = {"log_level": ["debug"]}
1013
+ self.assertQuerysetEqualAndNotEmpty(
1014
+ self.filterset(params, self.queryset).qs, self.queryset.filter(log_level="debug")
1015
+ )
1018
1016
 
1019
1017
  def test_grouping(self):
1020
1018
  params = {"grouping": ["run"]}
1021
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
1019
+ self.assertQuerysetEqualAndNotEmpty(
1020
+ self.filterset(params, self.queryset).qs, self.queryset.filter(grouping="run")
1021
+ )
1022
1022
 
1023
1023
  def test_message(self):
1024
- params = {"message": ["I am a success log."]}
1025
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1024
+ params = {"message": ["I am a debug log."]}
1025
+ self.assertQuerysetEqualAndNotEmpty(
1026
+ self.filterset(params, self.queryset).qs, self.queryset.filter(message="I am a debug log.")
1027
+ )
1026
1028
 
1027
1029
  def test_search(self):
1028
1030
  params = {"q": "run"}
1029
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
1031
+ self.assertQuerysetEqualAndNotEmpty(
1032
+ self.filterset(params, self.queryset).qs, self.queryset.filter(grouping__icontains="run")
1033
+ )
1030
1034
  params = {"q": "warning log"}
1031
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1032
- params = {"q": "success"}
1033
- self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1035
+ self.assertQuerysetEqualAndNotEmpty(
1036
+ self.filterset(params, self.queryset).qs, self.queryset.filter(message__icontains="warning log")
1037
+ )
1038
+ params = {"q": "debug"}
1039
+ self.assertQuerysetEqualAndNotEmpty(
1040
+ self.filterset(params, self.queryset).qs, self.queryset.filter(log_level__icontains="debug")
1041
+ )
1034
1042
 
1035
1043
 
1036
1044
  class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
@@ -1046,7 +1054,11 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1046
1054
  )
1047
1055
 
1048
1056
  location = Location.objects.first()
1049
- ipaddress = IPAddress.objects.create(address="192.0.2.1/24")
1057
+ ipaddr_status = Status.objects.get_for_model(IPAddress).first()
1058
+ prefix_status = Status.objects.get_for_model(Prefix).first()
1059
+ namespace = Namespace.objects.first()
1060
+ Prefix.objects.create(prefix="192.0.2.0/24", namespace=namespace, status=prefix_status)
1061
+ ipaddress = IPAddress.objects.create(address="192.0.2.1/24", namespace=namespace, status=ipaddr_status)
1050
1062
 
1051
1063
  ObjectChange.objects.create(
1052
1064
  user=users[0],
@@ -1082,7 +1094,7 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1082
1094
  action=ObjectChangeActionChoices.ACTION_CREATE,
1083
1095
  changed_object=ipaddress,
1084
1096
  object_repr=str(ipaddress),
1085
- object_data={"address": str(ipaddress.address), "status": ipaddress.status},
1097
+ object_data={"address": str(ipaddress.address), "status": str(ipaddress.status)},
1086
1098
  )
1087
1099
  ObjectChange.objects.create(
1088
1100
  user=users[2],
@@ -1091,7 +1103,7 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1091
1103
  action=ObjectChangeActionChoices.ACTION_UPDATE,
1092
1104
  changed_object=ipaddress,
1093
1105
  object_repr=str(ipaddress),
1094
- object_data={"address": str(ipaddress.address), "status": ipaddress.status},
1106
+ object_data={"address": str(ipaddress.address), "status": str(ipaddress.status)},
1095
1107
  )
1096
1108
  ObjectChange.objects.create(
1097
1109
  user=users[2],
@@ -1100,7 +1112,7 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1100
1112
  action=ObjectChangeActionChoices.ACTION_DELETE,
1101
1113
  changed_object=ipaddress,
1102
1114
  object_repr=str(ipaddress),
1103
- object_data={"address": str(ipaddress.address), "status": ipaddress.status},
1115
+ object_data={"address": str(ipaddress.address), "status": str(ipaddress.status)},
1104
1116
  )
1105
1117
 
1106
1118
  def test_user(self):
@@ -1132,7 +1144,7 @@ class ObjectChangeTestCase(FilterTestCases.FilterTestCase):
1132
1144
  self.assertEqual(self.filterset(params, self.queryset).qs.values_list("pk", flat=True)[0], value)
1133
1145
 
1134
1146
 
1135
- class RelationshipTestCase(FilterTestCases.NameSlugFilterTestCase):
1147
+ class RelationshipTestCase(FilterTestCases.FilterTestCase):
1136
1148
  queryset = Relationship.objects.all()
1137
1149
  filterset = RelationshipFilterSet
1138
1150
 
@@ -1143,27 +1155,43 @@ class RelationshipTestCase(FilterTestCases.NameSlugFilterTestCase):
1143
1155
  vlan_type = ContentType.objects.get_for_model(VLAN)
1144
1156
 
1145
1157
  Relationship(
1146
- name="Device VLANs",
1147
- slug="device-vlans",
1158
+ label="Device VLANs",
1159
+ key="device_vlans",
1148
1160
  type="many-to-many",
1149
1161
  source_type=device_type,
1150
1162
  destination_type=vlan_type,
1151
1163
  ).validated_save()
1152
1164
  Relationship(
1153
- name="Primary VLAN",
1154
- slug="primary-vlan",
1165
+ label="Primary VLAN",
1166
+ key="primary_vlan",
1155
1167
  type="one-to-many",
1156
1168
  source_type=vlan_type,
1157
1169
  destination_type=device_type,
1158
1170
  ).validated_save()
1159
1171
  Relationship(
1160
- name="Primary Interface",
1161
- slug="primary-interface",
1172
+ label="Primary Interface",
1173
+ key="primary_interface",
1162
1174
  type="one-to-one",
1163
1175
  source_type=device_type,
1164
1176
  destination_type=interface_type,
1165
1177
  ).validated_save()
1166
1178
 
1179
+ def test_label(self):
1180
+ """Verify that the filterset supports filtering by label."""
1181
+ params = {"label": list(self.queryset.values_list("label", flat=True)[:2])}
1182
+ filterset = self.filterset(params, self.queryset)
1183
+ self.assertTrue(filterset.is_valid())
1184
+ self.assertQuerysetEqualAndNotEmpty(
1185
+ filterset.qs.order_by("label"), self.queryset.filter(label__in=params["label"]).order_by("label")
1186
+ )
1187
+
1188
+ def test_key(self):
1189
+ """Verify that the filterset supports filtering by key."""
1190
+ params = {"key": self.queryset.values_list("key", flat=True)[:2]}
1191
+ filterset = self.filterset(params, self.queryset)
1192
+ self.assertTrue(filterset.is_valid())
1193
+ self.assertEqual(filterset.qs.count(), 2)
1194
+
1167
1195
  def test_type(self):
1168
1196
  params = {"type": ["one-to-many"]}
1169
1197
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@@ -1188,22 +1216,22 @@ class RelationshipAssociationTestCase(FilterTestCases.FilterTestCase):
1188
1216
 
1189
1217
  cls.relationships = (
1190
1218
  Relationship(
1191
- name="Device VLANs",
1192
- slug="device-vlans",
1219
+ label="Device VLANs",
1220
+ key="device_vlans",
1193
1221
  type="many-to-many",
1194
1222
  source_type=cls.device_type,
1195
1223
  destination_type=cls.vlan_type,
1196
1224
  ),
1197
1225
  Relationship(
1198
- name="Primary VLAN",
1199
- slug="primary-vlan",
1226
+ label="Primary VLAN",
1227
+ key="primary_vlan",
1200
1228
  type="one-to-many",
1201
1229
  source_type=cls.vlan_type,
1202
1230
  destination_type=cls.device_type,
1203
1231
  ),
1204
1232
  Relationship(
1205
- name="Device Device",
1206
- slug="symmetric-device-device",
1233
+ label="Device Device",
1234
+ key="symmetric_device_device",
1207
1235
  type="symmetric-many-to-many",
1208
1236
  source_type=cls.device_type,
1209
1237
  destination_type=cls.device_type,
@@ -1212,18 +1240,26 @@ class RelationshipAssociationTestCase(FilterTestCases.FilterTestCase):
1212
1240
  for relationship in cls.relationships:
1213
1241
  relationship.validated_save()
1214
1242
 
1215
- manufacturer = Manufacturer.objects.create(name="Manufacturer 1", slug="manufacturer-1")
1243
+ manufacturer = Manufacturer.objects.first()
1216
1244
  devicetype = DeviceType.objects.create(manufacturer=manufacturer, model="Device Type 1", slug="device-type-1")
1217
1245
  devicerole = Role.objects.get_for_model(Device).first()
1246
+ devicestatus = Status.objects.get_for_model(Device).first()
1218
1247
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
1219
1248
  cls.devices = (
1220
- Device.objects.create(name="Device 1", device_type=devicetype, role=devicerole, location=location),
1221
- Device.objects.create(name="Device 2", device_type=devicetype, role=devicerole, location=location),
1222
- Device.objects.create(name="Device 3", device_type=devicetype, role=devicerole, location=location),
1249
+ Device.objects.create(
1250
+ name="Device 1", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1251
+ ),
1252
+ Device.objects.create(
1253
+ name="Device 2", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1254
+ ),
1255
+ Device.objects.create(
1256
+ name="Device 3", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1257
+ ),
1223
1258
  )
1259
+ vlan_status = Status.objects.get_for_model(VLAN).first()
1224
1260
  cls.vlans = (
1225
- VLAN.objects.create(vid=1, name="VLAN 1"),
1226
- VLAN.objects.create(vid=2, name="VLAN 2"),
1261
+ VLAN.objects.create(vid=1, name="VLAN 1", status=vlan_status),
1262
+ VLAN.objects.create(vid=2, name="VLAN 2", status=vlan_status),
1227
1263
  )
1228
1264
 
1229
1265
  RelationshipAssociation(
@@ -1270,7 +1306,7 @@ class RelationshipAssociationTestCase(FilterTestCases.FilterTestCase):
1270
1306
  ).validated_save()
1271
1307
 
1272
1308
  def test_relationship(self):
1273
- params = {"relationship": [self.relationships[0].slug]}
1309
+ params = {"relationship": [self.relationships[0].key]}
1274
1310
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
1275
1311
 
1276
1312
  def test_source_type(self):
@@ -1306,22 +1342,22 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1306
1342
  cls.vlan_type = ContentType.objects.get_for_model(VLAN)
1307
1343
  cls.relationships = (
1308
1344
  Relationship(
1309
- name="Device VLANs",
1310
- slug="device-vlans",
1345
+ label="Device VLANs",
1346
+ key="device_vlans",
1311
1347
  type="many-to-many",
1312
1348
  source_type=cls.device_type,
1313
1349
  destination_type=cls.vlan_type,
1314
1350
  ),
1315
1351
  Relationship(
1316
- name="Primary VLAN",
1317
- slug="primary-vlan",
1352
+ label="Primary VLAN",
1353
+ key="primary_vlan",
1318
1354
  type="one-to-many",
1319
1355
  source_type=cls.vlan_type,
1320
1356
  destination_type=cls.device_type,
1321
1357
  ),
1322
1358
  Relationship(
1323
- name="Device Peers",
1324
- slug="device-peers",
1359
+ label="Device Peers",
1360
+ key="device_peers",
1325
1361
  type="symmetric-many-to-many",
1326
1362
  source_type=cls.device_type,
1327
1363
  destination_type=cls.device_type,
@@ -1330,19 +1366,27 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1330
1366
  for relationship in cls.relationships:
1331
1367
  relationship.validated_save()
1332
1368
 
1333
- manufacturer = Manufacturer.objects.create(name="Manufacturer 1", slug="manufacturer-1")
1369
+ manufacturer = Manufacturer.objects.first()
1334
1370
  devicetype = DeviceType.objects.create(manufacturer=manufacturer, model="Device Type 1", slug="device-type-1")
1335
1371
  devicerole = Role.objects.get_for_model(Device).first()
1372
+ devicestatus = Status.objects.get_for_model(Device).first()
1336
1373
  location = Location.objects.filter(location_type=LocationType.objects.get(name="Campus")).first()
1337
1374
  cls.devices = (
1338
- Device.objects.create(name="Device 1", device_type=devicetype, role=devicerole, location=location),
1339
- Device.objects.create(name="Device 2", device_type=devicetype, role=devicerole, location=location),
1340
- Device.objects.create(name="Device 3", device_type=devicetype, role=devicerole, location=location),
1375
+ Device.objects.create(
1376
+ name="Device 1", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1377
+ ),
1378
+ Device.objects.create(
1379
+ name="Device 2", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1380
+ ),
1381
+ Device.objects.create(
1382
+ name="Device 3", device_type=devicetype, role=devicerole, status=devicestatus, location=location
1383
+ ),
1341
1384
  )
1385
+ vlan_status = Status.objects.get_for_model(VLAN).first()
1342
1386
  cls.vlans = (
1343
- VLAN.objects.create(vid=1, name="VLAN 1"),
1344
- VLAN.objects.create(vid=2, name="VLAN 2"),
1345
- VLAN.objects.create(vid=3, name="VLAN 3"),
1387
+ VLAN.objects.create(vid=1, name="VLAN 1", status=vlan_status),
1388
+ VLAN.objects.create(vid=2, name="VLAN 2", status=vlan_status),
1389
+ VLAN.objects.create(vid=3, name="VLAN 3", status=vlan_status),
1346
1390
  )
1347
1391
  cls.relationship_associations = (
1348
1392
  RelationshipAssociation(
@@ -1409,7 +1453,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1409
1453
  self.queryset = Device.objects.all()
1410
1454
  self.filterset = DeviceFilterSet
1411
1455
  self.assertEqual(
1412
- self.filterset({f"cr_{self.relationships[1].slug}__source": [self.vlans[0].pk]}, self.queryset).qs.count(),
1456
+ self.filterset({f"cr_{self.relationships[1].key}__source": [self.vlans[0].pk]}, self.queryset).qs.count(),
1413
1457
  3,
1414
1458
  )
1415
1459
 
@@ -1418,7 +1462,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1418
1462
  self.filterset = VLANFilterSet
1419
1463
  self.assertEqual(
1420
1464
  self.filterset(
1421
- {f"cr_{self.relationships[1].slug}__destination": [self.devices[0].pk, self.devices[1].pk]},
1465
+ {f"cr_{self.relationships[1].key}__destination": [self.devices[0].pk, self.devices[1].pk]},
1422
1466
  self.queryset,
1423
1467
  ).qs.count(),
1424
1468
  1,
@@ -1429,7 +1473,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1429
1473
  self.filterset = VLANFilterSet
1430
1474
  self.assertEqual(
1431
1475
  self.filterset(
1432
- {f"cr_{self.relationships[0].slug}__source": [self.devices[0].pk, self.devices[1].pk]}, self.queryset
1476
+ {f"cr_{self.relationships[0].key}__source": [self.devices[0].pk, self.devices[1].pk]}, self.queryset
1433
1477
  ).qs.count(),
1434
1478
  2,
1435
1479
  )
@@ -1439,7 +1483,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1439
1483
  self.filterset = DeviceFilterSet
1440
1484
  self.assertEqual(
1441
1485
  self.filterset(
1442
- {f"cr_{self.relationships[0].slug}__destination": [self.vlans[0].pk, self.vlans[1].pk]}, self.queryset
1486
+ {f"cr_{self.relationships[0].key}__destination": [self.vlans[0].pk, self.vlans[1].pk]}, self.queryset
1443
1487
  ).qs.count(),
1444
1488
  2,
1445
1489
  )
@@ -1449,12 +1493,12 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1449
1493
  self.filterset = DeviceFilterSet
1450
1494
  self.assertEqual(
1451
1495
  self.filterset(
1452
- {f"cr_{self.relationships[2].slug}__peer": [self.devices[0].pk, self.devices[1].pk]}, self.queryset
1496
+ {f"cr_{self.relationships[2].key}__peer": [self.devices[0].pk, self.devices[1].pk]}, self.queryset
1453
1497
  ).qs.count(),
1454
1498
  3,
1455
1499
  )
1456
1500
  self.assertEqual(
1457
- self.filterset({f"cr_{self.relationships[2].slug}__peer": [self.devices[2].pk]}, self.queryset).qs.count(),
1501
+ self.filterset({f"cr_{self.relationships[2].key}__peer": [self.devices[2].pk]}, self.queryset).qs.count(),
1458
1502
  2,
1459
1503
  )
1460
1504
 
@@ -1464,8 +1508,8 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1464
1508
  self.assertEqual(
1465
1509
  self.filterset(
1466
1510
  {
1467
- f"cr_{self.relationships[2].slug}__peer": [self.devices[0].pk, self.devices[1].pk],
1468
- f"cr_{self.relationships[0].slug}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1511
+ f"cr_{self.relationships[2].key}__peer": [self.devices[0].pk, self.devices[1].pk],
1512
+ f"cr_{self.relationships[0].key}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1469
1513
  },
1470
1514
  self.queryset,
1471
1515
  ).qs.count(),
@@ -1474,8 +1518,8 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1474
1518
  self.assertEqual(
1475
1519
  self.filterset(
1476
1520
  {
1477
- f"cr_{self.relationships[2].slug}__peer": [self.devices[2].pk],
1478
- f"cr_{self.relationships[0].slug}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1521
+ f"cr_{self.relationships[2].key}__peer": [self.devices[2].pk],
1522
+ f"cr_{self.relationships[0].key}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1479
1523
  },
1480
1524
  self.queryset,
1481
1525
  ).qs.count(),
@@ -1494,7 +1538,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1494
1538
  self.assertEqual(
1495
1539
  self.filterset(
1496
1540
  {
1497
- f"cr_{self.relationships[0].slug}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1541
+ f"cr_{self.relationships[0].key}__destination": [self.vlans[0].pk, self.vlans[1].pk],
1498
1542
  "manufacturer": ["manufacturer-1"],
1499
1543
  },
1500
1544
  self.queryset,
@@ -1503,7 +1547,7 @@ class RelationshipModelFilterSetTestCase(FilterTestCases.FilterTestCase):
1503
1547
  )
1504
1548
 
1505
1549
 
1506
- class SecretTestCase(FilterTestCases.NameSlugFilterTestCase):
1550
+ class SecretTestCase(FilterTestCases.NameOnlyFilterTestCase):
1507
1551
  queryset = Secret.objects.all()
1508
1552
  filterset = SecretFilterSet
1509
1553
 
@@ -1526,7 +1570,6 @@ class SecretTestCase(FilterTestCases.NameSlugFilterTestCase):
1526
1570
  parameters={"path": "/github-tokens/user/myusername.txt"},
1527
1571
  ),
1528
1572
  )
1529
-
1530
1573
  for secret in secrets:
1531
1574
  secret.validated_save()
1532
1575
  secrets[0].tags.set(Tag.objects.get_for_model(Secret))
@@ -1542,15 +1585,15 @@ class SecretTestCase(FilterTestCases.NameSlugFilterTestCase):
1542
1585
  self.assertEqual(self.filterset(params, self.queryset).qs.values_list("pk", flat=True)[0], value)
1543
1586
 
1544
1587
 
1545
- class SecretsGroupTestCase(FilterTestCases.NameSlugFilterTestCase):
1588
+ class SecretsGroupTestCase(FilterTestCases.NameOnlyFilterTestCase):
1546
1589
  queryset = SecretsGroup.objects.all()
1547
1590
  filterset = SecretsGroupFilterSet
1548
1591
 
1549
1592
  @classmethod
1550
1593
  def setUpTestData(cls):
1551
- SecretsGroup.objects.create(name="Group 1", slug="group-1")
1552
- SecretsGroup.objects.create(name="Group 2", slug="group-2")
1553
- SecretsGroup.objects.create(name="Group 3", slug="group-3")
1594
+ SecretsGroup.objects.create(name="Group 1")
1595
+ SecretsGroup.objects.create(name="Group 2")
1596
+ SecretsGroup.objects.create(name="Group 3")
1554
1597
 
1555
1598
  def test_search(self):
1556
1599
  value = self.queryset.values_list("pk", flat=True)[0]
@@ -1562,10 +1605,7 @@ class SecretsGroupAssociationTestCase(FilterTestCases.FilterTestCase):
1562
1605
  queryset = SecretsGroupAssociation.objects.all()
1563
1606
  filterset = SecretsGroupAssociationFilterSet
1564
1607
 
1565
- generic_filter_tests = (
1566
- ["secrets_group", "secrets_group__id"],
1567
- ["secrets_group", "secrets_group__slug"],
1568
- )
1608
+ generic_filter_tests = (["secrets_group", "secrets_group__id"],)
1569
1609
 
1570
1610
  @classmethod
1571
1611
  def setUpTestData(cls):
@@ -1591,9 +1631,9 @@ class SecretsGroupAssociationTestCase(FilterTestCases.FilterTestCase):
1591
1631
  secret.validated_save()
1592
1632
 
1593
1633
  cls.groups = (
1594
- SecretsGroup.objects.create(name="Group 1", slug="group-1"),
1595
- SecretsGroup.objects.create(name="Group 2", slug="group-2"),
1596
- SecretsGroup.objects.create(name="Group 3", slug="group-3"),
1634
+ SecretsGroup.objects.create(name="Group 1"),
1635
+ SecretsGroup.objects.create(name="Group 2"),
1636
+ SecretsGroup.objects.create(name="Group 3"),
1597
1637
  )
1598
1638
 
1599
1639
  SecretsGroupAssociation.objects.create(
@@ -1618,7 +1658,7 @@ class SecretsGroupAssociationTestCase(FilterTestCases.FilterTestCase):
1618
1658
  def test_secret(self):
1619
1659
  filter_params = [
1620
1660
  {"secret_id": [self.secrets[0].pk, self.secrets[1].pk]},
1621
- {"secret": [self.secrets[0].slug, self.secrets[1].pk]},
1661
+ {"secret": [self.secrets[0].name, self.secrets[1].pk]},
1622
1662
  ]
1623
1663
  for params in filter_params:
1624
1664
  self.assertQuerysetEqualAndNotEmpty(
@@ -1634,7 +1674,7 @@ class SecretsGroupAssociationTestCase(FilterTestCases.FilterTestCase):
1634
1674
  self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
1635
1675
 
1636
1676
 
1637
- class StatusTestCase(FilterTestCases.NameSlugFilterTestCase):
1677
+ class StatusTestCase(FilterTestCases.NameOnlyFilterTestCase):
1638
1678
  queryset = Status.objects.all()
1639
1679
  filterset = StatusFilterSet
1640
1680
 
@@ -1655,7 +1695,7 @@ class StatusTestCase(FilterTestCases.NameSlugFilterTestCase):
1655
1695
  params = {"q": "active"}
1656
1696
  # TODO: Remove pylint disable after issue is resolved (see: https://github.com/PyCQA/pylint/issues/7381)
1657
1697
  # pylint: disable=unsupported-binary-operation
1658
- q = Q(id__iexact="active") | Q(name__icontains="active") | Q(slug__icontains="active")
1698
+ q = Q(id__iexact="active") | Q(name__icontains="active")
1659
1699
  # pylint: enable=unsupported-binary-operation
1660
1700
  q |= Q(content_types__model__icontains="active")
1661
1701
  self.assertQuerysetEqualAndNotEmpty(
@@ -1758,7 +1798,7 @@ class WebhookTestCase(FilterTestCases.FilterTestCase):
1758
1798
  self.assertEqual(self.filterset(params, self.queryset).qs.values_list("pk", flat=True)[0], value)
1759
1799
 
1760
1800
 
1761
- class RoleTestCase(FilterTestCases.NameSlugFilterTestCase):
1801
+ class RoleTestCase(FilterTestCases.NameOnlyFilterTestCase):
1762
1802
  queryset = Role.objects.all()
1763
1803
  filterset = RoleFilterSet
1764
1804