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
@@ -594,3 +594,32 @@ class ExampleViewWithCustomPermissionsTest(TestCase):
594
594
  self.user.save()
595
595
  response = self.client.get(url)
596
596
  self.assertBodyContains(response, "You are viewing a table of example models")
597
+
598
+
599
+ class SearchRobotsTestCase(TestCase):
600
+ def test_robots_disallowed(self):
601
+ """
602
+ Test that the robots.txt file is accessible to all users and defaults to disallowing all bots.
603
+ """
604
+ url = reverse("robots_txt")
605
+ response = self.client.get(url)
606
+ self.assertHttpStatus(response, 200)
607
+ self.assertBodyContains(response, "User-Agent: *")
608
+ self.assertBodyContains(response, "Disallow: /")
609
+
610
+ url = reverse("home")
611
+ response = self.client.get(url)
612
+ self.assertContains(response, '<meta name="robots" content="noindex, nofollow">', html=True)
613
+
614
+ @override_settings(PUBLISH_ROBOTS_TXT=False)
615
+ def test_robots_allowed(self):
616
+ """
617
+ Test that the robots.txt file is not published if PUBLISH_ROBOTS_TXT is set to False.
618
+ """
619
+ url = reverse("robots_txt")
620
+ response = self.client.get(url)
621
+ self.assertHttpStatus(response, 404)
622
+
623
+ url = reverse("home")
624
+ response = self.client.get(url)
625
+ self.assertNotContains(response, '<meta name="robots" content="noindex, nofollow">', html=True)
nautobot/core/urls.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from django.conf import settings
2
+ from django.http import HttpResponse, HttpResponseNotFound
2
3
  from django.urls import include, path
3
4
  from django.views.generic import TemplateView
4
5
  from django.views.static import serve
@@ -72,6 +73,14 @@ urlpatterns = [
72
73
  path(
73
74
  "template.css", TemplateView.as_view(template_name="template.css", content_type="text/css"), name="template_css"
74
75
  ),
76
+ # The response is conditional as opposed to wrapping the path() call in an if statement to be able to test the setting with current test setup
77
+ path(
78
+ "robots.txt",
79
+ lambda x: HttpResponse("User-Agent: *\nDisallow: /", content_type="text/plain")
80
+ if settings.PUBLISH_ROBOTS_TXT
81
+ else HttpResponseNotFound(),
82
+ name="robots_txt",
83
+ ),
75
84
  ]
76
85
 
77
86
 
@@ -1,6 +1,9 @@
1
+ from __future__ import annotations # python 3.8
2
+
1
3
  from copy import deepcopy
2
4
  import logging
3
5
  import re
6
+ from typing import Optional
4
7
 
5
8
  from django.conf import settings
6
9
  from django.contrib import messages
@@ -13,7 +16,7 @@ from django.core.exceptions import (
13
16
  ValidationError,
14
17
  )
15
18
  from django.db import IntegrityError, transaction
16
- from django.db.models import ManyToManyField, ProtectedError, Q
19
+ from django.db.models import ManyToManyField, Model, ProtectedError, Q, QuerySet
17
20
  from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
18
21
  from django.http import HttpResponse
19
22
  from django.shortcuts import get_object_or_404, redirect, render
@@ -22,7 +25,8 @@ from django.utils.encoding import iri_to_uri
22
25
  from django.utils.html import format_html
23
26
  from django.utils.http import url_has_allowed_host_and_scheme
24
27
  from django.views.generic import View
25
- from django_tables2 import RequestConfig
28
+ from django_filters import FilterSet
29
+ from django_tables2 import RequestConfig, Table
26
30
 
27
31
  from nautobot.core.api.utils import get_serializer_for_model
28
32
  from nautobot.core.exceptions import AbortTransaction
@@ -78,8 +82,8 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
78
82
  template_name: Name of the template to use
79
83
  """
80
84
 
81
- queryset = None
82
- template_name = None
85
+ queryset: QuerySet
86
+ template_name: Optional[str] = None
83
87
 
84
88
  def get_required_permission(self):
85
89
  return get_permission_for_model(self.queryset.model, "view")
@@ -139,10 +143,10 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
139
143
  non_filter_params: List of query parameters that are **not** used for queryset filtering
140
144
  """
141
145
 
