nautobot 2.3.0__py3-none-any.whl → 2.3.0b1__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (366) hide show
  1. nautobot/cloud/factory.py +0 -2
  2. nautobot/cloud/filters.py +0 -3
  3. nautobot/cloud/forms.py +1 -7
  4. nautobot/cloud/migrations/0001_initial.py +1 -1
  5. nautobot/cloud/models.py +2 -1
  6. nautobot/cloud/tables.py +17 -1
  7. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +7 -1
  8. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -11
  9. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -4
  10. nautobot/cloud/tests/test_filters.py +0 -12
  11. nautobot/core/filters.py +1 -15
  12. nautobot/core/forms/forms.py +2 -10
  13. nautobot/core/graphql/generators.py +2 -2
  14. nautobot/core/graphql/schema.py +14 -6
  15. nautobot/core/jobs/__init__.py +1 -4
  16. nautobot/core/management/commands/generate_test_data.py +2 -2
  17. nautobot/core/models/__init__.py +2 -2
  18. nautobot/core/settings.py +2 -13
  19. nautobot/core/settings.yaml +2 -16
  20. nautobot/core/tables.py +0 -3
  21. nautobot/core/templates/generic/object_retrieve.html +5 -5
  22. nautobot/core/templates/nautobot_config.py.j2 +0 -15
  23. nautobot/core/testing/filters.py +1 -12
  24. nautobot/core/tests/integration/test_general_functionality.py +1 -1
  25. nautobot/core/tests/test_jobs.py +1 -74
  26. nautobot/core/views/generic.py +1 -1
  27. nautobot/core/views/mixins.py +1 -1
  28. nautobot/core/views/utils.py +6 -8
  29. nautobot/dcim/factory.py +1 -4
  30. nautobot/dcim/filters/__init__.py +0 -4
  31. nautobot/dcim/forms.py +0 -5
  32. nautobot/dcim/migrations/0061_module_models.py +0 -1
  33. nautobot/dcim/models/device_components.py +0 -7
  34. nautobot/dcim/models/devices.py +4 -6
  35. nautobot/dcim/models/racks.py +1 -0
  36. nautobot/dcim/tables/devices.py +3 -17
  37. nautobot/dcim/tables/devicetypes.py +1 -1
  38. nautobot/dcim/templates/dcim/device/base.html +1 -1
  39. nautobot/dcim/templates/dcim/device.html +2 -2
  40. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -6
  41. nautobot/dcim/templates/dcim/moduletype_retrieve.html +0 -17
  42. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +2 -2
  43. nautobot/dcim/tests/test_api.py +0 -2
  44. nautobot/dcim/tests/test_filters.py +7 -14
  45. nautobot/dcim/tests/test_models.py +0 -31
  46. nautobot/dcim/tests/test_views.py +0 -39
  47. nautobot/dcim/views.py +1 -4
  48. nautobot/extras/api/views.py +59 -7
  49. nautobot/extras/factory.py +12 -50
  50. nautobot/extras/forms/base.py +4 -10
  51. nautobot/extras/homepage.py +2 -12
  52. nautobot/extras/jobs.py +2 -2
  53. nautobot/extras/migrations/0111_metadata.py +4 -4
  54. nautobot/extras/models/jobs.py +0 -83
  55. nautobot/extras/models/metadata.py +18 -18
  56. nautobot/extras/models/models.py +0 -2
  57. nautobot/extras/signals.py +1 -14
  58. nautobot/extras/tables.py +14 -43
  59. nautobot/extras/templates/extras/job_detail.html +0 -11
  60. nautobot/extras/tests/test_api.py +9 -16
  61. nautobot/extras/tests/test_jobs.py +2 -2
  62. nautobot/extras/tests/test_models.py +18 -20
  63. nautobot/extras/tests/test_views.py +3 -23
  64. nautobot/extras/utils.py +6 -35
  65. nautobot/extras/views.py +50 -27
  66. nautobot/ipam/filters.py +1 -1
  67. nautobot/ipam/forms.py +1 -1
  68. nautobot/ipam/models.py +20 -9
  69. nautobot/ipam/tables.py +0 -4
  70. nautobot/ipam/tests/test_models.py +2 -3
  71. nautobot/ipam/views.py +11 -6
  72. nautobot/project-static/css/base.css +0 -1
  73. nautobot/project-static/docs/404.html +18 -18
  74. nautobot/project-static/docs/apps/index.html +18 -18
  75. nautobot/project-static/docs/apps/nautobot-apps.html +18 -18
  76. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css +1 -0
  77. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css.map +1 -0
  78. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +18 -18
  79. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +18 -18
  80. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +18 -66
  81. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +18 -18
  82. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +18 -18
  83. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +18 -18
  84. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +18 -18
  85. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +18 -18
  86. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +18 -66
  87. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +18 -34
  88. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +18 -82
  89. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +21 -75
  90. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +18 -18
  91. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +18 -34
  92. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +18 -34
  93. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +18 -18
  94. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +18 -18
  95. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +18 -18
  96. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +18 -18
  97. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +18 -18
  98. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +18 -18
  99. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +19 -21
  100. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +18 -34
  101. nautobot/project-static/docs/development/apps/api/configuration-view.html +18 -18
  102. nautobot/project-static/docs/development/apps/api/database-backend-config.html +18 -18
  103. nautobot/project-static/docs/development/apps/api/models/django-admin.html +18 -18
  104. nautobot/project-static/docs/development/apps/api/models/global-search.html +18 -18
  105. nautobot/project-static/docs/development/apps/api/models/graphql.html +18 -18
  106. nautobot/project-static/docs/development/apps/api/models/index.html +22 -33
  107. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +18 -18
  108. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +18 -18
  109. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +18 -18
  110. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +18 -18
  111. nautobot/project-static/docs/development/apps/api/platform-features/index.html +18 -18
  112. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +18 -18
  113. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +18 -18
  114. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +18 -18
  115. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +18 -18
  116. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +18 -18
  117. nautobot/project-static/docs/development/apps/api/prometheus.html +18 -18
  118. nautobot/project-static/docs/development/apps/api/setup.html +18 -18
  119. nautobot/project-static/docs/development/apps/api/testing.html +18 -18
  120. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +18 -18
  121. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +18 -18
  122. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +18 -18
  123. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +18 -18
  124. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +18 -18
  125. nautobot/project-static/docs/development/apps/api/views/base-template.html +18 -18
  126. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +18 -18
  127. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +18 -18
  128. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +18 -18
  129. nautobot/project-static/docs/development/apps/api/views/index.html +18 -18
  130. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +18 -18
  131. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +18 -18
  132. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +18 -18
  133. nautobot/project-static/docs/development/apps/api/views/notes.html +18 -18
  134. nautobot/project-static/docs/development/apps/api/views/rest-api.html +18 -18
  135. nautobot/project-static/docs/development/apps/api/views/urls.html +18 -18
  136. nautobot/project-static/docs/development/apps/index.html +18 -18
  137. nautobot/project-static/docs/development/apps/migration/code-updates.html +18 -18
  138. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +18 -18
  139. nautobot/project-static/docs/development/apps/migration/from-v1.html +18 -18
  140. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +18 -18
  141. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +18 -18
  142. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +18 -18
  143. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +18 -18
  144. nautobot/project-static/docs/development/apps/porting-from-netbox.html +18 -18
  145. nautobot/project-static/docs/development/core/application-registry.html +18 -18
  146. nautobot/project-static/docs/development/core/best-practices.html +18 -18
  147. nautobot/project-static/docs/development/core/bootstrap-ui.html +18 -18
  148. nautobot/project-static/docs/development/core/caching.html +18 -18
  149. nautobot/project-static/docs/development/core/controllers.html +18 -18
  150. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +18 -18
  151. nautobot/project-static/docs/development/core/generic-views.html +18 -18
  152. nautobot/project-static/docs/development/core/getting-started.html +18 -18
  153. nautobot/project-static/docs/development/core/homepage.html +18 -18
  154. nautobot/project-static/docs/development/core/index.html +18 -29
  155. nautobot/project-static/docs/development/core/model-checklist.html +20 -26
  156. nautobot/project-static/docs/development/core/model-features.html +18 -18
  157. nautobot/project-static/docs/development/core/natural-keys.html +18 -18
  158. nautobot/project-static/docs/development/core/navigation-menu.html +18 -18
  159. nautobot/project-static/docs/development/core/release-checklist.html +18 -18
  160. nautobot/project-static/docs/development/core/role-internals.html +18 -18
  161. nautobot/project-static/docs/development/core/settings.html +18 -18
  162. nautobot/project-static/docs/development/core/style-guide.html +19 -19
  163. nautobot/project-static/docs/development/core/templates.html +18 -18
  164. nautobot/project-static/docs/development/core/testing.html +18 -18
  165. nautobot/project-static/docs/development/core/user-preferences.html +18 -18
  166. nautobot/project-static/docs/development/index.html +18 -18
  167. nautobot/project-static/docs/development/jobs/index.html +379 -393
  168. nautobot/project-static/docs/development/jobs/migration/from-v1.html +18 -18
  169. nautobot/project-static/docs/index.html +13 -9032
  170. nautobot/project-static/docs/models/extras/metadatachoice.html +3 -3
  171. nautobot/project-static/docs/models/extras/metadatatype.html +3 -3
  172. nautobot/project-static/docs/models/extras/objectmetadata.html +3 -3
  173. nautobot/project-static/docs/objects.inv +0 -0
  174. nautobot/project-static/docs/overview/application_stack.html +18 -18
  175. nautobot/project-static/docs/overview/design_philosophy.html +20 -20
  176. nautobot/project-static/docs/overview/index.html +9032 -13
  177. nautobot/project-static/docs/release-notes/index.html +19 -252
  178. nautobot/project-static/docs/release-notes/version-1.0.html +18 -18
  179. nautobot/project-static/docs/release-notes/version-1.1.html +18 -18
  180. nautobot/project-static/docs/release-notes/version-1.2.html +18 -18
  181. nautobot/project-static/docs/release-notes/version-1.3.html +18 -18
  182. nautobot/project-static/docs/release-notes/version-1.4.html +18 -18
  183. nautobot/project-static/docs/release-notes/version-1.5.html +18 -18
  184. nautobot/project-static/docs/release-notes/version-1.6.html +18 -18
  185. nautobot/project-static/docs/release-notes/version-2.0.html +18 -18
  186. nautobot/project-static/docs/release-notes/version-2.1.html +18 -18
  187. nautobot/project-static/docs/release-notes/version-2.2.html +111 -248
  188. nautobot/project-static/docs/release-notes/version-2.3.html +90 -520
  189. nautobot/project-static/docs/requirements.txt +3 -3
  190. nautobot/project-static/docs/search/search_index.json +1 -1
  191. nautobot/project-static/docs/sitemap.xml +278 -278
  192. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  193. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +18 -18
  194. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +18 -18
  195. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +18 -18
  196. nautobot/project-static/docs/user-guide/administration/configuration/index.html +18 -18
  197. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +20 -52
  198. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +18 -18
  199. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +18 -18
  200. nautobot/project-static/docs/user-guide/administration/guides/caching.html +18 -18
  201. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +18 -22
  202. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +18 -18
  203. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +18 -18
  204. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +18 -18
  205. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +18 -18
  206. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +18 -18
  207. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +18 -18
  208. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +18 -18
  209. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +18 -18
  210. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +82 -69
  211. nautobot/project-static/docs/user-guide/administration/installation/index.html +24 -24
  212. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +52 -60
  213. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +87 -80
  214. nautobot/project-static/docs/user-guide/administration/installation/services.html +44 -37
  215. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +18 -18
  216. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +18 -18
  217. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +18 -18
  218. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +18 -18
  219. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +18 -18
  220. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +24 -76
  221. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +18 -18
  222. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +18 -18
  223. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +18 -18
  224. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +18 -18
  225. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +18 -18
  226. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +18 -18
  227. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +18 -18
  228. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +18 -18
  229. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +18 -18
  230. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +18 -18
  231. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +18 -18
  232. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +18 -18
  233. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +18 -18
  234. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +18 -18
  235. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +18 -18
  236. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +18 -18
  237. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +18 -18
  238. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +18 -18
  239. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +18 -18
  240. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +18 -18
  241. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +18 -18
  242. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +18 -18
  243. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +18 -18
  244. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +18 -18
  245. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +18 -18
  246. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +18 -18
  247. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +18 -18
  248. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +18 -18
  249. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +18 -18
  250. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +19 -19
  251. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +18 -18
  252. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +18 -18
  253. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +18 -18
  254. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +18 -18
  255. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +18 -18
  256. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +18 -18
  257. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +18 -18
  258. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +18 -18
  259. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +18 -18
  260. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +18 -18
  261. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +18 -18
  262. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +18 -18
  263. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +18 -18
  264. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +19 -19
  265. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +18 -18
  266. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +18 -18
  267. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +18 -18
  268. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +18 -18
  269. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +18 -18
  270. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +18 -18
  271. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +18 -18
  272. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +18 -18
  273. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +18 -18
  274. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +18 -18
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +18 -18
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +18 -18
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +18 -18
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +18 -18
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +18 -18
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +18 -18
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +18 -18
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +18 -18
  283. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +18 -18
  284. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +18 -62
  285. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +18 -18
  286. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +18 -18
  287. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +18 -18
  288. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +18 -18
  289. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +18 -18
  290. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +18 -18
  291. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +18 -18
  292. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +18 -18
  293. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +18 -18
  294. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +18 -18
  295. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +18 -18
  296. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +18 -18
  297. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +18 -18
  298. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +18 -18
  299. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +18 -18
  300. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +18 -18
  301. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +18 -18
  302. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +18 -18
  303. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +18 -18
  304. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +18 -18
  305. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +18 -18
  306. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +18 -18
  307. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +18 -18
  308. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +18 -18
  309. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +18 -18
  310. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +18 -18
  311. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +18 -18
  312. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +18 -18
  313. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +18 -18
  314. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +18 -18
  315. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +18 -18
  316. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +18 -18
  317. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +18 -18
  318. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +18 -18
  319. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +18 -18
  320. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +18 -18
  321. nautobot/project-static/docs/user-guide/index.html +18 -18
  322. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +18 -18
  323. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +18 -18
  324. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +18 -18
  325. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +18 -18
  326. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +18 -18
  327. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +18 -18
  328. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +18 -18
  329. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +18 -18
  330. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +18 -18
  331. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +18 -18
  332. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +18 -18
  333. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +18 -18
  334. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +21 -21
  335. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +18 -18
  336. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +18 -18
  337. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +18 -18
  338. nautobot/project-static/docs/user-guide/platform-functionality/{objectmetadata.html → metadata.html} +84 -197
  339. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +36 -36
  340. nautobot/project-static/docs/user-guide/platform-functionality/note.html +33 -33
  341. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +21 -21
  342. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +18 -18
  343. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +18 -18
  344. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +18 -18
  345. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +18 -18
  346. nautobot/project-static/docs/user-guide/platform-functionality/role.html +18 -18
  347. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +18 -18
  348. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +18 -18
  349. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +18 -18
  350. nautobot/project-static/docs/user-guide/platform-functionality/status.html +18 -18
  351. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +18 -18
  352. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +18 -18
  353. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +18 -18
  354. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +18 -18
  355. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +18 -18
  356. nautobot/tenancy/templates/tenancy/tenant.html +4 -4
  357. nautobot/virtualization/models.py +2 -0
  358. nautobot/virtualization/tables.py +5 -2
  359. {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/METADATA +3 -3
  360. {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/RECORD +364 -364
  361. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +0 -1
  362. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +0 -1
  363. {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/LICENSE.txt +0 -0
  364. {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/NOTICE +0 -0
  365. {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/WHEEL +0 -0
  366. {nautobot-2.3.0.dist-info → nautobot-2.3.0b1.dist-info}/entry_points.txt +0 -0
@@ -251,24 +251,6 @@ class MetadataTypeFactory(PrimaryModelFactory):
251
251
  )
252
252
 
253
253
 
254
- def _available_field_names(metadata_type, assigned_object):
255
- field_names = [field.name for field in assigned_object._meta.get_fields()]
256
- # Avoid collisions, see ObjectMetadata.clean()
257
- existing_metadata_scoped_fields = ObjectMetadata.objects.filter(
258
- metadata_type=metadata_type,
259
- assigned_object_type=ContentType.objects.get_for_model(assigned_object),
260
- assigned_object_id=assigned_object.pk,
261
- ).values_list("scoped_fields", flat=True)
262
- for existing_scoped_fields in existing_metadata_scoped_fields:
263
- if existing_scoped_fields:
264
- field_names = sorted(set(field_names).difference(existing_scoped_fields))
265
- else:
266
- field_names = []
267
- break
268
-
269
- return field_names
270
-
271
-
272
254
  class ObjectMetadataFactory(BaseModelFactory):
273
255
  """ObjectMetadata model factory"""
274
256
 
@@ -281,6 +263,7 @@ class ObjectMetadataFactory(BaseModelFactory):
281
263
  MetadataType.objects.all(),
282
264
  allow_null=False,
283
265
  )
266
+ scoped_fields = factory.Faker("pylist", allowed_types=[str])
284
267
 
285
268
  @factory.lazy_attribute
286
269
  def contact(self):
@@ -325,43 +308,22 @@ class ObjectMetadataFactory(BaseModelFactory):
325
308
  raise RuntimeError(f"Unsupported metadatatype datatype {metadata_type_data_type}")
326
309
 
327
310
  @factory.lazy_attribute
328
- def assigned_object(self):
329
- allowed_content_types = list(self.metadata_type.content_types.all())
330
- for content_type in factory.random.randgen.sample(allowed_content_types, len(allowed_content_types)):
311
+ def assigned_object_type(self):
312
+ while True:
313
+ allowed_content_types = list(self.metadata_type.content_types.values_list("pk", flat=True))
314
+ content_type = factory.random.randgen.choice(
315
+ ContentType.objects.filter(FeatureQuery("metadata").get_query(), pk__in=allowed_content_types)
316
+ )
331
317
  # It does not have a get_absolute_url attribute and is causing failure in API unittests
332
318
  if content_type.app_label == "extras" and content_type.model == "taggeditem":
333
319
  continue
334
-
335
- assigned_model = content_type.model_class()
336
- queryset = assigned_model.objects.all()
337
-
338
- if not queryset.exists():
339
- continue
340
-
341
- for _ in range(10):
342
- assigned_object = factory.random.randgen.choice(queryset)
343
- if _available_field_names(self.metadata_type, assigned_object):
344
- return assigned_object
345
-
346
- raise RuntimeError(f"Couldn't find any suitable instances not already covered by {self.metadata_type}")
320
+ if content_type.model_class().objects.exists():
321
+ return content_type
347
322
 
348
323
  @factory.lazy_attribute
349
- def scoped_fields(self):
350
- all_field_names = [field.name for field in self.assigned_object._meta.get_fields()]
351
- field_names = _available_field_names(self.metadata_type, self.assigned_object)
352
- if not field_names:
353
- raise RuntimeError(
354
- f"All existing scoped_fields for {self.metadata_type} are covered by existing ObjectMetadata for {self.assigned_object}"
355
- )
356
-
357
- if len(field_names) < len(all_field_names):
358
- minimum_fields = 1 # don't allow an empty list since that would cover all fields
359
- else:
360
- minimum_fields = 0
361
-
362
- return factory.random.randgen.sample(
363
- field_names, k=factory.random.randgen.randint(minimum_fields, len(field_names))
364
- )
324
+ def assigned_object_id(self):
325
+ queryset = self.assigned_object_type.model_class().objects.all()
326
+ return factory.random.randgen.choice(queryset).pk
365
327
 
366
328
 
367
329
  class ObjectChangeFactory(BaseModelFactory):
@@ -26,12 +26,11 @@ __all__ = (
26
26
 
27
27
 
28
28
  class NautobotModelForm(
29
- BootstrapMixin,
30
- # The below must be listed *after* BootstrapMixin so that BootstrapMixin applies to their dynamic form fields
31
29
  CustomFieldModelFormMixin,
32
30
  DynamicGroupModelFormMixin,
33
31
  NoteModelFormMixin,
34
32
  RelationshipModelFormMixin,
33
+ BootstrapMixin,
35
34
  ):
36
35
  """
37
36
  This class exists to combine common functionality and is used to inherit from throughout the
@@ -41,10 +40,9 @@ class NautobotModelForm(
41
40
 
42
41
 
43
42
  class NautobotFilterForm(
44
- BootstrapMixin,
45
- # The below must be listed *after* BootstrapMixin so that BootstrapMixin applies to their dynamic form fields
46
43
  ContactTeamModelFilterFormMixin,
47
- CustomFieldModelFilterFormMixin,
44
+ BootstrapMixin,
45
+ CustomFieldModelFilterFormMixin, # currently must come *after* BootstrapMixin to get proper CSS classes applied
48
46
  RelationshipModelFilterFormMixin,
49
47
  ):
50
48
  """
@@ -55,10 +53,6 @@ class NautobotFilterForm(
55
53
 
56
54
 
57
55
  class NautobotBulkEditForm(
58
- BootstrapMixin,
59
- # The below must be listed *after* BootstrapMixin so that BootstrapMixin applies to their dynamic form fields
60
- CustomFieldModelBulkEditFormMixin,
61
- NoteModelBulkEditFormMixin,
62
- RelationshipModelBulkEditFormMixin,
56
+ BootstrapMixin, CustomFieldModelBulkEditFormMixin, RelationshipModelBulkEditFormMixin, NoteModelBulkEditFormMixin
63
57
  ):
64
58
  """Base class for bulk-edit forms for models that support relationships, custom fields and notes."""
@@ -7,24 +7,14 @@ def get_job_results(request):
7
7
  """Callback function to collect job history for panel."""
8
8
  return (
9
9
  JobResult.objects.filter(status__in=JobResultStatusChoices.READY_STATES)
10
- .restrict(request.user, "view")
11
- .only("id", "name", "status", "date_done", "user")
10
+ .defer("result")
12
11
  .order_by("-date_done")[:10]
13
12
  )
14
13
 
15
14
 
16
15
  def get_changelog(request):
17
16
  """Callback function to collect changelog for panel."""
18
- return ObjectChange.objects.restrict(request.user, "view").only(
19
- "id",
20
- "action",
21
- "changed_object",
22
- "changed_object_id",
23
- "changed_object_type",
24
- "object_repr",
25
- "user_name",
26
- "time",
27
- )[:15]
17
+ return ObjectChange.objects.restrict(request.user, "view")[:15]
28
18
 
29
19
 
30
20
  layout = (
nautobot/extras/jobs.py CHANGED
@@ -20,7 +20,7 @@ from django.conf import settings
20
20
  from django.contrib.auth import get_user_model
21
21
  from django.core.exceptions import ObjectDoesNotExist
22
22
  from django.core.files.base import ContentFile
23
- from django.core.files.uploadedfile import UploadedFile
23
+ from django.core.files.uploadedfile import InMemoryUploadedFile
24
24
  from django.core.validators import RegexValidator
25
25
  from django.db.models import Model
26
26
  from django.db.models.query import QuerySet
@@ -539,7 +539,7 @@ class BaseJob:
539
539
  elif isinstance(value, Model):
540
540
  return_data[field_name] = value.pk
541
541
  # FileVar (Save each FileVar as a FileProxy)
542
- elif isinstance(value, UploadedFile):
542
+ elif isinstance(value, InMemoryUploadedFile):
543
543
  return_data[field_name] = BaseJob._save_file_to_proxy(value)
544
544
  # IPAddressVar, IPAddressWithMaskVar, IPNetworkVar
545
545
  elif isinstance(value, netaddr.ip.BaseIP):
@@ -72,7 +72,7 @@ class Migration(migrations.Migration):
72
72
  ("last_updated", models.DateTimeField(auto_now=True, null=True)),
73
73
  (
74
74
  "scoped_fields",
75
- nautobot.core.models.fields.JSONArrayField(base_field=models.CharField(max_length=255), blank=True),
75
+ nautobot.core.models.fields.JSONArrayField(base_field=models.CharField(max_length=255)),
76
76
  ),
77
77
  ("_value", models.JSONField(blank=True, null=True)),
78
78
  ("assigned_object_id", models.UUIDField(db_index=True)),
@@ -91,7 +91,7 @@ class Migration(migrations.Migration):
91
91
  blank=True,
92
92
  null=True,
93
93
  on_delete=django.db.models.deletion.PROTECT,
94
- related_name="object_metadata",
94
+ related_name="object_metadatas",
95
95
  to="extras.contact",
96
96
  ),
97
97
  ),
@@ -99,7 +99,7 @@ class Migration(migrations.Migration):
99
99
  "metadata_type",
100
100
  models.ForeignKey(
101
101
  on_delete=django.db.models.deletion.PROTECT,
102
- related_name="object_metadata",
102
+ related_name="object_metadatas",
103
103
  to="extras.metadatatype",
104
104
  ),
105
105
  ),
@@ -109,7 +109,7 @@ class Migration(migrations.Migration):
109
109
  blank=True,
110
110
  null=True,
111
111
  on_delete=django.db.models.deletion.PROTECT,
112
- related_name="object_metadata",
112
+ related_name="object_metadatas",
113
113
  to="extras.team",
114
114
  ),
115
115
  ),
@@ -456,8 +456,6 @@ class JobLogEntry(BaseModel):
456
456
  log_object = models.CharField(max_length=JOB_LOG_MAX_LOG_OBJECT_LENGTH, blank=True, default="")
457
457
  absolute_url = models.CharField(max_length=JOB_LOG_MAX_ABSOLUTE_URL_LENGTH, blank=True, default="")
458
458
 
459
- is_metadata_associable_model = False
460
-
461
459
  documentation_static_path = "docs/user-guide/platform-functionality/jobs/models.html"
462
460
 
463
461
  def __str__(self):
@@ -1077,87 +1075,6 @@ class ScheduledJob(BaseModel):
1077
1075
  day_of_week=day_of_week,
1078
1076
  )
