nautobot 2.2.0__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 (345) hide show
  1. nautobot/__init__.py +31 -0
  2. nautobot/circuits/apps.py +1 -1
  3. nautobot/core/api/routers.py +25 -0
  4. nautobot/core/cli/__init__.py +18 -11
  5. nautobot/core/constants.py +85 -0
  6. nautobot/core/forms/widgets.py +1 -2
  7. nautobot/core/graphql/schema.py +1 -0
  8. nautobot/core/models/__init__.py +1 -0
  9. nautobot/core/settings.py +23 -3
  10. nautobot/core/settings.yaml +20 -0
  11. nautobot/core/signals.py +1 -0
  12. nautobot/core/templates/generic/object_retrieve.html +2 -2
  13. nautobot/core/templates/inc/javascript.html +4 -4
  14. nautobot/core/templates/inc/media.html +2 -2
  15. nautobot/core/templates/nautobot_config.py.j2 +14 -1
  16. nautobot/core/testing/__init__.py +1 -1
  17. nautobot/core/testing/filters.py +1 -1
  18. nautobot/core/tests/integration/test_view_authentication.py +1 -0
  19. nautobot/core/tests/test_views.py +22 -0
  20. nautobot/core/utils/data.py +1 -2
  21. nautobot/core/utils/lookup.py +2 -0
  22. nautobot/core/views/generic.py +1 -3
  23. nautobot/core/views/mixins.py +0 -1
  24. nautobot/core/views/renderers.py +7 -5
  25. nautobot/dcim/apps.py +8 -4
  26. nautobot/dcim/elevations.py +5 -1
  27. nautobot/dcim/tables/devices.py +1 -1
  28. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +12 -4
  29. nautobot/dcim/templates/dcim/device.html +1 -1
  30. nautobot/dcim/templates/dcim/devicefamily_retrieve.html +4 -0
  31. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +1 -1
  32. nautobot/dcim/tests/test_api.py +36 -0
  33. nautobot/dcim/tests/test_signals.py +1 -1
  34. nautobot/dcim/views.py +6 -0
  35. nautobot/extras/api/serializers.py +20 -1
  36. nautobot/extras/apps.py +7 -0
  37. nautobot/extras/context_managers.py +15 -4
  38. nautobot/extras/filters/customfields.py +14 -9
  39. nautobot/extras/filters/mixins.py +6 -1
  40. nautobot/extras/health_checks.py +1 -0
  41. nautobot/extras/jobs.py +1 -0
  42. nautobot/extras/managers.py +1 -2
  43. nautobot/extras/models/contacts.py +1 -0
  44. nautobot/extras/models/customfields.py +25 -2
  45. nautobot/extras/models/datasources.py +1 -0
  46. nautobot/extras/models/mixins.py +1 -0
  47. nautobot/extras/plugins/__init__.py +2 -1
  48. nautobot/extras/querysets.py +1 -2
  49. nautobot/extras/secrets/providers.py +1 -0
  50. nautobot/extras/signals.py +15 -5
  51. nautobot/extras/tasks.py +70 -17
  52. nautobot/extras/tests/test_api.py +0 -4
  53. nautobot/extras/tests/test_customfields.py +72 -9
  54. nautobot/extras/views.py +8 -7
  55. nautobot/ipam/api/serializers.py +10 -0
  56. nautobot/ipam/apps.py +3 -2
  57. nautobot/project-static/docs/404.html +102 -70
  58. nautobot/project-static/docs/apps/index.html +103 -68
  59. nautobot/project-static/docs/apps/nautobot-apps.html +103 -68
  60. nautobot/project-static/docs/assets/javascripts/{bundle.8fd75fb4.min.js → bundle.bd41221c.min.js} +2 -2
  61. nautobot/project-static/docs/assets/javascripts/{bundle.8fd75fb4.min.js.map → bundle.bd41221c.min.js.map} +3 -3
  62. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css +1 -0
  63. nautobot/project-static/docs/assets/stylesheets/main.bcfcd587.min.css.map +1 -0
  64. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +103 -68
  65. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +103 -68
  66. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +141 -68
  67. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +141 -69
  68. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +103 -68
  69. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +103 -68
  70. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +103 -68
  71. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +103 -68
  72. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +103 -68
  73. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +103 -68
  74. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +103 -68
  75. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +103 -68
  76. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +103 -68
  77. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +103 -68
  78. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +103 -68
  79. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +103 -68
  80. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +103 -68
  81. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +103 -68
  82. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +104 -69
  83. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +103 -68
  84. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +103 -68
  85. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +103 -68
  86. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +103 -68
  87. nautobot/project-static/docs/development/apps/api/configuration-view.html +103 -68
  88. nautobot/project-static/docs/development/apps/api/database-backend-config.html +103 -68
  89. nautobot/project-static/docs/development/apps/api/models/django-admin.html +103 -68
  90. nautobot/project-static/docs/development/apps/api/models/global-search.html +103 -68
  91. nautobot/project-static/docs/development/apps/api/models/graphql.html +103 -68
  92. nautobot/project-static/docs/development/apps/api/models/index.html +103 -68
  93. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +103 -68
  94. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +103 -68
  95. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +103 -68
  96. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +103 -68
  97. nautobot/project-static/docs/development/apps/api/platform-features/index.html +103 -68
  98. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +103 -68
  99. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +103 -68
  100. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +103 -68
  101. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +103 -68
  102. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +103 -68
  103. nautobot/project-static/docs/development/apps/api/prometheus.html +103 -68
  104. nautobot/project-static/docs/development/apps/api/setup.html +103 -68
  105. nautobot/project-static/docs/development/apps/api/testing.html +103 -68
  106. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +103 -68
  107. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +103 -68
  108. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +103 -68
  109. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +103 -68
  110. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +103 -68
  111. nautobot/project-static/docs/development/apps/api/views/base-template.html +103 -68
  112. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +103 -68
  113. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +103 -68
  114. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +103 -68
  115. nautobot/project-static/docs/development/apps/api/views/index.html +103 -68
  116. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +103 -68
  117. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +103 -68
  118. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +103 -68
  119. nautobot/project-static/docs/development/apps/api/views/notes.html +103 -68
  120. nautobot/project-static/docs/development/apps/api/views/rest-api.html +103 -68
  121. nautobot/project-static/docs/development/apps/api/views/urls.html +103 -68
  122. nautobot/project-static/docs/development/apps/index.html +103 -68
  123. nautobot/project-static/docs/development/apps/migration/code-updates.html +103 -68
  124. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +103 -68
  125. nautobot/project-static/docs/development/apps/migration/from-v1.html +103 -68
  126. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +103 -68
  127. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +103 -68
  128. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +103 -68
  129. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +103 -68
  130. nautobot/project-static/docs/development/apps/porting-from-netbox.html +103 -68
  131. nautobot/project-static/docs/development/core/application-registry.html +103 -68
  132. nautobot/project-static/docs/development/core/best-practices.html +121 -76
  133. nautobot/project-static/docs/development/core/bootstrap-ui.html +103 -68
  134. nautobot/project-static/docs/development/core/caching.html +103 -68
  135. nautobot/project-static/docs/development/core/controllers.html +106 -71
  136. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +103 -68
  137. nautobot/project-static/docs/development/core/extending-models.html +13 -8187
  138. nautobot/project-static/docs/development/core/generic-views.html +118 -83
  139. nautobot/project-static/docs/development/core/getting-started.html +103 -68
  140. nautobot/project-static/docs/development/core/homepage.html +121 -86
  141. nautobot/project-static/docs/development/core/index.html +103 -68
  142. nautobot/project-static/docs/development/core/model-checklist.html +8354 -0
  143. nautobot/project-static/docs/development/core/model-features.html +106 -71
  144. nautobot/project-static/docs/development/core/natural-keys.html +103 -68
  145. nautobot/project-static/docs/development/core/navigation-menu.html +103 -68
  146. nautobot/project-static/docs/development/core/release-checklist.html +103 -68
  147. nautobot/project-static/docs/development/core/role-internals.html +103 -68
  148. nautobot/project-static/docs/development/core/settings.html +103 -68
  149. nautobot/project-static/docs/development/core/style-guide.html +103 -68
  150. nautobot/project-static/docs/development/core/templates.html +103 -68
  151. nautobot/project-static/docs/development/core/testing.html +103 -68
  152. nautobot/project-static/docs/development/core/user-preferences.html +103 -68
  153. nautobot/project-static/docs/development/extending-models.html +3 -3
  154. nautobot/project-static/docs/development/index.html +103 -68
  155. nautobot/project-static/docs/development/jobs/index.html +104 -69
  156. nautobot/project-static/docs/development/jobs/migration/from-v1.html +103 -68
  157. nautobot/project-static/docs/index.html +102 -70
  158. nautobot/project-static/docs/objects.inv +0 -0
  159. nautobot/project-static/docs/release-notes/index.html +103 -68
  160. nautobot/project-static/docs/release-notes/version-1.0.html +103 -68
  161. nautobot/project-static/docs/release-notes/version-1.1.html +103 -68
  162. nautobot/project-static/docs/release-notes/version-1.2.html +103 -68
  163. nautobot/project-static/docs/release-notes/version-1.3.html +103 -68
  164. nautobot/project-static/docs/release-notes/version-1.4.html +103 -68
  165. nautobot/project-static/docs/release-notes/version-1.5.html +103 -68
  166. nautobot/project-static/docs/release-notes/version-1.6.html +103 -68
  167. nautobot/project-static/docs/release-notes/version-2.0.html +103 -68
  168. nautobot/project-static/docs/release-notes/version-2.1.html +103 -68
  169. nautobot/project-static/docs/release-notes/version-2.2.html +334 -97
  170. nautobot/project-static/docs/requirements.txt +3 -3
  171. nautobot/project-static/docs/search/search_index.json +1 -1
  172. nautobot/project-static/docs/sitemap.xml +258 -258
  173. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  174. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +103 -68
  175. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +103 -68
  176. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +103 -68
  177. nautobot/project-static/docs/user-guide/administration/configuration/index.html +103 -68
  178. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +168 -68
  179. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +103 -68
  180. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +103 -68
  181. nautobot/project-static/docs/user-guide/administration/guides/caching.html +103 -68
  182. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +103 -68
  183. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +103 -68
  184. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +103 -68
  185. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +107 -68
  186. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +103 -68
  187. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +103 -68
  188. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +106 -71
  189. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +103 -68
  190. nautobot/project-static/docs/user-guide/administration/installation/docker.html +103 -68
  191. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +103 -68
  192. nautobot/project-static/docs/user-guide/administration/installation/health-checks.html +103 -68
  193. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +103 -68
  194. nautobot/project-static/docs/user-guide/administration/installation/index.html +103 -68
  195. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +103 -68
  196. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +103 -68
  197. nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +103 -68
  198. nautobot/project-static/docs/user-guide/administration/installation/services.html +103 -68
  199. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +103 -68
  200. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +103 -68
  201. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +103 -68
  202. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +103 -68
  203. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +103 -68
  204. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +103 -68
  205. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +103 -68
  206. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +103 -68
  207. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +103 -68
  208. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +103 -68
  209. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +103 -68
  210. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +103 -68
  211. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +103 -68
  212. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +103 -68
  213. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +103 -68
  214. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +103 -68
  215. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +103 -68
  216. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +103 -68
  217. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +103 -68
  218. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +103 -68
  219. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +103 -68
  220. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +103 -68
  221. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +103 -68
  222. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +103 -68
  223. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +103 -68
  224. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +103 -68
  225. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +103 -68
  226. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +103 -68
  227. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +103 -68
  228. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +103 -68
  229. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +103 -68
  230. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +103 -68
  231. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +103 -68
  232. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +103 -68
  233. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +103 -68
  234. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +103 -68
  235. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +103 -68
  236. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +103 -68
  237. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +103 -68
  238. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +103 -68
  239. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +103 -68
  240. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +103 -68
  241. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +103 -68
  242. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +103 -68
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +103 -68
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +103 -68
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +103 -68
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +103 -68
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +103 -68
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +103 -68
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +103 -68
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +103 -68
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +103 -68
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +103 -68
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +103 -68
  254. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +103 -68
  255. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +103 -68
  256. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +114 -68
  257. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +114 -68
  258. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +103 -68
  259. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +103 -68
  260. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +103 -68
  261. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +103 -68
  262. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +103 -68
  263. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +103 -68
  264. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +103 -68
  265. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +103 -68
  266. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +103 -68
  267. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +103 -68
  268. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +103 -68
  269. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +103 -68
  270. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +103 -68
  271. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +103 -68
  272. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +103 -68
  273. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +103 -68
  274. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +103 -68
  275. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +103 -68
  276. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +103 -68
  277. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +103 -68
  278. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +103 -68
  279. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +103 -68
  280. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +103 -68
  281. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +103 -68
  282. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +103 -68
  283. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +103 -68
  284. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +103 -68
  285. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +103 -68
  286. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +103 -68
  287. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +103 -68
  288. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +103 -68
  289. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +103 -68
  290. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +103 -68
  291. nautobot/project-static/docs/user-guide/index.html +103 -68
  292. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +103 -68
  293. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +103 -68
  294. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +103 -68
  295. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +103 -68
  296. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +103 -68
  297. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +103 -68
  298. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +103 -68
  299. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +103 -68
  300. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +103 -68
  301. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +103 -68
  302. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +103 -68
  303. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +103 -68
  304. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +103 -68
  305. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +103 -68
  306. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +103 -68
  307. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +103 -68
  308. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +103 -68
  309. nautobot/project-static/docs/user-guide/platform-functionality/note.html +103 -68
  310. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +103 -68
  311. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +103 -68
  312. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +103 -68
  313. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +103 -68
  314. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +103 -68
  315. nautobot/project-static/docs/user-guide/platform-functionality/role.html +103 -68
  316. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +103 -68
  317. nautobot/project-static/docs/user-guide/platform-functionality/status.html +103 -68
  318. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +103 -68
  319. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +103 -68
  320. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +103 -68
  321. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +103 -68
  322. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +103 -68
  323. nautobot/project-static/jquery/jquery-3.7.1.min.js +2 -0
  324. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_444444_256x240.png +0 -0
  325. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_555555_256x240.png +0 -0
  326. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_777620_256x240.png +0 -0
  327. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_777777_256x240.png +0 -0
  328. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_cc0000_256x240.png +0 -0
  329. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/images/ui-icons_ffffff_256x240.png +0 -0
  330. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +7 -0
  331. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +6 -0
  332. nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +5 -0
  333. nautobot/project-static/{jquery-ui-1.13.1 → jquery-ui-1.13.2}/jquery-ui.theme.min.css +1 -1
  334. {nautobot-2.2.0.dist-info → nautobot-2.2.1.dist-info}/METADATA +22 -22
  335. {nautobot-2.2.0.dist-info → nautobot-2.2.1.dist-info}/RECORD +339 -338
  336. nautobot/project-static/docs/assets/stylesheets/main.f2e4d321.min.css +0 -1
  337. nautobot/project-static/docs/assets/stylesheets/main.f2e4d321.min.css.map +0 -1
  338. nautobot/project-static/jquery/jquery-3.6.0.min.js +0 -2
  339. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.min.css +0 -7
  340. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.min.js +0 -6
  341. nautobot/project-static/jquery-ui-1.13.1/jquery-ui.structure.min.css +0 -5
  342. {nautobot-2.2.0.dist-info → nautobot-2.2.1.dist-info}/LICENSE.txt +0 -0
  343. {nautobot-2.2.0.dist-info → nautobot-2.2.1.dist-info}/NOTICE +0 -0
  344. {nautobot-2.2.0.dist-info → nautobot-2.2.1.dist-info}/WHEEL +0 -0
  345. {nautobot-2.2.0.dist-info → nautobot-2.2.1.dist-info}/entry_points.txt +0 -0
