nautobot 2.2.0b1__py3-none-any.whl → 2.2.1__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 (425) hide show
  1. nautobot/__init__.py +31 -0
  2. nautobot/apps/api.py +1 -2
  3. nautobot/apps/utils.py +4 -0
  4. nautobot/apps/views.py +2 -0
  5. nautobot/circuits/api/urls.py +1 -2
  6. nautobot/circuits/api/views.py +0 -12
  7. nautobot/circuits/apps.py +1 -1
  8. nautobot/circuits/tests/test_filters.py +1 -1
  9. nautobot/core/api/routers.py +50 -3
  10. nautobot/core/api/utils.py +4 -0
  11. nautobot/core/api/views.py +21 -15
  12. nautobot/core/cli/__init__.py +18 -11
  13. nautobot/core/constants.py +85 -0
  14. nautobot/core/filters.py +7 -1
  15. nautobot/core/forms/widgets.py +1 -2
  16. nautobot/core/graphql/schema.py +1 -0
  17. nautobot/core/management/commands/generate_test_data.py +4 -4
  18. nautobot/core/models/__init__.py +1 -0
  19. nautobot/core/settings.py +24 -3
  20. nautobot/core/settings.yaml +20 -0
  21. nautobot/core/signals.py +1 -0
  22. nautobot/core/tables.py +2 -1
  23. nautobot/core/templates/admin/base.html +23 -94
  24. nautobot/core/templates/generic/object_retrieve.html +2 -2
  25. nautobot/core/templates/graphene/graphiql.html +18 -47
  26. nautobot/core/templates/inc/footer.html +5 -5
  27. nautobot/core/templates/inc/javascript.html +4 -4
  28. nautobot/core/templates/inc/media.html +2 -2
  29. nautobot/core/templates/inc/nav_menu.html +0 -7
  30. nautobot/core/templates/nautobot_config.py.j2 +14 -1
  31. nautobot/core/templates/rest_framework/api.html +12 -5
  32. nautobot/core/templatetags/helpers.py +2 -2
  33. nautobot/core/testing/__init__.py +1 -1
  34. nautobot/core/testing/filters.py +1 -1
  35. nautobot/core/testing/views.py +30 -0
  36. nautobot/core/tests/integration/test_view_authentication.py +68 -0
  37. nautobot/core/tests/test_api.py +13 -6
  38. nautobot/core/tests/test_csv.py +5 -4
  39. nautobot/core/tests/test_filters.py +2 -1
  40. nautobot/core/tests/test_graphql.py +4 -14
  41. nautobot/core/tests/test_navigations.py +3 -0
  42. nautobot/core/tests/test_views.py +44 -16
  43. nautobot/core/utils/data.py +1 -2
  44. nautobot/core/utils/lookup.py +126 -0
  45. nautobot/core/views/__init__.py +3 -7
  46. nautobot/core/views/generic.py +20 -6
  47. nautobot/core/views/mixins.py +7 -1
  48. nautobot/core/views/renderers.py +11 -6
  49. nautobot/dcim/api/serializers.py +4 -4
  50. nautobot/dcim/api/urls.py +2 -3
  51. nautobot/dcim/api/views.py +7 -18
  52. nautobot/dcim/apps.py +8 -4
  53. nautobot/dcim/elevations.py +5 -1
  54. nautobot/dcim/factory.py +7 -7
  55. nautobot/dcim/filters/__init__.py +16 -17
  56. nautobot/dcim/forms.py +61 -45
  57. nautobot/dcim/homepage.py +11 -3
  58. nautobot/dcim/management/commands/migrate_location_contacts.py +218 -0
  59. nautobot/dcim/migrations/0057_controller_models.py +11 -70
  60. nautobot/dcim/models/__init__.py +2 -2
  61. nautobot/dcim/models/devices.py +14 -16
  62. nautobot/dcim/models/racks.py +1 -3
  63. nautobot/dcim/navigation.py +23 -31
  64. nautobot/dcim/signals.py +6 -6
  65. nautobot/dcim/tables/__init__.py +2 -2
  66. nautobot/dcim/tables/devices.py +13 -16
  67. nautobot/dcim/tables/template_code.py +1 -1
  68. nautobot/dcim/templates/dcim/controller_create.html +70 -0
  69. nautobot/dcim/templates/dcim/controller_retrieve.html +35 -18
  70. nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +88 -0
  71. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +75 -43
  72. nautobot/dcim/templates/dcim/device.html +11 -3
  73. nautobot/dcim/templates/dcim/device_edit.html +1 -1
  74. nautobot/dcim/templates/dcim/devicefamily_retrieve.html +4 -0
  75. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +1 -1
  76. nautobot/dcim/tests/test_api.py +47 -6
  77. nautobot/dcim/tests/test_filters.py +92 -81
  78. nautobot/dcim/tests/test_graphql.py +11 -1
  79. nautobot/dcim/tests/test_models.py +15 -15
  80. nautobot/dcim/tests/test_signals.py +3 -1
  81. nautobot/dcim/tests/test_views.py +24 -12
  82. nautobot/dcim/urls.py +1 -1
  83. nautobot/dcim/views.py +25 -15
  84. nautobot/extras/api/serializers.py +20 -1
  85. nautobot/extras/api/urls.py +1 -2
  86. nautobot/extras/api/views.py +0 -10
  87. nautobot/extras/apps.py +7 -0
  88. nautobot/extras/context_managers.py +15 -4
  89. nautobot/extras/filters/__init__.py +53 -2
  90. nautobot/extras/filters/customfields.py +14 -9
  91. nautobot/extras/filters/mixins.py +6 -1
  92. nautobot/extras/forms/contacts.py +7 -0
  93. nautobot/extras/health_checks.py +1 -0
  94. nautobot/extras/jobs.py +1 -0
  95. nautobot/extras/managers.py +15 -2
  96. nautobot/extras/models/contacts.py +1 -0
  97. nautobot/extras/models/customfields.py +25 -2
  98. nautobot/extras/models/datasources.py +1 -0
  99. nautobot/extras/models/mixins.py +1 -0
  100. nautobot/extras/navigation.py +71 -65
  101. nautobot/extras/plugins/__init__.py +2 -1
  102. nautobot/extras/plugins/views.py +7 -11
  103. nautobot/extras/querysets.py +1 -2
  104. nautobot/extras/secrets/providers.py +1 -0
  105. nautobot/extras/signals.py +15 -5
  106. nautobot/extras/tasks.py +70 -17
  107. nautobot/extras/tests/test_api.py +2 -4
  108. nautobot/extras/tests/test_customfields.py +72 -9
  109. nautobot/extras/tests/test_dynamicgroups.py +2 -0
  110. nautobot/extras/tests/test_filters.py +89 -4
  111. nautobot/extras/tests/test_models.py +9 -0
  112. nautobot/extras/tests/test_relationships.py +10 -1
  113. nautobot/extras/tests/test_views.py +112 -1
  114. nautobot/extras/views.py +18 -17
  115. nautobot/ipam/api/serializers.py +10 -0
  116. nautobot/ipam/api/urls.py +1 -2
  117. nautobot/ipam/api/views.py +0 -11
  118. nautobot/ipam/apps.py +3 -2
  119. nautobot/ipam/tables.py +2 -22
  120. nautobot/ipam/tests/test_graphql.py +2 -3
  121. nautobot/ipam/tests/test_tables.py +42 -0
  122. nautobot/ipam/tests/test_views.py +1 -0
  123. nautobot/ipam/views.py +9 -9
  124. nautobot/project-static/css/base.css +1 -0
  125. nautobot/project-static/docs/404.html +126 -73
  126. nautobot/project-static/docs/apps/index.html +127 -71
  127. nautobot/project-static/docs/apps/nautobot-apps.html +127 -71
  128. nautobot/project-static/docs/assets/javascripts/{bundle.8fd75fb4.min.js → bundle.bd41221c.min.js} +2 -2
  129. nautobot/project-static/docs/assets/javascripts/{bundle.8fd75fb4.min.js.map → bundle.bd41221c.min.js.map} +3 -3
  130. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css +1 -0
  131. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css.map +1 -0
  132. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +127 -71
  133. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +127 -71
  134. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +167 -73
  135. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +165 -72
  136. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +127 -71
  137. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +127 -71
  138. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +127 -71
  139. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +127 -71
  140. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +127 -71
  141. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +127 -71
  142. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +127 -71
  143. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +127 -71
  144. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +127 -71
  145. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +127 -71
  146. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +127 -71
  147. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +127 -71
  148. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +127 -71
  149. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +127 -71
  150. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +128 -72
  151. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +127 -71
  152. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +127 -71
  153. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +345 -71
  154. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +172 -73
  155. nautobot/project-static/docs/development/apps/api/configuration-view.html +127 -71
  156. nautobot/project-static/docs/development/apps/api/database-backend-config.html +127 -71
  157. nautobot/project-static/docs/development/apps/api/models/django-admin.html +127 -71
  158. nautobot/project-static/docs/development/apps/api/models/global-search.html +127 -71
  159. nautobot/project-static/docs/development/apps/api/models/graphql.html +127 -71
  160. nautobot/project-static/docs/development/apps/api/models/index.html +127 -71
  161. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +127 -71
  162. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +127 -71
  163. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +127 -71
  164. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +127 -71
  165. nautobot/project-static/docs/development/apps/api/platform-features/index.html +127 -71
  166. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +127 -71
  167. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +127 -71
  168. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +127 -71
  169. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +127 -71
  170. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +127 -71
  171. nautobot/project-static/docs/development/apps/api/prometheus.html +127 -71
  172. nautobot/project-static/docs/development/apps/api/setup.html +127 -71
  173. nautobot/project-static/docs/development/apps/api/testing.html +127 -71
  174. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +127 -71
  175. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +127 -71
  176. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +127 -71
  177. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +127 -71
  178. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +127 -71
  179. nautobot/project-static/docs/development/apps/api/views/base-template.html +127 -71
  180. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +141 -80
  181. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +144 -83
  182. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +127 -71
  183. nautobot/project-static/docs/development/apps/api/views/index.html +127 -71
  184. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +127 -71
  185. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +127 -71
  186. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +127 -71
  187. nautobot/project-static/docs/development/apps/api/views/notes.html +127 -71
  188. nautobot/project-static/docs/development/apps/api/views/rest-api.html +127 -71
  189. nautobot/project-static/docs/development/apps/api/views/urls.html +127 -71
  190. nautobot/project-static/docs/development/apps/index.html +127 -71
  191. nautobot/project-static/docs/development/apps/migration/code-updates.html +127 -71
  192. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +127 -71
  193. nautobot/project-static/docs/development/apps/migration/from-v1.html +127 -71
  194. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +127 -71
  195. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +127 -71
  196. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +127 -71
  197. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +127 -71
  198. nautobot/project-static/docs/development/apps/porting-from-netbox.html +127 -71
  199. nautobot/project-static/docs/development/core/application-registry.html +127 -71
  200. nautobot/project-static/docs/development/core/best-practices.html +145 -79
  201. nautobot/project-static/docs/development/core/bootstrap-ui.html +127 -71
  202. nautobot/project-static/docs/development/core/caching.html +127 -71
  203. nautobot/project-static/docs/development/core/controllers.html +141 -275
  204. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +127 -71
  205. nautobot/project-static/docs/development/core/extending-models.html +13 -8166
  206. nautobot/project-static/docs/development/core/generic-views.html +142 -86
  207. nautobot/project-static/docs/development/core/getting-started.html +146 -81
  208. nautobot/project-static/docs/development/core/homepage.html +145 -89
  209. nautobot/project-static/docs/development/core/index.html +127 -71
  210. nautobot/project-static/docs/development/core/model-checklist.html +8354 -0
  211. nautobot/project-static/docs/development/core/model-features.html +130 -74
  212. nautobot/project-static/docs/development/core/natural-keys.html +127 -71
  213. nautobot/project-static/docs/development/core/navigation-menu.html +127 -71
  214. nautobot/project-static/docs/development/core/release-checklist.html +127 -71
  215. nautobot/project-static/docs/development/core/role-internals.html +127 -71
  216. nautobot/project-static/docs/development/core/settings.html +127 -71
  217. nautobot/project-static/docs/development/core/style-guide.html +127 -71
  218. nautobot/project-static/docs/development/core/templates.html +127 -71
  219. nautobot/project-static/docs/development/core/testing.html +127 -71
  220. nautobot/project-static/docs/development/core/user-preferences.html +127 -71
  221. nautobot/project-static/docs/development/extending-models.html +3 -3
  222. nautobot/project-static/docs/development/index.html +127 -71
  223. nautobot/project-static/docs/development/jobs/index.html +128 -72
  224. nautobot/project-static/docs/development/jobs/migration/from-v1.html +127 -71
  225. nautobot/project-static/docs/index.html +126 -73
  226. nautobot/project-static/docs/models/dcim/{controllerdevicegroup.html → controllermanageddevicegroup.html} +3 -3
  227. nautobot/project-static/docs/objects.inv +0 -0
  228. nautobot/project-static/docs/release-notes/index.html +127 -71
  229. nautobot/project-static/docs/release-notes/version-1.0.html +127 -71
  230. nautobot/project-static/docs/release-notes/version-1.1.html +127 -71
  231. nautobot/project-static/docs/release-notes/version-1.2.html +127 -71
  232. nautobot/project-static/docs/release-notes/version-1.3.html +127 -71
  233. nautobot/project-static/docs/release-notes/version-1.4.html +127 -71
  234. nautobot/project-static/docs/release-notes/version-1.5.html +127 -71
  235. nautobot/project-static/docs/release-notes/version-1.6.html +127 -71
  236. nautobot/project-static/docs/release-notes/version-2.0.html +127 -71
  237. nautobot/project-static/docs/release-notes/version-2.1.html +538 -254
  238. nautobot/project-static/docs/release-notes/version-2.2.html +520 -101
  239. nautobot/project-static/docs/requirements.txt +3 -3
  240. nautobot/project-static/docs/search/search_index.json +1 -1
  241. nautobot/project-static/docs/sitemap.xml +264 -259
  242. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  243. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +127 -71
  244. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +127 -71
  245. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +127 -71
  246. nautobot/project-static/docs/user-guide/administration/configuration/index.html +127 -71
  247. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +192 -71
  248. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +127 -71
  249. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +127 -71
  250. nautobot/project-static/docs/user-guide/administration/guides/caching.html +127 -71
  251. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +127 -71
  252. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +127 -71
  253. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +127 -71
  254. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +131 -71
  255. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +127 -71
  256. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +127 -71
  257. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +130 -74
  258. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +127 -71
  259. nautobot/project-static/docs/user-guide/administration/installation/docker.html +134 -74
  260. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +127 -71
  261. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +8616 -0
  262. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +127 -71
  263. nautobot/project-static/docs/user-guide/administration/installation/index.html +127 -71
  264. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +127 -71
  265. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +127 -71
  266. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +130 -74
  267. nautobot/project-static/docs/user-guide/administration/installation/services.html +127 -71
  268. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +127 -71
  269. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +127 -71
  270. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +127 -71
  271. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +127 -71
  272. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +127 -71
  273. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +127 -71
  274. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +127 -71
  275. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +127 -71
  276. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +127 -71
  277. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +127 -71
  278. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +127 -71
  279. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +127 -71
  280. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +127 -71
  281. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +127 -71
  282. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +127 -71
  283. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +127 -71
  284. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +127 -71
  285. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +127 -71
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +127 -71
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +127 -71
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +127 -71
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +127 -71
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +127 -71
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +362 -79
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/{controllerdevicegroup.html → controllermanageddevicegroup.html} +210 -85
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +127 -71
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +127 -71
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +127 -71
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +127 -71
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +127 -71
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +127 -71
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +127 -71
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +127 -71
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +127 -71
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +127 -71
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +127 -71
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +127 -71
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +127 -71
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +127 -71
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +127 -71
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +127 -71
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +127 -71
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +127 -71
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +127 -71
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +127 -71
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +127 -71
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +127 -71
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +127 -71
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +127 -71
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +127 -71
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +127 -71
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +127 -71
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +127 -71
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +127 -71
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +127 -71
  323. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +130 -74
  324. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +127 -71
  325. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +138 -71
  326. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +138 -71
  327. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +127 -71
  328. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +127 -71
  329. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +127 -71
  330. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +127 -71
  331. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +127 -71
  332. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +127 -71
  333. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +127 -71
  334. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +127 -71
  335. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +127 -71
  336. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +127 -71
  337. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +127 -71
  338. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +127 -71
  339. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +127 -71
  340. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +127 -71
  341. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +127 -71
  342. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +127 -71
  343. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +127 -71
  344. nautobot/project-static/docs/user-guide/feature-guides/{contact-and-team.html → contacts-and-teams.html} +128 -72
  345. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +129 -73
  346. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +127 -71
  347. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +127 -71
  348. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +127 -71
  349. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +127 -71
  350. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +127 -71
  351. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +127 -71
  352. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +129 -73
  353. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +127 -71
  354. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +127 -71
  355. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +127 -71
  356. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +127 -71
  357. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +127 -71
  358. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +127 -71
  359. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +127 -71
  360. nautobot/project-static/docs/user-guide/index.html +127 -71
  361. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +127 -71
  362. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +127 -71
  363. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +127 -71
  364. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +127 -71
  365. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +127 -71
  366. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +127 -71
  367. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +127 -71
  368. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +127 -71
  369. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +127 -71
  370. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +127 -71
  371. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +127 -71
  372. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +127 -71
  373. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +127 -71
  374. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +127 -71
  375. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +127 -71
  376. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +127 -71
  377. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +127 -71
  378. nautobot/project-static/docs/user-guide/platform-functionality/note.html +127 -71
  379. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +127 -71
  380. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +127 -71
  381. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +127 -71
  382. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +127 -71
  383. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +127 -71
  384. nautobot/project-static/docs/user-guide/platform-functionality/role.html +127 -71
  385. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +127 -71
  386. nautobot/project-static/docs/user-guide/platform-functionality/status.html +127 -71
  387. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +127 -71
  388. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +127 -71
  389. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +127 -71
  390. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +127 -71
  391. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +127 -71
  392. nautobot/project-static/jquery/jquery-3.7.1.min.js +2 -0
  393. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_444444_256x240.png +0 -0
  394. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_555555_256x240.png +0 -0
  395. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_777620_256x240.png +0 -0
  396. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_777777_256x240.png +0 -0
  397. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_cc0000_256x240.png +0 -0
  398. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_ffffff_256x240.png +0 -0
  399. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +7 -0
  400. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +6 -0
  401. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +5 -0
  402. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/jquery-ui.theme.min.css +1 -1
  403. nautobot/tenancy/api/urls.py +1 -2
  404. nautobot/tenancy/api/views.py +0 -12
  405. nautobot/tenancy/tables.py +1 -1
  406. nautobot/tenancy/tests/test_views.py +1 -0
  407. nautobot/users/api/urls.py +1 -2
  408. nautobot/users/api/views.py +2 -65
  409. nautobot/users/views.py +8 -8
  410. nautobot/virtualization/api/urls.py +1 -2
  411. nautobot/virtualization/api/views.py +0 -12
  412. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/METADATA +24 -24
  413. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/RECORD +418 -412
  414. nautobot/dcim/templates/dcim/controllerdevicegroup_create.html +0 -43
  415. nautobot/project-static/docs/assets/stylesheets/main.f2e4d321.min.css +0 -1
  416. nautobot/project-static/docs/assets/stylesheets/main.f2e4d321.min.css.map +0 -1
  417. nautobot/project-static/jquery/jquery-3.6.0.min.js +0 -2
  418. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.min.css +0 -7
  419. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.min.js +0 -6
  420. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.structure.min.css +0 -5
  421. /nautobot/dcim/templates/dcim/{controllerdevicegroup_retrieve.html → controllermanageddevicegroup_retrieve.html} +0 -0
  422. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/LICENSE.txt +0 -0
  423. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/NOTICE +0 -0
  424. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/WHEEL +0 -0
  425. {nautobot-2.2.0b1.dist-info → nautobot-2.2.1.dist-info}/entry_points.txt +0 -0