1079
1077
 
1080
- @classmethod
1081
- def create_schedule(
1082
- cls,
1083
- job_model,
1084
- user,
1085
- name=None,
1086
- start_time=None,
1087
- interval=JobExecutionType.TYPE_IMMEDIATELY,
1088
- crontab="",
1089
- profile=False,
1090
- approval_required=False,
1091
- task_queue=None,
1092
- **job_kwargs,
1093
- ):
1094
- """
1095
- Schedule a job with the specified parameters.
1096
-
1097
- This method creates a schedule for a job to be executed at a specific time
1098
- or interval. It handles immediate execution, custom start times, and
1099
- crontab-based scheduling.
1100
-
1101
- Parameters:
1102
- job_model (JobModel): The job model instance.
1103
- user (User): The user who is scheduling the job.
1104
- name (str, optional): The name of the scheduled job. Defaults to None.
1105
- start_time (datetime, optional): The start time for the job. Defaults to None.
1106
- interval (JobExecutionType, optional): The interval type for the job execution.
1107
- Defaults to JobExecutionType.TYPE_IMMEDIATELY.
1108
- crontab (str, optional): The crontab string for the schedule. Defaults to "".
1109
- profile (bool, optional): Flag indicating whether to profile the job. Defaults to False.
1110
- approval_required (bool, optional): Flag indicating if approval is required. Defaults to False.
1111
- task_queue (str, optional): The task queue for the job. Defaults to None, which will use the configured default celery queue.
1112
- **job_kwargs: Additional keyword arguments to pass to the job.
1113
-
1114
- Returns:
1115
- ScheduledJob instance
1116
- """
1117
-
1118
- if interval == JobExecutionType.TYPE_IMMEDIATELY:
1119
- start_time = timezone.now()
1120
- name = name or f"{job_model.name} - {start_time}"
1121
- elif interval == JobExecutionType.TYPE_CUSTOM:
1122
- if start_time is None:
1123
- # "start_time" is checked against models.ScheduledJob.earliest_possible_time()
1124
- # which returns timezone.now() + timedelta(seconds=15)
1125
- start_time = timezone.now() + timedelta(seconds=20)
1126
-
1127
- celery_kwargs = {
1128
- "nautobot_job_profile": profile,
1129
- "queue": task_queue,
1130
- }
1131
- if job_model.soft_time_limit > 0:
1132
- celery_kwargs["soft_time_limit"] = job_model.soft_time_limit
1133
- if job_model.time_limit > 0:
1134
- celery_kwargs["time_limit"] = job_model.time_limit
1135
-
1136
- # 2.0 TODO: To revisit this as part of a larger Jobs cleanup in 2.0.
1137
- #
1138
- # We pass in task and job_model here partly for forward/backward compatibility logic, and
1139
- # part fallback safety. It's mildly useful to store both the task module/class name and the JobModel
1140
- # FK on the ScheduledJob, as in the case where the JobModel gets deleted (and the FK becomes
1141
- # null) you still have a bit of context on the ScheduledJob as to what it was originally
1142
- # scheduled for.
1143
- scheduled_job = cls(
1144
- name=name,
1145
- task=job_model.class_path,
1146
- job_model=job_model,
1147
- start_time=start_time,
1148
- description=f"Nautobot job {name} scheduled by {user} for {start_time}",
1149
- kwargs=job_kwargs,
1150
- celery_kwargs=celery_kwargs,
1151
- interval=interval,
1152
- one_off=(interval == JobExecutionType.TYPE_FUTURE),
1153
- user=user,
1154
- approval_required=approval_required,
1155
- crontab=crontab,
1156
- queue=task_queue,
1157
- )
1158
- scheduled_job.validated_save()
1159
- return scheduled_job
1160
-
1161
1078
  def to_cron(self):
