nautobot 2.3.0b1__py3-none-any.whl → 2.3.2__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 (399) hide show
  1. nautobot/cloud/factory.py +2 -0
  2. nautobot/cloud/filters.py +3 -0
  3. nautobot/cloud/forms.py +8 -2
  4. nautobot/cloud/migrations/0001_initial.py +1 -1
  5. nautobot/cloud/models.py +1 -2
  6. nautobot/cloud/tables.py +1 -17
  7. nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +1 -7
  8. nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +11 -0
  9. nautobot/cloud/templates/cloud/cloudservice_retrieve.html +4 -0
  10. nautobot/cloud/tests/test_filters.py +12 -0
  11. nautobot/cloud/tests/test_views.py +17 -0
  12. nautobot/cloud/views.py +1 -1
  13. nautobot/core/celery/__init__.py +5 -2
  14. nautobot/core/celery/schedulers.py +18 -0
  15. nautobot/core/filters.py +15 -1
  16. nautobot/core/forms/forms.py +10 -2
  17. nautobot/core/graphql/generators.py +2 -2
  18. nautobot/core/graphql/schema.py +6 -14
  19. nautobot/core/jobs/__init__.py +4 -1
  20. nautobot/core/management/commands/generate_test_data.py +2 -2
  21. nautobot/core/models/__init__.py +2 -2
  22. nautobot/core/settings.py +13 -2
  23. nautobot/core/settings.yaml +19 -5
  24. nautobot/core/tables.py +4 -1
  25. nautobot/core/templates/generic/object_retrieve.html +6 -6
  26. nautobot/core/templates/home.html +4 -3
  27. nautobot/core/templates/inc/computed_fields/panel_data.html +36 -24
  28. nautobot/core/templates/inc/object_details_advanced_panel.html +1 -1
  29. nautobot/core/templates/nautobot_config.py.j2 +15 -0
  30. nautobot/core/templatetags/buttons.py +1 -1
  31. nautobot/core/testing/filters.py +12 -1
  32. nautobot/core/tests/integration/test_general_functionality.py +1 -1
  33. nautobot/core/tests/test_jobs.py +74 -1
  34. nautobot/core/views/__init__.py +1 -1
  35. nautobot/core/views/generic.py +1 -1
  36. nautobot/core/views/mixins.py +1 -1
  37. nautobot/core/views/utils.py +11 -9
  38. nautobot/dcim/factory.py +7 -4
  39. nautobot/dcim/filters/__init__.py +4 -0
  40. nautobot/dcim/forms.py +24 -0
  41. nautobot/dcim/migrations/0061_module_models.py +1 -0
  42. nautobot/dcim/models/device_components.py +7 -0
  43. nautobot/dcim/models/devices.py +18 -19
  44. nautobot/dcim/models/racks.py +0 -1
  45. nautobot/dcim/tables/devices.py +24 -10
  46. nautobot/dcim/tables/devicetypes.py +1 -1
  47. nautobot/dcim/templates/dcim/device/base.html +1 -1
  48. nautobot/dcim/templates/dcim/device.html +15 -3
  49. nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +6 -0
  50. nautobot/dcim/templates/dcim/moduletype_retrieve.html +17 -0
  51. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +15 -3
  52. nautobot/dcim/tests/test_api.py +2 -0
  53. nautobot/dcim/tests/test_filters.py +14 -7
  54. nautobot/dcim/tests/test_forms.py +54 -0
  55. nautobot/dcim/tests/test_models.py +40 -1
  56. nautobot/dcim/tests/test_views.py +45 -2
  57. nautobot/dcim/utils.py +9 -6
  58. nautobot/dcim/views.py +4 -1
  59. nautobot/extras/api/serializers.py +2 -1
  60. nautobot/extras/api/views.py +7 -59
  61. nautobot/extras/factory.py +50 -12
  62. nautobot/extras/filters/__init__.py +18 -3
  63. nautobot/extras/forms/base.py +10 -4
  64. nautobot/extras/forms/forms.py +7 -0
  65. nautobot/extras/forms/mixins.py +2 -2
  66. nautobot/extras/homepage.py +12 -2
  67. nautobot/extras/jobs.py +2 -2
  68. nautobot/extras/management/__init__.py +3 -0
  69. nautobot/extras/migrations/0111_metadata.py +4 -4
  70. nautobot/extras/migrations/0114_computedfield_grouping.py +17 -0
  71. nautobot/extras/migrations/0115_scheduledjob_time_zone.py +23 -0
  72. nautobot/extras/models/customfields.py +54 -0
  73. nautobot/extras/models/jobs.py +105 -9
  74. nautobot/extras/models/metadata.py +18 -18
  75. nautobot/extras/models/models.py +2 -0
  76. nautobot/extras/signals.py +14 -1
  77. nautobot/extras/tables.py +77 -18
  78. nautobot/extras/templates/extras/computedfield.html +4 -0
  79. nautobot/extras/templates/extras/job_detail.html +11 -0
  80. nautobot/extras/templates/extras/scheduledjob.html +13 -2
  81. nautobot/extras/tests/test_api.py +33 -27
  82. nautobot/extras/tests/test_filters.py +57 -1
  83. nautobot/extras/tests/test_jobs.py +2 -2
  84. nautobot/extras/tests/test_models.py +319 -19
  85. nautobot/extras/tests/test_views.py +26 -5
  86. nautobot/extras/utils.py +35 -6
  87. nautobot/extras/views.py +35 -51
  88. nautobot/ipam/api/views.py +9 -2
  89. nautobot/ipam/choices.py +17 -0
  90. nautobot/ipam/factory.py +6 -0
  91. nautobot/ipam/filters.py +2 -2
  92. nautobot/ipam/forms.py +6 -4
  93. nautobot/ipam/migrations/0048_vrf_status.py +23 -0
  94. nautobot/ipam/migrations/0049_vrf_data_migration.py +25 -0
  95. nautobot/ipam/models.py +11 -20
  96. nautobot/ipam/querysets.py +26 -0
  97. nautobot/ipam/tables.py +7 -2
  98. nautobot/ipam/templates/ipam/vrf.html +4 -0
  99. nautobot/ipam/templates/ipam/vrf_edit.html +1 -0
  100. nautobot/ipam/tests/test_api.py +33 -3
  101. nautobot/ipam/tests/test_models.py +89 -2
  102. nautobot/ipam/tests/test_views.py +3 -0
  103. nautobot/ipam/views.py +10 -15
  104. nautobot/project-static/css/base.css +7 -0
  105. nautobot/project-static/docs/404.html +18 -18
  106. nautobot/project-static/docs/apps/index.html +18 -18
  107. nautobot/project-static/docs/apps/nautobot-apps.html +18 -18
  108. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css +1 -0
  109. nautobot/project-static/docs/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
  110. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +18 -18
  111. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +18 -18
  112. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +66 -18
  113. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +18 -18
  114. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +18 -18
  115. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +18 -18
  116. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +18 -18
  117. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +18 -18
  118. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +66 -18
  119. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +34 -18
  120. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +82 -63
  121. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +75 -111
  122. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +18 -18
  123. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +34 -18
  124. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +161 -18
  125. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +18 -18
  126. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +18 -18
  127. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +18 -18
  128. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +18 -18
  129. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +18 -18
  130. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +18 -18
  131. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +21 -19
  132. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +34 -18
  133. nautobot/project-static/docs/development/apps/api/configuration-view.html +18 -18
  134. nautobot/project-static/docs/development/apps/api/database-backend-config.html +18 -18
  135. nautobot/project-static/docs/development/apps/api/models/django-admin.html +18 -18
  136. nautobot/project-static/docs/development/apps/api/models/global-search.html +18 -18
  137. nautobot/project-static/docs/development/apps/api/models/graphql.html +18 -18
  138. nautobot/project-static/docs/development/apps/api/models/index.html +33 -22
  139. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +18 -18
  140. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +18 -18
  141. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +18 -18
  142. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +18 -18
  143. nautobot/project-static/docs/development/apps/api/platform-features/index.html +18 -18
  144. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +18 -18
  145. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +18 -18
  146. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +18 -18
  147. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +18 -18
  148. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +18 -18
  149. nautobot/project-static/docs/development/apps/api/prometheus.html +18 -18
  150. nautobot/project-static/docs/development/apps/api/setup.html +18 -18
  151. nautobot/project-static/docs/development/apps/api/testing.html +18 -18
  152. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +18 -18
  153. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +18 -18
  154. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +18 -18
  155. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +18 -18
  156. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +18 -18
  157. nautobot/project-static/docs/development/apps/api/views/base-template.html +18 -18
  158. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +18 -18
  159. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +18 -18
  160. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +18 -18
  161. nautobot/project-static/docs/development/apps/api/views/index.html +18 -18
  162. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +18 -18
  163. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +18 -18
  164. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +18 -18
  165. nautobot/project-static/docs/development/apps/api/views/notes.html +18 -18
  166. nautobot/project-static/docs/development/apps/api/views/rest-api.html +18 -18
  167. nautobot/project-static/docs/development/apps/api/views/urls.html +18 -18
  168. nautobot/project-static/docs/development/apps/index.html +18 -18
  169. nautobot/project-static/docs/development/apps/migration/code-updates.html +18 -18
  170. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +18 -18
  171. nautobot/project-static/docs/development/apps/migration/from-v1.html +18 -18
  172. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +18 -18
  173. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +18 -18
  174. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +18 -18
  175. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +18 -18
  176. nautobot/project-static/docs/development/apps/porting-from-netbox.html +18 -18
  177. nautobot/project-static/docs/development/core/application-registry.html +18 -18
  178. nautobot/project-static/docs/development/core/best-practices.html +18 -18
  179. nautobot/project-static/docs/development/core/bootstrap-ui.html +18 -18
  180. nautobot/project-static/docs/development/core/caching.html +18 -18
  181. nautobot/project-static/docs/development/core/controllers.html +18 -18
  182. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +18 -18
  183. nautobot/project-static/docs/development/core/generic-views.html +18 -18
  184. nautobot/project-static/docs/development/core/getting-started.html +18 -18
  185. nautobot/project-static/docs/development/core/homepage.html +18 -18
  186. nautobot/project-static/docs/development/core/index.html +29 -18
  187. nautobot/project-static/docs/development/core/model-checklist.html +26 -20
  188. nautobot/project-static/docs/development/core/model-features.html +18 -18
  189. nautobot/project-static/docs/development/core/natural-keys.html +18 -18
  190. nautobot/project-static/docs/development/core/navigation-menu.html +18 -18
  191. nautobot/project-static/docs/development/core/release-checklist.html +18 -18
  192. nautobot/project-static/docs/development/core/role-internals.html +18 -18
  193. nautobot/project-static/docs/development/core/settings.html +18 -18
  194. nautobot/project-static/docs/development/core/style-guide.html +19 -19
  195. nautobot/project-static/docs/development/core/templates.html +18 -18
  196. nautobot/project-static/docs/development/core/testing.html +18 -18
  197. nautobot/project-static/docs/development/core/user-preferences.html +18 -18
  198. nautobot/project-static/docs/development/index.html +18 -18
  199. nautobot/project-static/docs/development/jobs/index.html +393 -379
  200. nautobot/project-static/docs/development/jobs/migration/from-v1.html +18 -18
  201. nautobot/project-static/docs/index.html +9032 -13
  202. nautobot/project-static/docs/models/extras/metadatachoice.html +3 -3
  203. nautobot/project-static/docs/models/extras/metadatatype.html +3 -3
  204. nautobot/project-static/docs/models/extras/objectmetadata.html +3 -3
  205. nautobot/project-static/docs/objects.inv +0 -0
  206. nautobot/project-static/docs/overview/application_stack.html +18 -18
  207. nautobot/project-static/docs/overview/design_philosophy.html +20 -20
  208. nautobot/project-static/docs/overview/index.html +13 -9032
  209. nautobot/project-static/docs/release-notes/index.html +252 -19
  210. nautobot/project-static/docs/release-notes/version-1.0.html +18 -18
  211. nautobot/project-static/docs/release-notes/version-1.1.html +18 -18
  212. nautobot/project-static/docs/release-notes/version-1.2.html +18 -18
  213. nautobot/project-static/docs/release-notes/version-1.3.html +18 -18
  214. nautobot/project-static/docs/release-notes/version-1.4.html +18 -18
  215. nautobot/project-static/docs/release-notes/version-1.5.html +18 -18
  216. nautobot/project-static/docs/release-notes/version-1.6.html +18 -18
  217. nautobot/project-static/docs/release-notes/version-2.0.html +18 -18
  218. nautobot/project-static/docs/release-notes/version-2.1.html +18 -18
  219. nautobot/project-static/docs/release-notes/version-2.2.html +248 -111
  220. nautobot/project-static/docs/release-notes/version-2.3.html +775 -91
  221. nautobot/project-static/docs/requirements.txt +3 -3
  222. nautobot/project-static/docs/search/search_index.json +1 -1
  223. nautobot/project-static/docs/sitemap.xml +278 -278
  224. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  225. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +18 -18
  226. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +18 -18
  227. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +18 -18
  228. nautobot/project-static/docs/user-guide/administration/configuration/index.html +18 -18
  229. nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +55 -23
  230. nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +18 -18
  231. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +18 -18
  232. nautobot/project-static/docs/user-guide/administration/guides/caching.html +18 -18
  233. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +22 -18
  234. nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +18 -18
  235. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +18 -18
  236. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +18 -18
  237. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +18 -18
  238. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +18 -18
  239. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +18 -18
  240. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +18 -18
  241. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +18 -18
  242. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +69 -82
  243. nautobot/project-static/docs/user-guide/administration/installation/index.html +24 -24
  244. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +60 -52
  245. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +80 -87
  246. nautobot/project-static/docs/user-guide/administration/installation/services.html +37 -44
  247. nautobot/project-static/docs/user-guide/administration/installation-extras/docker.html +18 -18
  248. nautobot/project-static/docs/user-guide/administration/installation-extras/health-checks.html +18 -18
  249. nautobot/project-static/docs/user-guide/administration/installation-extras/selinux-troubleshooting.html +18 -18
  250. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +18 -18
  251. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +18 -18
  252. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +76 -24
  253. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +18 -18
  254. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +18 -18
  255. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +18 -18
  256. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +18 -18
  257. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +18 -18
  258. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +18 -18
  259. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +18 -18
  260. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +18 -18
  261. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +18 -18
  262. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +18 -18
  263. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +18 -18
  264. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +18 -18
  265. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +18 -18
  266. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +18 -18
  267. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +18 -18
  268. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +18 -18
  269. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +18 -18
  270. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +18 -18
  271. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +18 -18
  272. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +18 -18
  273. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +18 -18
  274. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +18 -18
  275. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +18 -18
  276. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +18 -18
  277. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +18 -18
  278. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +18 -18
  279. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +18 -18
  280. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +18 -18
  281. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +18 -18
  282. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +19 -19
  283. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +18 -18
  284. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +18 -18
  285. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +18 -18
  286. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +18 -18
  287. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +18 -18
  288. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +18 -18
  289. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +18 -18
  290. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +18 -18
  291. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +18 -18
  292. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +18 -18
  293. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +18 -18
  294. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +18 -18
  295. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +18 -18
  296. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +19 -19
  297. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +18 -18
  298. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +18 -18
  299. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +18 -18
  300. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +18 -18
  301. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +18 -18
  302. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +18 -18
  303. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +18 -18
  304. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +18 -18
  305. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +18 -18
  306. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +18 -18
  307. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +18 -18
  308. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +18 -18
  309. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +18 -18
  310. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +18 -18
  311. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +18 -18
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +18 -18
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +18 -18
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +18 -18
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +18 -18
  316. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +62 -18
  317. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +18 -18
  318. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +18 -18
  319. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +18 -18
  320. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +18 -18
  321. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +18 -18
  322. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +18 -18
  323. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +18 -18
  324. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +18 -18
  325. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +18 -18
  326. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +18 -18
  327. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +18 -18
  328. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +18 -18
  329. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +18 -18
  330. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +18 -18
  331. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +18 -18
  332. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +18 -18
  333. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +18 -18
  334. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +18 -18
  335. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +18 -18
  336. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +18 -18
  337. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +18 -18
  338. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +18 -18
  339. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +18 -18
  340. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +18 -18
  341. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +18 -18
  342. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +18 -18
  343. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +18 -18
  344. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +18 -18
  345. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +18 -18
  346. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +18 -18
  347. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +18 -18
  348. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +18 -18
  349. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +18 -18
  350. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +18 -18
  351. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +18 -18
  352. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +18 -18
  353. nautobot/project-static/docs/user-guide/index.html +18 -18
  354. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +18 -18
  355. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +18 -18
  356. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +18 -18
  357. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +18 -18
  358. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +18 -18
  359. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +18 -18
  360. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +18 -18
  361. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +18 -18
  362. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +18 -18
  363. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +18 -18
  364. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +18 -18
  365. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +18 -18
  366. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +21 -21
  367. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +18 -18
  368. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +18 -18
  369. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +18 -18
  370. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +36 -36
  371. nautobot/project-static/docs/user-guide/platform-functionality/note.html +33 -33
  372. nautobot/project-static/docs/user-guide/platform-functionality/{metadata.html → objectmetadata.html} +197 -84
  373. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +21 -21
  374. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +18 -18
  375. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +18 -18
  376. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +18 -18
  377. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +18 -18
  378. nautobot/project-static/docs/user-guide/platform-functionality/role.html +18 -18
  379. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +18 -18
  380. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +18 -18
  381. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +18 -18
  382. nautobot/project-static/docs/user-guide/platform-functionality/status.html +18 -18
  383. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +18 -18
  384. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +18 -18
  385. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +18 -18
  386. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +18 -18
  387. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +18 -18
  388. nautobot/project-static/js/homepage_layout.js +3 -0
  389. nautobot/tenancy/templates/tenancy/tenant.html +4 -4
  390. nautobot/virtualization/models.py +0 -2
  391. nautobot/virtualization/tables.py +2 -5
  392. {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/METADATA +3 -3
  393. {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/RECORD +397 -393
  394. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css +0 -1
  395. nautobot/project-static/docs/assets/stylesheets/main.76a95c52.min.css.map +0 -1
  396. {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/LICENSE.txt +0 -0
  397. {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/NOTICE +0 -0
  398. {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/WHEEL +0 -0
  399. {nautobot-2.3.0b1.dist-info → nautobot-2.3.2.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,3 @@
1
- from datetime import timedelta
2
-
3
1
  from django.conf import settings
4
2
  from django.contrib.contenttypes.models import ContentType
5
3
  from django.forms import ValidationError as FormsValidationError
@@ -492,59 +490,6 @@ class ImageAttachmentViewSet(ModelViewSet):
492
490
  #
493
491
 
494
492
 
495
- def _create_schedule(serializer, data, job_model, user, approval_required, task_queue=None):
496
- """
497
- This is an internal function to create a scheduled job from API data.
498
- It has to handle both once-offs (i.e. of type TYPE_FUTURE) and interval
499
- jobs.
500
- """
501
- type_ = serializer["interval"]
502
- if type_ == JobExecutionType.TYPE_IMMEDIATELY:
503
- time = timezone.now()
504
- name = serializer.get("name") or f"{job_model.name} - {time}"
505
- elif type_ == JobExecutionType.TYPE_CUSTOM:
506
- time = serializer.get("start_time") # doing .get("key", "default") returns None instead of "default"
507
- if time is None:
508
- # "start_time" is checked against models.ScheduledJob.earliest_possible_time()
509
- # which returns timezone.now() + timedelta(seconds=15)
510
- time = timezone.now() + timedelta(seconds=20)
511
- name = serializer["name"]
512
- else:
513
- time = serializer["start_time"]
514
- name = serializer["name"]
515
- crontab = serializer.get("crontab", "")
516
-
517
- celery_kwargs = {
518
- "nautobot_job_profile": False,
519
- "queue": task_queue,
520
- }
521
-
522
- # 2.0 TODO: To revisit this as part of a larger Jobs cleanup in 2.0.
523
- #
524
- # We pass in task and job_model here partly for forward/backward compatibility logic, and
525
- # part fallback safety. It's mildly useful to store both the task module/class name and the JobModel
526
- # FK on the ScheduledJob, as in the case where the JobModel gets deleted (and the FK becomes
527
- # null) you still have a bit of context on the ScheduledJob as to what it was originally
528
- # scheduled for.
529
- scheduled_job = ScheduledJob(
530
- name=name,
531
- task=job_model.class_path,
532
- job_model=job_model,
533
- start_time=time,
534
- description=f"Nautobot job {name} scheduled by {user} for {time}",
535
- kwargs=data,
536
- celery_kwargs=celery_kwargs,
537
- interval=type_,
538
- one_off=(type_ == JobExecutionType.TYPE_FUTURE),
539
- user=user,
540
- approval_required=approval_required,
541
- crontab=crontab,
542
- queue=task_queue,
543
- )
544
- scheduled_job.validated_save()
545
- return scheduled_job
546
-
547
-
548
493
  class JobViewSetBase(
549
494
  NautobotAPIVersionMixin,
550
495
  # note no CreateModelMixin
@@ -744,13 +689,16 @@ class JobViewSetBase(
744
689
 
745
690
  # Try to create a ScheduledJob, or...
746
691
  if schedule_data:
747
- schedule = _create_schedule(
748
- schedule_data,
749
- job_class.serialize_data(cleaned_data),
692
+ schedule = ScheduledJob.create_schedule(
750
693
  job_model,
751
694
  request.user,
752
- approval_required,
695
+ name=schedule_data.get("name"),
696
+ start_time=schedule_data.get("start_time"),
697
+ interval=schedule_data.get("interval"),
698
+ crontab=schedule_data.get("crontab", ""),
699
+ approval_required=approval_required,
753
700
  task_queue=input_serializer.validated_data.get("task_queue", None),
701
+ **job_class.serialize_data(cleaned_data),
754
702
  )
755
703
  else:
756
704
  schedule = None
@@ -251,6 +251,24 @@ 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
+
254
272
  class ObjectMetadataFactory(BaseModelFactory):
255
273
  """ObjectMetadata model factory"""
256
274
 
@@ -263,7 +281,6 @@ class ObjectMetadataFactory(BaseModelFactory):
263
281
  MetadataType.objects.all(),
264
282
  allow_null=False,
265
283
  )
266
- scoped_fields = factory.Faker("pylist", allowed_types=[str])
267
284
 
268
285
  @factory.lazy_attribute
269
286
  def contact(self):
@@ -308,22 +325,43 @@ class ObjectMetadataFactory(BaseModelFactory):
308
325
  raise RuntimeError(f"Unsupported metadatatype datatype {metadata_type_data_type}")
309
326
 
310
327
  @factory.lazy_attribute
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
- )
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)):
317
331
  # It does not have a get_absolute_url attribute and is causing failure in API unittests
318
332
  if content_type.app_label == "extras" and content_type.model == "taggeditem":
319
333
  continue
320
- if content_type.model_class().objects.exists():
321
- return content_type
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}")
322
347
 
323
348
  @factory.lazy_attribute
324
- def assigned_object_id(self):
325
- queryset = self.assigned_object_type.model_class().objects.all()
326
- return factory.random.randgen.choice(queryset).pk
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
+ )
327
365
 
328
366
 
329
367
  class ObjectChangeFactory(BaseModelFactory):
@@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
6
6
  from django.db.models import Q
7
7
  import django_filters
8
8
  from drf_spectacular.utils import extend_schema_field
9
+ from timezone_field import TimeZoneField
9
10
 
10
11
  from nautobot.core.api.exceptions import SerializerNotFound
11
12
  from nautobot.core.api.utils import get_serializer_for_model
@@ -181,6 +182,7 @@ class ComputedFieldFilterSet(BaseFilterSet):
181
182
  "description": "icontains",
182
183
  "content_type__app_label": "icontains",
183
184
  "content_type__model": "icontains",
185
+ "grouping": "icontains",
184
186
  "template": "icontains",
185
187
  "fallback_value": "icontains",
186
188
  },
@@ -192,6 +194,7 @@ class ComputedFieldFilterSet(BaseFilterSet):
192
194
  fields = (
193
195
  "content_type",
194
196
  "key",
197
+ "grouping",
195
198
  "template",
196
199
  "fallback_value",
197
200
  "weight",
@@ -422,6 +425,7 @@ class CustomFieldFilterSet(BaseFilterSet):
422
425
  filter_predicates={
423
426
  "label": "icontains",
424
427
  "description": "icontains",
428
+ "grouping": "icontains",
425
429
  },
426
430
  )
427
431
  content_types = ContentTypeMultipleChoiceFilter(
@@ -430,7 +434,7 @@ class CustomFieldFilterSet(BaseFilterSet):
430
434
 
431
435
  class Meta:
432
436
  model = CustomField
433
- fields = ["id", "content_types", "label", "required", "filter_logic", "weight"]
437
+ fields = ["id", "content_types", "label", "grouping", "required", "filter_logic", "weight"]
434
438
 
435
439
 
436
440
  class CustomFieldChoiceFilterSet(BaseFilterSet):
@@ -908,6 +912,7 @@ class JobResultFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
908
912
  "job_model__name": "icontains",
909
913
  "name": "icontains",
910
914
  "user__username": "icontains",
915
+ "scheduled_job__name": "icontains",
911
916
  },
912
917
  )
913
918
  job_model = NaturalKeyOrPKMultipleChoiceFilter(
@@ -919,11 +924,16 @@ class JobResultFilterSet(BaseFilterSet, CustomFieldModelFilterSetMixin):
919
924
  queryset=Job.objects.all(),
920
925
  label="Job (ID) - Deprecated (use job_model filter)",
921
926
  )
927
+ scheduled_job = NaturalKeyOrPKMultipleChoiceFilter(
928
+ to_field_name="name",
929
+ queryset=ScheduledJob.objects.all(),
930
+ label="Scheduled Job (name or ID)",
931
+ )
922
932
  status = django_filters.MultipleChoiceFilter(choices=JobResultStatusChoices, null_value=None)
923
933
 
924
934
  class Meta:
925
935
  model = JobResult
926
- fields = ["id", "date_created", "date_done", "name", "status", "user"]
936
+ fields = ["id", "date_created", "date_done", "name", "status", "user", "scheduled_job"]
927
937
 
928
938
 
929
939
  class JobLogEntryFilterSet(BaseFilterSet):
@@ -957,10 +967,15 @@ class ScheduledJobFilterSet(BaseFilterSet):
957
967
  queryset=Job.objects.all(),
958
968
  label="Job (ID) - Deprecated (use job_model filter)",
959
969
  )
