nautobot 2.3.15b1__py3-none-any.whl → 2.3.16__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (393) hide show
  1. nautobot/circuits/views.py +3 -3
  2. nautobot/cloud/models.py +1 -1
  3. nautobot/core/api/fields.py +5 -5
  4. nautobot/core/api/serializers.py +9 -9
  5. nautobot/core/api/views.py +3 -2
  6. nautobot/core/apps/__init__.py +5 -2
  7. nautobot/core/celery/schedulers.py +1 -1
  8. nautobot/core/filters.py +19 -16
  9. nautobot/core/forms/fields.py +5 -5
  10. nautobot/core/graphql/types.py +1 -1
  11. nautobot/core/jobs/__init__.py +4 -4
  12. nautobot/core/jobs/cleanup.py +1 -1
  13. nautobot/core/jobs/groups.py +1 -1
  14. nautobot/core/management/commands/validate_models.py +1 -1
  15. nautobot/core/models/__init__.py +1 -1
  16. nautobot/core/models/query_functions.py +2 -2
  17. nautobot/core/models/tree_queries.py +2 -2
  18. nautobot/core/settings.py +3 -0
  19. nautobot/core/settings.yaml +8 -0
  20. nautobot/core/tables.py +5 -5
  21. nautobot/core/templates/inc/media.html +3 -0
  22. nautobot/core/templates/nautobot_config.py.j2 +3 -0
  23. nautobot/core/testing/filters.py +7 -3
  24. nautobot/core/testing/views.py +5 -0
  25. nautobot/core/tests/integration/test_app_home.py +0 -1
  26. nautobot/core/tests/integration/test_app_navbar.py +0 -1
  27. nautobot/core/tests/integration/test_filters.py +0 -2
  28. nautobot/core/tests/integration/test_home.py +0 -1
  29. nautobot/core/tests/integration/test_navbar.py +0 -1
  30. nautobot/core/tests/integration/test_view_authentication.py +1 -0
  31. nautobot/core/tests/runner.py +1 -1
  32. nautobot/core/tests/test_views.py +29 -0
  33. nautobot/core/urls.py +9 -0
  34. nautobot/core/views/generic.py +51 -43
  35. nautobot/core/views/mixins.py +21 -11
  36. nautobot/dcim/api/serializers.py +48 -48
  37. nautobot/dcim/forms.py +2 -0
  38. nautobot/dcim/graphql/types.py +2 -2
  39. nautobot/dcim/models/device_component_templates.py +2 -2
  40. nautobot/dcim/models/device_components.py +22 -20
  41. nautobot/dcim/models/devices.py +1 -1
  42. nautobot/dcim/models/locations.py +3 -3
  43. nautobot/dcim/models/power.py +6 -5
  44. nautobot/dcim/models/racks.py +4 -4
  45. nautobot/dcim/tables/__init__.py +3 -3
  46. nautobot/dcim/tables/devicetypes.py +2 -2
  47. nautobot/dcim/tests/test_filters.py +1 -0
  48. nautobot/dcim/tests/test_graphql.py +52 -0
  49. nautobot/dcim/tests/test_models.py +4 -1
  50. nautobot/dcim/views.py +1 -1
  51. nautobot/extras/api/customfields.py +2 -2
  52. nautobot/extras/api/serializers.py +72 -69
  53. nautobot/extras/api/views.py +4 -4
  54. nautobot/extras/forms/mixins.py +1 -1
  55. nautobot/extras/health_checks.py +1 -2
  56. nautobot/extras/jobs.py +5 -5
  57. nautobot/extras/managers.py +3 -1
  58. nautobot/extras/migrations/0018_joblog_data_migration.py +7 -9
  59. nautobot/extras/models/customfields.py +12 -11
  60. nautobot/extras/models/groups.py +13 -9
  61. nautobot/extras/models/jobs.py +4 -4
  62. nautobot/extras/models/models.py +2 -2
  63. nautobot/extras/plugins/views.py +1 -1
  64. nautobot/extras/tables.py +5 -5
  65. nautobot/extras/test_jobs/api_test_job.py +1 -1
  66. nautobot/extras/test_jobs/atomic_transaction.py +2 -2
  67. nautobot/extras/test_jobs/dry_run.py +1 -1
  68. nautobot/extras/test_jobs/fail.py +5 -5
  69. nautobot/extras/test_jobs/file_output.py +1 -1
  70. nautobot/extras/test_jobs/file_upload_fail.py +1 -1
  71. nautobot/extras/test_jobs/file_upload_pass.py +1 -1
  72. nautobot/extras/test_jobs/ipaddress_vars.py +3 -1
  73. nautobot/extras/test_jobs/jobs_module/jobs_submodule/jobs.py +1 -1
  74. nautobot/extras/test_jobs/location_with_custom_field.py +1 -1
  75. nautobot/extras/test_jobs/log_redaction.py +1 -1
  76. nautobot/extras/test_jobs/log_skip_db_logging.py +1 -1
  77. nautobot/extras/test_jobs/modify_db.py +1 -1
  78. nautobot/extras/test_jobs/object_var_optional.py +1 -1
  79. nautobot/extras/test_jobs/object_var_required.py +1 -1
  80. nautobot/extras/test_jobs/object_vars.py +1 -1
  81. nautobot/extras/test_jobs/pass.py +3 -3
  82. nautobot/extras/test_jobs/profiling.py +1 -1
  83. nautobot/extras/test_jobs/relative_import.py +3 -3
  84. nautobot/extras/test_jobs/soft_time_limit_greater_than_time_limit.py +1 -1
  85. nautobot/extras/test_jobs/task_queues.py +1 -1
  86. nautobot/extras/tests/integration/test_plugin_banner.py +0 -2
  87. nautobot/extras/tests/test_api.py +13 -13
  88. nautobot/extras/tests/test_customfields.py +1 -1
  89. nautobot/extras/tests/test_datasources.py +2 -1
  90. nautobot/extras/tests/test_dynamicgroups.py +1 -1
  91. nautobot/extras/tests/test_filters.py +6 -6
  92. nautobot/extras/tests/test_forms.py +20 -1
  93. nautobot/extras/tests/test_jobs.py +11 -11
  94. nautobot/extras/tests/test_models.py +10 -10
  95. nautobot/extras/tests/test_relationships.py +1 -1
  96. nautobot/extras/tests/test_views.py +16 -16
  97. nautobot/extras/views.py +20 -16
  98. nautobot/ipam/api/fields.py +3 -3
  99. nautobot/ipam/api/serializers.py +33 -33
  100. nautobot/ipam/api/views.py +37 -61
  101. nautobot/ipam/querysets.py +2 -2
  102. nautobot/ipam/tests/test_api.py +12 -1
  103. nautobot/ipam/tests/test_forms.py +51 -47
  104. nautobot/ipam/tests/test_migrations.py +30 -30
  105. nautobot/ipam/tests/test_querysets.py +14 -0
  106. nautobot/project-static/docs/404.html +2 -2
  107. nautobot/project-static/docs/apps/index.html +2 -2
  108. nautobot/project-static/docs/apps/nautobot-apps.html +2 -2
  109. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js → bundle.88dd0f4e.min.js} +2 -2
  110. nautobot/project-static/docs/assets/javascripts/{bundle.83f73b43.min.js.map → bundle.88dd0f4e.min.js.map} +2 -2
  111. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +2 -2
  112. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +2 -2
  113. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +2 -2
  114. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +2 -2
  115. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +2 -2
  116. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +2 -2
  117. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +2 -2
  118. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +2 -2
  119. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +2 -2
  120. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +2 -2
  121. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +2 -2
  122. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +3 -3
  123. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +2 -2
  124. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2 -2
  125. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +2 -2
  126. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +2 -2
  127. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +2 -2
  128. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +2 -2
  129. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +3 -3
  130. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +2 -2
  131. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +2 -2
  132. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +2 -2
  133. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +4 -4
  134. nautobot/project-static/docs/development/apps/api/configuration-view.html +2 -2
  135. nautobot/project-static/docs/development/apps/api/database-backend-config.html +2 -2
  136. nautobot/project-static/docs/development/apps/api/models/django-admin.html +2 -2
  137. nautobot/project-static/docs/development/apps/api/models/global-search.html +2 -2
  138. nautobot/project-static/docs/development/apps/api/models/graphql.html +2 -2
  139. nautobot/project-static/docs/development/apps/api/models/index.html +2 -2
  140. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +2 -2
  141. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +2 -2
  142. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +2 -2
  143. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +2 -2
  144. nautobot/project-static/docs/development/apps/api/platform-features/index.html +2 -2
  145. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +2 -2
  146. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +2 -2
  147. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +2 -2
  148. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +2 -2
  149. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +2 -2
  150. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +2 -2
  151. nautobot/project-static/docs/development/apps/api/prometheus.html +2 -2
  152. nautobot/project-static/docs/development/apps/api/setup.html +2 -2
  153. nautobot/project-static/docs/development/apps/api/testing.html +2 -2
  154. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +2 -2
  155. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +2 -2
  156. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +2 -2
  157. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +2 -2
  158. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +2 -2
  159. nautobot/project-static/docs/development/apps/api/views/base-template.html +2 -2
  160. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +2 -2
  161. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +2 -2
  162. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +2 -2
  163. nautobot/project-static/docs/development/apps/api/views/index.html +2 -2
  164. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +2 -2
  165. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +2 -2
  166. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +2 -2
  167. nautobot/project-static/docs/development/apps/api/views/notes.html +2 -2
  168. nautobot/project-static/docs/development/apps/api/views/rest-api.html +2 -2
  169. nautobot/project-static/docs/development/apps/api/views/urls.html +2 -2
  170. nautobot/project-static/docs/development/apps/index.html +2 -2
  171. nautobot/project-static/docs/development/apps/migration/code-updates.html +2 -2
  172. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +2 -2
  173. nautobot/project-static/docs/development/apps/migration/from-v1.html +2 -2
  174. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +2 -2
  175. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +2 -2
  176. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +2 -2
  177. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +2 -2
  178. nautobot/project-static/docs/development/apps/porting-from-netbox.html +2 -2
  179. nautobot/project-static/docs/development/core/application-registry.html +2 -2
  180. nautobot/project-static/docs/development/core/best-practices.html +2 -2
  181. nautobot/project-static/docs/development/core/bootstrap-ui.html +2 -2
  182. nautobot/project-static/docs/development/core/caching.html +2 -2
  183. nautobot/project-static/docs/development/core/controllers.html +2 -2
  184. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +27 -70
  185. nautobot/project-static/docs/development/core/generic-views.html +2 -2
  186. nautobot/project-static/docs/development/core/getting-started.html +2 -2
  187. nautobot/project-static/docs/development/core/homepage.html +2 -2
  188. nautobot/project-static/docs/development/core/index.html +2 -2
  189. nautobot/project-static/docs/development/core/model-checklist.html +2 -2
  190. nautobot/project-static/docs/development/core/model-features.html +2 -2
  191. nautobot/project-static/docs/development/core/natural-keys.html +2 -2
  192. nautobot/project-static/docs/development/core/navigation-menu.html +2 -2
  193. nautobot/project-static/docs/development/core/release-checklist.html +2 -2
  194. nautobot/project-static/docs/development/core/role-internals.html +2 -2
  195. nautobot/project-static/docs/development/core/settings.html +2 -2
  196. nautobot/project-static/docs/development/core/style-guide.html +2 -2
  197. nautobot/project-static/docs/development/core/templates.html +2 -2
  198. nautobot/project-static/docs/development/core/testing.html +2 -2
  199. nautobot/project-static/docs/development/core/user-preferences.html +2 -2
  200. nautobot/project-static/docs/development/index.html +2 -2
  201. nautobot/project-static/docs/development/jobs/index.html +2 -2
  202. nautobot/project-static/docs/development/jobs/migration/from-v1.html +2 -2
  203. nautobot/project-static/docs/index.html +2 -2
  204. nautobot/project-static/docs/overview/application_stack.html +2 -2
  205. nautobot/project-static/docs/overview/design_philosophy.html +2 -2
  206. nautobot/project-static/docs/release-notes/index.html +2 -2
  207. nautobot/project-static/docs/release-notes/version-1.0.html +2 -2
  208. nautobot/project-static/docs/release-notes/version-1.1.html +2 -2
  209. nautobot/project-static/docs/release-notes/version-1.2.html +2 -2
  210. nautobot/project-static/docs/release-notes/version-1.3.html +2 -2
  211. nautobot/project-static/docs/release-notes/version-1.4.html +2 -2
  212. nautobot/project-static/docs/release-notes/version-1.5.html +2 -2
  213. nautobot/project-static/docs/release-notes/version-1.6.html +2 -2
  214. nautobot/project-static/docs/release-notes/version-2.0.html +2 -2
  215. nautobot/project-static/docs/release-notes/version-2.1.html +2 -2
  216. nautobot/project-static/docs/release-notes/version-2.2.html +2 -2
  217. nautobot/project-static/docs/release-notes/version-2.3.html +471 -220
  218. nautobot/project-static/docs/requirements.txt +2 -2
  219. nautobot/project-static/docs/search/search_index.json +1 -1
  220. nautobot/project-static/docs/sitemap.xml +270 -270
  221. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  222. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +2 -2
  223. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +2 -2
  224. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +2 -2
  225. nautobot/project-static/docs/user-guide/administration/configuration/index.html +2 -2
  226. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +2 -2
  227. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +29 -2
  228. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +2 -2
  229. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +2 -2
  230. nautobot/project-static/docs/user-guide/administration/guides/docker.html +2 -2
  231. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +2 -2
  232. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +2 -2
  233. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +2 -2
  234. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +2 -2
  235. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +2 -2
  236. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +2 -2
  237. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +2 -2
  238. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +2 -2
  239. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +2 -2
  240. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +2 -2
  241. nautobot/project-static/docs/user-guide/administration/installation/index.html +2 -2
  242. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +2 -2
  243. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +2 -2
  244. nautobot/project-static/docs/user-guide/administration/installation/services.html +2 -2
  245. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +2 -2
  246. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +2 -2
  247. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +2 -2
  248. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +2 -2
  249. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +2 -2
  250. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +2 -2
  251. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +2 -2
  252. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +2 -2
  253. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +2 -2
  254. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +2 -2
  255. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +2 -2
  256. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +2 -2
  257. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +2 -2
  258. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +2 -2
  259. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +2 -2
  260. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +2 -2
  261. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +2 -2
  262. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +2 -2
  263. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +2 -2
  264. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +2 -2
  265. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +2 -2
  266. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +2 -2
  267. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +2 -2
  268. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +2 -2
  269. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +2 -2
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +2 -2
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +2 -2
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +2 -2
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +2 -2
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +2 -2
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +2 -2
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +2 -2
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +2 -2
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +2 -2
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +2 -2
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +2 -2
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +2 -2
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +2 -2
  283. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +2 -2
  284. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +2 -2
  285. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +2 -2
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +2 -2
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +2 -2
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +2 -2
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +2 -2
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +2 -2
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +2 -2
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +2 -2
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +2 -2
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +2 -2
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +2 -2
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +2 -2
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +2 -2
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +2 -2
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +2 -2
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +2 -2
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +2 -2
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +2 -2
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +2 -2
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +2 -2
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +2 -2
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +2 -2
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +2 -2
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +2 -2
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +2 -2
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +2 -2
  311. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +2 -2
  312. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +2 -2
  313. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +2 -2
  314. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +2 -2
  315. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +2 -2
  316. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +2 -2
  317. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +2 -2
  318. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +2 -2
  319. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +2 -2
  320. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +2 -2
  321. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +2 -2
  322. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +2 -2
  323. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +2 -2
  324. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +2 -2
  325. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +2 -2
  326. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +2 -2
  327. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +2 -2
  328. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +2 -2
  329. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +2 -2
  330. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +2 -2
  331. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +2 -2
  332. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +2 -2
  333. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +2 -2
  334. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +2 -2
  335. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +2 -2
  336. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +2 -2
  337. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +2 -2
  338. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +2 -2
  339. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +2 -2
  340. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +2 -2
  341. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +2 -2
  342. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +2 -2
  343. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +2 -2
  344. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +2 -2
  345. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +2 -2
  346. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +2 -2
  347. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +2 -2
  348. nautobot/project-static/docs/user-guide/index.html +2 -2
  349. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +2 -2
  350. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +2 -2
  351. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +2 -2
  352. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +2 -2
  353. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +2 -2
  354. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +2 -2
  355. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +2 -2
  356. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +2 -2
  357. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +2 -2
  358. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +2 -2
  359. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +2 -2
  360. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +2 -2
  361. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +2 -2
  362. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +2 -2
  363. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +2 -2
  364. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +2 -2
  365. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +2 -2
  366. nautobot/project-static/docs/user-guide/platform-functionality/note.html +2 -2
  367. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +2 -2
  368. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +2 -2
  369. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +2 -2
  370. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +2 -2
  371. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +2 -2
  372. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +2 -2
  373. nautobot/project-static/docs/user-guide/platform-functionality/role.html +2 -2
  374. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +2 -2
  375. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +2 -2
  376. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +2 -2
  377. nautobot/project-static/docs/user-guide/platform-functionality/status.html +2 -2
  378. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +2 -2
  379. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +2 -2
  380. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +2 -2
  381. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +2 -2
  382. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +2 -2
  383. nautobot/users/admin.py +1 -1
  384. nautobot/users/api/serializers.py +4 -4
  385. nautobot/users/api/views.py +1 -1
  386. nautobot/virtualization/api/serializers.py +4 -4
  387. {nautobot-2.3.15b1.dist-info → nautobot-2.3.16.dist-info}/METADATA +5 -4
  388. {nautobot-2.3.15b1.dist-info → nautobot-2.3.16.dist-info}/RECORD +392 -393
  389. {nautobot-2.3.15b1.dist-info → nautobot-2.3.16.dist-info}/WHEEL +1 -1
  390. nautobot/core/fixtures/user-data.json +0 -59
  391. {nautobot-2.3.15b1.dist-info → nautobot-2.3.16.dist-info}/LICENSE.txt +0 -0
  392. {nautobot-2.3.15b1.dist-info → nautobot-2.3.16.dist-info}/NOTICE +0 -0
  393. {nautobot-2.3.15b1.dist-info → nautobot-2.3.16.dist-info}/entry_points.txt +0 -0