1162
1079
  t = self.start_time
1163
1080
  if self.interval == JobExecutionType.TYPE_HOURLY:
@@ -61,7 +61,7 @@ class MetadataType(PrimaryModel):
61
61
 
62
62
  objects = MetadataTypeManager()
63
63
  clone_fields = ["data_type"]
64
- documentation_static_path = "docs/user-guide/platform-functionality/objectmetadata.html"
64
+ documentation_static_path = "docs/user-guide/platform-functionality/metadata.html"
65
65
 
66
66
  class Meta:
67
67
  ordering = ["name"]
@@ -106,7 +106,7 @@ class MetadataChoice(ChangeLoggedModel, BaseModel):
106
106
  weight = models.PositiveSmallIntegerField(default=100, help_text="Higher weights appear later in the list")
107
107
  is_metadata_associable_model = False
108
108
 
109
- documentation_static_path = "docs/user-guide/platform-functionality/objectmetadata.html"
109
+ documentation_static_path = "docs/user-guide/platform-functionality/metadata.html"
110
110
 
111
111
  class Meta:
112
112
  ordering = ["metadata_type", "weight", "value"]
@@ -166,19 +166,19 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
166
166
  metadata_type = models.ForeignKey(
167
167
  to=MetadataType,
168
168
  on_delete=models.PROTECT,
169
- related_name="object_metadata",
169
+ related_name="object_metadatas",
170
170
  )