nautobot/dcim/views.py CHANGED
@@ -11,7 +11,7 @@ from django.forms import (
11
11
  ModelMultipleChoiceField,
12
12
  MultipleHiddenInput,
13
13
  )
14
- from django.shortcuts import get_object_or_404, redirect, render
14
+ from django.shortcuts import get_object_or_404, HttpResponse, redirect, render
15
15
  from django.utils.functional import cached_property
16
16
  from django.utils.html import format_html
17
17
  from django.views.generic import View
@@ -48,7 +48,7 @@ from .models import (
48
48
  ConsoleServerPort,
49
49
  ConsoleServerPortTemplate,
50
50
  Controller,
51
- ControllerDeviceGroup,
51
+ ControllerManagedDeviceGroup,
52
52
  Device,
53
53
  DeviceBay,
54
54
  DeviceBayTemplate,
@@ -1459,7 +1459,7 @@ class DeviceBulkEditView(generic.BulkEditView):
1459
1459
  "device_type__manufacturer",
1460
1460
  "secrets_group",
1461
1461
  "device_redundancy_group",
1462
- "controller_device_group",
1462
+ "controller_managed_device_group",
1463
1463
  )
1464
1464
  filterset = filters.DeviceFilterSet
1465
1465
  table = tables.DeviceTable