970
+ time_zone = django_filters.MultipleChoiceFilter(
971
+ choices=[(str(obj), name) for obj, name in TimeZoneField().choices],
972
+ label="Time zone",
973
+ null_value="",
974
+ )
960
975
 
961
976
  class Meta:
962
977
  model = ScheduledJob
963
- fields = ["id", "name", "total_run_count", "start_time", "last_run_at"]
978
+ fields = ["id", "name", "total_run_count", "start_time", "last_run_at", "time_zone"]
964
979
 
965
980
 
966
981
  #
@@ -26,11 +26,12 @@ __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
29
31
  CustomFieldModelFormMixin,
30
32
  DynamicGroupModelFormMixin,
31
33
  NoteModelFormMixin,
32
34
  RelationshipModelFormMixin,
33
- BootstrapMixin,
34
35
  ):
35
36
  """
36
37
  This class exists to combine common functionality and is used to inherit from throughout the
@@ -40,9 +41,10 @@ class NautobotModelForm(
40
41
 
41
42
 
42
43
  class NautobotFilterForm(
43
- ContactTeamModelFilterFormMixin,
44
44
  BootstrapMixin,
45
- CustomFieldModelFilterFormMixin, # currently must come *after* BootstrapMixin to get proper CSS classes applied
45
+ # The below must be listed *after* BootstrapMixin so that BootstrapMixin applies to their dynamic form fields
46
+ ContactTeamModelFilterFormMixin,
47
+ CustomFieldModelFilterFormMixin,
46
48
  RelationshipModelFilterFormMixin,
47
49
  ):
48
50
  """