171
171
  contact = models.ForeignKey(
172
172
  to=Contact,
173
173
  on_delete=models.PROTECT,
174
- related_name="object_metadata",
174
+ related_name="object_metadatas",
175
175
  blank=True,
176
176
  null=True,
177
177
  )
178
178
  team = models.ForeignKey(
179
179
  to=Team,
180
180
  on_delete=models.PROTECT,
181
- related_name="object_metadata",
181
+ related_name="object_metadatas",
182
182
  blank=True,
183
183
  null=True,
184
184
  )
@@ -186,7 +186,6 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
186
186
  base_field=models.CharField(
187
187
  max_length=CHARFIELD_MAX_LENGTH,
188
188
  ),
189
- blank=True,
190
189
  help_text="List of scoped fields, only direct fields on the model",
191
190
  )
192
191
  _value = models.JSONField(
@@ -200,7 +199,7 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
200
199
 
201
200
  objects = ObjectMetadataManager()
202
201
  natural_key_field_names = ["pk"]
203
- documentation_static_path = "docs/user-guide/platform-functionality/objectmetadata.html"
202
+ documentation_static_path = "docs/user-guide/platform-functionality/metadata.html"
204
203
 
205
204
  class Meta:
206
205
  ordering = ["metadata_type"]
@@ -362,17 +361,12 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
362
361
  raise ValidationError("Date values must be in the format YYYY-MM-DD.")
363
362
  # Validate datetime
364
363
  elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_DATETIME:
365
- if isinstance(value, datetime):
366
- # check if datetime object has tzinfo
367
- if value.tzinfo is None:
368
- value = value.replace(tzinfo=timezone.utc) # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
369
- value = value.replace(microsecond=0).isoformat() # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
370
- else:
371
- acceptable_datetime_formats = [
372
- "YYYY-MM-DDTHH:MM:SS",
373
- "YYYY-MM-DDTHH:MM:SS(+,-)zzzz",
374
- "YYYY-MM-DDTHH:MM:SS(+,-)zz:zz",
375
- ]
364
+ acceptable_datetime_formats = [
365
+ "YYYY-MM-DDTHH:MM:SS",
366
+ "YYYY-MM-DDTHH:MM:SS(+,-)zzzz",
367
+ "YYYY-MM-DDTHH:MM:SS(+,-)zz:zz",
368
+ ]
369
+ if not isinstance(value, datetime):
376
370
  try:
377
371
  datetime.strptime(value, "%Y-%m-%dT%H:%M:%S%z")
378
372
  except ValueError:
@@ -386,6 +380,12 @@ class ObjectMetadata(ChangeLoggedModel, BaseModel):
386
380
  )