142
- queryset = None
143
- filterset = None
144
- filterset_form = None
145
- table = None
146
+ queryset: QuerySet
147
+ filterset: Optional[type[FilterSet]] = None
148
+ filterset_form: Optional[type[Form]] = None
149
+ table: Optional[type[Table]] = None
146
150
  template_name = "generic/object_list.html"
147
151
  action_buttons = ("add", "import", "export")
148
152
  non_filter_params = (
@@ -159,7 +163,11 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
159
163
  def get_filter_params(self, request):
160
164
  """Helper function - take request.GET and discard any parameters that are not used for queryset filtering."""
161
165
  params = request.GET.copy()
162
- filter_params = get_filterable_params_from_filter_params(params, self.non_filter_params, self.filterset())
166
+ filter_params = get_filterable_params_from_filter_params(
167
+ params,
168
+ self.non_filter_params,
169
+ self.filterset(), # pylint: disable=not-callable # this fn is only called if filterset is not None
170
+ )
163
171
  if params.get("saved_view") and not filter_params and not params.get("all_filters_removed"):
164
172
  return SavedView.objects.get(pk=params.get("saved_view")).config.get("filter_params", {})
165
173
  return filter_params
@@ -233,9 +241,9 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
233
241
  except ObjectDoesNotExist:
234
242
  pass
235
243
 
236
- if self.filterset:
244
+ if self.filterset is not None:
237
245
  filter_params = self.get_filter_params(request)
238
- filterset = self.filterset(filter_params, self.queryset)
246
+ filterset = self.filterset(filter_params, self.queryset) # pylint:disable=not-callable # only if not None
239
247
  self.queryset = filterset.qs
240
248
  if not filterset.is_valid():
241
249
  messages.error(
@@ -263,7 +271,7 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
263
271
  else:
264
272
  dynamic_filter_form = DynamicFilterFormSet(filterset=filterset)
265
273
 
266
- if self.filterset_form:
274
+ if self.filterset_form is not None:
267
275
  filter_form = self.filterset_form(filter_params, label_suffix="")
268
276
 
269
277
  # Check for export template rendering
@@ -323,14 +331,14 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
323
331
  messages.error(request, f"Saved view {current_saved_view_pk} not found")
324
332
 
325
333
  # Construct the objects table
326
- if self.table:
334
+ if self.table is not None:
327
335
  if self.request.GET.getlist("sort") or (
328
336
  current_saved_view is not None and current_saved_view.config.get("sort_order")
329
337
  ):
330
338
  hide_hierarchy_ui = True # hide tree hierarchy if custom sort is used
331
339
  table_changes_pending = self.request.GET.get("table_changes_pending", False)
332
340
 
333
- table = self.table(
341
+ table = self.table( # pylint: disable=not-callable # we confirmed that self.table is not None
334
342
  self.queryset,
335
343
  table_changes_pending=table_changes_pending,
336
344
  saved_view=current_saved_view,
@@ -407,8 +415,8 @@ class ObjectEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
407
415
  template_name: The name of the template
408
416
  """
409
417
 
410
- queryset = None
411
- model_form = None
418
+ queryset: QuerySet
419
+ model_form: type[Form]
412
420
  template_name = "generic/object_create.html"
413
421
 
414
422
  def get_required_permission(self):
@@ -550,7 +558,7 @@ class ObjectDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
550
558
  template_name: The name of the template
551
559
  """
552
560
 
553
- queryset = None
561
+ queryset: QuerySet
554
562
  template_name = "generic/object_delete.html"
555
563
 
556
564
  def get_required_permission(self):
@@ -630,9 +638,9 @@ class BulkCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
630
638
  template_name: The name of the template
631
639
  """
632
640
 
633
- queryset = None
634
- form = None
635
- model_form = None
641
+ queryset: QuerySet
642
+ form: type[Form]
643
+ model_form: type[Form]
636
644
  pattern_target = ""
637
645
  template_name = None
638
646
 
@@ -739,8 +747,8 @@ class ObjectImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
739
747
  template_name: The name of the template
740
748
  """
741
749
 
742
- queryset = None
743
- model_form = None
750
+ queryset: QuerySet
751
+ model_form: type[Form]
744
752
  related_object_forms = {}
745
753
  template_name = "generic/object_import.html"
746
754
 
@@ -884,8 +892,8 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): #
884
892
  template_name: The name of the template
885
893
  """
886
894
 
887
- queryset = None
888
- table = None
895
+ queryset: QuerySet
896
+ table: type[Table]
889
897
  template_name = "generic/object_bulk_import.html"
890
898
 
891
899
  def __init__(self, *args, **kwargs):
@@ -990,10 +998,10 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, EditAndDele
990
998
  template_name: The name of the template
991
999
  """
992
1000
 
993
- queryset = None
994
- filterset = None
995
- table = None
996
- form = None
1001
+ queryset: QuerySet
1002
+ filterset: Optional[type[FilterSet]] = None
1003
+ table: type[Table]
1004
+ form: type[Form]
997
1005
  template_name = "generic/object_bulk_edit.html"
998
1006
 
999
1007
  def get_required_permission(self):
@@ -1164,7 +1172,7 @@ class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
1164
1172
  An extendable view for renaming objects in bulk.
1165
1173
  """
1166
1174
 
1167
- queryset = None
1175
+ queryset: QuerySet
1168
1176
  template_name = "generic/object_bulk_rename.html"
1169
1177
 
1170
1178
  def __init__(self, *args, **kwargs):
@@ -1270,10 +1278,10 @@ class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, EditAndDe
1270
1278
  template_name: The name of the template
1271
1279
  """
1272
1280
 
1273
- queryset = None
1274
- filterset = None
1275
- table = None
1276
- form = None
1281
+ queryset: QuerySet
1282
+ filterset: Optional[type[FilterSet]] = None
1283
+ table: type[Table]
1284
+ form: Optional[type[Form]] = None
1277
1285
  template_name = "generic/object_bulk_delete.html"
1278
1286
 
1279
1287
  def get_required_permission(self):
@@ -1388,9 +1396,9 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View
1388
1396
  Add one or more components (e.g. interfaces, console ports, etc.) to a Device or VirtualMachine.
1389
1397
  """
1390
1398
 
1391
- queryset = None
1392
- form = None
1393
- model_form = None
1399
+ queryset: QuerySet
1400
+ form: type[Form]
1401
+ model_form: type[Form]
1394
1402
  template_name = "dcim/device_component_add.html"
1395
1403
 
1396
1404
  def get_required_permission(self):
@@ -1490,13 +1498,13 @@ class BulkComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin,
1490
1498
  Add one or more components (e.g. interfaces, console ports, etc.) to a set of Devices or VirtualMachines.
1491
1499
  """
1492
1500
 
1493
- parent_model = None
1501
+ parent_model: type[Model]
1494
1502
  parent_field = None
1495
- form = None
1496
- queryset = None
1497
- model_form = None
1498
- filterset = None
1499
- table = None
1503
+ form: type[Form]
1504
+ queryset: QuerySet
1505
+ model_form: type[Form]
1506
+ filterset: Optional[type[FilterSet]] = None
1507
+ table: type[Table]
1500
1508
  template_name = "generic/object_bulk_add_component.html"
1501
1509
 
1502
1510
  def get_required_permission(self):
@@ -1510,7 +1518,7 @@ class BulkComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin,
1510
1518
 
1511
1519
  # Are we editing *all* objects in the queryset or just a selected subset?
1512
1520
  if request.POST.get("_all") and self.filterset is not None:
1513
- pk_list = [obj.pk for obj in self.filterset(request.GET, self.parent_model.objects.only("pk")).qs]
1521
+ pk_list = [obj.pk for obj in self.filterset(request.GET, self.parent_model.objects.only("pk")).qs] # pylint: disable=not-callable
1514
1522
  else:
1515
1523
  pk_list = request.POST.getlist("pk")
1516
1524
 
@@ -1,4 +1,7 @@
1
+ from __future__ import annotations # python 3.8
2
+
1
3
  import logging
4
+ from typing import Optional
2
5
 
3
6
  from django.contrib import messages
4
7
  from django.contrib.auth.mixins import AccessMixin
@@ -22,6 +25,7 @@ from django.utils.encoding import iri_to_uri
22
25
  from django.utils.html import format_html
23
26
  from django.utils.http import url_has_allowed_host_and_scheme
24
27
  from django.views.generic.edit import FormView
28
+ from django_filters import FilterSet
25
29
  from drf_spectacular.utils import extend_schema
26
30
  from rest_framework import exceptions, mixins
27
31
  from rest_framework.decorators import action as drf_action
@@ -223,7 +227,7 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
223
227
  # filterset and filter_params will be initialized in filter_queryset() in ObjectListViewMixin
224
228
  filter_params = None
225
229
  filterset = None
226
- filterset_class = None
230
+ filterset_class: Optional[type[FilterSet]] = None
227
231
  filterset_form_class = None
228
232
  form_class = None
229
233
  create_form_class = None
@@ -473,7 +477,11 @@ class NautobotViewSetMixin(GenericViewSet, AccessMixin, GetReturnURLMixin, FormV
473
477
  def get_filter_params(self, request):
474
478
  """Helper function - take request.GET and discard any parameters that are not used for queryset filtering."""
475
479
  params = request.GET.copy()
476
- filter_params = get_filterable_params_from_filter_params(params, self.non_filter_params, self.filterset_class())
480
+ filter_params = get_filterable_params_from_filter_params(
481
+ params,
482
+ self.non_filter_params,
483
+ self.filterset_class(), # pylint: disable=not-callable # only called if filterset_class is not None
484
+ )
477
485
  if params.get("saved_view") and not filter_params and not params.get("all_filters_removed"):
478
486
  return SavedView.objects.get(pk=params.get("saved_view")).config.get("filter_params", {})
479
487
  return filter_params
@@ -628,8 +636,8 @@ class ObjectListViewMixin(NautobotViewSetMixin, mixins.ListModelMixin):
628
636
  """
629
637
 
630
638
  action_buttons = ("add", "import", "export")
631
- filterset_class = None
632
- filterset_form_class = None
639
+ filterset_class: Optional[type[FilterSet]] = None
640
+ filterset_form_class: Optional[type[Form]] = None
633
641
  hide_hierarchy_ui = False
634
642
  non_filter_params = (
635
643
  "export", # trigger for CSV/export-template/YAML export # 3.0 TODO: remove, irrelevant after #4746
@@ -780,7 +788,8 @@ class ObjectDestroyViewMixin(NautobotViewSetMixin, mixins.DestroyModelMixin):
780
788
  return self.perform_destroy(request, **kwargs)
781
789
  return Response(context)
782
790
 
783
- def perform_destroy(self, request, **kwargs):
791
+ # TODO: this conflicts with DRF's DestroyModelMixin.perform_destroy(self, instance) API
792
+ def perform_destroy(self, request, **kwargs): # pylint:disable=arguments-renamed
784
793
  """
785
794
  Function to validate the ObjectDeleteConfirmationForm and to delete the object.
786
795
  """
@@ -934,7 +943,7 @@ class EditAndDeleteAllModelMixin:
934
943
  filterset_class = getattr(self, "filterset_class", None)
935
944
 
936
945
  if request.GET and filterset_class is not None:
937
- queryset = filterset_class(request.GET, model.objects.all()).qs
946
+ queryset = filterset_class(request.GET, model.objects.all()).qs # pylint: disable=not-callable
938
947
  # We take this approach because filterset.qs has already applied .distinct(),
939
948
  # and performing a .delete directly on a queryset with .distinct applied is not allowed.
940
949
  queryset = self.queryset.filter(pk__in=queryset)
@@ -958,8 +967,8 @@ class ObjectBulkDestroyViewMixin(NautobotViewSetMixin, BulkDestroyModelMixin, Ed
958
967
  UI mixin to bulk destroy model instances.
959
968
  """
960
969
 
961
- bulk_destroy_form_class = None
962
- filterset_class = None
970
+ bulk_destroy_form_class: Optional[type[Form]] = None
971
+ filterset_class: Optional[type[FilterSet]] = None
963
972
 
964
973
  def _process_bulk_destroy_form(self, form):
965
974
  request = self.request
@@ -992,7 +1001,8 @@ class ObjectBulkDestroyViewMixin(NautobotViewSetMixin, BulkDestroyModelMixin, Ed
992
1001
  """
993
1002
  return self.perform_bulk_destroy(request, **kwargs)
994
1003
 
995
- def perform_bulk_destroy(self, request, **kwargs):
1004
+ # TODO: this conflicts with BulkDestroyModelMixin.perform_bulk_destroy(self, objects)
1005
+ def perform_bulk_destroy(self, request, **kwargs): # pylint:disable=arguments-renamed
996
1006
  """
997
1007
  request.POST "_delete": Function to render the user selection of objects in a table form/BulkDestroyConfirmationForm via Response that is passed to NautobotHTMLRenderer.
998
1008
  request.POST "_confirm": Function to validate the table form/BulkDestroyConfirmationForm and to perform the action of bulk destroy. Render the form with errors if exceptions are raised.
@@ -1087,8 +1097,8 @@ class ObjectBulkUpdateViewMixin(NautobotViewSetMixin, BulkUpdateModelMixin, Edit
1087
1097
  UI mixin to bulk update model instances.
1088
1098
  """
1089
1099
 
1090
- filterset_class = None
1091
- bulk_update_form_class = None
1100
+ filterset_class: Optional[type[FilterSet]] = None
1101
+ bulk_update_form_class: Optional[type[Form]] = None
1092
1102
 
1093
1103
  def _process_bulk_update_form(self, form):
1094
1104
  request = self.request
@@ -274,15 +274,15 @@ class LocationSerializer(
274
274
  ],
275
275
  }
276
276
 
277
- def validate(self, data):
277
+ def validate(self, attrs):
278
278
  # Validate uniqueness of (parent, name) since we omitted the automatically created validator from Meta.
279
- if data.get("parent") and data.get("name"):
279
+ if attrs.get("parent") and attrs.get("name"):
280
280
  validator = UniqueTogetherValidator(queryset=Location.objects.all(), fields=("parent", "name"))
281
- validator(data, self)
281
+ validator(attrs, self)
282
282
 
283
- super().validate(data)
283
+ super().validate(attrs)
284
284
 
285
- return data
285
+ return attrs
286
286
 
287
287
 
288
288
  #
@@ -328,20 +328,20 @@ class RackSerializer(TaggedModelSerializerMixin, NautobotModelSerializer):
328
328
  "include_others": True,
329
329
  }
330
330
 
331
- def validate(self, data):
331
+ def validate(self, attrs):
332
332
  # Validate uniqueness of (rack_group, name) since we omitted the automatically-created validator above.
333
- if data.get("rack_group", None):
333
+ if attrs.get("rack_group", None):
334
334
  validator = UniqueTogetherValidator(queryset=Rack.objects.all(), fields=("rack_group", "name"))
335
- validator(data, self)
335
+ validator(attrs, self)
336
336
  # Validate uniqueness of (rack_group, facility_id) since we omitted the automatically-created validator above.
337
- if data.get("facility_id", None) and data.get("rack_group", None):
337
+ if attrs.get("facility_id", None) and attrs.get("rack_group", None):
338
338
  validator = UniqueTogetherValidator(queryset=Rack.objects.all(), fields=("rack_group", "facility_id"))
339
- validator(data, self)
339
+ validator(attrs, self)
340
340
 
341
341
  # Enforce model validation
342
- super().validate(data)
342
+ super().validate(attrs)
343
343
 
344
- return data
344
+ return attrs
345
345
 
346
346
 
347
347
  class RackUnitSerializer(serializers.Serializer):
@@ -673,20 +673,20 @@ class DeviceSerializer(TaggedModelSerializerMixin, NautobotModelSerializer):
673
673
  def get_config_context(self, obj):
674
674
  return obj.get_config_context()
675
675
 
676
- def validate(self, data):
676
+ def validate(self, attrs):
677
677
  # Validate uniqueness of (rack, position, face) since we omitted the automatically-created validator from Meta.
678
- if data.get("rack") and data.get("position") and data.get("face"):
678
+ if attrs.get("rack") and attrs.get("position") and attrs.get("face"):
679
679
  validator = UniqueTogetherValidator(
680
680
  queryset=Device.objects.all(),
681
681
  fields=("rack", "position", "face"),
682
682
  message=f"The position and face is already occupied on this rack. {UniqueTogetherValidator.message}",
683
683
  )
684
- validator(data, self)
684
+ validator(attrs, self)
685
685
 
686
686
  # Enforce model validation
687
- super().validate(data)
687
+ super().validate(attrs)
688
688
 
689
- return data
689
+ return attrs
690
690
 
691
691
 
692
692
  class DeviceNAPALMSerializer(serializers.Serializer):
@@ -759,22 +759,22 @@ class PowerPortSerializer(
759
759
 
760
760
 
761
761
  class InterfaceCommonSerializer(TaggedModelSerializerMixin, NautobotModelSerializer):
762
- def validate(self, data):
762
+ def validate(self, attrs):
763
763
  # Validate many-to-many VLAN assignments
764
- mode = data.get("mode", getattr(self.instance, "mode", None))
764
+ mode = attrs.get("mode", getattr(self.instance, "mode", None))
765
765
 
766
766
  if mode != InterfaceModeChoices.MODE_TAGGED:
767
- if data.get("tagged_vlans"):
767
+ if attrs.get("tagged_vlans"):
768
768
  raise serializers.ValidationError(
769
769
  {
770
770
  "tagged_vlans": f"Mode must be set to {InterfaceModeChoices.MODE_TAGGED} when specifying tagged_vlans"
771
771
  }
772
772
  )
773
773
 
774
- if data.get("tagged_vlans") != [] and self.instance and self.instance.tagged_vlans.exists():
774
+ if attrs.get("tagged_vlans") != [] and self.instance and self.instance.tagged_vlans.exists():
775
775
  raise serializers.ValidationError({"tagged_vlans": f"Clear tagged_vlans to set mode to {mode}"})
776
776
 
777
- return super().validate(data)
777
+ return super().validate(attrs)
778
778
 
779
779
 
780
780
  class InterfaceSerializer(
@@ -877,18 +877,18 @@ class InventoryItemSerializer(TaggedModelSerializerMixin, TreeModelSerializerMix
877
877
  # https://www.django-rest-framework.org/api-guide/validators/#optional-fields
878
878
  validators = []
879
879
 
880
- def validate(self, data):
880
+ def validate(self, attrs):
881
881
  # Validate uniqueness of (device, parent, name) since we omitted the automatically created validator from Meta.
882
- if data.get("device") and data.get("parent") and data.get("name"):
882
+ if attrs.get("device") and attrs.get("parent") and attrs.get("name"):
883
883
  validator = UniqueTogetherValidator(
884
884
  queryset=InventoryItem.objects.all(),
885
885
  fields=("device", "parent", "name"),
886
886
  )
887
- validator(data, self)
887
+ validator(attrs, self)
888
888
 
889
- super().validate(data)
889
+ super().validate(attrs)
890
890
 
891
- return data
891
+ return attrs
892
892
 
893
893
 
894
894
  #
@@ -1135,21 +1135,21 @@ class ModuleBaySerializer(TaggedModelSerializerMixin, NautobotModelSerializer):
1135
1135
  fields = "__all__"
1136
1136
  validators = []
1137
1137
 
1138
- def validate(self, data):
1138
+ def validate(self, attrs):
1139
1139
  """Validate device and module field constraints for module bay."""
1140
- if data.get("parent_device") and data.get("parent_module"):
1140
+ if attrs.get("parent_device") and attrs.get("parent_module"):
1141
1141
  raise serializers.ValidationError("Only one of parent_device or parent_module must be set")
1142
- if data.get("parent_device"):
1142
+ if attrs.get("parent_device"):
1143
1143
  validator = UniqueTogetherValidator(
1144
1144
  queryset=self.Meta.model.objects.all(), fields=("parent_device", "name")
1145
1145
  )
1146
- validator(data, self)
1147
- if data.get("parent_module"):
1146
+ validator(attrs, self)
1147
+ if attrs.get("parent_module"):
1148
1148
  validator = UniqueTogetherValidator(
1149
1149
  queryset=self.Meta.model.objects.all(), fields=("parent_module", "name")
1150
1150
  )
1151
- validator(data, self)
1152
- return super().validate(data)
1151
+ validator(attrs, self)
1152
+ return super().validate(attrs)
1153
1153
 
1154
1154
 
1155
1155
  class ModuleBayTemplateSerializer(NautobotModelSerializer):
@@ -1158,17 +1158,17 @@ class ModuleBayTemplateSerializer(NautobotModelSerializer):
1158
1158
  fields = "__all__"
1159
1159
  validators = []
1160
1160
 
1161
- def validate(self, data):
1161
+ def validate(self, attrs):
1162
1162
  """Validate device_type and module_type field constraints for module bay template."""
1163
- if data.get("device_type") and data.get("module_type"):
1163
+ if attrs.get("device_type") and attrs.get("module_type"):
1164
1164
  raise serializers.ValidationError("Only one of device_type or module_type must be set")
1165
- if data.get("device_type"):
1165
+ if attrs.get("device_type"):
1166
1166
  validator = UniqueTogetherValidator(queryset=self.Meta.model.objects.all(), fields=("device_type", "name"))
1167
- validator(data, self)
1168
- if data.get("module_type"):
1167
+ validator(attrs, self)
1168
+ if attrs.get("module_type"):
1169
1169
  validator = UniqueTogetherValidator(queryset=self.Meta.model.objects.all(), fields=("module_type", "name"))
1170
- validator(data, self)
1171
- return super().validate(data)
1170
+ validator(attrs, self)
1171
+ return super().validate(attrs)
1172
1172
 
1173
1173
 
1174
1174
  class ModuleSerializer(TaggedModelSerializerMixin, NautobotModelSerializer):
@@ -1178,17 +1178,17 @@ class ModuleSerializer(TaggedModelSerializerMixin, NautobotModelSerializer):
1178
1178
 
1179
1179
  validators = []
1180
1180
 
1181
- def validate(self, data):
1181
+ def validate(self, attrs):
1182
1182
  """Validate asset_Tag, serial, parent_module_bay and location field constraints for module."""
1183
- if data.get("parent_module_bay") and data.get("location"):
1183
+ if attrs.get("parent_module_bay") and attrs.get("location"):
1184
1184
  raise serializers.ValidationError("Only one of parent_module_bay or location must be set")
1185
- if data.get("serial"):
1185
+ if attrs.get("serial"):
1186
1186
  validator = UniqueTogetherValidator(queryset=Module.objects.all(), fields=("module_type", "serial"))
1187
- validator(data, self)
1188
- if data.get("asset_tag"):
1187
+ validator(attrs, self)
1188
+ if attrs.get("asset_tag"):
1189
1189
  validator = UniqueValidator(queryset=Module.objects.all())
1190
- validator(data["asset_tag"], self.fields["asset_tag"])
1191
- return super().validate(data)
1190
+ validator(attrs["asset_tag"], self.fields["asset_tag"])
1191
+ return super().validate(attrs)
1192
1192
 
1193
1193
 
1194
1194
  class ModuleTypeSerializer(TaggedModelSerializerMixin, NautobotModelSerializer):
nautobot/dcim/forms.py CHANGED
@@ -1651,6 +1651,8 @@ class ComponentTemplateImportForm(BootstrapMixin, CustomFieldModelCSVForm):
1651
1651
  netbox-community/devicetype-library repository.
1652
1652
  """
1653
1653
 
1654
+ Meta: type # to be defined by concrete subclasses
1655
+
1654
1656
  def __init__(self, data=None, *args, **kwargs):
1655
1657
  super().__init__(data, *args, **kwargs)
1656
1658
 
@@ -98,13 +98,13 @@ class CableType(OptimizedNautobotObjectType):
98
98
 
99
99
  def resolve_termination_a_type(self, args):
100
100
  if self.termination_a_type:
101
- model = self.termination_a_type.model_class()
101
+ model = self.termination_a_type.model_class() # pylint: disable=no-member
102
102
  return f"{model._meta.app_label}.{model._meta.model_name}"
103
103
  return None
104
104
 
105
105
  def resolve_termination_b_type(self, args):
106
106
  if self.termination_b_type:
107
- model = self.termination_b_type.model_class()
107
+ model = self.termination_b_type.model_class() # pylint: disable=no-member
108
108
  return f"{model._meta.app_label}.{model._meta.model_name}"
109
109
  return None
110
110
 
@@ -100,7 +100,7 @@ class ComponentTemplateModel(
100
100
  def get_absolute_url(self, api=False):
101
101
  # TODO: in the new UI, this should be able to link directly to the object, instead of the device-type.
102
102
  if not api:
103
- return self.device_type.get_absolute_url(api=api)
103
+ return self.device_type.get_absolute_url(api=api) # pylint: disable=no-member
104
104
  return super().get_absolute_url(api=api)
105
105
 
106
106
  def instantiate_model(self, model, device, **kwargs):
@@ -460,7 +460,7 @@ class DeviceBayTemplate(ComponentTemplateModel):
460
460
  return self.instantiate_model(model=DeviceBay, device=device)
461
461
 
462
462
  def clean(self):
463
- if self.device_type and self.device_type.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT:
463
+ if self.device_type and self.device_type.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT: # pylint: disable=no-member
464
464
  raise ValidationError(
465
465
  f'Subdevice role of device type ({self.device_type}) must be set to "parent" to allow device bays.'
466
466
  )