nautobot 2.4.1__py3-none-any.whl → 2.4.3__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 (461) hide show
  1. nautobot/circuits/templates/circuits/inc/circuit_termination.html +1 -1
  2. nautobot/circuits/tests/integration/test_circuit.py +135 -0
  3. nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +43 -0
  4. nautobot/circuits/tests/integration/test_relationships.py +1 -1
  5. nautobot/circuits/views.py +4 -1
  6. nautobot/cloud/api/views.py +3 -3
  7. nautobot/core/apps/__init__.py +0 -5
  8. nautobot/core/constants.py +0 -1
  9. nautobot/core/forms/__init__.py +2 -0
  10. nautobot/core/forms/forms.py +2 -1
  11. nautobot/core/forms/widgets.py +8 -0
  12. nautobot/core/management/commands/generate_performance_test_endpoints.py +268 -0
  13. nautobot/core/templates/generic/object_bulk_delete.html +1 -1
  14. nautobot/core/templates/generic/object_bulk_destroy.html +1 -1
  15. nautobot/core/templates/generic/object_bulk_edit.html +1 -1
  16. nautobot/core/templates/generic/object_bulk_import.html +1 -1
  17. nautobot/core/templates/generic/object_create.html +5 -0
  18. nautobot/core/templates/generic/object_delete.html +1 -1
  19. nautobot/core/templates/generic/object_detail.html +1 -1
  20. nautobot/core/templates/generic/object_edit.html +1 -1
  21. nautobot/core/templates/inc/javascript.html +2 -0
  22. nautobot/core/templates/widgets/clearable_file.html +5 -0
  23. nautobot/core/templatetags/helpers.py +3 -3
  24. nautobot/core/testing/integration.py +469 -12
  25. nautobot/core/tests/test_commands.py +31 -0
  26. nautobot/core/tests/test_jobs.py +34 -2
  27. nautobot/core/tests/test_utils.py +17 -2
  28. nautobot/core/utils/git.py +7 -2
  29. nautobot/core/utils/lookup.py +12 -1
  30. nautobot/core/views/generic.py +10 -2
  31. nautobot/core/views/mixins.py +22 -7
  32. nautobot/core/views/utils.py +2 -2
  33. nautobot/dcim/api/views.py +11 -10
  34. nautobot/dcim/forms.py +15 -6
  35. nautobot/dcim/models/devices.py +1 -2
  36. nautobot/dcim/tables/devices.py +2 -1
  37. nautobot/dcim/templates/dcim/cable.html +1 -1
  38. nautobot/dcim/templates/dcim/cable_trace.html +4 -4
  39. nautobot/dcim/templates/dcim/consoleport.html +14 -4
  40. nautobot/dcim/templates/dcim/consoleserverport.html +14 -4
  41. nautobot/dcim/templates/dcim/device/base.html +1 -1
  42. nautobot/dcim/templates/dcim/device/lldp_neighbors.html +3 -3
  43. nautobot/dcim/templates/dcim/device.html +2 -2
  44. nautobot/dcim/templates/dcim/device_component.html +1 -1
  45. nautobot/dcim/templates/dcim/devicetype.html +1 -1
  46. nautobot/dcim/templates/dcim/frontport.html +7 -2
  47. nautobot/dcim/templates/dcim/interface.html +9 -4
  48. nautobot/dcim/templates/dcim/location.html +1 -1
  49. nautobot/dcim/templates/dcim/locationtype.html +1 -1
  50. nautobot/dcim/templates/dcim/locationtype_retrieve.html +1 -1
  51. nautobot/dcim/templates/dcim/manufacturer.html +1 -1
  52. nautobot/dcim/templates/dcim/platform.html +1 -1
  53. nautobot/dcim/templates/dcim/powerfeed.html +9 -4
  54. nautobot/dcim/templates/dcim/poweroutlet.html +14 -4
  55. nautobot/dcim/templates/dcim/powerpanel.html +1 -1
  56. nautobot/dcim/templates/dcim/powerport.html +14 -4
  57. nautobot/dcim/templates/dcim/rack.html +1 -1
  58. nautobot/dcim/templates/dcim/rackgroup.html +1 -1
  59. nautobot/dcim/templates/dcim/rackreservation.html +2 -2
  60. nautobot/dcim/templates/dcim/rearport.html +7 -2
  61. nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
  62. nautobot/dcim/tests/integration/test_device_bulk_operations.py +30 -0
  63. nautobot/dcim/tests/integration/test_fileinputpicker.py +87 -0
  64. nautobot/dcim/tests/integration/test_location_bulk_operations.py +43 -0
  65. nautobot/dcim/tests/test_models.py +1 -1
  66. nautobot/dcim/tests/test_views.py +9 -1
  67. nautobot/dcim/views.py +12 -15
  68. nautobot/extras/api/serializers.py +33 -0
  69. nautobot/extras/api/views.py +13 -5
  70. nautobot/extras/constants.py +1 -0
  71. nautobot/extras/datasources/git.py +125 -0
  72. nautobot/extras/forms/forms.py +4 -0
  73. nautobot/extras/jobs.py +8 -1
  74. nautobot/extras/migrations/0122_add_graphqlquery_owner_content_type.py +34 -0
  75. nautobot/extras/models/customfields.py +29 -12
  76. nautobot/extras/models/datasources.py +85 -0
  77. nautobot/extras/models/models.py +15 -0
  78. nautobot/extras/models/relationships.py +17 -5
  79. nautobot/extras/signals.py +15 -1
  80. nautobot/extras/templates/extras/computedfield.html +1 -1
  81. nautobot/extras/templates/extras/configcontext.html +1 -1
  82. nautobot/extras/templates/extras/configcontextschema.html +1 -1
  83. nautobot/extras/templates/extras/customfield.html +1 -1
  84. nautobot/extras/templates/extras/customlink.html +1 -1
  85. nautobot/extras/templates/extras/dynamicgroup.html +1 -1
  86. nautobot/extras/templates/extras/exporttemplate.html +1 -1
  87. nautobot/extras/templates/extras/gitrepository.html +1 -1
  88. nautobot/extras/templates/extras/graphqlquery.html +1 -1
  89. nautobot/extras/templates/extras/job.html +1 -0
  90. nautobot/extras/templates/extras/job_detail.html +1 -1
  91. nautobot/extras/templates/extras/jobbutton_retrieve.html +1 -1
  92. nautobot/extras/templates/extras/jobhook.html +1 -1
  93. nautobot/extras/templates/extras/jobresult.html +1 -1
  94. nautobot/extras/templates/extras/objectchange.html +1 -1
  95. nautobot/extras/templates/extras/plugin_detail.html +1 -1
  96. nautobot/extras/templates/extras/relationship.html +1 -63
  97. nautobot/extras/templates/extras/role_retrieve.html +1 -1
  98. nautobot/extras/templates/extras/scheduledjob.html +1 -1
  99. nautobot/extras/templates/extras/secret.html +1 -1
  100. nautobot/extras/templates/extras/secretsgroup.html +1 -1
  101. nautobot/extras/templates/extras/status.html +1 -1
  102. nautobot/extras/templates/extras/tag.html +1 -1
  103. nautobot/extras/templates/extras/webhook.html +1 -1
  104. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_interfaces.gql +8 -0
  105. nautobot/extras/tests/git_data/01-valid-files/graphql_queries/device_names.gql +5 -0
  106. nautobot/extras/tests/git_data/02-invalid-files/graphql_queries/bad_device_names.gql +5 -0
  107. nautobot/extras/tests/git_helper.py +9 -1
  108. nautobot/extras/tests/integration/__init__.py +29 -16
  109. nautobot/extras/tests/test_api.py +6 -0
  110. nautobot/extras/tests/test_customfields.py +49 -51
  111. nautobot/extras/tests/test_datasources.py +27 -0
  112. nautobot/extras/tests/test_dynamicgroups.py +14 -0
  113. nautobot/extras/tests/test_models.py +283 -0
  114. nautobot/extras/tests/test_utils.py +22 -1
  115. nautobot/extras/tests/test_views.py +197 -9
  116. nautobot/extras/utils.py +47 -8
  117. nautobot/extras/views.py +84 -26
  118. nautobot/ipam/api/views.py +3 -3
  119. nautobot/ipam/forms.py +2 -6
  120. nautobot/ipam/models.py +8 -2
  121. nautobot/ipam/tables.py +2 -2
  122. nautobot/ipam/templates/ipam/ipaddress.html +1 -1
  123. nautobot/ipam/templates/ipam/prefix.html +1 -1
  124. nautobot/ipam/templates/ipam/rir.html +1 -1
  125. nautobot/ipam/templates/ipam/routetarget.html +1 -1
  126. nautobot/ipam/templates/ipam/service.html +1 -1
  127. nautobot/ipam/templates/ipam/vlan.html +1 -1
  128. nautobot/ipam/templates/ipam/vlangroup.html +1 -1
  129. nautobot/ipam/templates/ipam/vrf.html +1 -1
  130. nautobot/ipam/tests/test_models.py +24 -0
  131. nautobot/ipam/tests/test_utils.py +41 -2
  132. nautobot/ipam/utils/__init__.py +18 -11
  133. nautobot/project-static/bootstrap-filestyle-1.2.3/bootstrap-filestyle.min.js +11 -0
  134. nautobot/project-static/docs/404.html +87 -12
  135. nautobot/project-static/docs/apps/index.html +88 -13
  136. nautobot/project-static/docs/apps/nautobot-apps.html +88 -13
  137. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js → bundle.60a45f97.min.js} +1 -1
  138. nautobot/project-static/docs/assets/javascripts/{bundle.88dd0f4e.min.js.map → bundle.60a45f97.min.js.map} +1 -1
  139. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js → search.f8cc74c7.min.js} +1 -1
  140. nautobot/project-static/docs/assets/javascripts/workers/{search.6ce7567c.min.js.map → search.f8cc74c7.min.js.map} +1 -1
  141. nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css → main.a40c8224.min.css} +1 -1
  142. nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +87 -12
  143. nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +87 -12
  144. nautobot/project-static/docs/code-reference/nautobot/apps/api.html +87 -12
  145. nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +87 -12
  146. nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +87 -12
  147. nautobot/project-static/docs/code-reference/nautobot/apps/config.html +87 -12
  148. nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +87 -12
  149. nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +87 -12
  150. nautobot/project-static/docs/code-reference/nautobot/apps/events.html +87 -12
  151. nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +87 -12
  152. nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +87 -12
  153. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +87 -12
  154. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +87 -12
  155. nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +87 -12
  156. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +87 -12
  157. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +87 -12
  158. nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +87 -12
  159. nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +87 -12
  160. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +87 -12
  161. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +87 -12
  162. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +87 -12
  163. nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +87 -12
  164. nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +177 -20
  165. nautobot/project-static/docs/code-reference/nautobot/apps/views.html +114 -17
  166. nautobot/project-static/docs/development/apps/api/configuration-view.html +87 -12
  167. nautobot/project-static/docs/development/apps/api/database-backend-config.html +87 -12
  168. nautobot/project-static/docs/development/apps/api/models/django-admin.html +87 -12
  169. nautobot/project-static/docs/development/apps/api/models/global-search.html +87 -12
  170. nautobot/project-static/docs/development/apps/api/models/graphql.html +96 -21
  171. nautobot/project-static/docs/development/apps/api/models/index.html +87 -12
  172. nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +87 -12
  173. nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +87 -12
  174. nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +89 -14
  175. nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +87 -12
  176. nautobot/project-static/docs/development/apps/api/platform-features/index.html +87 -12
  177. nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +87 -12
  178. nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +87 -12
  179. nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +87 -12
  180. nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +87 -12
  181. nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +87 -12
  182. nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +87 -12
  183. nautobot/project-static/docs/development/apps/api/prometheus.html +87 -12
  184. nautobot/project-static/docs/development/apps/api/setup.html +88 -13
  185. nautobot/project-static/docs/development/apps/api/testing.html +87 -12
  186. nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +87 -12
  187. nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +87 -12
  188. nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +87 -12
  189. nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +87 -12
  190. nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +87 -12
  191. nautobot/project-static/docs/development/apps/api/views/base-template.html +87 -12
  192. nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +87 -12
  193. nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +87 -12
  194. nautobot/project-static/docs/development/apps/api/views/help-documentation.html +87 -12
  195. nautobot/project-static/docs/development/apps/api/views/index.html +87 -12
  196. nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +87 -12
  197. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +87 -12
  198. nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +87 -12
  199. nautobot/project-static/docs/development/apps/api/views/notes.html +87 -12
  200. nautobot/project-static/docs/development/apps/api/views/rest-api.html +87 -12
  201. nautobot/project-static/docs/development/apps/api/views/urls.html +87 -12
  202. nautobot/project-static/docs/development/apps/index.html +87 -12
  203. nautobot/project-static/docs/development/apps/migration/code-updates.html +93 -17
  204. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +89 -14
  205. nautobot/project-static/docs/development/apps/migration/from-v1.html +90 -15
  206. nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +87 -12
  207. nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +87 -12
  208. nautobot/project-static/docs/development/apps/migration/model-updates/global.html +87 -12
  209. nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +87 -12
  210. nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +87 -12
  211. nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +87 -12
  212. nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +88 -13
  213. nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +87 -12
  214. nautobot/project-static/docs/development/apps/porting-from-netbox.html +87 -12
  215. nautobot/project-static/docs/development/core/application-registry.html +87 -12
  216. nautobot/project-static/docs/development/core/best-practices.html +88 -13
  217. nautobot/project-static/docs/development/core/bootstrap-ui.html +88 -13
  218. nautobot/project-static/docs/development/core/caching.html +87 -12
  219. nautobot/project-static/docs/development/core/controllers.html +87 -12
  220. nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +94 -19
  221. nautobot/project-static/docs/development/core/generic-views.html +87 -12
  222. nautobot/project-static/docs/development/core/getting-started.html +89 -14
  223. nautobot/project-static/docs/development/core/homepage.html +87 -12
  224. nautobot/project-static/docs/development/core/index.html +88 -13
  225. nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +90 -15
  226. nautobot/project-static/docs/development/core/model-checklist.html +88 -13
  227. nautobot/project-static/docs/development/core/model-features.html +87 -12
  228. nautobot/project-static/docs/development/core/natural-keys.html +87 -12
  229. nautobot/project-static/docs/development/core/navigation-menu.html +88 -13
  230. nautobot/project-static/docs/development/core/release-checklist.html +88 -13
  231. nautobot/project-static/docs/development/core/role-internals.html +87 -12
  232. nautobot/project-static/docs/development/core/settings.html +88 -13
  233. nautobot/project-static/docs/development/core/style-guide.html +91 -16
  234. nautobot/project-static/docs/development/core/templates.html +88 -13
  235. nautobot/project-static/docs/development/core/testing.html +87 -12
  236. nautobot/project-static/docs/development/core/ui-component-framework.html +87 -12
  237. nautobot/project-static/docs/development/core/user-preferences.html +87 -12
  238. nautobot/project-static/docs/development/index.html +87 -12
  239. nautobot/project-static/docs/development/jobs/index.html +95 -13
  240. nautobot/project-static/docs/development/jobs/migration/from-v1.html +90 -14
  241. nautobot/project-static/docs/index.html +90 -14
  242. nautobot/project-static/docs/objects.inv +0 -0
  243. nautobot/project-static/docs/overview/application_stack.html +89 -14
  244. nautobot/project-static/docs/overview/design_philosophy.html +87 -12
  245. nautobot/project-static/docs/release-notes/index.html +87 -12
  246. nautobot/project-static/docs/release-notes/version-1.0.html +89 -14
  247. nautobot/project-static/docs/release-notes/version-1.1.html +89 -14
  248. nautobot/project-static/docs/release-notes/version-1.2.html +90 -15
  249. nautobot/project-static/docs/release-notes/version-1.3.html +88 -13
  250. nautobot/project-static/docs/release-notes/version-1.4.html +104 -29
  251. nautobot/project-static/docs/release-notes/version-1.5.html +95 -20
  252. nautobot/project-static/docs/release-notes/version-1.6.html +91 -16
  253. nautobot/project-static/docs/release-notes/version-2.0.html +97 -22
  254. nautobot/project-static/docs/release-notes/version-2.1.html +94 -19
  255. nautobot/project-static/docs/release-notes/version-2.2.html +88 -13
  256. nautobot/project-static/docs/release-notes/version-2.3.html +91 -16
  257. nautobot/project-static/docs/release-notes/version-2.4.html +465 -12
  258. nautobot/project-static/docs/requirements.txt +1 -1
  259. nautobot/project-static/docs/search/search_index.json +1 -1
  260. nautobot/project-static/docs/sitemap.xml +296 -288
  261. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  262. nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +90 -15
  263. nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +87 -12
  264. nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +91 -16
  265. nautobot/project-static/docs/user-guide/administration/configuration/index.html +87 -12
  266. nautobot/project-static/docs/user-guide/administration/configuration/redis.html +88 -13
  267. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +90 -15
  268. nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +87 -12
  269. nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +95 -20
  270. nautobot/project-static/docs/user-guide/administration/guides/docker.html +90 -15
  271. nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +88 -13
  272. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +87 -12
  273. nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +91 -16
  274. nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +87 -12
  275. nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +102 -27
  276. nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +89 -14
  277. nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +87 -12
  278. nautobot/project-static/docs/user-guide/administration/installation/app-install.html +88 -13
  279. nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +87 -12
  280. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +87 -12
  281. nautobot/project-static/docs/user-guide/administration/installation/index.html +87 -12
  282. nautobot/project-static/docs/user-guide/administration/installation/install_system.html +88 -13
  283. nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +93 -18
  284. nautobot/project-static/docs/user-guide/administration/installation/services.html +88 -13
  285. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +87 -12
  286. nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +87 -12
  287. nautobot/project-static/docs/user-guide/administration/security/index.html +9420 -0
  288. nautobot/project-static/docs/user-guide/administration/security/notices.html +9844 -0
  289. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +87 -12
  290. nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +88 -13
  291. nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +87 -12
  292. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +87 -12
  293. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +87 -12
  294. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +87 -12
  295. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +87 -12
  296. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +87 -12
  297. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +87 -12
  298. nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +98 -20
  299. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +87 -12
  300. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +87 -12
  301. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +87 -12
  302. nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +87 -12
  303. nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +87 -12
  304. nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +87 -12
  305. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +87 -12
  306. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +87 -12
  307. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +87 -12
  308. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +87 -12
  309. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +87 -12
  310. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +87 -12
  311. nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +87 -12
  312. nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +87 -12
  313. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +87 -12
  314. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +87 -12
  315. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +87 -12
  316. nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +87 -12
  317. nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +87 -12
  318. nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +87 -12
  319. nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +87 -12
  320. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +87 -12
  321. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +87 -12
  322. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +87 -12
  323. nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +87 -12
  324. nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +87 -12
  325. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +87 -12
  326. nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +87 -12
  327. nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +87 -12
  328. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +87 -12
  329. nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +87 -12
  330. nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +87 -12
  331. nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +87 -12
  332. nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +87 -12
  333. nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +87 -12
  334. nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +87 -12
  335. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +87 -12
  336. nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +87 -12
  337. nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +87 -12
  338. nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +87 -12
  339. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +87 -12
  340. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +87 -12
  341. nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +87 -12
  342. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +87 -12
  343. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +87 -12
  344. nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +87 -12
  345. nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +87 -12
  346. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +87 -12
  347. nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +87 -12
  348. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +87 -12
  349. nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +87 -12
  350. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +87 -12
  351. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +87 -12
  352. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +87 -12
  353. nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +87 -12
  354. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +87 -12
  355. nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +87 -12
  356. nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +87 -12
  357. nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +87 -12
  358. nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +87 -12
  359. nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +87 -12
  360. nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +87 -12
  361. nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +87 -12
  362. nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +87 -12
  363. nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +87 -12
  364. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +87 -12
  365. nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +87 -12
  366. nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +87 -12
  367. nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +87 -12
  368. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +87 -12
  369. nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +87 -12
  370. nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +87 -12
  371. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +87 -12
  372. nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +87 -12
  373. nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +87 -12
  374. nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +87 -12
  375. nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +87 -12
  376. nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +87 -12
  377. nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +87 -12
  378. nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +87 -12
  379. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +87 -12
  380. nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +99 -24
  381. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +87 -12
  382. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +87 -12
  383. nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +90 -15
  384. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +87 -12
  385. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +87 -12
  386. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +87 -12
  387. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +87 -12
  388. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +87 -12
  389. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +87 -12
  390. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +188 -30
  391. nautobot/project-static/docs/user-guide/feature-guides/graphql.html +87 -12
  392. nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +87 -12
  393. nautobot/project-static/docs/user-guide/feature-guides/relationships.html +87 -12
  394. nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +87 -12
  395. nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +87 -12
  396. nautobot/project-static/docs/user-guide/index.html +87 -12
  397. nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +87 -12
  398. nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +87 -12
  399. nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +87 -12
  400. nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +87 -12
  401. nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +88 -13
  402. nautobot/project-static/docs/user-guide/platform-functionality/events.html +87 -12
  403. nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +87 -12
  404. nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +87 -12
  405. nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +407 -14
  406. nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +90 -15
  407. nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +87 -12
  408. nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +87 -12
  409. nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +87 -12
  410. nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +87 -12
  411. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +87 -12
  412. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +87 -12
  413. nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +89 -14
  414. nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +93 -18
  415. nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +87 -12
  416. nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +87 -12
  417. nautobot/project-static/docs/user-guide/platform-functionality/note.html +87 -12
  418. nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +87 -12
  419. nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +87 -12
  420. nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +87 -12
  421. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +87 -12
  422. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +87 -12
  423. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +87 -12
  424. nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +87 -12
  425. nautobot/project-static/docs/user-guide/platform-functionality/role.html +87 -12
  426. nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +87 -12
  427. nautobot/project-static/docs/user-guide/platform-functionality/secret.html +87 -12
  428. nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +87 -12
  429. nautobot/project-static/docs/user-guide/platform-functionality/status.html +87 -12
  430. nautobot/project-static/docs/user-guide/platform-functionality/tag.html +87 -12
  431. nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +87 -12
  432. nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +87 -12
  433. nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +87 -12
  434. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +87 -12
  435. nautobot/project-static/js/dropdown.js +28 -0
  436. nautobot/tenancy/forms.py +9 -0
  437. nautobot/tenancy/templates/tenancy/tenant.html +1 -2
  438. nautobot/tenancy/templates/tenancy/tenant_create.html +21 -0
  439. nautobot/tenancy/templates/tenancy/tenant_edit.html +2 -21
  440. nautobot/tenancy/templates/tenancy/tenantgroup.html +2 -44
  441. nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +1 -0
  442. nautobot/tenancy/tests/test_views.py +5 -1
  443. nautobot/tenancy/urls.py +7 -79
  444. nautobot/tenancy/views.py +51 -80
  445. nautobot/virtualization/templates/virtualization/cluster.html +1 -1
  446. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -1
  447. nautobot/virtualization/templates/virtualization/clustertype.html +1 -1
  448. nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
  449. nautobot/virtualization/templates/virtualization/vminterface.html +1 -1
  450. nautobot/wireless/api/serializers.py +6 -1
  451. nautobot/wireless/api/views.py +3 -3
  452. nautobot/wireless/tests/test_api.py +5 -0
  453. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/METADATA +12 -12
  454. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/RECORD +459 -443
  455. nautobot/dcim/tests/integration/test_device_bulk_delete.py +0 -189
  456. nautobot/dcim/tests/integration/test_device_bulk_edit.py +0 -181
  457. /nautobot/project-static/docs/assets/stylesheets/{main.6f8fc17f.min.css.map → main.a40c8224.min.css.map} +0 -0
  458. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/LICENSE.txt +0 -0
  459. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/NOTICE +0 -0
  460. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/WHEEL +0 -0
  461. {nautobot-2.4.1.dist-info → nautobot-2.4.3.dist-info}/entry_points.txt +0 -0