387
381
  except TypeError:
388
382
  raise ValidationError("Value must be a datetime or str object.")
383
+ else:
384
+ # if value is a datetime object
385
+ # check if datetime object has tzinfo
386
+ if value.tzinfo is None:
387
+ value = value.replace(tzinfo=timezone.utc)
388
+ value = value.replace(microsecond=0).isoformat()
389
389
  # Validate selected choice
390
390
  elif metadata_type_data_type == MetadataTypeDataTypeChoices.TYPE_SELECT:
391
391
  if value not in self.metadata_type.choices.values_list("value", flat=True):
@@ -822,8 +822,6 @@ class Note(ChangeLoggedModel, BaseModel):
822
822
  note = models.TextField()
823
823
  objects = BaseManager.from_queryset(NotesQuerySet)()
824
824
 
825
- is_metadata_associable_model = False
826
-
827
825
  class Meta:
828
826
  ordering = ["created"]
829
827
  unique_together = [["assigned_object_type", "assigned_object_id", "user_name", "created"]]
@@ -13,7 +13,7 @@ from django.core.cache import cache
13
13
  from django.core.exceptions import ValidationError
14
14
  from django.core.files.storage import get_storage_class
15
15
  from django.db import transaction
16
- from django.db.models.signals import m2m_changed, post_delete, post_migrate, post_save, pre_delete, pre_save
16
+ from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete, pre_save
17
17
  from django.dispatch import receiver