@@ -53,6 +55,10 @@ class NautobotFilterForm(
53
55
 
54
56
 
55
57
  class NautobotBulkEditForm(
56
- BootstrapMixin, CustomFieldModelBulkEditFormMixin, RelationshipModelBulkEditFormMixin, NoteModelBulkEditFormMixin
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,
57
63
  ):
58
64
  """Base class for bulk-edit forms for models that support relationships, custom fields and notes."""
@@ -219,6 +219,7 @@ class ComputedFieldForm(BootstrapMixin, forms.ModelForm):
219
219
  fields = (
220
220
  "content_type",
221
221
  "label",
222
+ "grouping",
222
223
  "key",
223
224
  "description",
224
225
  "template",
@@ -1285,6 +1286,12 @@ class JobResultFilterForm(BootstrapMixin, forms.Form):
1285
1286
  required=False,
1286
1287
  widget=StaticSelect2Multiple(),
1287
1288
  )
1289
+ scheduled_job = DynamicModelMultipleChoiceField(
1290
+ label="Scheduled Job",
1291
+ queryset=ScheduledJob.objects.all(),
1292
+ required=False,
1293
+ to_field_name="name",
1294
+ )
1288
1295
 
1289
1296
 
1290
1297
  class ScheduledJobFilterForm(BootstrapMixin, forms.Form):
@@ -174,7 +174,7 @@ class DynamicGroupModelFormMixin(forms.ModelForm):
174
174
 
175
175
  def __init__(self, *args, **kwargs):
176
176
  super().__init__(*args, **kwargs)
177
- if self._meta.model.is_dynamic_group_associable_model:
177
+ if getattr(self._meta.model, "is_dynamic_group_associable_model", False):
178
178
  self.fields["dynamic_groups"] = DynamicModelMultipleChoiceField(
179
179
  required=False,
180
180
  initial=self.instance.dynamic_groups if self.instance else None,
@@ -193,7 +193,7 @@ class DynamicGroupModelFormMixin(forms.ModelForm):
193
193
 
194
194
  def save(self, commit=True):
195
195
  obj = super().save(commit=commit)
196
- if commit and obj.is_dynamic_group_associable_model:
196
+ if commit and getattr(obj, "is_dynamic_group_associable_model", False):
197
197
  current_groups = set(obj.dynamic_groups.filter(group_type=DynamicGroupTypeChoices.TYPE_STATIC))
198
198
  for dynamic_group in set(self.cleaned_data.get("dynamic_groups")).difference(current_groups):
199
199
  dynamic_group.add_members([obj])
@@ -7,14 +7,24 @@ 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
- .defer("result")
10
+ .restrict(request.user, "view")
11
+ .only("id", "name", "status", "date_done", "user")
11
12
  .order_by("-date_done")[:10]
12
13
  )
13
14
 
14
15
 
15
16
  def get_changelog(request):
16
17
  """Callback function to collect changelog for panel."""
17
- return ObjectChange.objects.restrict(request.user, "view")[:15]
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]
18
28
 