@@ -1,63 +1 @@
1
- {% extends 'generic/object_detail.html' %}
2
- {% load buttons %}
3
- {% load plugins %}
4
- {% load perms %}
5
- {% load helpers %}
6
-
7
- {% block content_left_page %}
8
- <div class="panel panel-default">
9
- <div class="panel-heading">
10
- <strong>Relationship</strong>
11
- </div>
12
- <table class="table table-hover panel-body attr-table">
13
- <tr>
14
- <td>Description</td>
15
- <td>{{ object.description | placeholder }}</td>
16
- </tr>
17
- <tr>
18
- <td>Type</td>
19
- <td>{{ object.type }}</td>
20
- </tr>
21
- <tr>
22
- <td>Required On</td>
23
- <td>{{ object.get_required_on_display }}</td>
24
- </tr>
25
- <tr>
26
- <td>On Advanced Tab</td>
27
- <td>{{ object.advanced_ui | render_boolean }}</td>
28
- </tr>
29
- <tr>
30
- <td>Source Content Type</td>
31
- <td>{{ object.source_type }}</td>
32
- </tr>
33
- <tr>
34
- <td>Source Label</td>
35
- <td>{{ object.source_label | placeholder }}</td>
36
- </tr>
37
- <tr>
38
- <td>Source Filter</td>
39
- <td>{% if object.source_filter %}<pre>{{ object.source_filter | render_json }}</pre>{% else %}{{ None | placeholder }}{% endif %}</td>
40
- </tr>
41
- <tr>
42
- <td>Hide on Source Object</td>
43
- <td>{{ object.source_hidden | render_boolean }}</td>
44
- </tr>
45
- <tr>
46
- <td>Destination Content Type</td>
47
- <td>{{ object.destination_type }}</td>
48
- </tr>
49
- <tr>
50
- <td>Destination Label</td>
51
- <td>{{ object.destination_label | placeholder }}</td>
52
- </tr>
53
- <tr>
54
- <td>Destination Filter</td>
55
- <td>{% if object.destination_filter %}<pre>{{ object.destination_filter | render_json }}</pre>{% else %}{{ None | placeholder }}{% endif %}</td>
56
- </tr>
57
- <tr>
58
- <td>Hide on Destination Object</td>
59
- <td>{{ object.destination_hidden | render_boolean }}</td>
60
- </tr>
61
- </table>
62
- </div>
63
- {% endblock content_left_page %}
1
+ {% extends 'generic/object_retrieve.html' %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block content_left_page %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load buttons %}
3
3
  {% load custom_links %}