nautobot/extras/tasks.py CHANGED
@@ -14,7 +14,7 @@ logger = getLogger("nautobot.extras.tasks")
14
14
 
15
15
 
16
16
  @nautobot_task
17
- def update_custom_field_choice_data(field_id, old_value, new_value):
17
+ def update_custom_field_choice_data(field_id, old_value, new_value, change_context=None):
18
18
  """
19
19
  Update the values for a custom field choice used in objects' _custom_field_data for the given field.
20
20
 
@@ -23,6 +23,8 @@ def update_custom_field_choice_data(field_id, old_value, new_value):
23
23
  old_value (str): The existing value of the choice
24
24
  new_value (str): The value which will be used as replacement
25
25
  """
26
+ # Circular Import
27
+ from nautobot.extras.context_managers import web_request_context
26
28
  from nautobot.extras.models import CustomField
27
29
 
28
30
  try:
@@ -35,19 +37,43 @@ def update_custom_field_choice_data(field_id, old_value, new_value):
35
37
  # Loop through all field content types and search for values to update
36
38
  for ct in field.content_types.all():
37
39
  model = ct.model_class()
38
- for obj in model.objects.filter(**{f"_custom_field_data__{field.key}": old_value}):
39
- obj._custom_field_data[field.key] = new_value
40
- obj.save()
40
+ if change_context is not None:
41
+ with web_request_context(
42
+ user=change_context.get("user"),
43
+ change_id=change_context.get("change_id"),
44
+ context_detail=change_context.get("context_detail"),
45
+ context=change_context.get("context"),
46
+ ):
47
+ for obj in model.objects.filter(**{f"_custom_field_data__{field.key}": old_value}):
48
+ obj._custom_field_data[field.key] = new_value
49
+ obj.save()
50
+ else:
51
+ for obj in model.objects.filter(**{f"_custom_field_data__{field.key}": old_value}):
52
+ obj._custom_field_data[field.key] = new_value
53
+ obj.save()
41
54
 
