Browse Source

Add missing tests for code examples (#14569)

Co-authored-by: Sebastián Ramírez <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nils-Hero Lindemann <[email protected]>
pull/14604/head
Motov Yurii 5 months ago
committed by GitHub
parent
commit
3063ada72f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      docs/de/docs/advanced/dataclasses.md
  2. 2
      docs/de/docs/how-to/graphql.md
  3. 6
      docs/en/docs/advanced/dataclasses.md
  4. 2
      docs/en/docs/how-to/graphql.md
  5. 6
      docs/es/docs/advanced/dataclasses.md
  6. 2
      docs/es/docs/how-to/graphql.md
  7. 6
      docs/pt/docs/advanced/dataclasses.md
  8. 2
      docs/pt/docs/how-to/graphql.md
  9. 6
      docs/ru/docs/advanced/dataclasses.md
  10. 2
      docs/ru/docs/how-to/graphql.md
  11. 6
      docs/zh/docs/advanced/dataclasses.md
  12. 0
      docs_src/additional_responses/__init__.py
  13. 0
      docs_src/additional_status_codes/__init__.py
  14. 0
      docs_src/advanced_middleware/__init__.py
  15. 0
      docs_src/authentication_error_status_code/__init__.py
  16. 0
      docs_src/background_tasks/__init__.py
  17. 0
      docs_src/behind_a_proxy/__init__.py
  18. 0
      docs_src/body/__init__.py
  19. 0
      docs_src/body_fields/__init__.py
  20. 0
      docs_src/body_multiple_params/__init__.py
  21. 0
      docs_src/body_nested_models/__init__.py
  22. 0
      docs_src/body_updates/__init__.py
  23. 0
      docs_src/conditional_openapi/__init__.py
  24. 0
      docs_src/configure_swagger_ui/__init__.py
  25. 0
      docs_src/cookie_param_models/__init__.py
  26. 0
      docs_src/cookie_params/__init__.py
  27. 0
      docs_src/cors/__init__.py
  28. 0
      docs_src/custom_docs_ui/__init__.py
  29. 0
      docs_src/custom_request_and_route/__init__.py
  30. 0
      docs_src/custom_response/__init__.py
  31. 0
      docs_src/dataclasses_/__init__.py
  32. 0
      docs_src/dataclasses_/tutorial001_py310.py
  33. 0
      docs_src/dataclasses_/tutorial001_py39.py
  34. 0
      docs_src/dataclasses_/tutorial002_py310.py
  35. 0
      docs_src/dataclasses_/tutorial002_py39.py
  36. 0
      docs_src/dataclasses_/tutorial003_py310.py
  37. 0
      docs_src/dataclasses_/tutorial003_py39.py
  38. 0
      docs_src/debugging/__init__.py
  39. 0
      docs_src/dependencies/__init__.py
  40. 0
      docs_src/dependency_testing/__init__.py
  41. 0
      docs_src/encoder/__init__.py
  42. 0
      docs_src/events/__init__.py
  43. 0
      docs_src/extending_openapi/__init__.py
  44. 0
      docs_src/extra_data_types/__init__.py
  45. 0
      docs_src/extra_models/__init__.py
  46. 0
      docs_src/first_steps/__init__.py
  47. 0
      docs_src/generate_clients/__init__.py
  48. 0
      docs_src/graphql_/__init__.py
  49. 0
      docs_src/graphql_/tutorial001_py39.py
  50. 0
      docs_src/handling_errors/__init__.py
  51. 0
      docs_src/header_param_models/__init__.py
  52. 0
      docs_src/header_params/__init__.py
  53. 0
      docs_src/metadata/__init__.py
  54. 0
      docs_src/middleware/__init__.py
  55. 0
      docs_src/openapi_callbacks/__init__.py
  56. 0
      docs_src/openapi_webhooks/__init__.py
  57. 0
      docs_src/path_operation_advanced_configuration/__init__.py
  58. 0
      docs_src/path_operation_configuration/__init__.py
  59. 0
      docs_src/path_params/__init__.py
  60. 0
      docs_src/path_params_numeric_validations/__init__.py
  61. 0
      docs_src/pydantic_v1_in_v2/__init__.py
  62. 0
      docs_src/python_types/__init__.py
  63. 0
      docs_src/query_param_models/__init__.py
  64. 0
      docs_src/query_params/__init__.py
  65. 0
      docs_src/query_params_str_validations/__init__.py
  66. 0
      docs_src/request_files/__init__.py
  67. 0
      docs_src/request_form_models/__init__.py
  68. 0
      docs_src/request_forms/__init__.py
  69. 0
      docs_src/request_forms_and_files/__init__.py
  70. 0
      docs_src/response_change_status_code/__init__.py
  71. 0
      docs_src/response_cookies/__init__.py
  72. 0
      docs_src/response_directly/__init__.py
  73. 0
      docs_src/response_headers/__init__.py
  74. 0
      docs_src/response_model/__init__.py
  75. 0
      docs_src/response_status_code/__init__.py
  76. 0
      docs_src/schema_extra_example/__init__.py
  77. 0
      docs_src/security/__init__.py
  78. 0
      docs_src/separate_openapi_schemas/__init__.py
  79. 0
      docs_src/settings/__init__.py
  80. 0
      docs_src/static_files/__init__.py
  81. 0
      docs_src/sub_applications/__init__.py
  82. 0
      docs_src/templates/__init__.py
  83. 0
      docs_src/templates/static/__init__.py
  84. 0
      docs_src/templates/templates/__init__.py
  85. 0
      docs_src/using_request_directly/__init__.py
  86. 0
      docs_src/wsgi/__init__.py
  87. 11
      pyproject.toml
  88. 1
      requirements-tests.txt
  89. 161
      tests/test_tutorial/test_body/test_tutorial002.py
  90. 171
      tests/test_tutorial/test_body/test_tutorial003.py
  91. 182
      tests/test_tutorial/test_body/test_tutorial004.py
  92. 361
      tests/test_tutorial/test_body_multiple_params/test_tutorial002.py
  93. 290
      tests/test_tutorial/test_body_multiple_params/test_tutorial004.py
  94. 272
      tests/test_tutorial/test_body_multiple_params/test_tutorial005.py
  95. 251
      tests/test_tutorial/test_body_nested_models/test_tutorial001_tutorial002_tutorial003.py
  96. 275
      tests/test_tutorial/test_body_nested_models/test_tutorial004.py
  97. 301
      tests/test_tutorial/test_body_nested_models/test_tutorial005.py
  98. 269
      tests/test_tutorial/test_body_nested_models/test_tutorial006.py
  99. 344
      tests/test_tutorial/test_body_nested_models/test_tutorial007.py
  100. 157
      tests/test_tutorial/test_body_nested_models/test_tutorial008.py

6
docs/de/docs/advanced/dataclasses.md

@ -4,7 +4,7 @@ FastAPI basiert auf **Pydantic**, und ich habe Ihnen gezeigt, wie Sie Pydantic-M
Aber FastAPI unterstützt auf die gleiche Weise auch die Verwendung von <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>: Aber FastAPI unterstützt auf die gleiche Weise auch die Verwendung von <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>:
{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *} {* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Das ist dank **Pydantic** ebenfalls möglich, da es <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">`dataclasses` intern unterstützt</a>. Das ist dank **Pydantic** ebenfalls möglich, da es <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">`dataclasses` intern unterstützt</a>.
@ -32,7 +32,7 @@ Wenn Sie jedoch eine Menge Datenklassen herumliegen haben, ist dies ein guter Tr
Sie können `dataclasses` auch im Parameter `response_model` verwenden: Sie können `dataclasses` auch im Parameter `response_model` verwenden:
{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *} {* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
Die Datenklasse wird automatisch in eine Pydantic-Datenklasse konvertiert. Die Datenklasse wird automatisch in eine Pydantic-Datenklasse konvertiert.
@ -48,7 +48,7 @@ In einigen Fällen müssen Sie möglicherweise immer noch Pydantics Version von
In diesem Fall können Sie einfach die Standard-`dataclasses` durch `pydantic.dataclasses` ersetzen, was einen direkten Ersatz darstellt: In diesem Fall können Sie einfach die Standard-`dataclasses` durch `pydantic.dataclasses` ersetzen, was einen direkten Ersatz darstellt:
{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *} {* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Wir importieren `field` weiterhin von Standard-`dataclasses`. 1. Wir importieren `field` weiterhin von Standard-`dataclasses`.

2
docs/de/docs/how-to/graphql.md

@ -35,7 +35,7 @@ Abhängig von Ihrem Anwendungsfall könnten Sie eine andere Bibliothek vorziehen
Hier ist eine kleine Vorschau, wie Sie Strawberry mit FastAPI integrieren können: Hier ist eine kleine Vorschau, wie Sie Strawberry mit FastAPI integrieren können:
{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *} {* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
Weitere Informationen zu Strawberry finden Sie in der <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry-Dokumentation</a>. Weitere Informationen zu Strawberry finden Sie in der <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry-Dokumentation</a>.

6
docs/en/docs/advanced/dataclasses.md

@ -4,7 +4,7 @@ FastAPI is built on top of **Pydantic**, and I have been showing you how to use
But FastAPI also supports using <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> the same way: But FastAPI also supports using <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> the same way:
{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *} {* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
This is still supported thanks to **Pydantic**, as it has <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">internal support for `dataclasses`</a>. This is still supported thanks to **Pydantic**, as it has <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">internal support for `dataclasses`</a>.
@ -32,7 +32,7 @@ But if you have a bunch of dataclasses laying around, this is a nice trick to us
You can also use `dataclasses` in the `response_model` parameter: You can also use `dataclasses` in the `response_model` parameter:
{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *} {* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
The dataclass will be automatically converted to a Pydantic dataclass. The dataclass will be automatically converted to a Pydantic dataclass.
@ -48,7 +48,7 @@ In some cases, you might still have to use Pydantic's version of `dataclasses`.
In that case, you can simply swap the standard `dataclasses` with `pydantic.dataclasses`, which is a drop-in replacement: In that case, you can simply swap the standard `dataclasses` with `pydantic.dataclasses`, which is a drop-in replacement:
{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *} {* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. We still import `field` from standard `dataclasses`. 1. We still import `field` from standard `dataclasses`.

2
docs/en/docs/how-to/graphql.md

@ -35,7 +35,7 @@ Depending on your use case, you might prefer to use a different library, but if
Here's a small preview of how you could integrate Strawberry with FastAPI: Here's a small preview of how you could integrate Strawberry with FastAPI:
{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *} {* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
You can learn more about Strawberry in the <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry documentation</a>. You can learn more about Strawberry in the <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry documentation</a>.

6
docs/es/docs/advanced/dataclasses.md

@ -4,7 +4,7 @@ FastAPI está construido sobre **Pydantic**, y te he estado mostrando cómo usar
Pero FastAPI también soporta el uso de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> de la misma manera: Pero FastAPI también soporta el uso de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> de la misma manera:
{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *} {* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Esto sigue siendo soportado gracias a **Pydantic**, ya que tiene <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">soporte interno para `dataclasses`</a>. Esto sigue siendo soportado gracias a **Pydantic**, ya que tiene <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">soporte interno para `dataclasses`</a>.
@ -32,7 +32,7 @@ Pero si tienes un montón de dataclasses por ahí, este es un buen truco para us
También puedes usar `dataclasses` en el parámetro `response_model`: También puedes usar `dataclasses` en el parámetro `response_model`:
{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *} {* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
El dataclass será automáticamente convertido a un dataclass de Pydantic. El dataclass será automáticamente convertido a un dataclass de Pydantic.
@ -48,7 +48,7 @@ En algunos casos, todavía podrías tener que usar la versión de `dataclasses`
En ese caso, simplemente puedes intercambiar los `dataclasses` estándar con `pydantic.dataclasses`, que es un reemplazo directo: En ese caso, simplemente puedes intercambiar los `dataclasses` estándar con `pydantic.dataclasses`, que es un reemplazo directo:
{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *} {* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Todavía importamos `field` de los `dataclasses` estándar. 1. Todavía importamos `field` de los `dataclasses` estándar.

2
docs/es/docs/how-to/graphql.md

@ -35,7 +35,7 @@ Dependiendo de tu caso de uso, podrías preferir usar un paquete diferente, pero
Aquí tienes una pequeña vista previa de cómo podrías integrar Strawberry con FastAPI: Aquí tienes una pequeña vista previa de cómo podrías integrar Strawberry con FastAPI:
{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *} {* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
Puedes aprender más sobre Strawberry en la <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentación de Strawberry</a>. Puedes aprender más sobre Strawberry en la <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentación de Strawberry</a>.

6
docs/pt/docs/advanced/dataclasses.md

@ -4,7 +4,7 @@ FastAPI é construído em cima do **Pydantic**, e eu tenho mostrado como usar mo
Mas o FastAPI também suporta o uso de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> da mesma forma: Mas o FastAPI também suporta o uso de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> da mesma forma:
{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *} {* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Isso ainda é suportado graças ao **Pydantic**, pois ele tem <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">suporte interno para `dataclasses`</a>. Isso ainda é suportado graças ao **Pydantic**, pois ele tem <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">suporte interno para `dataclasses`</a>.
@ -32,7 +32,7 @@ Mas se você tem um monte de dataclasses por aí, este é um truque legal para u
Você também pode usar `dataclasses` no parâmetro `response_model`: Você também pode usar `dataclasses` no parâmetro `response_model`:
{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *} {* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
A dataclass será automaticamente convertida para uma dataclass Pydantic. A dataclass será automaticamente convertida para uma dataclass Pydantic.
@ -48,7 +48,7 @@ Em alguns casos, você ainda pode ter que usar a versão do Pydantic das `datacl
Nesse caso, você pode simplesmente trocar as `dataclasses` padrão por `pydantic.dataclasses`, que é um substituto direto: Nesse caso, você pode simplesmente trocar as `dataclasses` padrão por `pydantic.dataclasses`, que é um substituto direto:
{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *} {* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Ainda importamos `field` das `dataclasses` padrão. 1. Ainda importamos `field` das `dataclasses` padrão.

2
docs/pt/docs/how-to/graphql.md

@ -35,7 +35,7 @@ Dependendo do seu caso de uso, você pode preferir usar uma biblioteca diferente
Aqui está uma pequena prévia de como você poderia integrar Strawberry com FastAPI: Aqui está uma pequena prévia de como você poderia integrar Strawberry com FastAPI:
{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *} {* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
Você pode aprender mais sobre Strawberry na <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentação do Strawberry</a>. Você pode aprender mais sobre Strawberry na <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentação do Strawberry</a>.

6
docs/ru/docs/advanced/dataclasses.md

@ -4,7 +4,7 @@ FastAPI построен поверх **Pydantic**, и я показывал в
Но FastAPI также поддерживает использование <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> тем же способом: Но FastAPI также поддерживает использование <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> тем же способом:
{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *} {* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Это по-прежнему поддерживается благодаря **Pydantic**, так как в нём есть <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">встроенная поддержка `dataclasses`</a>. Это по-прежнему поддерживается благодаря **Pydantic**, так как в нём есть <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">встроенная поддержка `dataclasses`</a>.
@ -32,7 +32,7 @@ FastAPI построен поверх **Pydantic**, и я показывал в
Вы также можете использовать `dataclasses` в параметре `response_model`: Вы также можете использовать `dataclasses` в параметре `response_model`:
{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *} {* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
Этот dataclass будет автоматически преобразован в Pydantic dataclass. Этот dataclass будет автоматически преобразован в Pydantic dataclass.
@ -48,7 +48,7 @@ FastAPI построен поверх **Pydantic**, и я показывал в
В таком случае вы можете просто заменить стандартные `dataclasses` на `pydantic.dataclasses`, которая является полностью совместимой заменой (drop-in replacement): В таком случае вы можете просто заменить стандартные `dataclasses` на `pydantic.dataclasses`, которая является полностью совместимой заменой (drop-in replacement):
{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *} {* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Мы по-прежнему импортируем `field` из стандартных `dataclasses`. 1. Мы по-прежнему импортируем `field` из стандартных `dataclasses`.

2
docs/ru/docs/how-to/graphql.md

@ -35,7 +35,7 @@
Вот небольшой пример того, как можно интегрировать Strawberry с FastAPI: Вот небольшой пример того, как можно интегрировать Strawberry с FastAPI:
{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *} {* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
Подробнее о Strawberry можно узнать в <a href="https://strawberry.rocks/" class="external-link" target="_blank">документации Strawberry</a>. Подробнее о Strawberry можно узнать в <a href="https://strawberry.rocks/" class="external-link" target="_blank">документации Strawberry</a>.

6
docs/zh/docs/advanced/dataclasses.md

@ -4,7 +4,7 @@ FastAPI 基于 **Pydantic** 构建,前文已经介绍过如何使用 Pydantic
但 FastAPI 还可以使用数据类(<a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>): 但 FastAPI 还可以使用数据类(<a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>):
{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *} {* ../../docs_src/dataclasses_/tutorial001.py hl[1,7:12,19:20] *}
这还是借助于 **Pydantic** 及其<a href="https://pydantic-docs.helpmanual.io/usage/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">内置的 `dataclasses`</a> 这还是借助于 **Pydantic** 及其<a href="https://pydantic-docs.helpmanual.io/usage/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">内置的 `dataclasses`</a>
@ -32,7 +32,7 @@ FastAPI 基于 **Pydantic** 构建,前文已经介绍过如何使用 Pydantic
`response_model` 参数中使用 `dataclasses` `response_model` 参数中使用 `dataclasses`
{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *} {* ../../docs_src/dataclasses_/tutorial002.py hl[1,7:13,19] *}
本例把数据类自动转换为 Pydantic 数据类。 本例把数据类自动转换为 Pydantic 数据类。
@ -49,7 +49,7 @@ API 文档中也会显示相关概图:
本例把标准的 `dataclasses` 直接替换为 `pydantic.dataclasses` 本例把标准的 `dataclasses` 直接替换为 `pydantic.dataclasses`
```{ .python .annotate hl_lines="1 5 8-11 14-17 23-25 28" } ```{ .python .annotate hl_lines="1 5 8-11 14-17 23-25 28" }
{!../../docs_src/dataclasses/tutorial003.py!} {!../../docs_src/dataclasses_/tutorial003.py!}
``` ```
1. 本例依然要从标准的 `dataclasses` 中导入 `field` 1. 本例依然要从标准的 `dataclasses` 中导入 `field`

0
docs_src/additional_responses/__init__.py

0
docs_src/additional_status_codes/__init__.py

0
docs_src/advanced_middleware/__init__.py

0
docs_src/authentication_error_status_code/__init__.py

0
docs_src/background_tasks/__init__.py

0
docs_src/behind_a_proxy/__init__.py

0
docs_src/body/__init__.py

0
docs_src/body_fields/__init__.py

0
docs_src/body_multiple_params/__init__.py

0
docs_src/body_nested_models/__init__.py

0
docs_src/body_updates/__init__.py

0
docs_src/conditional_openapi/__init__.py

0
docs_src/configure_swagger_ui/__init__.py

0
docs_src/cookie_param_models/__init__.py

0
docs_src/cookie_params/__init__.py

0
docs_src/cors/__init__.py

0
docs_src/custom_docs_ui/__init__.py

0
docs_src/custom_request_and_route/__init__.py

0
docs_src/custom_response/__init__.py

0
docs_src/dataclasses_/__init__.py

0
docs_src/dataclasses/tutorial001_py310.py → docs_src/dataclasses_/tutorial001_py310.py

0
docs_src/dataclasses/tutorial001_py39.py → docs_src/dataclasses_/tutorial001_py39.py

0
docs_src/dataclasses/tutorial002_py310.py → docs_src/dataclasses_/tutorial002_py310.py

0
docs_src/dataclasses/tutorial002_py39.py → docs_src/dataclasses_/tutorial002_py39.py

0
docs_src/dataclasses/tutorial003_py310.py → docs_src/dataclasses_/tutorial003_py310.py

0
docs_src/dataclasses/tutorial003_py39.py → docs_src/dataclasses_/tutorial003_py39.py

0
docs_src/debugging/__init__.py

0
docs_src/dependencies/__init__.py

0
docs_src/dependency_testing/__init__.py

0
docs_src/encoder/__init__.py

0
docs_src/events/__init__.py

0
docs_src/extending_openapi/__init__.py

0
docs_src/extra_data_types/__init__.py

0
docs_src/extra_models/__init__.py

0
docs_src/first_steps/__init__.py

0
docs_src/generate_clients/__init__.py

0
docs_src/graphql_/__init__.py

0
docs_src/graphql/tutorial001_py39.py → docs_src/graphql_/tutorial001_py39.py

0
docs_src/handling_errors/__init__.py

0
docs_src/header_param_models/__init__.py

0
docs_src/header_params/__init__.py

0
docs_src/metadata/__init__.py

0
docs_src/middleware/__init__.py

0
docs_src/openapi_callbacks/__init__.py

0
docs_src/openapi_webhooks/__init__.py

0
docs_src/path_operation_advanced_configuration/__init__.py

0
docs_src/path_operation_configuration/__init__.py

0
docs_src/path_params/__init__.py

0
docs_src/path_params_numeric_validations/__init__.py

0
docs_src/pydantic_v1_in_v2/__init__.py

0
docs_src/python_types/__init__.py

0
docs_src/query_param_models/__init__.py

0
docs_src/query_params/__init__.py

0
docs_src/query_params_str_validations/__init__.py

0
docs_src/request_files/__init__.py

0
docs_src/request_form_models/__init__.py

0
docs_src/request_forms/__init__.py

0
docs_src/request_forms_and_files/__init__.py

0
docs_src/response_change_status_code/__init__.py

0
docs_src/response_cookies/__init__.py

0
docs_src/response_directly/__init__.py

0
docs_src/response_headers/__init__.py

0
docs_src/response_model/__init__.py

0
docs_src/response_status_code/__init__.py

0
docs_src/schema_extra_example/__init__.py

0
docs_src/security/__init__.py

0
docs_src/separate_openapi_schemas/__init__.py

0
docs_src/settings/__init__.py

0
docs_src/static_files/__init__.py

0
docs_src/sub_applications/__init__.py

0
docs_src/templates/__init__.py

0
docs_src/templates/static/__init__.py

0
docs_src/templates/templates/__init__.py

0
docs_src/using_request_directly/__init__.py

0
docs_src/wsgi/__init__.py

11
pyproject.toml

@ -196,6 +196,17 @@ dynamic_context = "test_function"
omit = [ omit = [
"docs_src/response_model/tutorial003_04_py39.py", "docs_src/response_model/tutorial003_04_py39.py",
"docs_src/response_model/tutorial003_04_py310.py", "docs_src/response_model/tutorial003_04_py310.py",
"docs_src/dependencies/tutorial008_an_py39.py", # difficult to mock
"docs_src/dependencies/tutorial013_an_py310.py", # temporary code example?
"docs_src/dependencies/tutorial014_an_py310.py", # temporary code example?
# Pydantic V1
"docs_src/schema_extra_example/tutorial001_pv1_py310.py",
"docs_src/query_param_models/tutorial002_pv1_py310.py",
"docs_src/query_param_models/tutorial002_pv1_an_py310.py",
"docs_src/header_param_models/tutorial002_pv1_py310.py",
"docs_src/header_param_models/tutorial002_pv1_an_py310.py",
"docs_src/cookie_param_models/tutorial002_pv1_py310.py",
"docs_src/cookie_param_models/tutorial002_pv1_an_py310.py",
] ]
[tool.coverage.report] [tool.coverage.report]

1
requirements-tests.txt

@ -6,6 +6,7 @@ mypy ==1.14.1
dirty-equals ==0.9.0 dirty-equals ==0.9.0
sqlmodel==0.0.27 sqlmodel==0.0.27
flask >=1.1.2,<4.0.0 flask >=1.1.2,<4.0.0
strawberry-graphql >=0.200.0,< 1.0.0
anyio[trio] >=3.2.1,<5.0.0 anyio[trio] >=3.2.1,<5.0.0
PyJWT==2.9.0 PyJWT==2.9.0
pyyaml >=5.3.1,<7.0.0 pyyaml >=5.3.1,<7.0.0

161
tests/test_tutorial/test_body/test_tutorial002.py

@ -0,0 +1,161 @@
import importlib
from typing import Union
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body.{request.param}")
client = TestClient(mod.app)
return client
@pytest.mark.parametrize("price", ["50.5", 50.5])
def test_post_with_tax(client: TestClient, price: Union[str, float]):
response = client.post(
"/items/",
json={"name": "Foo", "price": price, "description": "Some Foo", "tax": 0.3},
)
assert response.status_code == 200
assert response.json() == {
"name": "Foo",
"price": 50.5,
"description": "Some Foo",
"tax": 0.3,
"price_with_tax": 50.8,
}
@pytest.mark.parametrize("price", ["50.5", 50.5])
def test_post_without_tax(client: TestClient, price: Union[str, float]):
response = client.post(
"/items/", json={"name": "Foo", "price": price, "description": "Some Foo"}
)
assert response.status_code == 200
assert response.json() == {
"name": "Foo",
"price": 50.5,
"description": "Some Foo",
"tax": None,
}
def test_post_with_no_data(client: TestClient):
response = client.post("/items/", json={})
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"type": "missing",
"loc": ["body", "name"],
"msg": "Field required",
"input": {},
},
{
"type": "missing",
"loc": ["body", "price"],
"msg": "Field required",
"input": {},
},
]
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Create Item",
"operationId": "create_item_items__post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"price": {"title": "Price", "type": "number"},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

171
tests/test_tutorial/test_body/test_tutorial003.py

@ -0,0 +1,171 @@
import importlib
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial003_py39"),
pytest.param("tutorial003_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body.{request.param}")
client = TestClient(mod.app)
return client
def test_put_all(client: TestClient):
response = client.put(
"/items/123",
json={"name": "Foo", "price": 50.1, "description": "Some Foo", "tax": 0.3},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 123,
"name": "Foo",
"price": 50.1,
"description": "Some Foo",
"tax": 0.3,
}
def test_put_only_required(client: TestClient):
response = client.put(
"/items/123",
json={"name": "Foo", "price": 50.1},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 123,
"name": "Foo",
"price": 50.1,
"description": None,
"tax": None,
}
def test_put_with_no_data(client: TestClient):
response = client.put("/items/123", json={})
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"type": "missing",
"loc": ["body", "name"],
"msg": "Field required",
"input": {},
},
{
"type": "missing",
"loc": ["body", "price"],
"msg": "Field required",
"input": {},
},
]
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"put": {
"parameters": [
{
"in": "path",
"name": "item_id",
"required": True,
"schema": {
"title": "Item Id",
"type": "integer",
},
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Update Item",
"operationId": "update_item_items__item_id__put",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"price": {"title": "Price", "type": "number"},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

182
tests/test_tutorial/test_body/test_tutorial004.py

@ -0,0 +1,182 @@
import importlib
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial004_py39"),
pytest.param("tutorial004_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body.{request.param}")
client = TestClient(mod.app)
return client
def test_put_all(client: TestClient):
response = client.put(
"/items/123",
json={"name": "Foo", "price": 50.1, "description": "Some Foo", "tax": 0.3},
params={"q": "somequery"},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 123,
"name": "Foo",
"price": 50.1,
"description": "Some Foo",
"tax": 0.3,
"q": "somequery",
}
def test_put_only_required(client: TestClient):
response = client.put(
"/items/123",
json={"name": "Foo", "price": 50.1},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 123,
"name": "Foo",
"price": 50.1,
"description": None,
"tax": None,
}
def test_put_with_no_data(client: TestClient):
response = client.put("/items/123", json={})
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"type": "missing",
"loc": ["body", "name"],
"msg": "Field required",
"input": {},
},
{
"type": "missing",
"loc": ["body", "price"],
"msg": "Field required",
"input": {},
},
]
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"put": {
"parameters": [
{
"in": "path",
"name": "item_id",
"required": True,
"schema": {
"title": "Item Id",
"type": "integer",
},
},
{
"required": False,
"schema": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"title": "Q",
},
"name": "q",
"in": "query",
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Update Item",
"operationId": "update_item_items__item_id__put",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"price": {"title": "Price", "type": "number"},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

361
tests/test_tutorial/test_body_multiple_params/test_tutorial002.py

@ -0,0 +1,361 @@
import importlib
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body_multiple_params.{request.param}")
client = TestClient(mod.app)
return client
def test_post_all(client: TestClient):
response = client.put(
"/items/5",
json={
"item": {
"name": "Foo",
"price": 50.5,
"description": "Some Foo",
"tax": 0.1,
},
"user": {"username": "johndoe", "full_name": "John Doe"},
},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 5,
"item": {
"name": "Foo",
"price": 50.5,
"description": "Some Foo",
"tax": 0.1,
},
"user": {"username": "johndoe", "full_name": "John Doe"},
}
def test_post_required(client: TestClient):
response = client.put(
"/items/5",
json={
"item": {"name": "Foo", "price": 50.5},
"user": {"username": "johndoe"},
},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 5,
"item": {
"name": "Foo",
"price": 50.5,
"description": None,
"tax": None,
},
"user": {"username": "johndoe", "full_name": None},
}
def test_post_no_body(client: TestClient):
response = client.put("/items/5", json=None)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"input": None,
"loc": [
"body",
"item",
],
"msg": "Field required",
"type": "missing",
},
{
"input": None,
"loc": [
"body",
"user",
],
"msg": "Field required",
"type": "missing",
},
],
}
def test_post_no_item(client: TestClient):
response = client.put("/items/5", json={"user": {"username": "johndoe"}})
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"input": None,
"loc": [
"body",
"item",
],
"msg": "Field required",
"type": "missing",
},
],
}
def test_post_no_user(client: TestClient):
response = client.put("/items/5", json={"item": {"name": "Foo", "price": 50.5}})
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"input": None,
"loc": [
"body",
"user",
],
"msg": "Field required",
"type": "missing",
},
],
}
def test_post_missing_required_field_in_item(client: TestClient):
response = client.put(
"/items/5", json={"item": {"name": "Foo"}, "user": {"username": "johndoe"}}
)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"input": {"name": "Foo"},
"loc": [
"body",
"item",
"price",
],
"msg": "Field required",
"type": "missing",
},
],
}
def test_post_missing_required_field_in_user(client: TestClient):
response = client.put(
"/items/5",
json={"item": {"name": "Foo", "price": 50.5}, "user": {"ful_name": "John Doe"}},
)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"input": {"ful_name": "John Doe"},
"loc": [
"body",
"user",
"username",
],
"msg": "Field required",
"type": "missing",
},
],
}
def test_post_id_foo(client: TestClient):
response = client.put(
"/items/foo",
json={
"item": {"name": "Foo", "price": 50.5},
"user": {"username": "johndoe"},
},
)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"type": "int_parsing",
"loc": ["path", "item_id"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo",
}
]
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"info": {
"title": "FastAPI",
"version": "0.1.0",
},
"openapi": "3.1.0",
"paths": {
"/items/{item_id}": {
"put": {
"operationId": "update_item_items__item_id__put",
"parameters": [
{
"in": "path",
"name": "item_id",
"required": True,
"schema": {
"title": "Item Id",
"type": "integer",
},
},
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_update_item_items__item_id__put",
},
},
},
"required": True,
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {},
},
},
"description": "Successful Response",
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError",
},
},
},
"description": "Validation Error",
},
},
"summary": "Update Item",
},
},
},
"components": {
"schemas": {
"Body_update_item_items__item_id__put": {
"properties": {
"item": {
"$ref": "#/components/schemas/Item",
},
"user": {
"$ref": "#/components/schemas/User",
},
},
"required": [
"item",
"user",
],
"title": "Body_update_item_items__item_id__put",
"type": "object",
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError",
},
"title": "Detail",
"type": "array",
},
},
"title": "HTTPValidationError",
"type": "object",
},
"Item": {
"properties": {
"name": {
"title": "Name",
"type": "string",
},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"price": {"title": "Price", "type": "number"},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
},
"required": [
"name",
"price",
],
"title": "Item",
"type": "object",
},
"User": {
"properties": {
"username": {
"title": "Username",
"type": "string",
},
"full_name": {
"title": "Full Name",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
},
"required": [
"username",
],
"title": "User",
"type": "object",
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string",
},
{
"type": "integer",
},
],
},
"title": "Location",
"type": "array",
},
"msg": {
"title": "Message",
"type": "string",
},
"type": {
"title": "Error Type",
"type": "string",
},
},
"required": [
"loc",
"msg",
"type",
],
"title": "ValidationError",
"type": "object",
},
},
},
}

290
tests/test_tutorial/test_body_multiple_params/test_tutorial004.py

@ -0,0 +1,290 @@
import importlib
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial004_py39"),
pytest.param("tutorial004_py310", marks=needs_py310),
pytest.param("tutorial004_an_py39"),
pytest.param("tutorial004_an_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body_multiple_params.{request.param}")
client = TestClient(mod.app)
return client
def test_put_all(client: TestClient):
response = client.put(
"/items/5",
json={
"importance": 2,
"item": {"name": "Foo", "price": 50.5},
"user": {"username": "Dave"},
},
params={"q": "somequery"},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 5,
"importance": 2,
"item": {
"name": "Foo",
"price": 50.5,
"description": None,
"tax": None,
},
"user": {"username": "Dave", "full_name": None},
"q": "somequery",
}
def test_put_only_required(client: TestClient):
response = client.put(
"/items/5",
json={
"importance": 2,
"item": {"name": "Foo", "price": 50.5},
"user": {"username": "Dave"},
},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 5,
"importance": 2,
"item": {
"name": "Foo",
"price": 50.5,
"description": None,
"tax": None,
},
"user": {"username": "Dave", "full_name": None},
}
def test_put_missing_body(client: TestClient):
response = client.put("/items/5")
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"input": None,
"loc": [
"body",
"item",
],
"msg": "Field required",
"type": "missing",
},
{
"input": None,
"loc": [
"body",
"user",
],
"msg": "Field required",
"type": "missing",
},
{
"input": None,
"loc": [
"body",
"importance",
],
"msg": "Field required",
"type": "missing",
},
],
}
def test_put_empty_body(client: TestClient):
response = client.put("/items/5", json={})
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"type": "missing",
"loc": ["body", "item"],
"msg": "Field required",
"input": None,
},
{
"type": "missing",
"loc": ["body", "user"],
"msg": "Field required",
"input": None,
},
{
"type": "missing",
"loc": ["body", "importance"],
"msg": "Field required",
"input": None,
},
]
}
def test_put_invalid_importance(client: TestClient):
response = client.put(
"/items/5",
json={
"importance": 0,
"item": {"name": "Foo", "price": 50.5},
"user": {"username": "Dave"},
},
)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"loc": ["body", "importance"],
"msg": "Input should be greater than 0",
"type": "greater_than",
"input": 0,
"ctx": {"gt": 0},
},
],
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"put": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Update Item",
"operationId": "update_item_items__item_id__put",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "integer"},
"name": "item_id",
"in": "path",
},
{
"required": False,
"schema": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"title": "Q",
},
"name": "q",
"in": "query",
},
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_update_item_items__item_id__put"
}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"price": {"title": "Price", "type": "number"},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
},
},
"User": {
"title": "User",
"required": ["username"],
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
"full_name": {
"title": "Full Name",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
},
},
"Body_update_item_items__item_id__put": {
"title": "Body_update_item_items__item_id__put",
"required": ["item", "user", "importance"],
"type": "object",
"properties": {
"item": {"$ref": "#/components/schemas/Item"},
"user": {"$ref": "#/components/schemas/User"},
"importance": {
"title": "Importance",
"type": "integer",
"exclusiveMinimum": 0.0,
},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

272
tests/test_tutorial/test_body_multiple_params/test_tutorial005.py

@ -0,0 +1,272 @@
import importlib
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial005_py39"),
pytest.param("tutorial005_py310", marks=needs_py310),
pytest.param("tutorial005_an_py39"),
pytest.param("tutorial005_an_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body_multiple_params.{request.param}")
client = TestClient(mod.app)
return client
def test_post_all(client: TestClient):
response = client.put(
"/items/5",
json={
"item": {
"name": "Foo",
"price": 50.5,
"description": "Some Foo",
"tax": 0.1,
},
},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 5,
"item": {
"name": "Foo",
"price": 50.5,
"description": "Some Foo",
"tax": 0.1,
},
}
def test_post_required(client: TestClient):
response = client.put(
"/items/5",
json={
"item": {"name": "Foo", "price": 50.5},
},
)
assert response.status_code == 200
assert response.json() == {
"item_id": 5,
"item": {
"name": "Foo",
"price": 50.5,
"description": None,
"tax": None,
},
}
def test_post_no_body(client: TestClient):
response = client.put("/items/5", json=None)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"input": None,
"loc": [
"body",
"item",
],
"msg": "Field required",
"type": "missing",
},
],
}
def test_post_like_not_embeded(client: TestClient):
response = client.put(
"/items/5",
json={
"name": "Foo",
"price": 50.5,
},
)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"input": None,
"loc": [
"body",
"item",
],
"msg": "Field required",
"type": "missing",
},
],
}
def test_post_missing_required_field_in_item(client: TestClient):
response = client.put(
"/items/5", json={"item": {"name": "Foo"}, "user": {"username": "johndoe"}}
)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
"input": {"name": "Foo"},
"loc": [
"body",
"item",
"price",
],
"msg": "Field required",
"type": "missing",
},
],
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"info": {
"title": "FastAPI",
"version": "0.1.0",
},
"openapi": "3.1.0",
"paths": {
"/items/{item_id}": {
"put": {
"operationId": "update_item_items__item_id__put",
"parameters": [
{
"in": "path",
"name": "item_id",
"required": True,
"schema": {
"title": "Item Id",
"type": "integer",
},
},
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_update_item_items__item_id__put",
},
},
},
"required": True,
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {},
},
},
"description": "Successful Response",
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError",
},
},
},
"description": "Validation Error",
},
},
"summary": "Update Item",
},
},
},
"components": {
"schemas": {
"Body_update_item_items__item_id__put": {
"properties": {
"item": {
"$ref": "#/components/schemas/Item",
},
},
"required": ["item"],
"title": "Body_update_item_items__item_id__put",
"type": "object",
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError",
},
"title": "Detail",
"type": "array",
},
},
"title": "HTTPValidationError",
"type": "object",
},
"Item": {
"properties": {
"name": {
"title": "Name",
"type": "string",
},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"price": {"title": "Price", "type": "number"},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
},
"required": [
"name",
"price",
],
"title": "Item",
"type": "object",
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string",
},
{
"type": "integer",
},
],
},
"title": "Location",
"type": "array",
},
"msg": {
"title": "Message",
"type": "string",
},
"type": {
"title": "Error Type",
"type": "string",
},
},
"required": [
"loc",
"msg",
"type",
],
"title": "ValidationError",
"type": "object",
},
},
},
}

251
tests/test_tutorial/test_body_nested_models/test_tutorial001_tutorial002_tutorial003.py

@ -0,0 +1,251 @@
import importlib
import pytest
from dirty_equals import IsList
from fastapi.testclient import TestClient
from ...utils import needs_py310
UNTYPED_LIST_SCHEMA = {"type": "array", "items": {}}
LIST_OF_STR_SCHEMA = {"type": "array", "items": {"type": "string"}}
SET_OF_STR_SCHEMA = {"type": "array", "items": {"type": "string"}, "uniqueItems": True}
@pytest.fixture(
name="mod_name",
params=[
pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
pytest.param("tutorial003_py39"),
pytest.param("tutorial003_py310", marks=needs_py310),
],
)
def get_mod_name(request: pytest.FixtureRequest):
return request.param
@pytest.fixture(name="client")
def get_client(mod_name: str):
mod = importlib.import_module(f"docs_src.body_nested_models.{mod_name}")
client = TestClient(mod.app)
return client
def test_put_all(client: TestClient, mod_name: str):
if mod_name.startswith("tutorial003"):
tags_expected = IsList("foo", "bar", check_order=False)
else:
tags_expected = ["foo", "bar", "foo"]
response = client.put(
"/items/123",
json={
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
"tags": ["foo", "bar", "foo"],
},
)
assert response.status_code == 200, response.text
assert response.json() == {
"item_id": 123,
"item": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
"tags": tags_expected,
},
}
def test_put_only_required(client: TestClient):
response = client.put(
"/items/5",
json={"name": "Foo", "price": 35.4},
)
assert response.status_code == 200, response.text
assert response.json() == {
"item_id": 5,
"item": {
"name": "Foo",
"description": None,
"price": 35.4,
"tax": None,
"tags": [],
},
}
def test_put_empty_body(client: TestClient):
response = client.put(
"/items/5",
json={},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "name"],
"input": {},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "price"],
"input": {},
"msg": "Field required",
"type": "missing",
},
]
}
def test_put_missing_required(client: TestClient):
response = client.put(
"/items/5",
json={"description": "A very nice Item"},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "name"],
"input": {"description": "A very nice Item"},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "price"],
"input": {"description": "A very nice Item"},
"msg": "Field required",
"type": "missing",
},
]
}
def test_openapi_schema(client: TestClient, mod_name: str):
tags_schema = {"default": [], "title": "Tags"}
if mod_name.startswith("tutorial001"):
tags_schema.update(UNTYPED_LIST_SCHEMA)
elif mod_name.startswith("tutorial002"):
tags_schema.update(LIST_OF_STR_SCHEMA)
elif mod_name.startswith("tutorial003"):
tags_schema.update(SET_OF_STR_SCHEMA)
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"put": {
"parameters": [
{
"in": "path",
"name": "item_id",
"required": True,
"schema": {
"title": "Item Id",
"type": "integer",
},
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Update Item",
"operationId": "update_item_items__item_id__put",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item",
}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Item": {
"properties": {
"name": {
"title": "Name",
"type": "string",
},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"price": {
"title": "Price",
"type": "number",
},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
"tags": tags_schema,
},
"required": [
"name",
"price",
],
"title": "Item",
"type": "object",
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

275
tests/test_tutorial/test_body_nested_models/test_tutorial004.py

@ -0,0 +1,275 @@
import importlib
import pytest
from dirty_equals import IsList
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial004_py39"),
pytest.param("tutorial004_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
client = TestClient(mod.app)
return client
def test_put_all(client: TestClient):
response = client.put(
"/items/123",
json={
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
"tags": ["foo", "bar", "foo"],
"image": {"url": "http://example.com/image.png", "name": "example image"},
},
)
assert response.status_code == 200, response.text
assert response.json() == {
"item_id": 123,
"item": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
"tags": IsList("foo", "bar", check_order=False),
"image": {"url": "http://example.com/image.png", "name": "example image"},
},
}
def test_put_only_required(client: TestClient):
response = client.put(
"/items/5",
json={"name": "Foo", "price": 35.4},
)
assert response.status_code == 200, response.text
assert response.json() == {
"item_id": 5,
"item": {
"name": "Foo",
"description": None,
"price": 35.4,
"tax": None,
"tags": [],
"image": None,
},
}
def test_put_empty_body(client: TestClient):
response = client.put(
"/items/5",
json={},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "name"],
"input": {},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "price"],
"input": {},
"msg": "Field required",
"type": "missing",
},
]
}
def test_put_missing_required_in_item(client: TestClient):
response = client.put(
"/items/5",
json={"description": "A very nice Item"},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "name"],
"input": {"description": "A very nice Item"},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "price"],
"input": {"description": "A very nice Item"},
"msg": "Field required",
"type": "missing",
},
]
}
def test_put_missing_required_in_image(client: TestClient):
response = client.put(
"/items/5",
json={
"name": "Foo",
"price": 35.4,
"image": {"url": "http://example.com/image.png"},
},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "image", "name"],
"input": {"url": "http://example.com/image.png"},
"msg": "Field required",
"type": "missing",
},
]
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"put": {
"parameters": [
{
"in": "path",
"name": "item_id",
"required": True,
"schema": {
"title": "Item Id",
"type": "integer",
},
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Update Item",
"operationId": "update_item_items__item_id__put",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item",
}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Image": {
"properties": {
"url": {
"title": "Url",
"type": "string",
},
"name": {
"title": "Name",
"type": "string",
},
},
"required": ["url", "name"],
"title": "Image",
"type": "object",
},
"Item": {
"properties": {
"name": {
"title": "Name",
"type": "string",
},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"price": {
"title": "Price",
"type": "number",
},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
"tags": {
"title": "Tags",
"default": [],
"type": "array",
"items": {"type": "string"},
"uniqueItems": True,
},
"image": {
"anyOf": [
{"$ref": "#/components/schemas/Image"},
{"type": "null"},
],
},
},
"required": [
"name",
"price",
],
"title": "Item",
"type": "object",
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

301
tests/test_tutorial/test_body_nested_models/test_tutorial005.py

@ -0,0 +1,301 @@
import importlib
import pytest
from dirty_equals import IsList
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial005_py39"),
pytest.param("tutorial005_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
client = TestClient(mod.app)
return client
def test_put_all(client: TestClient):
response = client.put(
"/items/123",
json={
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
"tags": ["foo", "bar", "foo"],
"image": {"url": "http://example.com/image.png", "name": "example image"},
},
)
assert response.status_code == 200, response.text
assert response.json() == {
"item_id": 123,
"item": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
"tags": IsList("foo", "bar", check_order=False),
"image": {"url": "http://example.com/image.png", "name": "example image"},
},
}
def test_put_only_required(client: TestClient):
response = client.put(
"/items/5",
json={"name": "Foo", "price": 35.4},
)
assert response.status_code == 200, response.text
assert response.json() == {
"item_id": 5,
"item": {
"name": "Foo",
"description": None,
"price": 35.4,
"tax": None,
"tags": [],
"image": None,
},
}
def test_put_empty_body(client: TestClient):
response = client.put(
"/items/5",
json={},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "name"],
"input": {},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "price"],
"input": {},
"msg": "Field required",
"type": "missing",
},
]
}
def test_put_missing_required_in_item(client: TestClient):
response = client.put(
"/items/5",
json={"description": "A very nice Item"},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "name"],
"input": {"description": "A very nice Item"},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "price"],
"input": {"description": "A very nice Item"},
"msg": "Field required",
"type": "missing",
},
]
}
def test_put_missing_required_in_image(client: TestClient):
response = client.put(
"/items/5",
json={
"name": "Foo",
"price": 35.4,
"image": {"url": "http://example.com/image.png"},
},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "image", "name"],
"input": {"url": "http://example.com/image.png"},
"msg": "Field required",
"type": "missing",
},
]
}
def test_put_wrong_url(client: TestClient):
response = client.put(
"/items/5",
json={
"name": "Foo",
"price": 35.4,
"image": {"url": "not a valid url", "name": "example image"},
},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "image", "url"],
"input": "not a valid url",
"msg": "Input should be a valid URL, relative URL without a base",
"type": "url_parsing",
"ctx": {"error": "relative URL without a base"},
},
]
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"put": {
"parameters": [
{
"in": "path",
"name": "item_id",
"required": True,
"schema": {
"title": "Item Id",
"type": "integer",
},
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Update Item",
"operationId": "update_item_items__item_id__put",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item",
}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Image": {
"properties": {
"url": {
"title": "Url",
"type": "string",
"format": "uri",
"maxLength": 2083,
"minLength": 1,
},
"name": {
"title": "Name",
"type": "string",
},
},
"required": ["url", "name"],
"title": "Image",
"type": "object",
},
"Item": {
"properties": {
"name": {
"title": "Name",
"type": "string",
},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"price": {
"title": "Price",
"type": "number",
},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
"tags": {
"title": "Tags",
"default": [],
"type": "array",
"items": {"type": "string"},
"uniqueItems": True,
},
"image": {
"anyOf": [
{"$ref": "#/components/schemas/Image"},
{"type": "null"},
],
},
},
"required": [
"name",
"price",
],
"title": "Item",
"type": "object",
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

269
tests/test_tutorial/test_body_nested_models/test_tutorial006.py

@ -0,0 +1,269 @@
import importlib
import pytest
from dirty_equals import IsList
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial006_py39"),
pytest.param("tutorial006_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
client = TestClient(mod.app)
return client
def test_put_all(client: TestClient):
response = client.put(
"/items/123",
json={
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
"tags": ["foo", "bar", "foo"],
"images": [
{"url": "http://example.com/image.png", "name": "example image"}
],
},
)
assert response.status_code == 200, response.text
assert response.json() == {
"item_id": 123,
"item": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
"tags": IsList("foo", "bar", check_order=False),
"images": [
{"url": "http://example.com/image.png", "name": "example image"}
],
},
}
def test_put_only_required(client: TestClient):
response = client.put(
"/items/5",
json={"name": "Foo", "price": 35.4},
)
assert response.status_code == 200, response.text
assert response.json() == {
"item_id": 5,
"item": {
"name": "Foo",
"description": None,
"price": 35.4,
"tax": None,
"tags": [],
"images": None,
},
}
def test_put_empty_body(client: TestClient):
response = client.put(
"/items/5",
json={},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "name"],
"input": {},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "price"],
"input": {},
"msg": "Field required",
"type": "missing",
},
]
}
def test_put_images_not_list(client: TestClient):
response = client.put(
"/items/5",
json={
"name": "Foo",
"price": 35.4,
"images": {"url": "http://example.com/image.png", "name": "example image"},
},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "images"],
"input": {
"url": "http://example.com/image.png",
"name": "example image",
},
"msg": "Input should be a valid list",
"type": "list_type",
},
]
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"put": {
"parameters": [
{
"in": "path",
"name": "item_id",
"required": True,
"schema": {
"title": "Item Id",
"type": "integer",
},
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Update Item",
"operationId": "update_item_items__item_id__put",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item",
}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Image": {
"properties": {
"url": {
"title": "Url",
"type": "string",
"format": "uri",
"maxLength": 2083,
"minLength": 1,
},
"name": {
"title": "Name",
"type": "string",
},
},
"required": ["url", "name"],
"title": "Image",
"type": "object",
},
"Item": {
"properties": {
"name": {
"title": "Name",
"type": "string",
},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"price": {
"title": "Price",
"type": "number",
},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
"tags": {
"title": "Tags",
"default": [],
"type": "array",
"items": {"type": "string"},
"uniqueItems": True,
},
"images": {
"anyOf": [
{
"items": {
"$ref": "#/components/schemas/Image",
},
"type": "array",
},
{
"type": "null",
},
],
"title": "Images",
},
},
"required": [
"name",
"price",
],
"title": "Item",
"type": "object",
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

344
tests/test_tutorial/test_body_nested_models/test_tutorial007.py

@ -0,0 +1,344 @@
import importlib
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial007_py39"),
pytest.param("tutorial007_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
client = TestClient(mod.app)
return client
def test_post_all(client: TestClient):
data = {
"name": "Special Offer",
"description": "This is a special offer",
"price": 38.6,
"items": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
"tags": ["foo"],
"images": [
{
"url": "http://example.com/image.png",
"name": "example image",
}
],
}
],
}
response = client.post(
"/offers/",
json=data,
)
assert response.status_code == 200, response.text
assert response.json() == data
def test_put_only_required(client: TestClient):
response = client.post(
"/offers/",
json={
"name": "Special Offer",
"price": 38.6,
"items": [
{
"name": "Foo",
"price": 35.4,
"images": [
{
"url": "http://example.com/image.png",
"name": "example image",
}
],
}
],
},
)
assert response.status_code == 200, response.text
assert response.json() == {
"name": "Special Offer",
"description": None,
"price": 38.6,
"items": [
{
"name": "Foo",
"description": None,
"price": 35.4,
"tax": None,
"tags": [],
"images": [
{
"url": "http://example.com/image.png",
"name": "example image",
}
],
}
],
}
def test_put_empty_body(client: TestClient):
response = client.post(
"/offers/",
json={},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "name"],
"input": {},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "price"],
"input": {},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "items"],
"input": {},
"msg": "Field required",
"type": "missing",
},
]
}
def test_put_missing_required_in_items(client: TestClient):
response = client.post(
"/offers/",
json={
"name": "Special Offer",
"price": 38.6,
"items": [{}],
},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "items", 0, "name"],
"input": {},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "items", 0, "price"],
"input": {},
"msg": "Field required",
"type": "missing",
},
]
}
def test_put_missing_required_in_images(client: TestClient):
response = client.post(
"/offers/",
json={
"name": "Special Offer",
"price": 38.6,
"items": [
{"name": "Foo", "price": 35.4, "images": [{}]},
],
},
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", "items", 0, "images", 0, "url"],
"input": {},
"msg": "Field required",
"type": "missing",
},
{
"loc": ["body", "items", 0, "images", 0, "name"],
"input": {},
"msg": "Field required",
"type": "missing",
},
]
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/offers/": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Create Offer",
"operationId": "create_offer_offers__post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Offer",
}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Image": {
"properties": {
"url": {
"title": "Url",
"type": "string",
"format": "uri",
"maxLength": 2083,
"minLength": 1,
},
"name": {
"title": "Name",
"type": "string",
},
},
"required": ["url", "name"],
"title": "Image",
"type": "object",
},
"Item": {
"properties": {
"name": {
"title": "Name",
"type": "string",
},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"price": {
"title": "Price",
"type": "number",
},
"tax": {
"title": "Tax",
"anyOf": [{"type": "number"}, {"type": "null"}],
},
"tags": {
"title": "Tags",
"default": [],
"type": "array",
"items": {"type": "string"},
"uniqueItems": True,
},
"images": {
"anyOf": [
{
"items": {
"$ref": "#/components/schemas/Image",
},
"type": "array",
},
{
"type": "null",
},
],
"title": "Images",
},
},
"required": [
"name",
"price",
],
"title": "Item",
"type": "object",
},
"Offer": {
"properties": {
"name": {
"title": "Name",
"type": "string",
},
"description": {
"title": "Description",
"anyOf": [{"type": "string"}, {"type": "null"}],
},
"price": {
"title": "Price",
"type": "number",
},
"items": {
"title": "Items",
"type": "array",
"items": {"$ref": "#/components/schemas/Item"},
},
},
"required": ["name", "price", "items"],
"title": "Offer",
"type": "object",
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

157
tests/test_tutorial/test_body_nested_models/test_tutorial008.py

@ -0,0 +1,157 @@
import importlib
import pytest
from fastapi.testclient import TestClient
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial008_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
client = TestClient(mod.app)
return client
def test_post_body(client: TestClient):
data = [
{"url": "http://example.com/", "name": "Example"},
{"url": "http://fastapi.tiangolo.com/", "name": "FastAPI"},
]
response = client.post("/images/multiple", json=data)
assert response.status_code == 200, response.text
assert response.json() == data
def test_post_invalid_list_item(client: TestClient):
data = [{"url": "not a valid url", "name": "Example"}]
response = client.post("/images/multiple", json=data)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body", 0, "url"],
"input": "not a valid url",
"msg": "Input should be a valid URL, relative URL without a base",
"type": "url_parsing",
"ctx": {"error": "relative URL without a base"},
},
]
}
def test_post_not_a_list(client: TestClient):
data = {"url": "http://example.com/", "name": "Example"}
response = client.post("/images/multiple", json=data)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"loc": ["body"],
"input": {
"name": "Example",
"url": "http://example.com/",
},
"msg": "Input should be a valid list",
"type": "list_type",
}
]
}
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/images/multiple/": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Create Multiple Images",
"operationId": "create_multiple_images_images_multiple__post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"title": "Images",
"type": "array",
"items": {"$ref": "#/components/schemas/Image"},
}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Image": {
"properties": {
"url": {
"title": "Url",
"type": "string",
"format": "uri",
"maxLength": 2083,
"minLength": 1,
},
"name": {
"title": "Name",
"type": "string",
},
},
"required": ["url", "name"],
"title": "Image",
"type": "object",
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save