4
4
  {% load helpers %}
@@ -1 +1 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
@@ -1,4 +1,4 @@
1
- {% extends "generic/object_detail.html" %}
1
+ {% extends "generic/object_retrieve.html" %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block content_left_page %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block content_left_page %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block content_left_page %}
@@ -1,4 +1,4 @@
1
- {% extends 'generic/object_detail.html' %}
1
+ {% extends 'generic/object_retrieve.html' %}
2
2
  {% load helpers %}
3
3
 
4
4
  {% block content_left_page %}
@@ -0,0 +1,8 @@
1
+ query {
2
+ devices {
3
+ name
4
+ interfaces {
5
+ name
6
+ }
7
+ }
8
+ }
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
14
14
  SOURCE_DIR = os.path.join(os.path.dirname(__file__), "git_data")
15
15
 
16
16
 
17
- def create_and_populate_git_repository(target_path):
17
+ def create_and_populate_git_repository(target_path, divergent_branch=None):
18
18
  """
19
19
  Create a Git repository in `target_path` and populate it with commits and tags based on contents of `SOURCE_DIR`.
20
20
 
@@ -40,6 +40,9 @@ def create_and_populate_git_repository(target_path):
40
40
  Note that each commit is fully defined by the files in the appropriate subdirectory; if you want a file to exist
41
41
  across multiple separate commits, it must exist in multiple subdirectories. Use of symlinks is encouraged in such
42
42
  a scenario.
43
+
44
+ You can optionally create and check out a divergent branch from the main branch by passing a branch name as the `divergent_branch`.
45
+ This will write a commit to the divergent branch and tag it with the branch name with the `-tag` suffix.
43
46
  """