@@ -212,13 +212,13 @@ class ContactSerializer(NautobotModelSerializer):
212
212
  "phone": {"default": ""},
213
213
  }
214
214
 
215
- def validate(self, data):
216
- attrs = data.copy()
217
- attrs.pop("teams", None)
215
+ def validate(self, attrs):
216
+ local_attrs = attrs.copy()
217
+ local_attrs.pop("teams", None)
218
218
  validator = UniqueTogetherValidator(queryset=Contact.objects.all(), fields=("name", "phone", "email"))
219
- validator(attrs, self)
220
- super().validate(attrs)
221
- return data
219
+ validator(local_attrs, self)
220
+ super().validate(local_attrs)
221
+ return attrs
222
222
 
223
223
 
224
224
  class ContactAssociationSerializer(NautobotModelSerializer):
@@ -233,18 +233,18 @@ class ContactAssociationSerializer(NautobotModelSerializer):
233
233
  "team": {"required": False},
234
234
  }
235
235
 
236
- def validate(self, data):
236
+ def validate(self, attrs):
237
237
  # Validate uniqueness of (associated object, associated object type, contact/team, role)
238
238
  unique_together_fields = None
239
239
 
240
- if data.get("contact") and data.get("role"):
240
+ if attrs.get("contact") and attrs.get("role"):
241
241
  unique_together_fields = (
242
242
  "associated_object_type",
243
243
  "associated_object_id",
244
244
  "contact",
245
245
  "role",
246
246
  )