19
29
 
20
30
  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 InMemoryUploadedFile
23
+ from django.core.files.uploadedfile import UploadedFile
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, InMemoryUploadedFile):
542
+ elif isinstance(value, UploadedFile):
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):
@@ -32,6 +32,7 @@ STATUS_CHOICESET_MAP = {
32
32
  "ipam.IPAddress": ipam_choices.IPAddressStatusChoices,
33
33
  "ipam.Prefix": ipam_choices.PrefixStatusChoices,
34
34
  "ipam.VLAN": ipam_choices.VLANStatusChoices,
35
+ "ipam.VRF": ipam_choices.VRFStatusChoices,
35
36
  "virtualization.VirtualMachine": vm_choices.VirtualMachineStatusChoices,
36
37
  "virtualization.VMInterface": vm_choices.VMInterfaceStatusChoices,
37
38
  }
@@ -48,6 +49,7 @@ STATUS_COLOR_MAP = {
48
49
  "Decommissioning": ColorChoices.COLOR_AMBER,
49
50
  "Deprecated": ColorChoices.COLOR_RED,
50
51
  "Deprovisioning": ColorChoices.COLOR_AMBER,
52
+ "Down": ColorChoices.COLOR_AMBER,
51
53
  "End-of-Life": ColorChoices.COLOR_RED,
52
54
  "Extended Support": ColorChoices.COLOR_CYAN,
53
55
  "Failed": ColorChoices.COLOR_RED,
@@ -76,6 +78,7 @@ STATUS_DESCRIPTION_MAP = {
76
78
  "Decommissioning": "Unit is being decommissioned",
77
79
  "Deprecated": "Unit has been deprecated",
78
80
  "Deprovisioning": "Circuit is being deprovisioned",
81
+ "Down": "VRF is down",
79
82
  "End-of-Life": "Unit has reached end-of-life",
80
83
  "Extended Support": "Software is in extended support",
81
84
  "Failed": "Unit has failed",
@@ -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)),
75
+ nautobot.core.models.fields.JSONArrayField(base_field=models.CharField(max_length=255), blank=True),
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_metadatas",
94
+ related_name="object_metadata",
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_metadatas",
102
+ related_name="object_metadata",
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_metadatas",
112
+ related_name="object_metadata",
113
113
  to="extras.team",