44
47
  os.makedirs(target_path, exist_ok=True)
45
48
  repo = Repo.init(target_path, initial_branch="main")
@@ -69,6 +72,11 @@ def create_and_populate_git_repository(target_path):
69
72
  # Directory "01-valid-files" --> tag "valid-files" so that we won't break tests if we renumber the directories
70
73
  repo.create_tag(dirname[3:], message=f"Tag based on {dirname} files")
71
74
 
75
+ if divergent_branch:
76
+ repo.create_head(divergent_branch)
77
+ repo.index.commit("divergent-branch")
78
+ repo.create_tag(f"{divergent_branch}-tag", message=f"Tag for divergent branch {divergent_branch}")
79
+
72
80
 
73
81
  if __name__ == "__main__":
74
82
  directory_path = tempfile.TemporaryDirectory().name # pylint: disable=consider-using-with
@@ -6,28 +6,41 @@ from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Man
6
6
  from nautobot.extras.models import Role, Status
7
7
 
8
8
 
9
- def create_test_device():
10
- test_uuid = str(uuid.uuid4())
11
- device_role, _ = Role.objects.get_or_create(name="Device Role")
12
- device_ct = ContentType.objects.get_for_model(Device)
13
- device_role.content_types.add(device_ct)
14
- manufacturer = Manufacturer.objects.create(
15
- name=f"Test Manufacturer {test_uuid}",
16
- )
17
- device_type = DeviceType.objects.create(manufacturer=manufacturer, model=f"Test Model {test_uuid}")
18
- location_type = LocationType.objects.create(name=f"Test Location Type {test_uuid}")
19
- location_type.content_types.add(ContentType.objects.get_for_model(Device))
9
+ def create_test_device(name=None, location_name=None, test_uuid=None):
10
+ if not test_uuid:
11
+ test_uuid = str(uuid.uuid4())
12
+ if not name:
13
+ name = f"Test Device {test_uuid}"
14
+ if not location_name:
15
+ location_name = f"Test Location {test_uuid}"
16
+
17
+ location_type, location_type_created = LocationType.objects.get_or_create(name=f"Test Location Type {test_uuid}")
18
+ if location_type_created:
19
+ location_type.content_types.add(ContentType.objects.get_for_model(Device))
20
+ location_type.save()
21
+
20
22
  location_status = Status.objects.get_for_model(Location).first()