247
- elif data.get("team") and data.get("role"):
247
+ elif attrs.get("team") and attrs.get("role"):
248
248
  unique_together_fields = (
249
249
  "associated_object_type",
250
250
  "associated_object_id",
@@ -257,11 +257,11 @@ class ContactAssociationSerializer(NautobotModelSerializer):
257
257
  queryset=ContactAssociation.objects.all(),
258
258
  fields=unique_together_fields,
259
259
  )
260
- validator(data, self)
260
+ validator(attrs, self)
261
261
 
262
- super().validate(data)
262
+ super().validate(attrs)
263
263
 
264
- return data
264
+ return attrs
265
265
 
266
266
 
267
267
  #
@@ -279,8 +279,8 @@ class ContentTypeSerializer(BaseModelSerializer):
279
279
  fields = "__all__"
280
280
 
281
281
  @extend_schema_field(serializers.CharField)
282
- def get_display(self, obj):
283
- return obj.app_labeled_name
282
+ def get_display(self, instance):
283
+ return instance.app_labeled_name
284
284
 
285
285
 
286
286
  #
@@ -501,17 +501,17 @@ class ImageAttachmentSerializer(ValidatedModelSerializer):
501
501
  model = ImageAttachment
502
502
  fields = "__all__"
503
503
 