42
55
  elif field.type == CustomFieldTypeChoices.TYPE_MULTISELECT:
43
56
  # Loop through all field content types and search for values to update
44
57
  for ct in field.content_types.all():
45
58
  model = ct.model_class()
46
- for obj in model.objects.filter(**{f"_custom_field_data__{field.key}__contains": old_value}):
47
- old_list = obj._custom_field_data[field.key]
48
- new_list = [new_value if e == old_value else e for e in old_list]
49
- obj._custom_field_data[field.key] = new_list
50
- obj.save()
59
+ if change_context is not None:
60
+ with web_request_context(
61
+ user=change_context.get("user"),
62
+ change_id=change_context.get("change_id"),
63
+ context_detail=change_context.get("context_detail"),
64
+ context=change_context.get("context"),
65
+ ):
66
+ for obj in model.objects.filter(**{f"_custom_field_data__{field.key}__contains": old_value}):
67
+ old_list = obj._custom_field_data[field.key]
68
+ new_list = [new_value if e == old_value else e for e in old_list]
69
+ obj._custom_field_data[field.key] = new_list
70
+ obj.save()
71
+ else:
72
+ for obj in model.objects.filter(**{f"_custom_field_data__{field.key}__contains": old_value}):
73
+ old_list = obj._custom_field_data[field.key]
74
+ new_list = [new_value if e == old_value else e for e in old_list]
75
+ obj._custom_field_data[field.key] = new_list
76
+ obj.save()
51
77
 