21
- location = Location.objects.create(
22
- name=f"Test Location {test_uuid}",
23
+ location, _ = Location.objects.get_or_create(
24
+ name=location_name,
23
25
  status=location_status,
24
26
  location_type=location_type,
25
27
  )
26
- device = Device.objects.create(
27
- name=f"Test Device {test_uuid}",
28
+
29
+ device_role, device_role_created = Role.objects.get_or_create(name="Device Role")
30
+ if device_role_created:
31
+ device_role.content_types.add(ContentType.objects.get_for_model(Device))
32
+ device_role.save()
33
+
34
+ manufacturer, _ = Manufacturer.objects.get_or_create(
35
+ name=f"Test Manufacturer {test_uuid}",
36
+ )
37
+
38
+ device_type, _ = DeviceType.objects.get_or_create(manufacturer=manufacturer, model=f"Test Model {test_uuid}")
39
+
40
+ return Device.objects.create(
41
+ name=name,
28
42
  role=device_role,
29
43
  device_type=device_type,
30
44
  location=location,
31
45
  status=location_status,
32
46
  )
33
- return device
@@ -1207,6 +1207,10 @@ class GitRepositoryTest(APIViewTestCases.APIViewTestCase):
1207
1207
  url = reverse("extras-api:gitrepository-sync", kwargs={"pk": self.repos[0].id})
1208
1208
  response = self.client.post(url, format="json", **self.header)
1209
1209
  self.assertHttpStatus(response, status.HTTP_200_OK)
1210
+ self.assertIn("message", response.data)
1211
+ self.assertIn("job_result", response.data)
1212
+ self.assertEqual(response.data["message"], f"Repository {self.repos[0].name} sync job added to queue.")
1213
+ self.assertIsInstance(response.data["job_result"], dict)
1210
1214
 
1211
1215
  def test_create_with_app_provided_contents(self):
1212
1216
  """Test that `provided_contents` published by an App works."""
@@ -1241,6 +1245,8 @@ class GraphQLQueryTest(APIViewTestCases.APIViewTestCase):
1241
1245
  },
1242
1246
  ]