@@ -2359,7 +2359,7 @@ class CableCreateView(generic.ObjectEditView):
2359
2359
  "rear-port": forms.ConnectCableToRearPortForm,
2360
2360
  "power-feed": forms.ConnectCableToPowerFeedForm,
2361
2361
  "circuit-termination": forms.ConnectCableToCircuitTerminationForm,
2362
- }[kwargs.get("termination_b_type")]
2362
+ }.get(kwargs.get("termination_b_type"), None)
2363
2363
 
2364
2364
  return super().dispatch(request, *args, **kwargs)
2365
2365
 
@@ -2376,6 +2376,9 @@ class CableCreateView(generic.ObjectEditView):
2376
2376
  return obj
2377
2377
 
2378
2378
  def get(self, request, *args, **kwargs):
2379
+ if self.model_form is None:
2380
+ return HttpResponse(status_code=400)
2381
+
2379
2382
  obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
2380
2383
 
2381
2384
  # Parse initial data manually to avoid setting field values as lists
@@ -2975,6 +2978,12 @@ class DeviceFamilyUIViewSet(NautobotUIViewSet):
2975
2978
  RequestConfig(request, paginate).configure(device_type_table)
2976
2979
 
2977
2980
  context["device_type_table"] = device_type_table
2981
+
2982
+ total_devices = 0
2983
+ for device_type in device_types:
2984
+ total_devices += device_type.device_count
2985
+ context["total_devices"] = total_devices
2986
+
2978
2987
  return context