114
114
  ),
115
115
  ),
@@ -0,0 +1,17 @@
1
+ # Generated by Django 4.2.15 on 2024-08-09 14:28
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("extras", "0113_saved_views"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AddField(
13
+ model_name="computedfield",
14
+ name="grouping",
15
+ field=models.CharField(blank=True, max_length=255),
16
+ ),
17
+ ]
@@ -0,0 +1,23 @@
1
+ # Generated by Django 4.2.15 on 2024-08-19 13:44
2
+
3
+ from django.db import migrations
4
+ from django.utils import timezone
5
+ import timezone_field.fields
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+ dependencies = [
10
+ ("extras", "0114_computedfield_grouping"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name="scheduledjob",
16
+ name="time_zone",
17
+ field=timezone_field.fields.TimeZoneField(default=timezone.get_default_timezone_name),
18
+ ),
19
+ migrations.AlterModelOptions(
20
+ name="scheduledjob",
21
+ options={"ordering": ["name"]},
22
+ ),
23
+ ]
@@ -88,6 +88,11 @@ class ComputedField(
88
88
  help_text="Internal field name. Please use underscores rather than dashes in this key.",
89
89
  slugify_function=slugify_dashes_to_underscores,
90
90
  )
91
+ grouping = models.CharField(
92
+ max_length=CHARFIELD_MAX_LENGTH,
93
+ blank=True,
94
+ help_text="Human-readable grouping that this computed field belongs to.",
95
+ )
91
96
  label = models.CharField(max_length=CHARFIELD_MAX_LENGTH, help_text="Name of the field as displayed to users")