18
18
  from django.utils import timezone
19
19
  from django_prometheus.models import model_deletes, model_inserts, model_updates
@@ -289,19 +289,6 @@ def _handle_deleted_object(sender, instance, **kwargs):
289
289
  model_deletes.labels(instance._meta.model_name).inc()
290
290
 
291
291
 
292
- #
293
- # Content types
294
- #
295
-
296
-
297
- @receiver(post_migrate)
298
- def post_migrate_clear_content_type_caches(sender, app_config, signal, **kwargs):
299
- """Clear various content-type caches after a migration."""
300
- with contextlib.suppress(redis.exceptions.ConnectionError):
301
- cache.delete("nautobot.extras.utils.change_logged_models_queryset")
302
- cache.delete_pattern("nautobot.extras.utils.FeatureQuery.*")
303
-
304
-
305
292
  #
306
293
  # Custom fields
307
294
  #
nautobot/extras/tables.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from django.conf import settings
2
- from django.utils.html import format_html, format_html_join
2
+ from django.utils.html import format_html
3
3
  import django_tables2 as tables
4
4
  from django_tables2.utils import Accessor
5
5
  from jsonschema.exceptions import ValidationError as JSONSchemaValidationError
@@ -107,7 +107,6 @@ GITREPOSITORY_BUTTONS = """
107
107
 
108
108
  JOB_BUTTONS = """