52
78
  else:
53
79
  logger.error(f"Unknown field type, failing to act on choice data for this field {field.key}.")
@@ -57,7 +83,7 @@ def update_custom_field_choice_data(field_id, old_value, new_value):
57
83
 
58
84
 
59
85
  @nautobot_task
60
- def delete_custom_field_data(field_key, content_type_pk_set):
86
+ def delete_custom_field_data(field_key, content_type_pk_set, change_context=None):
61
87
  """
62
88
  Delete the values for a custom field
63
89
 
@@ -65,16 +91,30 @@ def delete_custom_field_data(field_key, content_type_pk_set):
65
91
  field_key (str): The key of the custom field which is being deleted
66
92
  content_type_pk_set (list): List of PKs for content types to act upon
67
93
  """
94
+ # Circular Import
95
+ from nautobot.extras.context_managers import web_request_context
96
+
68
97
  with transaction.atomic():
69
98
  for ct in ContentType.objects.filter(pk__in=content_type_pk_set):
70
99
  model = ct.model_class()
71
- for obj in model.objects.filter(**{f"_custom_field_data__{field_key}__isnull": False}):
72
- del obj._custom_field_data[field_key]
73
- obj.save()
100
+ if change_context is not None:
101
+ with web_request_context(
102
+ user=change_context.get("user"),
103
+ change_id=change_context.get("change_id"),
104
+ context_detail=change_context.get("context_detail"),
105
+ context=change_context.get("context"),
106
+ ):
107
+ for obj in model.objects.filter(**{f"_custom_field_data__{field_key}__isnull": False}):
108
+ del obj._custom_field_data[field_key]
109
+ obj.save()
110
+ else:
111
+ for obj in model.objects.filter(**{f"_custom_field_data__{field_key}__isnull": False}):
112
+ del obj._custom_field_data[field_key]
113
+ obj.save()
74
114
 