2979
2988
 
2980
2989
 
@@ -3021,12 +3030,13 @@ class ControllerUIViewSet(NautobotUIViewSet):
3021
3030
  queryset = Controller.objects.all()
3022
3031
  serializer_class = serializers.ControllerSerializer
3023
3032
  table_class = tables.ControllerTable
3033
+ template_name = "dcim/controller_create.html"
3024
3034
 
3025
3035
  def get_extra_context(self, request, instance):
3026
3036
  context = super().get_extra_context(request, instance)
3027
3037
 
3028
3038
  if self.action == "retrieve" and instance:
3029
- devices = Device.objects.restrict(request.user).filter(controller_device_group__controller=instance)
3039
+ devices = Device.objects.restrict(request.user).filter(controller_managed_device_group__controller=instance)
3030
3040
  devices_table = tables.DeviceTable(devices)
3031
3041
 
3032
3042
  paginate = {
@@ -3040,19 +3050,19 @@ class ControllerUIViewSet(NautobotUIViewSet):
3040
3050
  return context
3041
3051
 
3042
3052
 
3043
- class ControllerDeviceGroupUIViewSet(NautobotUIViewSet):
3044
- filterset_class = filters.ControllerDeviceGroupFilterSet
3045
- filterset_form_class = forms.ControllerDeviceGroupFilterForm
3046
- form_class = forms.ControllerDeviceGroupForm
3047
- bulk_update_form_class = forms.ControllerDeviceGroupBulkEditForm
3053
+ class ControllerManagedDeviceGroupUIViewSet(NautobotUIViewSet):
3054
+ filterset_class = filters.ControllerManagedDeviceGroupFilterSet
3055
+ filterset_form_class = forms.ControllerManagedDeviceGroupFilterForm
3056
+ form_class = forms.ControllerManagedDeviceGroupForm
3057
+ bulk_update_form_class = forms.ControllerManagedDeviceGroupBulkEditForm
3048
3058
  queryset = (
3049
- ControllerDeviceGroup.objects.all()
3059
+ ControllerManagedDeviceGroup.objects.all()
3050
3060
  .prefetch_related("devices")
3051
- .annotate(device_count=count_related(Device, "controller_device_group"))
3061
+ .annotate(device_count=count_related(Device, "controller_managed_device_group"))
3052
3062
  )
3053
- serializer_class = serializers.ControllerDeviceGroupSerializer
3054
- table_class = tables.ControllerDeviceGroupTable
3055
- template_name = "dcim/controllerdevicegroup_create.html"
3063
+ serializer_class = serializers.ControllerManagedDeviceGroupSerializer
3064
+ table_class = tables.ControllerManagedDeviceGroupTable
3065
+ template_name = "dcim/controllermanageddevicegroup_create.html"
3056
3066
 
3057
3067
  def get_extra_context(self, request, instance):
3058
3068
  context = super().get_extra_context(request, instance)
@@ -193,10 +193,18 @@ class ContactSerializer(NautobotModelSerializer):
193
193
  class Meta:
194
194
  model = Contact
195
195
  fields = "__all__"
196
+ # https://www.django-rest-framework.org/api-guide/validators/#optional-fields
197
+ validators = []
198
+ extra_kwargs = {
199
+ "email": {"default": ""},
200
+ "phone": {"default": ""},
201
+ }
196
202
 
197
203
  def validate(self, data):
198
204
  attrs = data.copy()
199
205
  attrs.pop("teams", None)
206
+ validator = UniqueTogetherValidator(queryset=Contact.objects.all(), fields=("name", "phone", "email"))
207
+ validator(attrs, self)
200
208
  super().validate(attrs)
201
209
  return data
202
210
 
@@ -946,7 +954,18 @@ class TeamSerializer(NautobotModelSerializer):
946
954
  class Meta:
947
955
  model = Team
948
956
  fields = "__all__"
949
- extra_kwargs = {"contacts": {"required": False}}
957
+ extra_kwargs = {
958
+ "contacts": {"required": False},
959
+ "email": {"default": ""},
960
+ "phone": {"default": ""},
961
+ }
962
+ # https://www.django-rest-framework.org/api-guide/validators/#optional-fields
963
+ validators = []
964
+
965
+ def validate(self, data):
966
+ validator = UniqueTogetherValidator(queryset=Team.objects.all(), fields=("name", "phone", "email"))
967
+ validator(data, self)
968
+ return super().validate(data)
950
969
 
951
970
 
952
971
  #
@@ -2,8 +2,7 @@ from nautobot.core.api.routers import OrderedDefaultRouter
2
2
 
3
3
  from . import views
4
4
 
5
- router = OrderedDefaultRouter()
6
- router.APIRootView = views.ExtrasRootView
5
+ router = OrderedDefaultRouter(view_name="Extras")
7
6
 
8
7
  # Computed Fields
9
8
  router.register("computed-fields", views.ComputedFieldViewSet)
@@ -16,7 +16,6 @@ from rest_framework.exceptions import MethodNotAllowed, PermissionDenied, Valida
16
16
  from rest_framework.parsers import JSONParser, MultiPartParser
17
17
  from rest_framework.permissions import IsAuthenticated
18
18
  from rest_framework.response import Response
19
- from rest_framework.routers import APIRootView
20
19
 
21
20
  from nautobot.core.api.authentication import TokenPermissions
22
21
  from nautobot.core.api.utils import get_serializer_for_model
@@ -77,15 +76,6 @@ from nautobot.extras.utils import get_worker_count
77
76
  from . import serializers
78
77
 
79
78
 
80
- class ExtrasRootView(APIRootView):
81
- """
82
- Extras API root view
83
- """
84
-
85
- def get_view_name(self):
86
- return "Extras"
87
-
88
-
89
79
  class NotesViewSetMixin:
90
80
  def restrict_queryset(self, request, *args, **kwargs):
91
81
  """
nautobot/extras/apps.py CHANGED
@@ -12,6 +12,13 @@ logger = logging.getLogger(__name__)
12
12
 
13
13
  class ExtrasConfig(NautobotConfig):
14
14
  name = "nautobot.extras"
15
+ searchable_models = [
16
+ "contact",
17
+ "dynamicgroup",
18
+ "externalintegration",
19
+ "gitrepository",
20
+ "team",
21
+ ]
15
22
 
16
23
  def ready(self):
17
24
  super().ready()
@@ -7,7 +7,7 @@ from django.test.client import RequestFactory
7
7
 
8
8
  from nautobot.extras.choices import ObjectChangeEventContextChoices
9
9
  from nautobot.extras.models import ObjectChange
10
- from nautobot.extras.signals import change_context_state
10
+ from nautobot.extras.signals import change_context_state, get_user_if_authenticated
11
11
  from nautobot.extras.webhooks import enqueue_webhooks
12
12
 
13
13
 
@@ -46,11 +46,22 @@ class ChangeContext:
46
46
  if self.change_id is None:
47
47
  self.change_id = uuid.uuid4()
48
48
 
49
- def get_user(self):
49
+ def get_user(self, instance=None):
50
50
  """Return self.user if set, otherwise return self.request.user"""
51
51
  if self.user is not None:
52
- return self.user
53
- return self.request.user
52
+ return get_user_if_authenticated(self.user, instance)
53
+ return get_user_if_authenticated(self.request.user, instance)
54
+
55
+ def as_dict(self, instance=None):
56
+ """
57
+ Return ChangeContext attributes in dictionary format
58
+ """
59
+ context = {
60
+ "user": self.get_user(instance),
61
+ "change_id": self.change_id,
62
+ "context": self.context,
63
+ }
64
+ return context
54
65
 
55
66
 
56
67
  class JobChangeContext(ChangeContext):
@@ -1,7 +1,11 @@
1
+ from difflib import get_close_matches
2
+
1
3
  from django.conf import settings
2
4
  from django.contrib.auth import get_user_model
3
5
  from django.contrib.contenttypes.models import ContentType
6
+ from django.db.models import Q
4
7
  import django_filters
8
+ from drf_spectacular.utils import extend_schema_field
5
9
 
6
10
  from nautobot.core.api.exceptions import SerializerNotFound
7
11
  from nautobot.core.api.utils import get_serializer_for_model
@@ -447,7 +451,54 @@ class NautobotFilterSet(
447
451
  #
448
452
 
449
453
 
450
- class ContactFilterSet(NameSearchFilterSet, NautobotFilterSet):
454
+ class ContactTeamFilterSet(NameSearchFilterSet, NautobotFilterSet):
455
+ """Base filter set for Contacts and Teams."""
456
+
457
+ similar_to_location_data = NaturalKeyOrPKMultipleChoiceFilter(
458
+ queryset=Location.objects.all(),
459
+ label="Similar to location contact data",
460
+ method="_similar_to_location_data",
461
+ )
462
+
463
+ def generate_query__similar_to_location_data(self, queryset, locations):
464
+ """Helper method used by _similar_to_location_data() method."""
465
+ query_params = Q()
466
+ for location in locations:
467
+ contact_name = location.contact_name
468
+ contact_phone = location.contact_phone
469
+ contact_email = location.contact_email
470
+ if contact_name:
471
+ contact_names = list(queryset.order_by().values_list("name", flat=True).distinct())
472
+ name_matches = get_close_matches(contact_name, contact_names, cutoff=0.8)
473
+ if name_matches:
474
+ query_params |= Q(name__in=name_matches)
475
+ if contact_phone:
476
+ contact_phones = list(queryset.order_by().values_list("phone", flat=True).distinct())
477
+ phone_matches = get_close_matches(contact_phone, contact_phones, cutoff=0.8)
478
+ if phone_matches:
479
+ query_params |= Q(phone__in=phone_matches)
480
+ if contact_email:
481
+ contact_emails = list(queryset.order_by().values_list("email", flat=True).distinct())
482
+ # fuzzy matching for emails doesn't make sense, use case insensitive match here
483
+ email_matches = [e for e in contact_emails if e.casefold() == contact_email.casefold()]
484
+ if email_matches:
485
+ query_params |= Q(email__in=email_matches)
486
+
487
+ return query_params
488
+
489
+ @extend_schema_field({"type": "string"})
490
+ def _similar_to_location_data(self, queryset, name, value):
491
+ """FilterSet method for getting Contacts or Teams that are similar to the explicit contact fields of a location"""
492
+ if value:
493
+ params = self.generate_query__similar_to_location_data(queryset, value)
494
+ if len(params) > 0:
495
+ return queryset.filter(params)
496
+ else:
497
+ return queryset.none()
498
+ return queryset
499
+
500
+
501
+ class ContactFilterSet(ContactTeamFilterSet):
451
502
  class Meta:
452
503
  model = Contact
453
504
  fields = "__all__"
@@ -1109,7 +1160,7 @@ class TagFilterSet(NautobotFilterSet):
1109
1160
  #
1110
1161
 
1111
1162
 
1112
- class TeamFilterSet(NameSearchFilterSet, NautobotFilterSet):
1163
+ class TeamFilterSet(ContactTeamFilterSet):
1113
1164
  class Meta:
1114
1165
  model = Team
1115
1166
  fields = "__all__"
@@ -8,8 +8,9 @@ from nautobot.core.filters import (
8
8
  MultiValueNumberFilter,
9
9
  )
10
10
  from nautobot.core.forms import NullableDateField
11
- from nautobot.core.forms.widgets import StaticSelect2Multiple
11
+ from nautobot.core.utils.data import is_uuid
12
12
  from nautobot.extras.choices import CustomFieldFilterLogicChoices, CustomFieldTypeChoices
13
+ from nautobot.extras.models import CustomFieldChoice
13
14
 
14
15
  EXACT_FILTER_TYPES = (
15
16
  CustomFieldTypeChoices.TYPE_BOOLEAN,
@@ -71,19 +72,23 @@ class CustomFieldJSONFilter(CustomFieldFilterMixin, django_filters.Filter):
71
72
  """Custom field single value filter for backwards compatibility"""
72
73
 
73
74
 
74
- class CustomFieldMultiSelectFilter(CustomFieldFilterMixin, MultiValueCharFilter):
75
- """This provides functionality for filtering custom fields with multiple select type"""
75
+ class CustomFieldSelectFilter(CustomFieldFilterMixin, MultiValueCharFilter):
76
+ """Filter for custom fields of type TYPE_SELECT."""
76
77
 
77
- def __init__(self, *args, **kwargs):
78
- kwargs.setdefault("lookup_expr", "contains")
79
- super().__init__(*args, **kwargs)
78
+ def get_filter_predicate(self, v):
79
+ if is_uuid(v):
80
+ try:
81
+ v = self.custom_field.custom_field_choices.get(pk=v).value
82
+ except CustomFieldChoice.DoesNotExist:
83
+ v = ""
84
+ return super().get_filter_predicate(v)
80
85
 
81
86
 
82
- class CustomFieldMultiValueSelectFilter(CustomFieldFilterMixin, django_filters.MultipleChoiceFilter):
83
- """This provides functionality for filtering custom fields with select type"""
87
+ class CustomFieldMultiSelectFilter(CustomFieldSelectFilter):
88
+ """Filter for custom fields of type TYPE_MULTISELECT."""
84
89
 
85
90
  def __init__(self, *args, **kwargs):
86
- self.field_class.widget = StaticSelect2Multiple
91
+ kwargs.setdefault("lookup_expr", "contains")
87
92
  super().__init__(*args, **kwargs)
88
93
 
89
94
 
@@ -27,6 +27,7 @@ from nautobot.extras.filters.customfields import (
27
27
  CustomFieldMultiValueDateFilter,
28
28
  CustomFieldMultiValueNumberFilter,
29
29
  CustomFieldNumberFilter,
30
+ CustomFieldSelectFilter,
30
31
  )
31
32
  from nautobot.extras.models import (
32
33
  ConfigContextSchema,
@@ -61,12 +62,16 @@ class CustomFieldModelFilterSetMixin(django_filters.FilterSet):
61
62
  super().__init__(*args, **kwargs)
62
63
 
63
64
  custom_field_filter_classes = {
65
+ # Here, for the "base" filters for each custom field, for backwards compatibility, use single-value filters.
66
+ # For the "extended" filters, see below, we use multi-value filters.
67
+ # 3.0 TODO: switch the "base" filters to multi-value filters as well.
64
68
  CustomFieldTypeChoices.TYPE_DATE: CustomFieldDateFilter,
65
69
  CustomFieldTypeChoices.TYPE_BOOLEAN: CustomFieldBooleanFilter,
66
70
  CustomFieldTypeChoices.TYPE_INTEGER: CustomFieldNumberFilter,
67
71
  CustomFieldTypeChoices.TYPE_JSON: CustomFieldJSONFilter,
72
+ # The below are multi-value filters already:
68
73
  CustomFieldTypeChoices.TYPE_MULTISELECT: CustomFieldMultiSelectFilter,
69
- CustomFieldTypeChoices.TYPE_SELECT: CustomFieldMultiSelectFilter,
74
+ CustomFieldTypeChoices.TYPE_SELECT: CustomFieldSelectFilter,
70
75
  }
71
76
 
72
77
  custom_fields = CustomField.objects.get_for_model(self._meta.model, exclude_filter_disabled=True)
@@ -28,6 +28,13 @@ class ContactForm(NautobotModelForm):
28
28
  "tags",
29
29
  ]
30
30
 
31
+ def __init__(self, instance=None, initial=None, **kwargs):
32
+ if instance is not None:
33
+ if initial is None:
34
+ initial = {}
35
+ initial.setdefault("teams", instance.teams.all())
36
+ super().__init__(instance=instance, initial=initial, **kwargs)
37
+
31
38
  def save(self, *args, **kwargs):
32
39
  """
33
40
  Since `teams` field on Contact Model is the reverse side of an M2M,
@@ -1,4 +1,5 @@
1
1
  """Nautobot custom health checks."""
2
+
2
3
  from typing import Optional
3
4
  from urllib.parse import urlparse
4
5
 
nautobot/extras/jobs.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Jobs functionality - consolidates and replaces legacy "custom scripts" and "reports" features."""
2
+
2
3
  from collections import OrderedDict
3
4
  import functools
4
5
  import inspect
@@ -1,6 +1,5 @@
1
1
  from celery import states
2
2
  from django.utils import timezone
3
- from django_celery_beat.managers import ExtendedManager
4
3
  from django_celery_results.managers import TaskResultManager, transaction_retry
5
4
 
6
5
  from nautobot.core.models import BaseManager
@@ -8,6 +7,20 @@ from nautobot.core.models.querysets import RestrictedQuerySet
8
7
 
9
8
 
10
9
  class JobResultManager(BaseManager.from_queryset(RestrictedQuerySet), TaskResultManager):
10
+ def get_task(self, task_id):
11
+ """Get result for task by ``task_id``.
12
+
13
+ This overloads `TaskResultManager.get_task` provided by `django-celery-results` to manage custom
14
+ behaviors for integration with Nautobot.
15
+ """
16
+ try:
17
+ return self.get(id=task_id)
18
+ except self.model.DoesNotExist:
19
+ if self._last_id == task_id:
20
+ self.warn_if_repeatable_read()
21
+ self._last_id = task_id
22
+ return self.model(id=task_id)
23
+
11
24
  @transaction_retry(max_retries=2)
12
25
  def store_result(
13
26
  self,
@@ -122,5 +135,5 @@ class JobResultManager(BaseManager.from_queryset(RestrictedQuerySet), TaskResult
122
135
  return obj
123
136
 
124
137
 
125
- class ScheduledJobsManager(BaseManager.from_queryset(RestrictedQuerySet), ExtendedManager):
138
+ class ScheduledJobsManager(BaseManager.from_queryset(RestrictedQuerySet)):
126
139
  pass
@@ -19,6 +19,7 @@ class ContactTeamSharedBase(PrimaryModel):
19
19
  address = models.TextField(blank=True)
20
20
 
21
21
  comments = models.TextField(blank=True)
22
+ is_contact_associable_model = False
22
23
 
23
24
  class Meta:
24
25
  abstract = True
@@ -724,7 +724,16 @@ class CustomField(BaseModel, ChangeLoggedModel, NotesMixin):
724
724
 
725
725
  super().delete(*args, **kwargs)
726
726
 
727
- delete_custom_field_data.delay(self.key, content_types)
727
+ # Circular Import
728
+ from nautobot.extras.signals import change_context_state
729
+
730
+ change_context = change_context_state.get()
731
+ if change_context is None:
732
+ context = None
733
+ else:
734
+ context = change_context.as_dict(instance=self)
735
+ context["context_detail"] = "delete custom field data"
736
+ delete_custom_field_data.delay(self.key, content_types, context)
728
737
 
729
738
  def add_prefix_to_cf_key(self):
730
739
  return "cf_" + str(self.key)
@@ -783,8 +792,22 @@ class CustomFieldChoice(BaseModel, ChangeLoggedModel):
783
792
  super().save(*args, **kwargs)
784
793
 
785
794
  if self.value != database_object.value:
795
+ # Circular Import
796
+ from nautobot.extras.signals import change_context_state
797
+
798
+ change_context = change_context_state.get()
799
+ if change_context is None:
800
+ context = None
801
+ else:
802
+ context = change_context.as_dict(instance=self)
803
+ context["context_detail"] = "update custom field choice data"
786
804
  transaction.on_commit(
787
- lambda: update_custom_field_choice_data.delay(self.custom_field.pk, database_object.value, self.value)
805
+ lambda: update_custom_field_choice_data.delay(
806
+ self.custom_field.pk,
807
+ database_object.value,
808
+ self.value,
809
+ context,
810
+ )
788
811
  )
789
812
 
790
813
  def delete(self, *args, **kwargs):
@@ -1,4 +1,5 @@
1
1
  """Models for representing external data sources."""
2
+
2
3
  from importlib.util import find_spec
3
4
  import os
4
5
 
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Class-modifying mixins that need to be standalone to avoid circular imports.
3
3
  """
4
+
4
5
  from django.urls import NoReverseMatch, reverse
5
6
 
6
7
  from nautobot.core.utils.lookup import get_route_for_model