109
109
  <a href="{% url 'extras:job' pk=record.pk %}" class="btn btn-default btn-xs" title="Details"><i class="mdi mdi-information-outline" aria-hidden="true"></i></a>
110
- <a href="{% url 'extras:jobresult_list' %}?job_model={{ record.name | urlencode }}" class="btn btn-default btn-xs" title="Job Results"><i class="mdi mdi-format-list-bulleted" aria-hidden="true"></i></a>
111
110
  """
112
111
 
113
112
  OBJECTCHANGE_OBJECT = """
@@ -716,10 +715,7 @@ class JobTable(BaseTable):
716
715
  return render_markdown(value)
717
716
 
718
717
  def render_name(self, value):
719
- return format_html(
720
- '<span class="btn btn-primary btn-xs"><i class="mdi mdi-play"></i></span>{}',
721
- value,
722
- )
718
+ return format_html('<span class="btn btn-primary btn-xs"><i class="mdi mdi-play"></i></span>{}', value)
723
719
 
724
720
  class Meta(BaseTable.Meta):
725
721
  model = JobModel
@@ -891,16 +887,7 @@ class JobResultTable(BaseTable):
891
887
  "summary",
892
888
  "actions",
893
889
  )
894
- default_columns = (
895
- "pk",
896
- "date_created",
897
- "name",
898
- "job_model",
899
- "user",
900
- "status",
901
- "summary",
902
- "actions",
903
- )
890
+ default_columns = ("pk", "date_created", "name", "job_model", "user", "status", "summary", "actions")
904
891
 
905
892
 
906
893
  class JobButtonTable(BaseTable):
@@ -970,8 +957,10 @@ class MetadataTypeTable(BaseTable):
970
957
  class ObjectMetadataTable(BaseTable):
971
958
  pk = ToggleColumn()
972
959
  metadata_type = tables.Column(linkify=True)
973
- assigned_object = tables.TemplateColumn(
974
- template_code=ASSIGNED_OBJECT, verbose_name="Assigned object", orderable=False
960
+ assigned_object = tables.TemplateColumn(template_code=ASSIGNED_OBJECT, orderable=False)
961
+ actions = ButtonsColumn(
962
+ ObjectMetadata,
963
+ buttons=("delete"),
975
964
  )
976
965
  # This is needed so that render_value method below does not skip itself
977
966
  # when metadata_type.data_type is TYPE_CONTACT_TEAM and we need it to display either contact or team
@@ -985,6 +974,7 @@ class ObjectMetadataTable(BaseTable):
985
974
  "metadata_type",
986
975
  "scoped_fields",
987
976
  "value",
977
+ "actions",
988
978
  )
989
979
  default_columns = (
990
980
  "pk",
@@ -992,12 +982,11 @@ class ObjectMetadataTable(BaseTable):
992
982
  "scoped_fields",
993
983
  "value",
994
984
  "metadata_type",
985
+ "actions",
995
986
  )
996
987
 
997
- def render_scoped_fields(self, value):
998
- if not value:
999
- return "(all fields)"
1000
- return format_html_join(", ", "<code>{}</code>", ([v] for v in sorted(value)))
988
+ def render_scoped_fields(self, record):
989
+ return render_json(record.scoped_fields, pretty_print=True)
1001
990
 
1002
991
  def render_value(self, record):
1003
992
  if record.value is not None and record.metadata_type.data_type == MetadataTypeDataTypeChoices.TYPE_JSON:
@@ -1119,15 +1108,7 @@ class RelationshipAssociationTable(BaseTable):
1119
1108
 
1120
1109
  class Meta(BaseTable.Meta):
1121
1110
  model = RelationshipAssociation
1122
- fields = (
1123
- "pk",
1124
- "relationship",
1125
- "source_type",
1126
- "source",
1127
- "destination_type",
1128
- "destination",
1129
- "actions",
1130
- )
1111
+ fields = ("pk", "relationship", "source_type", "source", "destination_type", "destination", "actions")
1131
1112
  default_columns = ("pk", "relationship", "source", "destination", "actions")
1132
1113
 
1133
1114
 
@@ -1243,15 +1224,7 @@ class TagTable(BaseTable):
1243
1224
 
1244
1225
  class Meta(BaseTable.Meta):
1245
1226
  model = Tag
1246
- fields = (
1247
- "pk",
1248
- "name",
1249
- "items",
1250
- "color",
1251
- "content_types",
1252
- "description",
1253
- "actions",
1254
- )
1227
+ fields = ("pk", "name", "items", "color", "content_types", "description", "actions")
1255
1228
 
1256
1229
 
1257
1230
  class TaggedItemTable(BaseTable):
@@ -1331,9 +1304,7 @@ class WebhookTable(BaseTable):
1331
1304
  class AssociatedContactsTable(StatusTableMixin, RoleTableMixin, BaseTable):