75
115
 
76
116
  @nautobot_task
77
- def provision_field(field_id, content_type_pk_set):
117
+ def provision_field(field_id, content_type_pk_set, change_context=None):
78
118
  """
79
119
  Provision a new custom field on all relevant content type object instances.
80
120
 
@@ -82,6 +122,8 @@ def provision_field(field_id, content_type_pk_set):
82
122
  field_id (uuid4): The PK of the custom field being provisioned
83
123
  content_type_pk_set (list): List of PKs for content types to act upon
84
124
  """
125
+ # Circular Import
126
+ from nautobot.extras.context_managers import web_request_context
85
127
  from nautobot.extras.models import CustomField
86
128
 
87
129
  try:
@@ -93,9 +135,20 @@ def provision_field(field_id, content_type_pk_set):
93
135
  with transaction.atomic():
94
136
  for ct in ContentType.objects.filter(pk__in=content_type_pk_set):
95
137
  model = ct.model_class()
96
- for obj in model.objects.all():
97
- obj._custom_field_data.setdefault(field.key, field.default)
98
- obj.save()
138
+ if change_context is not None:
139
+ with web_request_context(
140
+ user=change_context.get("user"),
141
+ change_id=change_context.get("change_id"),
142
+ context_detail=change_context.get("context_detail"),
143
+ context=change_context.get("context"),
144
+ ):
145
+ for obj in model.objects.all():
146
+ obj._custom_field_data.setdefault(field.key, field.default)
147
+ obj.save()
148
+ else:
149
+ for obj in model.objects.all():
150
+ obj._custom_field_data.setdefault(field.key, field.default)
151
+ obj.save()
99
152
 
100
153
  return True
101
154
 
@@ -409,11 +409,9 @@ class ContactTest(APIViewTestCases.APIViewTestCase):
409
409
  {
410
410
  "name": "Contact 3",
411
411
  "phone": "555-0123",
412
- "email": "",
413
412
  },