1243
1247
 
1248
+ choices_fields = ["owner_content_type"]
1249
+
1244
1250
  @classmethod
1245
1251
  def setUpTestData(cls):
1246
1252
  cls.graphqlqueries = (
@@ -53,12 +53,12 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
53
53
 
54
54
  instance.refresh_from_db()
55
55
  instance.key = "custom_field_2"
56
- with self.assertRaises(ValidationError):
56
+ with self.assertRaisesRegex(ValidationError, "Key cannot be changed once created"):
57
57
  instance.validated_save()
58
58
 
59
59
  instance.refresh_from_db()
60
60
  instance.type = CustomFieldTypeChoices.TYPE_SELECT
61
- with self.assertRaises(ValidationError):
61
+ with self.assertRaisesRegex(ValidationError, "Type cannot be changed once created"):
62
62
  instance.validated_save()
63
63
 
64
64
  def test_simple_fields(self):
@@ -165,9 +165,14 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
165
165
  cf.save()
166
166
  cf.content_types.set([obj_type])
167
167
 
168
- CustomFieldChoice.objects.create(custom_field=cf, value="Option A")
169
- CustomFieldChoice.objects.create(custom_field=cf, value="Option B")
170
- CustomFieldChoice.objects.create(custom_field=cf, value="Option C")
168
+ CustomFieldChoice.objects.create(custom_field=cf, value="Option A", weight=100)
169
+ self.assertEqual(["Option A"], cf.choices)
170
+ CustomFieldChoice.objects.create(custom_field=cf, value="Option B", weight=200)
171
+ self.assertEqual(["Option A", "Option B"], cf.choices)
172
+ CustomFieldChoice.objects.create(custom_field=cf, value="Option C", weight=300)
173
+ self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
174
+ with self.assertNumQueries(0): # verify caching
175
+ self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
171
176
 
172
177
  # Assign a value to the first Location
173
178
  location = Location.objects.get(name="Location A")
@@ -199,9 +204,14 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
199
204
  cf.save()
200
205
  cf.content_types.set([obj_type])
201
206
 
202
- CustomFieldChoice.objects.create(custom_field=cf, value="Option A")
203
- CustomFieldChoice.objects.create(custom_field=cf, value="Option B")
204
- CustomFieldChoice.objects.create(custom_field=cf, value="Option C")
207
+ CustomFieldChoice.objects.create(custom_field=cf, value="Option A", weight=100)
208
+ self.assertEqual(["Option A"], cf.choices)
209
+ CustomFieldChoice.objects.create(custom_field=cf, value="Option B", weight=200)
210
+ self.assertEqual(["Option A", "Option B"], cf.choices)
211
+ CustomFieldChoice.objects.create(custom_field=cf, value="Option C", weight=300)
212
+ self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
213
+ with self.assertNumQueries(0): # verify caching
214
+ self.assertEqual(["Option A", "Option B", "Option C"], cf.choices)
205
215
 
206
216
  # Assign a value to the first Location
207
217
  location = Location.objects.get(name="Location A")
@@ -293,21 +303,18 @@ class CustomFieldTest(ModelTestCases.BaseModelTestCase, TestCase):
293
303
  # Assign a disallowed value (list) to the first Location
294
304
  location = Location.objects.get(name="Location A")
295
305
  location.cf[cf.key] = ["I", "am", "a", "list"]
296
- with self.assertRaises(ValidationError) as context:
306
+ with self.assertRaisesRegex(ValidationError, "Value must be a string"):
297
307
  location.validated_save()
298
- self.assertIn("Value must be a string", str(context.exception))
299
308
 
300
309
  # Assign another disallowed value (int) to the first Location
301
310
  location.cf[cf.key] = 2
302
- with self.assertRaises(ValidationError) as context:
311
+ with self.assertRaisesRegex(ValidationError, "Value must be a string"):
303
312
  location.validated_save()
304
- self.assertIn("Value must be a string", str(context.exception))
305
313
 
306
314
  # Assign another disallowed value (bool) to the first Location
307
315
  location.cf[cf.key] = True
308
- with self.assertRaises(ValidationError) as context:
316
+ with self.assertRaisesRegex(ValidationError, "Value must be a string"):
309
317
  location.validated_save()
310
- self.assertIn("Value must be a string", str(context.exception))
311
318
 
312
319
  # Delete the stored value
313
320
  location.cf.pop(cf.key)
@@ -1294,7 +1301,7 @@ class CustomFieldModelTest(TestCase):
1294
1301
  custom_field.content_types.set([ContentType.objects.get_for_model(Provider)])
1295
1302
 
1296
1303
  provider = Provider.objects.create(name="Test")
1297
- with self.assertRaises(ValidationError):
1304
+ with self.assertRaisesRegex(ValidationError, "Missing required custom field 'custom_field'"):
1298
1305
  provider.validated_save()
1299
1306
 
1300
1307
  def test_custom_field_required_on_update(self):
@@ -1312,7 +1319,7 @@ class CustomFieldModelTest(TestCase):
1312
1319
  provider = Provider.objects.create(name="Test", _custom_field_data={"custom_field": "Value"})
1313
1320
  provider.validated_save()
1314
1321
  provider._custom_field_data.pop("custom_field")
1315
- with self.assertRaises(ValidationError):
1322
+ with self.assertRaisesRegex(ValidationError, "Missing required custom field 'custom_field'"):
1316
1323
  provider.validated_save()
1317
1324
 
1318
1325
  def test_update_removed_custom_field(self):
@@ -1373,7 +1380,7 @@ class CustomFieldModelTest(TestCase):
1373
1380
  """
1374
1381
  Check that a ValidationError is raised if any required custom fields are not present.
1375
1382
  """
1376
- cf3 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, label="Baz", required=True)
1383
+ cf3 = CustomField(key="baz", type=CustomFieldTypeChoices.TYPE_TEXT, label="Baz", required=True)
1377
1384
  cf3.save()