92
97
  description = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True)
93
98
  template = models.TextField(max_length=500, help_text="Jinja2 template code for field value")
@@ -295,6 +300,55 @@ class CustomFieldModel(models.Model):
295
300
  return computed_field.render(context={"obj": self})
296
301
  return computed_field.template
297
302
 
303
+ def get_computed_fields_grouping_basic(self):
304
+ """
305
+ This method exists to help call get_computed_field_groupings() in templates where a function argument (advanced_ui) cannot be specified.
306
+ Return a dictonary of computed fields grouped by the same grouping in the form
307
+ {
308
+ <grouping_1>: [(cf1, <value for cf1>), (cf2, <value for cf2>), ...],
309
+ ...
310
+ <grouping_5>: [(cf8, <value for cf8>), (cf9, <value for cf9>), ...],
311
+ ...
312
+ }
313
+ which have advanced_ui set to False
314
+ """
315
+ return self.get_computed_fields_grouping(advanced_ui=False)
316
+
317
+ def get_computed_fields_grouping_advanced(self):
318
+ """
319
+ This method exists to help call get_computed_field_groupings() in templates where a function argument (advanced_ui) cannot be specified.
320
+ Return a dictonary of computed fields grouped by the same grouping in the form
321
+ {
322
+ <grouping_1>: [(cf1, <value for cf1>), (cf2, <value for cf2>), ...],
323
+ ...
324
+ <grouping_5>: [(cf8, <value for cf8>), (cf9, <value for cf9>), ...],
325
+ ...
326
+ }
327
+ which have advanced_ui set to True
328
+ """
329
+ return self.get_computed_fields_grouping(advanced_ui=True)
330
+
331
+ def get_computed_fields_grouping(self, advanced_ui=None):
332
+ """
333
+ Return a dictonary of computed fields grouped by the same grouping in the form
334
+ {
335
+ <grouping_1>: [(cf1, <value for cf1>), (cf2, <value for cf2>), ...],
336
+ ...
337
+ <grouping_5>: [(cf8, <value for cf8>), (cf9, <value for cf9>), ...],
338
+ ...
339
+ }
340
+ """
341
+ record = {}
342
+ computed_fields = ComputedField.objects.get_for_model(self)
343
+ if advanced_ui is not None:
344
+ computed_fields = computed_fields.filter(advanced_ui=advanced_ui)
345
+
346
+ for field in computed_fields:
347
+ data = (field, field.render(context={"obj": self}))
348
+ record.setdefault(field.grouping, []).append(data)
349
+ record = dict(sorted(record.items()))
350
+ return record
351
+
298
352
  def get_computed_fields(self, label_as_key=False, advanced_ui=None):
299
353
  """
300
354
  Return a dictionary of all computed fields and their rendered values for this model.