414
413
  {
415
414
  "name": "Contact 4",
416
- "phone": "",
417
415
  "email": "contact4@example.com",
418
416
  },
419
417
  ]
@@ -3613,11 +3611,9 @@ class TeamTest(APIViewTestCases.APIViewTestCase):
3613
3611
  {
3614
3612
  "name": "Team 3",
3615
3613
  "phone": "555-0123",
3616
- "email": "",
3617
3614
  },
3618
3615
  {
3619
3616
  "name": "Team 4",
3620
- "phone": "",
3621
3617
  "email": "team4@example.com",
3622
3618
  "address": "Rainbow Bridge, Central NJ",
3623
3619
  },
@@ -16,11 +16,13 @@ from nautobot.core.tables import CustomFieldColumn
16
16
  from nautobot.core.testing import APITestCase, TestCase, TransactionTestCase
17
17
  from nautobot.core.testing.models import ModelTestCases
18
18
  from nautobot.core.testing.utils import post_data
19
+ from nautobot.core.utils.lookup import get_changes_for_model
19
20
  from nautobot.dcim.filters import LocationFilterSet
20
21
  from nautobot.dcim.forms import RackFilterForm
21
22
  from nautobot.dcim.models import Device, Location, LocationType, Rack
22
23
  from nautobot.dcim.tables import LocationTable
23
24
  from nautobot.extras.choices import CustomFieldFilterLogicChoices, CustomFieldTypeChoices
25
+ from nautobot.extras.context_managers import web_request_context
24
26
  from nautobot.extras.models import ComputedField, CustomField, CustomFieldChoice, Status
25
27
  from nautobot.users.models import ObjectPermission
26
28
  from nautobot.virtualization.models import VirtualMachine
@@ -1537,8 +1539,10 @@ class CustomFieldFilterTest(TestCase):
1537
1539
  cf.save()
1538
1540
  cf.content_types.set([obj_type])
1539
1541
 
1540
- CustomFieldChoice.objects.create(custom_field=cf, value="Foo")
1541
- CustomFieldChoice.objects.create(custom_field=cf, value="Bar")
1542
+ cls.select_choices = (
1543
+ CustomFieldChoice.objects.create(custom_field=cf, value="Foo"),
1544
+ CustomFieldChoice.objects.create(custom_field=cf, value="Bar"),
1545
+ )
1542
1546
 
1543
1547
  # Multi-select filtering