1378
1385
  cf3.content_types.set([ContentType.objects.get_for_model(Location)])
1379
1386
 
@@ -1381,7 +1388,7 @@ class CustomFieldModelTest(TestCase):
1381
1388
 
1382
1389
  # Set custom field data with a required field omitted
1383
1390
  location.cf["foo"] = "abc"
1384
- with self.assertRaises(ValidationError):
1391
+ with self.assertRaisesRegex(ValidationError, "Missing required custom field 'baz'"):
1385
1392
  location.clean()
1386
1393
 
1387
1394
  location.cf["baz"] = "def"
@@ -1427,38 +1434,20 @@ class CustomFieldModelTest(TestCase):
1427
1434
  """
1428
1435
  Check the GraphQL validation method on CustomField Key Attribute.
1429
1436
  """
1430
- # Check if it catches the cf.key starting with a digit.
1431
- cf1 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, label="Test 1", key="12_test_1")
1432
- with self.assertRaises(ValidationError) as error:
1433
- cf1.validated_save()
1434
- self.assertIn(
1435
- "This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
1436
- str(error.exception),
1437
- )
1438
- # Check if it catches the cf.key with whitespace.
1439
- cf1.key = "test 1"
1440
- with self.assertRaises(ValidationError) as error:
1441
- cf1.validated_save()
1442
- self.assertIn(
1443
- "This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
1444
- str(error.exception),
1445
- )
1446
- # Check if it catches the cf.key with hyphens.
1447
- cf1.key = "test-1-custom-field"
1448
- with self.assertRaises(ValidationError) as error:
1449
- cf1.validated_save()
1450
- self.assertIn(
1451
- "This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
1452
- str(error.exception),
1453
- )
1454
- # Check if it catches the cf.key with special characters
1455
- cf1.key = "test_1_custom_f)(&d"
1456
- with self.assertRaises(ValidationError) as error:
1457
- cf1.validated_save()
1458
- self.assertIn(
1459
- "This key is not Python/GraphQL safe. Please do not start the key with a digit and do not use hyphens or whitespace",
1460
- str(error.exception),
1461
- )
1437
+ cf1 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, label="Test 1")
1438
+ for invalid_key in [
1439
+ "12_test_1", # Check if it catches the cf.key starting with a digit.
1440
+ "test 1", # Check if it catches the cf.key with whitespace.
1441
+ "test-1-custom-field", # Check if it catches the cf.key with hyphens.
1442
+ "test_1_custom_f)(&d", # Check if it catches the cf.key with special characters
1443
+ ]:
1444
+ with self.assertRaisesRegex(
1445
+ ValidationError,
1446
+ "This key is not Python/GraphQL safe. "
1447
+ "Please do not start the key with a digit and do not use hyphens or whitespace",
1448
+ ):
1449
+ cf1.key = invalid_key
1450
+ cf1.validated_save()
1462
1451
 
1463
1452
 
1464
1453
  class CustomFieldFilterTest(TestCase):
@@ -1934,9 +1923,11 @@ class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
1934
1923
  )
1935
1924
  self.cf.save()
1936
1925
  self.cf.content_types.set([obj_type])
1926
+ self.assertEqual(self.cf.choices, [])
1937
1927
 
1938
1928
  self.choice = CustomFieldChoice(custom_field=self.cf, value="Foo")
1939
1929
  self.choice.save()
1930
+ self.assertEqual(self.cf.choices, ["Foo"])
1940
1931
 
1941
1932
  location_status = Status.objects.get_for_model(Location).first()
1942
1933
  self.location_type = LocationType.objects.get(name="Campus")
@@ -1952,7 +1943,7 @@ class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
1952
1943
 
1953
1944
  def test_default_value_must_be_valid_choice_sad_path(self):
1954
1945
  self.cf.default = "invalid value"
1955
- with self.assertRaises(ValidationError):
1946
+ with self.assertRaisesRegex(ValidationError, 'Invalid default value "invalid value"'):
1956
1947
  self.cf.full_clean()
1957
1948
 
1958
1949
  def test_default_value_must_be_valid_choice_happy_path(self):
@@ -1965,6 +1956,13 @@ class CustomFieldChoiceTest(ModelTestCases.BaseModelTestCase):
1965
1956
  with self.assertRaises(ProtectedError):
1966
1957
  self.choice.delete()
1967
1958
 
1959
+ def test_inactive_choice_can_be_deleted(self):
1960
+ self.location._custom_field_data.pop("cf1")
1961
+ self.location.validated_save()
1962
+ self.assertEqual(self.cf.choices, ["Foo"])
1963
+ self.choice.delete()
1964
+ self.assertEqual(self.cf.choices, [])
1965
+
1968
1966
  def test_custom_choice_deleted_with_field(self):
1969
1967
  self.cf.delete()
1970
1968
  self.assertEqual(CustomField.objects.count(), 1) # custom field automatically added by the Example App
@@ -30,6 +30,7 @@ from nautobot.extras.models import (
30
30
  ConfigContextSchema,
31
31
  ExportTemplate,
32
32
  GitRepository,
33
+ GraphQLQuery,
33
34
  Job,
34
35
  JobButton,
35
36
  JobHook,
@@ -197,6 +198,15 @@ class GitTest(TransactionTestCase):
197
198
  )
198
199
  self.assertIsNotNone(export_template_vlan)
199
200
 