504
- def validate(self, data):
504
+ def validate(self, attrs):
505
505
  # Validate that the parent object exists
506
506
  try:
507
- data["content_type"].get_object_for_this_type(id=data["object_id"])
507
+ attrs["content_type"].get_object_for_this_type(id=attrs["object_id"])
508
508
  except ObjectDoesNotExist:
509
- raise serializers.ValidationError(f"Invalid parent object: {data['content_type']} ID {data['object_id']}")
509
+ raise serializers.ValidationError(f"Invalid parent object: {attrs['content_type']} ID {attrs['object_id']}")
510
510
 
511
511
  # Enforce model validation
512
- super().validate(data)
512
+ super().validate(attrs)
513
513
 
514
- return data
514
+ return attrs
515
515
 
516
516
  @extend_schema_field(
517
517
  PolymorphicProxySerializer(
@@ -539,24 +539,24 @@ class JobSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
539
539
  model = Job
540
540
  fields = "__all__"
541
541
 
542
- def validate(self, data):
542
+ def validate(self, attrs):
543
543
  # note no validation for on creation of jobs because we do not support user creation of Job records via API
544
544
  if self.instance:
545
- has_sensitive_variables = data.get("has_sensitive_variables", self.instance.has_sensitive_variables)
546
- approval_required = data.get("approval_required", self.instance.approval_required)
545
+ has_sensitive_variables = attrs.get("has_sensitive_variables", self.instance.has_sensitive_variables)
546
+ approval_required = attrs.get("approval_required", self.instance.approval_required)
547
547
 
548
548
  if approval_required and has_sensitive_variables:
549
549
  error_message = "A job with sensitive variables cannot also be marked as requiring approval"
550
550
  errors = {}
551
551
 
552
- if "approval_required" in data:
552
+ if "approval_required" in attrs:
553
553
  errors["approval_required"] = [error_message]
554
- if "has_sensitive_variables" in data:
554
+ if "has_sensitive_variables" in attrs:
555
555
  errors["has_sensitive_variables"] = [error_message]
556
556
 
557
557
  raise serializers.ValidationError(errors)
558
558
 
559
- return super().validate(data)
559
+ return super().validate(attrs)
560
560
 
561
561
 
562
562
  class JobVariableSerializer(serializers.Serializer):
@@ -668,22 +668,22 @@ class JobHookSerializer(NautobotModelSerializer):
668
668
  model = JobHook
669
669
  fields = "__all__"
670
670
 
671
- def validate(self, data):
672
- validated_data = super().validate(data)
671
+ def validate(self, attrs):
672
+ validated_attrs = super().validate(attrs)
673
673
 
674
674
  conflicts = JobHook.check_for_conflicts(
675
675
  instance=self.instance,
676
- content_types=data.get("content_types"),
677
- job=data.get("job"),
678
- type_create=data.get("type_create"),
679
- type_update=data.get("type_update"),
680
- type_delete=data.get("type_delete"),
676
+ content_types=attrs.get("content_types"),
677
+ job=attrs.get("job"),
678
+ type_create=attrs.get("type_create"),
679
+ type_update=attrs.get("type_update"),
680
+ type_delete=attrs.get("type_delete"),
681
681
  )
682
682
 
683
683
  if conflicts:
684
684
  raise serializers.ValidationError(conflicts)
685
685
 
686
- return validated_data
686
+ return validated_attrs
687
687
 
688
688
 
689
689
  class JobCreationSerializer(BaseModelSerializer):
@@ -702,15 +702,15 @@ class JobCreationSerializer(BaseModelSerializer):
702
702
  model = ScheduledJob
703
703
  fields = ["url", "name", "start_time", "interval", "crontab"]
704
704
 
705
- def validate(self, data):
706
- data = super().validate(data)
705
+ def validate(self, attrs):
706
+ attrs = super().validate(attrs)
707
707
 
708
- if data["interval"] in choices.JobExecutionType.SCHEDULE_CHOICES:
709
- if "name" not in data:
708
+ if attrs["interval"] in choices.JobExecutionType.SCHEDULE_CHOICES:
709
+ if "name" not in attrs:
710
710
  raise serializers.ValidationError({"name": "Please provide a name for the job schedule."})
711
711
 
712
- if ("start_time" not in data and data["interval"] != choices.JobExecutionType.TYPE_CUSTOM) or (
713
- "start_time" in data and data["start_time"] < models.ScheduledJob.earliest_possible_time()
712
+ if ("start_time" not in attrs and attrs["interval"] != choices.JobExecutionType.TYPE_CUSTOM) or (
713
+ "start_time" in attrs and attrs["start_time"] < models.ScheduledJob.earliest_possible_time()
714
714
  ):
715
715
  raise serializers.ValidationError(
716
716
  {
@@ -718,15 +718,15 @@ class JobCreationSerializer(BaseModelSerializer):
718
718
  }
719
719
  )
720
720
 
721
- if data["interval"] == choices.JobExecutionType.TYPE_CUSTOM:
722
- if data.get("crontab") is None:
721
+ if attrs["interval"] == choices.JobExecutionType.TYPE_CUSTOM:
722
+ if attrs.get("crontab") is None:
723
723
  raise serializers.ValidationError({"crontab": "Please enter a valid crontab."})
724
724
  try:
725
- models.ScheduledJob.get_crontab(data["crontab"])
725
+ models.ScheduledJob.get_crontab(attrs["crontab"])
726
726
  except Exception as e:
727
727
  raise serializers.ValidationError({"crontab": e})
728
728
 
729
- return data
729
+ return attrs
730
730
 
731
731
 
732
732
  class JobInputSerializer(serializers.Serializer):
@@ -744,15 +744,18 @@ class JobMultiPartInputSerializer(serializers.Serializer):
744
744
  _schedule_crontab = serializers.CharField(required=False, allow_blank=True)
745
745
  _task_queue = serializers.CharField(required=False, allow_blank=True)
746
746
 
747
- def validate(self, data):
748
- data = super().validate(data)
747
+ def validate(self, attrs):
748
+ attrs = super().validate(attrs)
749
749
 
750
- if "_schedule_interval" in data and data["_schedule_interval"] != JobExecutionType.TYPE_IMMEDIATELY:
751
- if "_schedule_name" not in data:
750
+ if "_schedule_interval" in attrs and attrs["_schedule_interval"] != JobExecutionType.TYPE_IMMEDIATELY:
751
+ if "_schedule_name" not in attrs:
752
752
  raise serializers.ValidationError({"_schedule_name": "Please provide a name for the job schedule."})
753
753
 
754
- if ("_schedule_start_time" not in data and data["_schedule_interval"] != JobExecutionType.TYPE_CUSTOM) or (
755
- "_schedule_start_time" in data and data["_schedule_start_time"] < ScheduledJob.earliest_possible_time()
754
+ if (
755
+ "_schedule_start_time" not in attrs and attrs["_schedule_interval"] != JobExecutionType.TYPE_CUSTOM
756
+ ) or (
757
+ "_schedule_start_time" in attrs
758
+ and attrs["_schedule_start_time"] < ScheduledJob.earliest_possible_time()
756
759
  ):
757
760
  raise serializers.ValidationError(
758
761
  {
@@ -760,15 +763,15 @@ class JobMultiPartInputSerializer(serializers.Serializer):
760
763
  }
761
764
  )
762
765
 
763
- if data["_schedule_interval"] == JobExecutionType.TYPE_CUSTOM:
764
- if data.get("_schedule_crontab") is None:
766
+ if attrs["_schedule_interval"] == JobExecutionType.TYPE_CUSTOM:
767
+ if attrs.get("_schedule_crontab") is None:
765
768
  raise serializers.ValidationError({"_schedule_crontab": "Please enter a valid crontab."})
766
769
  try:
767
- ScheduledJob.get_crontab(data["_schedule_crontab"])
770
+ ScheduledJob.get_crontab(attrs["_schedule_crontab"])
768
771
  except Exception as e:
769
772
  raise serializers.ValidationError({"_schedule_crontab": e})
770
773
 
771
- return data
774
+ return attrs
772
775
 
773
776
 
774
777
  class JobLogEntrySerializer(BaseModelSerializer):
@@ -1054,18 +1057,18 @@ class TagSerializer(NautobotModelSerializer):
1054
1057
  "color": {"help_text": "RGB color in hexadecimal (e.g. 00ff00)"},
1055
1058
  }
1056
1059
 
1057
- def validate(self, data):
1058
- data = super().validate(data)
1060
+ def validate(self, attrs):
1061
+ attrs = super().validate(attrs)
1059
1062
 
1060
1063
  # check if tag is assigned to any of the removed content_types
1061
- if self.instance is not None and self.instance.present_in_database and "content_types" in data:
1062
- content_types_id = [content_type.id for content_type in data["content_types"]]
1064
+ if self.instance is not None and self.instance.present_in_database and "content_types" in attrs:
1065
+ content_types_id = [content_type.id for content_type in attrs["content_types"]]
1063
1066
  errors = self.instance.validate_content_types_removal(content_types_id)
1064
1067
 
1065
1068
  if errors:
1066
1069
  raise serializers.ValidationError(errors)
1067
1070
 
1068
- return data
1071
+ return attrs
1069
1072
 
1070
1073
 
1071
1074
  #
@@ -1087,10 +1090,10 @@ class TeamSerializer(NautobotModelSerializer):
1087
1090
  # https://www.django-rest-framework.org/api-guide/validators/#optional-fields
1088
1091
  validators = []
1089
1092
 
1090
- def validate(self, data):
1093
+ def validate(self, attrs):
1091
1094
  validator = UniqueTogetherValidator(queryset=Team.objects.all(), fields=("name", "phone", "email"))
1092
- validator(data, self)
1093
- return super().validate(data)
1095
+ validator(attrs, self)
1096
+ return super().validate(attrs)
1094
1097
 
1095
1098
 
1096
1099
  #
@@ -1108,19 +1111,19 @@ class WebhookSerializer(ValidatedModelSerializer, NotesSerializerMixin):
1108
1111
  model = Webhook
1109
1112
  fields = "__all__"
1110
1113
 
1111
- def validate(self, data):
1112
- validated_data = super().validate(data)
1114
+ def validate(self, attrs):
1115
+ validated_attrs = super().validate(attrs)
1113
1116
 
1114
1117
  conflicts = Webhook.check_for_conflicts(
1115
1118
  instance=self.instance,
1116
- content_types=data.get("content_types"),
1117
- payload_url=data.get("payload_url"),
1118
- type_create=data.get("type_create"),
1119
- type_update=data.get("type_update"),
1120
- type_delete=data.get("type_delete"),
1119
+ content_types=attrs.get("content_types"),
1120
+ payload_url=attrs.get("payload_url"),
1121
+ type_create=attrs.get("type_create"),
1122
+ type_update=attrs.get("type_update"),
1123
+ type_delete=attrs.get("type_delete"),
1121
1124
  )
1122
1125
 
1123
1126
  if conflicts:
1124
1127
  raise serializers.ValidationError(conflicts)
1125
1128
 
1126
- return validated_data
1129
+ return validated_attrs
@@ -732,13 +732,13 @@ class JobViewSet(
732
732
  ):
733
733
  lookup_value_regex = r"[-0-9a-fA-F]+"
734
734
 
735
- def perform_destroy(self, obj):
736
- if obj.module_name.startswith("nautobot."):
735
+ def perform_destroy(self, instance):
736
+ if instance.module_name.startswith("nautobot."):
737
737
  raise ProtectedError(
738
- f"Unable to delete Job {obj}. System Job cannot be deleted",
738
+ f"Unable to delete Job {instance}. System Job cannot be deleted",
739
739
  [],
740
740
  )
741
- super().perform_destroy(obj)
741
+ super().perform_destroy(instance)
742
742
 
743
743
 
744
744
  @extend_schema_view(
@@ -837,7 +837,7 @@ class StatusModelFilterFormMixin(forms.Form):
837
837
  self.order_fields(self.field_order) # Reorder fields again
838
838
 
839
839
 
840
- class TagsBulkEditFormMixin(forms.Form):
840
+ class TagsBulkEditFormMixin(BulkEditForm):
841
841
  def __init__(self, *args, **kwargs):
842
842
  super().__init__(*args, **kwargs)
843
843
 
@@ -160,5 +160,4 @@ class RedisBackend(RedisHealthCheck):
160
160
  elif client_class == "django_redis.client.DefaultClient":
161
161
  self.check_redis(redis_url=location, **options.get("CONNECTION_POOL_KWARGS", {}))
162
162
  else:
163
- if self.redis_url is None:
164
- self.add_error(ServiceUnavailable(f"{client_class} is an unsupported CLIENT_CLASS!"))
163
+ self.add_error(ServiceUnavailable(f"{client_class} is an unsupported CLIENT_CLASS!"))
nautobot/extras/jobs.py CHANGED
@@ -295,7 +295,7 @@ class BaseJob:
295
295
  - my_plugin.jobs.MyPluginJob - App-provided Job
296
296
  - git_repository.jobs.myjob.MyJob - GitRepository Job
297
297
  """
298
- return f"{cls.__module__}.{cls.__name__}"
298
+ return f"{cls.__module__}.{cls.__name__}" # pylint: disable=no-member
299
299
 
300
300
  @final
301
301
  @classproperty
@@ -332,7 +332,7 @@ class BaseJob:
332
332
  @final
333
333
  @classproperty
334
334
  def name(cls) -> str: # pylint: disable=no-self-argument
335
- return cls._get_meta_attr_and_assert_type("name", cls.__name__, expected_type=str)
335
+ return cls._get_meta_attr_and_assert_type("name", cls.__name__, expected_type=str) # pylint: disable=no-member
336
336
 
337
337
  @final
338
338
  @classproperty
@@ -420,7 +420,7 @@ class BaseJob:
420
420
  @classproperty
421
421
  def registered_name(cls) -> str: # pylint: disable=no-self-argument
422
422
  """Deprecated - use class_path classproperty instead."""
423
- return f"{cls.__module__}.{cls.__name__}"
423
+ return f"{cls.__module__}.{cls.__name__}" # pylint: disable=no-member
424
424
 
425
425
  @classmethod
426
426
  def _get_vars(cls):
@@ -1011,7 +1011,7 @@ class JobHookReceiver(Job):
1011
1011
 
1012
1012
  object_change = ObjectVar(model=ObjectChange)
1013
1013
 
1014
- def run(self, object_change):
1014
+ def run(self, object_change): # pylint: disable=arguments-differ
1015
1015
  """JobHookReceiver subclasses generally shouldn't need to override this method."""
1016
1016
  self.receive_job_hook(
1017
1017
  change=object_change,
@@ -1040,7 +1040,7 @@ class JobButtonReceiver(Job):
1040
1040
  object_pk = StringVar()
1041
1041
  object_model_name = StringVar()
1042
1042
 
1043
- def run(self, object_pk, object_model_name):
1043
+ def run(self, object_pk, object_model_name): # pylint: disable=arguments-differ
1044
1044
  """JobButtonReceiver subclasses generally shouldn't need to override this method."""
1045
1045
  model = get_model_from_name(object_model_name)
1046
1046
  obj = model.objects.get(pk=object_pk)
@@ -22,8 +22,9 @@ class JobResultManager(BaseManager.from_queryset(RestrictedQuerySet), TaskResult
22
22
  return self.model(id=task_id)
23
23
 
24
24
  @transaction_retry(max_retries=2)
25
- def store_result(
25
+ def store_result( # pylint:disable=arguments-differ # Nautobot adds kwargs like job_model_id and scheduled_job_id
26
26
  self,
27
+ *,
27
28
  task_id,
28
29
  result,
29
30
  status,
@@ -41,6 +42,7 @@ class JobResultManager(BaseManager.from_queryset(RestrictedQuerySet), TaskResult
41
42
  using=None,
42
43
  content_type=None,
43
44
  content_encoding=None,
45
+ **kwargs,
44
46
  ):
45
47
  """
46
48
  Store the result and status of a Celery task.
@@ -2,7 +2,6 @@ from collections import OrderedDict
2
2
 
3
3
  from django.db import migrations
4
4
 
5
- from nautobot.extras.choices import LogLevelChoices
6
5
  from nautobot.extras.constants import (
7
6
  JOB_LOG_MAX_ABSOLUTE_URL_LENGTH,
8
7
  JOB_LOG_MAX_GROUPING_LENGTH,
@@ -123,14 +122,13 @@ def reverse_migrate_params(apps, schema_editor):
123
122
  ]
124
123
  )
125
124
 
126
- if entry.log_level != LogLevelChoices.LOG_DEFAULT:
127
- job_result.data[entry.grouping].setdefault(entry.log_level, 0)
128
- job_result.data[entry.grouping][entry.log_level] += 1
129
- if "total" not in job_result.data:
130
- job_result.data["total"] = _data_grouping_struct()
131
- del job_result.data["total"]["log"]
132
- job_result.data["total"].setdefault(entry.log_level, 0)
133
- job_result.data["total"][entry.log_level] += 1
125
+ job_result.data[entry.grouping].setdefault(entry.log_level, 0)
126
+ job_result.data[entry.grouping][entry.log_level] += 1
127
+ if "total" not in job_result.data:
128
+ job_result.data["total"] = _data_grouping_struct()
129
+ del job_result.data["total"]["log"]
130
+ job_result.data["total"].setdefault(entry.log_level, 0)
131
+ job_result.data["total"][entry.log_level] += 1
134
132
 
135
133
  job_result.save()
136
134
 
@@ -578,7 +578,13 @@ class CustomField(
578
578
  )
579
579
 
580
580
  def to_form_field(
581
- self, set_initial=True, enforce_required=True, for_csv_import=False, simple_json_filter=False, label=None
581
+ self,
582
+ set_initial=True,
583
+ enforce_required=True,
584
+ for_csv_import=False,
585
+ simple_json_filter=False,
586
+ label=None,
587
+ for_filter_form=False,
582
588
  ):
583
589
  """
584
590
  Return a form field suitable for setting a CustomField's value for an object.
@@ -590,6 +596,7 @@ class CustomField(
590
596
  this is *not* used for CSV imports since 2.0, but it *is* used for JSON/YAML import of DeviceTypes.
591
597
  simple_json_filter: Return a TextInput widget for JSON filtering instead of the default TextArea widget.
592
598
  label: Set the input label manually (if required); otherwise, defaults to field's __str__() implementation.
599
+ for_filter_form: If True return the relevant form field for filter form
593
600
  """
594
601
  initial = self.default if set_initial else None
595
602
  required = self.required if enforce_required else False
@@ -676,7 +683,7 @@ class CustomField(
676
683
  default_choice = self.custom_field_choices.filter(value=self.default).first()
677
684
 
678
685
  # Set the initial value to the first available choice (if any)
679
- if self.type == CustomFieldTypeChoices.TYPE_SELECT:
686
+ if self.type == CustomFieldTypeChoices.TYPE_SELECT and not for_filter_form:
680
687
  if not required or default_choice is None:
681
688
  choices = add_blank_choice(choices)
682
689
  field_class = CSVChoiceField if for_csv_import else forms.ChoiceField
@@ -704,16 +711,10 @@ class CustomField(
704
711
 
705
712
  def to_filter_form_field(self, lookup_expr="exact", *args, **kwargs):
706
713
  """Return a filter form field suitable for filtering a CustomField's value for an object."""
707
- form_field = self.to_form_field(*args, **kwargs)
708
- # We would handle type selection differently because:
709
- # 1. We'd need to use StaticSelect2Multiple for lookup_type 'exact' because self.type `select` uses StaticSelect2 by default.
710
- # 2. Remove the blank choice since StaticSelect2Multiple is always blank and interprets the blank choice as an extra option.
711
- # 3. If lookup_type is not the same as exact, use MultiValueCharInput
714
+ form_field = self.to_form_field(*args, **kwargs, for_filter_form=True)
715
+ # We would handle type selection differently because: If lookup_type is not the same as exact, use MultiValueCharInput
712
716
  if self.type == CustomFieldTypeChoices.TYPE_SELECT:
713
- if lookup_expr in ["exact", "contains"]:
714
- choices = form_field.choices[1:]
715
- form_field.widget = StaticSelect2Multiple(choices=choices)
716
- else:
717
+ if lookup_expr not in ["exact", "contains"]:
717
718
  form_field.widget = MultiValueCharInput()
718
719
  return form_field
719
720
 
@@ -1,6 +1,9 @@
1
1
  """Dynamic Groups Models."""
2
2
 
3
+ from __future__ import annotations # python 3.8
4
+
3
5
  import logging
6
+ from typing import Optional
4
7
 
5
8
  from django import forms
6
9
  from django.contrib.contenttypes.fields import GenericForeignKey
@@ -116,7 +119,7 @@ class DynamicGroup(PrimaryModel):
116
119
  return self._model
117
120
 
118
121
  @property
119
- def filterset_class(self):
122
+ def filterset_class(self) -> Optional[type[django_filters.FilterSet]]:
120
123
  if getattr(self, "_filterset_class", None) is None:
121
124
  try:
122
125
  self._filterset_class = get_filterset_for_model(self.model)
@@ -125,7 +128,7 @@ class DynamicGroup(PrimaryModel):
125
128
  return self._filterset_class
126
129
 
127
130
  @property
128
- def filterform_class(self):
131
+ def filterform_class(self) -> Optional[type[forms.Form]]:
129
132
  if getattr(self, "_filterform_class", None) is None:
130
133
  try:
131
134
  self._filterform_class = get_form_for_model(self.model, form_prefix="Filter")
@@ -134,7 +137,7 @@ class DynamicGroup(PrimaryModel):
134
137
  return self._filterform_class
135
138
 
136
139
  @property
137
- def form_class(self):
140
+ def form_class(self) -> Optional[type[forms.Form]]:
138
141
  if getattr(self, "_form_class", None) is None:
139
142
  try:
140
143
  self._form_class = get_form_for_model(self.model)
@@ -147,19 +150,19 @@ class DynamicGroup(PrimaryModel):
147
150
  """Return all FilterForm fields in a dictionary."""
148
151
 
149
152
  # Fail gracefully with an empty dict if nothing is working yet.
150
- if not self.form_class:
153
+ if self.form_class is None or self.filterform_class is None or self.filterset_class is None:
151
154
  return {}
152
155
 
153
156
  # Get model form and fields
154
- modelform = self.form_class()
157
+ modelform = self.form_class() # pylint: disable=not-callable
155
158
  modelform_fields = modelform.fields
156
159
 
157
160
  # Get filter form and fields
158
- filterform = self.filterform_class()
161
+ filterform = self.filterform_class() # pylint: disable=not-callable
159
162
  filterform_fields = filterform.fields
160
163
 
161
164
  # Get filterset and fields
162
- filterset = self.filterset_class()
165
+ filterset = self.filterset_class() # pylint: disable=not-callable
163
166
  filterset_fields = filterset.filters
164
167
 
165
168
  # Get dynamic group filter field mappings (if any)
@@ -303,6 +306,7 @@ class DynamicGroup(PrimaryModel):
303
306
  # Since associated_object is a GenericForeignKey, we can't just do:
304
307
  # return self.static_group_associations.values_list("associated_object", flat=True)
305
308
  return self.model.objects.filter(
309
+ # pylint: disable=no-member # false positive about self.static_group_associations
306
310
  pk__in=self.static_group_associations(manager="all_objects").values_list("associated_object_id", flat=True)
307
311
  )
308
312
 
@@ -611,7 +615,7 @@ class DynamicGroup(PrimaryModel):
611
615
  raise ValidationError({"filter": "Filter can only be set for groups of type `dynamic-filter`."})
612
616
  else:
613
617
  # Validate against the filterset's internal form validation.
614
- filterset = self.filterset_class(self.filter)
618
+ filterset = self.filterset_class(self.filter) # pylint: disable=not-callable
615
619
  if not filterset.is_valid():
616
620
  raise ValidationError(filterset.errors)
617
621
 
@@ -725,7 +729,7 @@ class DynamicGroup(PrimaryModel):
725
729
  if self.group_type != DynamicGroupTypeChoices.TYPE_DYNAMIC_FILTER:
726
730
  raise RuntimeError(f"{self} is not a dynamic-filter group")
727
731
 
728
- filterset = self.filterset_class(self.filter, self.model.objects.all())
732
+ filterset = self.filterset_class(self.filter, self.model.objects.all()) # pylint: disable=not-callable
729
733
  query = models.Q()
730
734
 
731
735
  # In this case we want all filters for a group's filter dict in a set intersection (boolean
@@ -234,13 +234,13 @@ class Job(PrimaryModel):
234
234
  def __str__(self):
235
235
  return self.name
236
236
 
237
- def delete(self):
237
+ def delete(self, *args, **kwargs):
238
238
  if self.module_name.startswith("nautobot."):
239
239
  raise ProtectedError(
240
240
  f"Unable to delete Job {self}. System Job cannot be deleted",
241
241
  [],
242
242
  )
243
- super().delete()
243
+ super().delete(*args, **kwargs)
244
244
 
245
245
  @property
246
246
  def job_class(self):
@@ -610,7 +610,7 @@ class JobResult(BaseModel, CustomFieldModel):
610
610
  # Only add metrics if we have a related job model. If we are moving to a terminal state we should always
611
611
  # have a related job model, so this shouldn't be too tight of a restriction.
612
612
  if self.job_model:
613
- duration = self.date_done - self.created
613
+ duration = self.date_done - self.date_created
614
614
  JOB_RESULT_METRIC.labels(self.job_model.grouping, self.job_model.name, status).observe(
615
615
  duration.total_seconds()
616
616
  )
@@ -1180,7 +1180,7 @@ class ScheduledJob(BaseModel):
1180
1180
 
1181
1181
  def to_cron(self):
1182
1182
  tz = self.time_zone
1183
- t = self.start_time.astimezone(tz)
1183
+ t = self.start_time.astimezone(tz) # pylint: disable=no-member
1184
1184
  if self.interval == JobExecutionType.TYPE_HOURLY:
1185
1185
  return TzAwareCrontab(minute=t.minute, tz=tz)
1186
1186
  elif self.interval == JobExecutionType.TYPE_DAILY:
@@ -207,7 +207,7 @@ class ConfigContextModel(models.Model, ConfigContextSchemaValidationMixin):
207
207
  # Annotation not available, so fall back to manually querying for the config context
208
208
  config_context_data = ConfigContext.objects.get_for_object(self).values_list("data", flat=True)
209
209
  else:
210
- config_context_data = self.config_context_data or []
210
+ config_context_data = self.config_context_data or [] # pylint: disable=no-member
211
211
  config_context_data = [
212
212
  c["data"] for c in sorted(config_context_data, key=lambda k: (k["weight"], k["name"]))
213
213
  ]
@@ -831,7 +831,7 @@ class Note(ChangeLoggedModel, BaseModel):
831
831
  unique_together = [["assigned_object_type", "assigned_object_id", "user_name", "created"]]
832
832
 
833
833
  def __str__(self):
834
- return f"{self.assigned_object} - {self.created.isoformat() if self.created else None}"
834
+ return f"{self.assigned_object} - {self.created.isoformat() if self.created else None}" # pylint: disable=no-member
835
835
 
836
836
  def save(self, *args, **kwargs):
837
837
  # Record the user's name as static strings
@@ -166,7 +166,7 @@ class AppsAPIRootView(AuthenticatedAPIRootView):
166
166
  return entry
167
167
 
168
168
  @extend_schema(exclude=True)
169
- def get(self, request, format=None): # pylint: disable=redefined-builtin
169
+ def get(self, request, *args, format=None, **kwargs): # pylint: disable=redefined-builtin
170
170
  entries = []
171
171
  for app_name in settings.PLUGINS:
172
172
  app_config = apps.get_app_config(app_name)