1544
1548
  cf = CustomField(
@@ -1548,8 +1552,11 @@ class CustomFieldFilterTest(TestCase):
1548
1552
  cf.save()
1549
1553
  cf.content_types.set([obj_type])
1550
1554
 
1551
- CustomFieldChoice.objects.create(custom_field=cf, value="Foo")
1552
- CustomFieldChoice.objects.create(custom_field=cf, value="Bar")
1555
+ cls.multiselect_choices = (
1556
+ CustomFieldChoice.objects.create(custom_field=cf, value="Foo"),
1557
+ CustomFieldChoice.objects.create(custom_field=cf, value="Bar"),
1558
+ )
1559
+
1553
1560
  cls.location_type = LocationType.objects.get(name="Campus")
1554
1561
  location_status = Status.objects.get_for_model(Location).first()
1555
1562
  Location.objects.create(
@@ -1844,6 +1851,10 @@ class CustomFieldFilterTest(TestCase):
1844
1851
  self.filterset({"cf_cf8": ["Foo", "AR"]}, self.queryset).qs,
1845
1852
  self.queryset.filter(_custom_field_data__cf8__in=["Foo", "AR"]),
1846
1853
  )
1854
+ self.assertQuerysetEqualAndNotEmpty( # https://github.com/nautobot/nautobot/issues/5009
1855
+ self.filterset({"cf_cf8": [str(choice.pk) for choice in self.select_choices]}, self.queryset).qs,
1856
+ self.queryset.filter(_custom_field_data__cf8__in=[choice.value for choice in self.select_choices]),
1857
+ )
1847
1858
  self.assertQuerysetEqual(
1848
1859
  self.filterset({"cf_cf8__n": ["Foo"]}, self.queryset).qs,
1849
1860
  self.queryset.exclude(_custom_field_data__cf8="Foo")
@@ -1913,6 +1924,10 @@ class CustomFieldFilterTest(TestCase):
1913
1924
  self.filterset({"cf_cf9": "Bar"}, self.queryset).qs,
1914
1925
  self.queryset.filter(_custom_field_data__cf9__contains="Bar"),
1915
1926
  )
1927
+ self.assertQuerysetEqualAndNotEmpty( # https://github.com/nautobot/nautobot/issues/5009
1928
+ self.filterset({"cf_cf9": str(self.multiselect_choices[0].pk)}, self.queryset).qs,
1929
+ self.queryset.filter(_custom_field_data__cf9__contains=self.multiselect_choices[0].value),
1930
+ )
1916
1931
 
1917
1932
 
1918
1933
  class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
@@ -2016,30 +2031,64 @@ class CustomFieldBackgroundTasks(TransactionTestCase):
2016
2031
 
2017
2032
  self.assertEqual(location.cf["cf1"], "Foo")
2018
2033
 
2034
+ with web_request_context(self.user):
2035
+ cf = CustomField(label="CF2", type=CustomFieldTypeChoices.TYPE_TEXT, default="Bar")
2036
+ cf.save()
2037
+ cf.content_types.set([obj_type])
2038
+
2039
+ location.refresh_from_db()
2040
+
2041
+ self.assertEqual(location.cf["cf2"], "Bar")
2042
+
2043
+ oc_list = get_changes_for_model(location).order_by("pk")
2044
+ self.assertEqual(len(oc_list), 1)
2045
+ self.assertEqual(oc_list[0].changed_object, location)
2046
+ self.assertEqual(oc_list[0].change_context_detail, "provision custom field data for new content types")
2047
+ self.assertEqual(oc_list[0].user, self.user)
2048
+
2019
2049
  def test_delete_custom_field_data_task(self):
2020
2050
  obj_type = ContentType.objects.get_for_model(Location)
2021
- cf = CustomField(
2051
+ cf_1 = CustomField(
2022
2052
  label="CF1",
2023
2053
  type=CustomFieldTypeChoices.TYPE_TEXT,
2024
2054
  )
2025
- cf.save()
2026
- cf.content_types.set([obj_type])
2055
+ cf_1.save()
2056
+ cf_1.content_types.set([obj_type])
2057
+ cf_2 = CustomField(
2058
+ label="CF2",
2059
+ type=CustomFieldTypeChoices.TYPE_TEXT,
2060
+ )
2061
+ cf_2.save()
2062
+ cf_2.content_types.set([obj_type])
2027
2063
  location_type = LocationType.objects.create(name="Root Type 2")
2028
2064
  location_status = Status.objects.get_for_model(Location).first()
2029
2065
  location = Location(
2030
2066
  name="Location 1",
2031
2067
  location_type=location_type,
2032
2068
  status=location_status,
2033
- _custom_field_data={"cf1": "foo"},
2069
+ _custom_field_data={"cf1": "foo", "cf2": "bar"},
2034
2070
  )
2035
2071
  location.save()
2036
2072
 
2037
- cf.delete()
2073
+ cf_1.delete()
2038
2074
 
2039
2075
  location.refresh_from_db()
2040
2076
 
2041
2077
  self.assertTrue("cf1" not in location.cf)
2042
2078
 
2079
+ with web_request_context(self.user):
2080
+ cf_2.delete()
2081
+
2082
+ location.refresh_from_db()
2083
+
2084
+ self.assertTrue("cf2" not in location.cf)
2085
+
2086
+ oc_list = get_changes_for_model(location).order_by("pk")
2087
+ self.assertEqual(len(oc_list), 1)
2088
+ self.assertEqual(oc_list[0].changed_object, location)
2089
+ self.assertEqual(oc_list[0].change_context_detail, "delete custom field data")
2090
+ self.assertEqual(oc_list[0].user, self.user)
2091
+
2043
2092
  def test_update_custom_field_choice_data_task(self):
2044
2093
  obj_type = ContentType.objects.get_for_model(Location)
2045
2094
  cf = CustomField(
@@ -2068,6 +2117,20 @@ class CustomFieldBackgroundTasks(TransactionTestCase):
2068
2117
 
2069
2118
  self.assertEqual(location.cf["cf1"], "Bar")
2070
2119
 
2120
+ with web_request_context(self.user):
2121
+ choice.value = "FizzBuzz"
2122
+ choice.save()
2123
+
2124
+ location.refresh_from_db()
2125
+
2126
+ self.assertEqual(location.cf["cf1"], "FizzBuzz")
2127
+
2128
+ oc_list = get_changes_for_model(location).order_by("pk")
2129
+ self.assertEqual(len(oc_list), 1)
2130
+ self.assertEqual(oc_list[0].changed_object, location)
2131
+ self.assertEqual(oc_list[0].change_context_detail, "update custom field choice data")
2132
+ self.assertEqual(oc_list[0].user, self.user)
2133
+
2071
2134
 
2072
2135
  class CustomFieldTableTest(TestCase):
2073
2136
  """
nautobot/extras/views.py CHANGED
@@ -40,6 +40,7 @@ from nautobot.core.views.viewsets import NautobotUIViewSet
40
40
  from nautobot.dcim.models import Controller, Device, Interface, Location, Rack
41
41
  from nautobot.dcim.tables import ControllerTable, DeviceTable, RackTable
42
42
  from nautobot.extras.constants import JOB_OVERRIDABLE_FIELDS
43
+ from nautobot.extras.signals import change_context_state
43
44
  from nautobot.extras.tasks import delete_custom_field_data
44
45
  from nautobot.extras.utils import get_base_template, get_worker_count
45
46
  from nautobot.ipam.models import IPAddress, Prefix, VLAN
@@ -369,7 +370,6 @@ class ContactUIViewSet(NautobotUIViewSet):
369
370
  queryset = Contact.objects.all()
370
371
  serializer_class = serializers.ContactSerializer
371
372
  table_class = tables.ContactTable
372
- is_contact_associatable_model = False
373
373
 
374
374
  def get_extra_context(self, request, instance):
375
375
  context = super().get_extra_context(request, instance)
@@ -677,8 +677,14 @@ class CustomFieldBulkDeleteView(generic.BulkDeleteView):
677
677
  """
678
678
  Helper method to construct a list of celery tasks to execute when bulk deleting custom fields.
679
679
  """
680
+ change_context = change_context_state.get()
681
+ if change_context is None:
682
+ context = None
683
+ else:
684
+ context = change_context.as_dict(queryset)
685
+ context["context_detail"] = "bulk delete custom field data"
680
686
  tasks = [
681
- delete_custom_field_data.si(obj.key, set(obj.content_types.values_list("pk", flat=True)))
687
+ delete_custom_field_data.si(obj.key, set(obj.content_types.values_list("pk", flat=True)), context)
682
688
  for obj in queryset
683
689
  ]
684
690
  return tasks
@@ -1939,8 +1945,6 @@ class ObjectChangeLogView(generic.GenericView):
1939
1945
  "table": objectchanges_table,
1940
1946
  "base_template": self.base_template,
1941
1947
  "active_tab": "changelog",
1942
- # Currently only Contact and Team models are not contact_associatable.
1943
- "is_contact_associatable_model": type(obj) not in [Contact, Team],
1944
1948
  },
1945
1949
  )
1946
1950
 
@@ -2022,8 +2026,6 @@ class ObjectNotesView(generic.GenericView):
2022
2026
  "base_template": self.base_template,
2023
2027
  "active_tab": "notes",
2024
2028
  "form": notes_form,
2025
- # Currently only Contact and Team models are not contact_associatable.
2026
- "is_contact_associatable_model": type(obj) not in [Contact, Team],
2027
2029
  },
2028
2030
  )
2029
2031
 
@@ -2532,7 +2534,6 @@ class TeamUIViewSet(NautobotUIViewSet):
2532
2534
  queryset = Team.objects.all()
2533
2535
  serializer_class = serializers.TeamSerializer
2534
2536
  table_class = tables.TeamTable
2535
- is_contact_associatable_model = False
2536
2537
 
2537
2538
  def get_extra_context(self, request, instance):
2538
2539
  context = super().get_extra_context(request, instance)
@@ -495,6 +495,7 @@ class ServiceSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
495
495
  class Meta:
496
496
  model = Service
497
497
  fields = "__all__"
498
+ validators = []
498
499
  extra_kwargs = {
499
500
  "device": {"help_text": "Required if no virtual_machine is specified"},
500
501
  "virtual_machine": {"help_text": "Required if no device is specified"},
@@ -504,3 +505,12 @@ class ServiceSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
504
505
  # `device`.
505
506
  # list_display_fields = ["name", "parent", "protocol", "ports", "description"]
506
507
  list_display_fields = ["name", "device", "protocol", "ports", "description"]
508
+
509
+ def validate(self, data):
510
+ if data.get("device"):
511
+ validator = UniqueTogetherValidator(queryset=Service.objects.all(), fields=("name", "device"))
512
+ validator(data, self)
513
+ if data.get("virtual_machine"):
514
+ validator = UniqueTogetherValidator(queryset=Service.objects.all(), fields=("name", "virtual_machine"))
515
+ validator(data, self)
516
+ return super().validate(data)
nautobot/ipam/apps.py CHANGED
@@ -5,10 +5,11 @@ class IPAMConfig(NautobotConfig):
5
5
  name = "nautobot.ipam"
6
6
  verbose_name = "IPAM"
7
7
  searchable_models = [
8
- "vrf",
9
- "prefix",
10
8
  "ipaddress",
9
+ "namespace",
10
+ "prefix",
11
11
  "vlan",
12
+ "vrf",
12
13
  ]
13
14
 
14
15
  def ready(self):