201
+ def assert_graphql_query_exists(self, name="device_names.gql"):
202
+ """Helper function to assert Graphql query exists."""
203
+ graphql_query = GraphQLQuery.objects.get(
204
+ name=name,
205
+ owner_object_id=self.repo.pk,
206
+ owner_content_type=ContentType.objects.get_for_model(GitRepository),
207
+ )
208
+ self.assertIsNotNone(graphql_query)
209
+
200
210
  def assert_job_exists(self, name="MyJob", installed=True):
201
211
  """Helper function to assert JobModel and registered Job exist."""
202
212
  # Is it registered correctly in the database?
@@ -348,6 +358,10 @@ class GitTest(TransactionTestCase):
348
358
  # Case when ContentType.model != ContentType.name, template was added and deleted during sync (#570)
349
359
  self.assert_export_template_vlan_exists("template.j2")
350
360
 
361
+ # Make sure Graphgl queries were loaded
362
+ self.assert_graphql_query_exists("device_names")
363
+ self.assert_graphql_query_exists("device_interfaces")
364
+
351
365
  # Make sure Jobs were successfully loaded from file and registered as JobModels
352
366
  self.assert_job_exists(name="MyJob")
353
367
  self.assert_job_exists(name="MyJobButtonReceiver")
@@ -439,6 +453,7 @@ class GitTest(TransactionTestCase):
439
453
  self.assertFalse(ConfigContextSchema.objects.filter(owner_object_id=self.repo.id).exists())
440
454
  self.assertFalse(ConfigContext.objects.filter(owner_object_id=self.repo.id).exists())
441
455
  self.assertFalse(ExportTemplate.objects.filter(owner_object_id=self.repo.id).exists())
456
+ self.assertFalse(GraphQLQuery.objects.filter(owner_object_id=self.repo.id).exists())
442
457
  self.assertFalse(Job.objects.filter(module_name__startswith=f"{self.repo.slug}.").exists())
443
458
  device = Device.objects.get(name=self.device.name)
444
459
  self.assertIsNone(device.local_config_context_data)
@@ -505,6 +520,11 @@ class GitTest(TransactionTestCase):
505
520
  grouping="jobs",
506
521
  message__contains="Error in loading Jobs from Git repository: ",
507
522
  )
523
+ failure_logs.get(
524
+ grouping="graphql queries",
525
+ message__contains="Error processing GraphQL query file 'bad_device_names.gql': Syntax Error GraphQL (4:5) Expected Name, found }",
526
+ )
527
+
508
528
  except (AssertionError, JobLogEntry.DoesNotExist):
509
529
  for log in log_entries:
510
530
  print(log.message)
@@ -630,6 +650,8 @@ class GitTest(TransactionTestCase):
630
650
  self.assert_export_template_device("template.j2")
631
651
  self.assert_export_template_html_exist("template2.html")
632
652
  self.assert_export_template_vlan_exists("template.j2")
653
+ self.assert_graphql_query_exists(name="device_names")
654
+ self.assert_graphql_query_exists(name="device_interfaces")
633
655
  self.assert_job_exists(name="MyJob")
634
656
  self.assert_job_exists(name="MyJobButtonReceiver")
635
657
  self.assert_job_exists(name="MyJobHookReceiver")
@@ -669,6 +691,8 @@ class GitTest(TransactionTestCase):
669
691
  self.assert_export_template_device("template.j2")
670
692
  self.assert_export_template_html_exist("template2.html")
671
693
  self.assert_export_template_vlan_exists("template.j2")
694
+ self.assert_graphql_query_exists("device_names")
695
+ self.assert_graphql_query_exists("device_interfaces")
672
696
  self.assert_job_exists(name="MyJob")
673
697
  self.assert_job_exists(name="MyJobButtonReceiver")
674
698
  self.assert_job_exists(name="MyJobHookReceiver")
@@ -711,6 +735,8 @@ class GitTest(TransactionTestCase):
711
735
  log_entries.get(message__contains="Addition - `export_templates/dcim/device/template.j2`")
712
736
  log_entries.get(message__contains="Addition - `export_templates/dcim/device/template2.html`")
713
737
  log_entries.get(message__contains="Addition - `export_templates/ipam/vlan/template.j2`")
738
+ log_entries.get(message__contains="Addition - `graphql_queries/device_interfaces.gql`")
739
+ log_entries.get(message__contains="Addition - `graphql_queries/device_names.gql`")
714
740
  log_entries.get(message__contains="Addition - `jobs/__init__.py`")
715
741
  log_entries.get(message__contains="Addition - `jobs/my_job.py`")
716
742
  except JobLogEntry.DoesNotExist:
@@ -721,6 +747,7 @@ class GitTest(TransactionTestCase):
721
747
  self.assertFalse(ConfigContextSchema.objects.filter(owner_object_id=self.repo.pk).exists())
722
748
  self.assertFalse(ConfigContext.objects.filter(owner_object_id=self.repo.pk).exists())
723
749
  self.assertFalse(ExportTemplate.objects.filter(owner_object_id=self.repo.pk).exists())
750
+ self.assertFalse(GraphQLQuery.objects.filter(owner_object_id=self.repo.pk).exists())
724
751
  self.assertFalse(Job.objects.filter(module_name__startswith=self.repo.slug).exists())
725
752
 
726
753
  # TODO: test dry-run against a branch name
@@ -667,6 +667,20 @@ class DynamicGroupModelTest(DynamicGroupTestBase): # TODO: BaseModelTestCase mi
667
667
  # Cleanup because we're using class-based fixtures in `setUpTestData()`
668
668
  group.refresh_from_db()
669
669
 
670
+ def test_set_filter_on_ipaddress_dynamic_group(self):
671
+ """
672
+ Test `DynamicGroup.set_filter()` for an IPAddress Dynamic Group.
673
+ https://github.com/nautobot/nautobot/issues/6805
674
+ """
675
+ ipaddress_dg = DynamicGroup.objects.create(
676
+ name="IP Address Dynamic Group",
677
+ content_type=ContentType.objects.get_for_model(IPAddress),
678
+ description="IP Address Dynamic Group",
679
+ )
680
+ # Test the fact that set_filter correctly discard an empty PrefixQuerySet
681
+ ipaddress_dg.set_filter({"parent": Prefix.objects.none()})
682
+ self.assertEqual(ipaddress_dg.filter, {})
683
+
670
684
  def test_add_child(self):
671
685
  """Test `DynamicGroup.add_child()`."""
672
686
  self.parent.add_child(