1332
1305
  pk = ToggleColumn()
1333
1306
  contact_type = tables.TemplateColumn(
1334
- CONTACT_OR_TEAM_ICON,
1335
- verbose_name="Type",
1336
- attrs={"td": {"style": "width:20px;"}},
1307
+ CONTACT_OR_TEAM_ICON, verbose_name="Type", attrs={"td": {"style": "width:20px;"}}
1337
1308
  )
1338
1309
  name = tables.TemplateColumn(CONTACT_OR_TEAM, verbose_name="Name")
1339
1310
  contact_or_team_phone = tables.TemplateColumn(PHONE, accessor="contact_or_team.phone", verbose_name="Phone")
@@ -115,17 +115,6 @@
115
115
  <td>{{ object.enabled | render_boolean }}</td>
116
116
  <td></td>
117
117
  </tr>
118
- <tr>
119
- <td>Job Results</td>
120
- <td>
121
- {% if object.job_results.exists %}
122
- <a href="{% url 'extras:jobresult_list' %}?job_model={{ object.name | urlencode }}">{{ object.job_results.count }}</a>
123
- {% else %}
124
- {{ None|placeholder }}
125
- {% endif %}
126
- </td>
127
- <td></td>
128
- </tr>
129
118
  </table>
130
119
  </div>
131
120
  {% endblock content_left_page %}
@@ -15,7 +15,7 @@ from rest_framework import status
15
15
  from nautobot.core.choices import ColorChoices
16
16
  from nautobot.core.models.fields import slugify_dashes_to_underscores
17
17
  from nautobot.core.testing import APITestCase, APIViewTestCases
18
- from nautobot.core.testing.utils import disable_warnings, get_deletable_objects
18
+ from nautobot.core.testing.utils import disable_warnings
19
19
  from nautobot.core.utils.lookup import get_route_for_model
20
20
  from nautobot.core.utils.permissions import get_permission_for_model
21
21
  from nautobot.dcim.models import (
@@ -2741,21 +2741,21 @@ class ObjectMetadataTest(APIViewTestCases.APIViewTestCase):
2741
2741
  value="Hey",
2742
2742
  scoped_fields=["parent", "status"],
2743
2743
  assigned_object_type=ContentType.objects.get_for_model(IPAddress),
2744
- assigned_object_id=IPAddress.objects.filter(associated_object_metadata__isnull=True).first().pk,
2744
+ assigned_object_id=IPAddress.objects.first().pk,
2745
2745
  )
2746
2746
  ObjectMetadata.objects.create(
2747
2747
  metadata_type=mdts[0],
2748
2748
  value="Hello",
2749
2749
  scoped_fields=["namespace"],
2750
2750
  assigned_object_type=ContentType.objects.get_for_model(Prefix),
2751
- assigned_object_id=Prefix.objects.filter(associated_object_metadata__isnull=True).first().pk,
2751
+ assigned_object_id=Prefix.objects.first().pk,
2752
2752
  )
2753
2753
  ObjectMetadata.objects.create(
2754
2754
  metadata_type=mdts[2],
2755
2755
  contact=Contact.objects.first(),
2756
2756
  scoped_fields=["status"],
2757
2757
  assigned_object_type=ContentType.objects.get_for_model(Prefix),
2758
- assigned_object_id=Prefix.objects.filter(associated_object_metadata__isnull=True).last().pk,
2758
+ assigned_object_id=Prefix.objects.last().pk,
2759
2759
  )
2760
2760
  cls.create_data = [
2761
2761
  {
@@ -2763,41 +2763,34 @@ class ObjectMetadataTest(APIViewTestCases.APIViewTestCase):
2763
2763
  "scoped_fields": ["location_type"],
2764
2764
  "value": "random words",
2765
2765
  "assigned_object_type": "dcim.location",
2766
- "assigned_object_id": Location.objects.filter(associated_object_metadata__isnull=True).first().pk,
2766
+ "assigned_object_id": Location.objects.first().pk,
2767
2767
  },
2768
2768
  {
2769
2769
  "metadata_type": mdts[1].pk,
2770
2770
  "scoped_fields": ["name"],
2771
2771
  "value": "random words",
2772
2772
  "assigned_object_type": "dcim.location",
2773
- "assigned_object_id": Location.objects.filter(associated_object_metadata__isnull=True).first().pk,
2773
+ "assigned_object_id": Location.objects.first().pk,
2774
2774
  },
2775
2775
  {
2776
2776
  "metadata_type": mdts[2].pk,
2777
- "scoped_fields": [],
2777
+ "scoped_fields": ["device_type"],
2778
2778
  "contact": Contact.objects.first().pk,
2779
2779
  "assigned_object_type": "dcim.device",
2780
- "assigned_object_id": Device.objects.filter(associated_object_metadata__isnull=True).first().pk,
2780
+ "assigned_object_id": Device.objects.first().pk,
2781
2781
  },
2782
2782
  {
2783
2783
  "metadata_type": mdts[2].pk,
2784
2784
  "scoped_fields": ["interfaces"],
2785
2785
  "team": Team.objects.first().pk,
2786
2786
  "assigned_object_type": "dcim.device",
2787
- "assigned_object_id": Device.objects.filter(associated_object_metadata__isnull=True).last().pk,
2787
+ "assigned_object_id": Device.objects.first().pk,
2788
2788
  },
2789
2789
  ]
2790
2790
  cls.update_data = {
2791
2791
  "scoped_fields": ["pk"],
2792
2792
  }
2793
2793
 
2794
- def get_deletable_object(self):
2795
- # TODO: CSV round-trip doesn't work for empty scoped_fields values at present. :-(
2796
- instance = get_deletable_objects(self.model, self._get_queryset().exclude(scoped_fields=[])).first()
2797
- if instance is None:
2798
- self.fail("Couldn't find a single deletable object with non-empty scoped_fields")
2799
- return instance
2800
-
2801
2794
 
2802
2795
  class NoteTest(APIViewTestCases.APIViewTestCase):
2803
2796
  model = Note