Browse Source

📝 Update all docs to use `Annotated` as the main recommendation, with new examples and tests (#9268)

* 🍱 Add new source examples with Annotated for Query Params and String Validations

* 📝 Add new docs with Annotated for Query Params and String Validations

* 🚚 Rename incorrectly named tests for Query Params and str validations

*  Add new tests with Annotated for Query Params and Sring Validations examples

* 🍱 Add new examples with Annotated for Intro to Python Types

* 📝 Update Python Types Intro, include Annotated

* 🎨 Fix formatting in Query params and string validation, and highlight

* 🍱 Add new Annotated source examples for Path Params and Numeric Validations

* 📝 Update docs for Path Params and Numeric Validations with Annotated

* 🍱 Add new source examples with Annotated for Body - Multiple Params

* 📝 Update docs with Annotated for Body - Multiple Parameters

*  Add test for new Annotated examples in Body - Multiple Parameters

* 🍱 Add new Annotated source examples for Body Fields

* 📝 Update docs for Body Fields with new Annotated examples

*  Add new tests for new Annotated examples for Body Fields

* 🍱 Add new Annotated source examples for Schema Extra (Example Data)

* 📝 Update docs for Schema Extra with Annotated

*  Add tests for new Annotated examples for Schema Extra

* 🍱 Add new Annnotated source examples for Extra Data Types

* 📝 Update docs with Annotated for Extra Data Types

*  Add tests for new Annotated examples for Extra Data Types

* 🍱 Add new Annotated source examples for Cookie Parameters

* 📝 Update docs for Cookie Parameters with Annotated examples

*  Add tests for new Annotated source examples in Cookie Parameters

* 🍱 Add new Annotated examples for Header Params

* 📝 Update docs with Annotated examples for Header Parameters

*  Add tests for new Annotated examples for Header Params

* 🍱 Add new Annotated examples for Form Data

* 📝 Update Annotated docs for Form Data

*  Add tests for new Annotated examples in Form Data

* 🍱 Add new Annotated source examples for Request Files

* 📝 Update Annotated docs for Request Files

*  Test new Annotated examples for Request Files

* 🍱 Add new Annotated source examples for Request Forms and Files

*  Add tests for new Anotated examples for Request Forms and Files

* 🍱 Add new Annotated source examples for Dependencies and Advanced Dependencies

*  Add tests for new Annotated dependencies

* 📝 Add new docs for using Annotated with dependencies including type aliases

* 📝 Update docs for Classes as Dependencies with Annotated

* 📝 Update docs for Sub-dependencies with Annotated

* 📝 Update docs for Dependencies in path operation decorators with Annotated

* 📝 Update docs for Global Dependencies with Annotated

* 📝 Update docs for Dependencies with yield with Annotated

* 🎨 Update format in example for dependencies with Annotated

* 🍱 Add source examples with Annotated for Security

*  Add tests for new Annotated examples for security

* 📝 Update docs for Security - First Steps with Annotated

* 📝 Update docs for Security: Get Current User with Annotated

* 📝 Update docs for Simple OAuth2 with Password and Bearer with Annotated

* 📝 Update docs for OAuth2 with Password (and hashing), Bearer with JWT tokens with Annotated

* 📝 Update docs for Request Forms and Files with Annotated

* 🍱 Add new source examples for Bigger Applications with Annotated

*  Add new tests for Bigger Applications with Annotated

* 📝 Update docs for Bigger Applications - Multiple Files with Annotated

* 🍱 Add source examples for background tasks with Annotated

* 📝 Update docs for Background Tasks with Annotated

*  Add test for Background Tasks with Anotated

* 🍱 Add new source examples for docs for Testing with Annotated

* 📝 Update docs for Testing with Annotated

*  Add tests for Annotated examples for Testing

* 🍱 Add new source examples for Additional Status Codes with Annotated

*  Add tests for new Annotated examples for Additional Status Codes

* 📝 Update docs for Additional Status Codes with Annotated

* 📝 Update docs for Advanced Dependencies with Annotated

* 📝 Update docs for OAuth2 scopes with Annotated

* 📝 Update docs for HTTP Basic Auth with Annotated

* 🍱 Add source examples with Annotated for WebSockets

*  Add tests for new Annotated examples for WebSockets

* 📝 Update docs for WebSockets with new Annotated examples

* 🍱 Add source examples with Annotated for Settings and Environment Variables

* 📝 Update docs for Settings and Environment Variables with Annotated

* 🍱 Add new source examples for testing dependencies with Annotated

*  Add tests for new examples for testing dependencies

* 📝 Update docs for testing dependencies with new Annotated examples

*  Update and fix marker for Python 3.9 test

* 🔧 Update Ruff ignores for source examples in docs

*  Fix some tests in the grid for Python 3.9 (incorrectly testing 3.10)

* 🔥 Remove source examples and tests for (non existent) docs section about Annotated, as it's covered in all the rest of the docs
pull/9269/head
Sebastián Ramírez 2 years ago
committed by GitHub
parent
commit
9eaed2eb37
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 38
      docs/en/docs/advanced/additional-status-codes.md
  2. 92
      docs/en/docs/advanced/advanced-dependencies.md
  3. 69
      docs/en/docs/advanced/security/http-basic-auth.md
  4. 376
      docs/en/docs/advanced/security/oauth2-scopes.md
  5. 69
      docs/en/docs/advanced/settings.md
  6. 38
      docs/en/docs/advanced/testing-dependencies.md
  7. 52
      docs/en/docs/advanced/websockets.md
  8. 45
      docs/en/docs/python-types.md
  9. 26
      docs/en/docs/tutorial/background-tasks.md
  10. 23
      docs/en/docs/tutorial/bigger-applications.md
  11. 52
      docs/en/docs/tutorial/body-fields.md
  12. 106
      docs/en/docs/tutorial/body-multiple-params.md
  13. 52
      docs/en/docs/tutorial/cookie-params.md
  14. 291
      docs/en/docs/tutorial/dependencies/classes-as-dependencies.md
  15. 92
      docs/en/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
  16. 46
      docs/en/docs/tutorial/dependencies/dependencies-with-yield.md
  17. 24
      docs/en/docs/tutorial/dependencies/global-dependencies.md
  18. 119
      docs/en/docs/tutorial/dependencies/index.md
  19. 98
      docs/en/docs/tutorial/dependencies/sub-dependencies.md
  20. 52
      docs/en/docs/tutorial/extra-data-types.md
  21. 109
      docs/en/docs/tutorial/header-params.md
  22. 183
      docs/en/docs/tutorial/path-params-numeric-validations.md
  23. 542
      docs/en/docs/tutorial/query-params-str-validations.md
  24. 165
      docs/en/docs/tutorial/request-files.md
  25. 46
      docs/en/docs/tutorial/request-forms-and-files.md
  26. 46
      docs/en/docs/tutorial/request-forms.md
  27. 46
      docs/en/docs/tutorial/schema-extra-example.md
  28. 70
      docs/en/docs/tutorial/security/first-steps.md
  29. 153
      docs/en/docs/tutorial/security/get-current-user.md
  30. 104
      docs/en/docs/tutorial/security/oauth2-jwt.md
  31. 132
      docs/en/docs/tutorial/security/simple-oauth2.md
  32. 26
      docs/en/docs/tutorial/testing.md
  33. 26
      docs_src/additional_status_codes/tutorial001_an.py
  34. 25
      docs_src/additional_status_codes/tutorial001_an_py310.py
  35. 25
      docs_src/additional_status_codes/tutorial001_an_py39.py
  36. 23
      docs_src/additional_status_codes/tutorial001_py310.py
  37. 18
      docs_src/annotated/tutorial001.py
  38. 17
      docs_src/annotated/tutorial001_py39.py
  39. 21
      docs_src/annotated/tutorial002.py
  40. 20
      docs_src/annotated/tutorial002_py39.py
  41. 15
      docs_src/annotated/tutorial003.py
  42. 16
      docs_src/annotated/tutorial003_py39.py
  43. 0
      docs_src/app_testing/app_b_an/__init__.py
  44. 39
      docs_src/app_testing/app_b_an/main.py
  45. 65
      docs_src/app_testing/app_b_an/test_main.py
  46. 0
      docs_src/app_testing/app_b_an_py310/__init__.py
  47. 38
      docs_src/app_testing/app_b_an_py310/main.py
  48. 65
      docs_src/app_testing/app_b_an_py310/test_main.py
  49. 0
      docs_src/app_testing/app_b_an_py39/__init__.py
  50. 38
      docs_src/app_testing/app_b_an_py39/main.py
  51. 65
      docs_src/app_testing/app_b_an_py39/test_main.py
  52. 27
      docs_src/background_tasks/tutorial002_an.py
  53. 26
      docs_src/background_tasks/tutorial002_an_py310.py
  54. 26
      docs_src/background_tasks/tutorial002_an_py39.py
  55. 0
      docs_src/bigger_applications/app_an/__init__.py
  56. 12
      docs_src/bigger_applications/app_an/dependencies.py
  57. 0
      docs_src/bigger_applications/app_an/internal/__init__.py
  58. 8
      docs_src/bigger_applications/app_an/internal/admin.py
  59. 23
      docs_src/bigger_applications/app_an/main.py
  60. 0
      docs_src/bigger_applications/app_an/routers/__init__.py
  61. 38
      docs_src/bigger_applications/app_an/routers/items.py
  62. 18
      docs_src/bigger_applications/app_an/routers/users.py
  63. 0
      docs_src/bigger_applications/app_an_py39/__init__.py
  64. 13
      docs_src/bigger_applications/app_an_py39/dependencies.py
  65. 0
      docs_src/bigger_applications/app_an_py39/internal/__init__.py
  66. 8
      docs_src/bigger_applications/app_an_py39/internal/admin.py
  67. 23
      docs_src/bigger_applications/app_an_py39/main.py
  68. 0
      docs_src/bigger_applications/app_an_py39/routers/__init__.py
  69. 38
      docs_src/bigger_applications/app_an_py39/routers/items.py
  70. 18
      docs_src/bigger_applications/app_an_py39/routers/users.py
  71. 22
      docs_src/body_fields/tutorial001_an.py
  72. 21
      docs_src/body_fields/tutorial001_an_py310.py
  73. 21
      docs_src/body_fields/tutorial001_an_py39.py
  74. 28
      docs_src/body_multiple_params/tutorial001_an.py
  75. 27
      docs_src/body_multiple_params/tutorial001_an_py310.py
  76. 27
      docs_src/body_multiple_params/tutorial001_an_py39.py
  77. 27
      docs_src/body_multiple_params/tutorial003_an.py
  78. 26
      docs_src/body_multiple_params/tutorial003_an_py310.py
  79. 26
      docs_src/body_multiple_params/tutorial003_an_py39.py
  80. 34
      docs_src/body_multiple_params/tutorial004_an.py
  81. 33
      docs_src/body_multiple_params/tutorial004_an_py310.py
  82. 33
      docs_src/body_multiple_params/tutorial004_an_py39.py
  83. 20
      docs_src/body_multiple_params/tutorial005_an.py
  84. 19
      docs_src/body_multiple_params/tutorial005_an_py310.py
  85. 19
      docs_src/body_multiple_params/tutorial005_an_py39.py
  86. 11
      docs_src/cookie_params/tutorial001_an.py
  87. 10
      docs_src/cookie_params/tutorial001_an_py310.py
  88. 10
      docs_src/cookie_params/tutorial001_an_py39.py
  89. 25
      docs_src/dependencies/tutorial001_02_an.py
  90. 22
      docs_src/dependencies/tutorial001_02_an_py310.py
  91. 24
      docs_src/dependencies/tutorial001_02_an_py39.py
  92. 22
      docs_src/dependencies/tutorial001_an.py
  93. 19
      docs_src/dependencies/tutorial001_an_py310.py
  94. 21
      docs_src/dependencies/tutorial001_an_py39.py
  95. 26
      docs_src/dependencies/tutorial002_an.py
  96. 25
      docs_src/dependencies/tutorial002_an_py310.py
  97. 25
      docs_src/dependencies/tutorial002_an_py39.py
  98. 26
      docs_src/dependencies/tutorial003_an.py
  99. 25
      docs_src/dependencies/tutorial003_an_py310.py
  100. 25
      docs_src/dependencies/tutorial003_an_py39.py

38
docs/en/docs/advanced/additional-status-codes.md

@ -14,9 +14,41 @@ But you also want it to accept new items. And when the items didn't exist before
To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want:
```Python hl_lines="4 25"
{!../../../docs_src/additional_status_codes/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="4 26"
{!> ../../../docs_src/additional_status_codes/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="4 25"
{!> ../../../docs_src/additional_status_codes/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="4 25"
{!> ../../../docs_src/additional_status_codes/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="4 25"
{!> ../../../docs_src/additional_status_codes/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="2 23"
{!> ../../../docs_src/additional_status_codes/tutorial001_py310.py!}
```
!!! warning
When you return a `Response` directly, like in the example above, it will be returned directly.

92
docs/en/docs/advanced/advanced-dependencies.md

@ -18,9 +18,26 @@ Not the class itself (which is already a callable), but an instance of that clas
To do that, we declare a method `__call__`:
```Python hl_lines="10"
{!../../../docs_src/dependencies/tutorial011.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="11"
{!> ../../../docs_src/dependencies/tutorial011_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10"
{!> ../../../docs_src/dependencies/tutorial011.py!}
```
In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later.
@ -28,9 +45,26 @@ In this case, this `__call__` is what **FastAPI** will use to check for addition
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
```Python hl_lines="7"
{!../../../docs_src/dependencies/tutorial011.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="8"
{!> ../../../docs_src/dependencies/tutorial011_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/dependencies/tutorial011.py!}
```
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
@ -38,9 +72,26 @@ In this case, **FastAPI** won't ever touch or care about `__init__`, we will use
We could create an instance of this class with:
```Python hl_lines="16"
{!../../../docs_src/dependencies/tutorial011.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial011_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="18"
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="16"
{!> ../../../docs_src/dependencies/tutorial011.py!}
```
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
@ -56,9 +107,26 @@ checker(q="somequery")
...and pass whatever that returns as the value of the dependency in our *path operation function* as the parameter `fixed_content_included`:
```Python hl_lines="20"
{!../../../docs_src/dependencies/tutorial011.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="21"
{!> ../../../docs_src/dependencies/tutorial011_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="22"
{!> ../../../docs_src/dependencies/tutorial011_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="20"
{!> ../../../docs_src/dependencies/tutorial011.py!}
```
!!! tip
All this might seem contrived. And it might not be very clear how is it useful yet.

69
docs/en/docs/advanced/security/http-basic-auth.md

@ -20,9 +20,26 @@ Then, when you type that username and password, the browser sends them in the he
* It returns an object of type `HTTPBasicCredentials`:
* It contains the `username` and `password` sent.
```Python hl_lines="2 6 10"
{!../../../docs_src/security/tutorial006.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="2 7 11"
{!> ../../../docs_src/security/tutorial006_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="4 8 12"
{!> ../../../docs_src/security/tutorial006_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="2 6 10"
{!> ../../../docs_src/security/tutorial006.py!}
```
When you try to open the URL for the first time (or click the "Execute" button in the docs) the browser will ask you for your username and password:
@ -42,9 +59,26 @@ To handle that, we first convert the `username` and `password` to `bytes` encodi
Then we can use `secrets.compare_digest()` to ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`.
```Python hl_lines="1 11-21"
{!../../../docs_src/security/tutorial007.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="1 12-24"
{!> ../../../docs_src/security/tutorial007_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="1 12-24"
{!> ../../../docs_src/security/tutorial007_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1 11-21"
{!> ../../../docs_src/security/tutorial007.py!}
```
This would be similar to:
@ -108,6 +142,23 @@ That way, using `secrets.compare_digest()` in your application code, it will be
After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again:
```Python hl_lines="23-27"
{!../../../docs_src/security/tutorial007.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="26-30"
{!> ../../../docs_src/security/tutorial007_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="26-30"
{!> ../../../docs_src/security/tutorial007_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="23-27"
{!> ../../../docs_src/security/tutorial007.py!}
```

376
docs/en/docs/advanced/security/oauth2-scopes.md

@ -56,9 +56,50 @@ They are normally used to declare specific security permissions, for example:
First, let's quickly see the parts that change from the examples in the main **Tutorial - User Guide** for [OAuth2 with Password (and hashing), Bearer with JWT tokens](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Now using OAuth2 scopes:
```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="2 4 8 12 47 65 106 108-116 122-125 129-135 140 156"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 155"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="4 8 12 46 64 105 107-115 121-124 128-134 139 155"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153"
{!> ../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="3 7 11 45 63 104 106-114 120-123 127-133 138 152"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
Now let's review those changes step by step.
@ -68,9 +109,50 @@ The first change is that now we are declaring the OAuth2 security scheme with tw
The `scopes` parameter receives a `dict` with each scope as a key and the description as the value:
```Python hl_lines="62-65"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="63-66"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="62-65"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="62-65"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="62-65"
{!> ../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="62-65"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="61-64"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
Because we are now declaring those scopes, they will show up in the API docs when you log-in/authorize.
@ -93,9 +175,50 @@ And we return the scopes as part of the JWT token.
But in your application, for security, you should make sure you only add the scopes that the user is actually able to have, or the ones you have predefined.
```Python hl_lines="153"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="156"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="155"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="155"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="153"
{!> ../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="153"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="152"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
## Declare scopes in *path operations* and dependencies
@ -118,9 +241,50 @@ In this case, it requires the scope `me` (it could require more than one scope).
We are doing it here to demonstrate how **FastAPI** handles scopes declared at different levels.
```Python hl_lines="4 139 166"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="4 140 171"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="4 139 170"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="4 139 170"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="4 139 166"
{!> ../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="4 139 166"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="3 138 165"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
!!! info "Technical Details"
`Security` is actually a subclass of `Depends`, and it has just one extra parameter that we'll see later.
@ -143,9 +307,50 @@ We also declare a special parameter of type `SecurityScopes`, imported from `fas
This `SecurityScopes` class is similar to `Request` (`Request` was used to get the request object directly).
```Python hl_lines="8 105"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="8 106"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="8 105"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="8 105"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8 105"
{!> ../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8 105"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7 104"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
## Use the `scopes`
@ -159,9 +364,50 @@ We create an `HTTPException` that we can re-use (`raise`) later at several point
In this exception, we include the scopes required (if any) as a string separated by spaces (using `scope_str`). We put that string containing the scopes in the `WWW-Authenticate` header (this is part of the spec).
```Python hl_lines="105 107-115"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="106 108-116"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="105 107-115"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="105 107-115"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="105 107-115"
{!> ../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="105 107-115"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="104 106-114"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
## Verify the `username` and data shape
@ -177,9 +423,50 @@ Instead of, for example, a `dict`, or something else, as it could break the appl
We also verify that we have a user with that username, and if not, we raise that same exception we created before.
```Python hl_lines="46 116-127"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="47 117-128"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="46 116-127"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="46 116-127"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="46 116-127"
{!> ../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="46 116-127"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="45 115-126"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
## Verify the `scopes`
@ -187,9 +474,50 @@ We now verify that all the scopes required, by this dependency and all the depen
For this, we use `security_scopes.scopes`, that contains a `list` with all these scopes as `str`.
```Python hl_lines="128-134"
{!../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="129-135"
{!> ../../../docs_src/security/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="128-134"
{!> ../../../docs_src/security/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="128-134"
{!> ../../../docs_src/security/tutorial005_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="128-134"
{!> ../../../docs_src/security/tutorial005.py!}
```
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="128-134"
{!> ../../../docs_src/security/tutorial005_py39.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="127-133"
{!> ../../../docs_src/security/tutorial005_py310.py!}
```
## Dependency tree and scopes

69
docs/en/docs/advanced/settings.md

@ -216,9 +216,26 @@ Notice that now we don't create a default instance `settings = Settings()`.
Now we create a dependency that returns a new `config.Settings()`.
```Python hl_lines="5 11-12"
{!../../../docs_src/settings/app02/main.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="6 12-13"
{!> ../../../docs_src/settings/app02_an/main.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="6 12-13"
{!> ../../../docs_src/settings/app02_an_py39/main.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="5 11-12"
{!> ../../../docs_src/settings/app02/main.py!}
```
!!! tip
We'll discuss the `@lru_cache()` in a bit.
@ -227,9 +244,26 @@ Now we create a dependency that returns a new `config.Settings()`.
And then we can require it from the *path operation function* as a dependency and use it anywhere we need it.
```Python hl_lines="16 18-20"
{!../../../docs_src/settings/app02/main.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="17 19-21"
{!> ../../../docs_src/settings/app02_an/main.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="17 19-21"
{!> ../../../docs_src/settings/app02_an_py39/main.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="16 18-20"
{!> ../../../docs_src/settings/app02/main.py!}
```
### Settings and testing
@ -304,9 +338,26 @@ we would create that object for each request, and we would be reading the `.env`
But as we are using the `@lru_cache()` decorator on top, the `Settings` object will be created only once, the first time it's called. ✔️
```Python hl_lines="1 10"
{!../../../docs_src/settings/app03/main.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="1 11"
{!> ../../../docs_src/settings/app03_an/main.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="1 11"
{!> ../../../docs_src/settings/app03_an_py39/main.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1 10"
{!> ../../../docs_src/settings/app03/main.py!}
```
Then for any subsequent calls of `get_settings()` in the dependencies for the next requests, instead of executing the internal code of `get_settings()` and creating a new `Settings` object, it will return the same object that was returned on the first call, again and again.

38
docs/en/docs/advanced/testing-dependencies.md

@ -28,9 +28,41 @@ To override a dependency for testing, you put as a key the original dependency (
And then **FastAPI** will call that override instead of the original dependency.
```Python hl_lines="28-29 32"
{!../../../docs_src/dependency_testing/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="29-30 33"
{!> ../../../docs_src/dependency_testing/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="28-29 32"
{!> ../../../docs_src/dependency_testing/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="26-27 30"
{!> ../../../docs_src/dependency_testing/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="28-29 32"
{!> ../../../docs_src/dependency_testing/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="24-25 28"
{!> ../../../docs_src/dependency_testing/tutorial001_py310.py!}
```
!!! tip
You can set a dependency override for a dependency used anywhere in your **FastAPI** application.

52
docs/en/docs/advanced/websockets.md

@ -112,9 +112,41 @@ In WebSocket endpoints you can import from `fastapi` and use:
They work the same way as for other FastAPI endpoints/*path operations*:
```Python hl_lines="66-77 76-91"
{!../../../docs_src/websockets/tutorial002.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="69-70 83"
{!> ../../../docs_src/websockets/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="68-69 82"
{!> ../../../docs_src/websockets/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="68-69 82"
{!> ../../../docs_src/websockets/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="68-69 81"
{!> ../../../docs_src/websockets/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="66-67 79"
{!> ../../../docs_src/websockets/tutorial002_py310.py!}
```
!!! info
As this is a WebSocket it doesn't really make sense to raise an `HTTPException`, instead we raise a `WebSocketException`.
@ -153,9 +185,17 @@ With that you can connect the WebSocket and then send and receive messages:
When a WebSocket connection is closed, the `await websocket.receive_text()` will raise a `WebSocketDisconnect` exception, which you can then catch and handle like in this example.
```Python hl_lines="81-83"
{!../../../docs_src/websockets/tutorial003.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="81-83"
{!> ../../../docs_src/websockets/tutorial003.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="79-81"
{!> ../../../docs_src/websockets/tutorial003_py39.py!}
```
To try it out:

45
docs/en/docs/python-types.md

@ -1,8 +1,8 @@
# Python Types Intro
Python has support for optional "type hints".
Python has support for optional "type hints" (also called "type annotations").
These **"type hints"** are a special syntax that allow declaring the <abbr title="for example: str, int, float, bool">type</abbr> of a variable.
These **"type hints"** or annotations are a special syntax that allow declaring the <abbr title="for example: str, int, float, bool">type</abbr> of a variable.
By declaring types for your variables, editors and tools can give you better support.
@ -422,6 +422,10 @@ And then, again, you get all the editor support:
<img src="/img/python-types/image06.png">
Notice that this means "`one_person` is an **instance** of the class `Person`".
It doesn't mean "`one_person` is the **class** called `Person`".
## Pydantic models
<a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> is a Python library to perform data validation.
@ -464,6 +468,43 @@ You will see a lot more of all this in practice in the [Tutorial - User Guide](t
!!! tip
Pydantic has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about <a href="https://pydantic-docs.helpmanual.io/usage/models/#required-optional-fields" class="external-link" target="_blank">Required Optional fields</a>.
## Type Hints with Metadata Annotations
Python also has a feature that allows putting **additional metadata** in these type hints using `Annotated`.
=== "Python 3.7 and above"
In versions below Python 3.9, you import `Annotated` from `typing_extensions`.
It will already be installed with **FastAPI**.
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial013.py!}
```
=== "Python 3.9 and above"
In Python 3.9, `Annotated` is part of the standard library, so you can import it from `typing`.
```Python hl_lines="1 4"
{!> ../../../docs_src/python_types/tutorial013_py39.py!}
```
Python itself doesn't do anything with this `Annotated`. And for editors and other tools, the type is still `str`.
But you can use this space in `Annotated` to provide **FastAPI** with additional metadata about how you want your application to behave.
The important thing to remember is that **the first *type parameter*** you pass to `Annotated` is the **actual type**. The rest, is just metadata for other tools.
For now, you just need to know that `Annotated` exists, and that it's standard Python. 😎
Later you will see how **powerful** it can be.
!!! tip
The fact that this is **standard Python** means that you will still get the **best possible developer experience** in your editor, with the tools you use to analyze and refactor your code, etc. ✨
And also that your code will be very compatible with many other Python tools and libraries. 🚀
## Type hints in **FastAPI**
**FastAPI** takes advantage of these type hints to do several things.

26
docs/en/docs/tutorial/background-tasks.md

@ -59,12 +59,36 @@ Using `BackgroundTasks` also works with the dependency injection system, you can
=== "Python 3.6 and above"
```Python hl_lines="14 16 23 26"
{!> ../../../docs_src/background_tasks/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="13 15 22 25"
{!> ../../../docs_src/background_tasks/tutorial002.py!}
{!> ../../../docs_src/background_tasks/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="13 15 22 25"
{!> ../../../docs_src/background_tasks/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="13 15 22 25"
{!> ../../../docs_src/background_tasks/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="11 13 20 23"
{!> ../../../docs_src/background_tasks/tutorial002_py310.py!}
```

23
docs/en/docs/tutorial/bigger-applications.md

@ -112,9 +112,26 @@ So we put them in their own `dependencies` module (`app/dependencies.py`).
We will now use a simple dependency to read a custom `X-Token` header:
```Python hl_lines="1 4-6"
{!../../../docs_src/bigger_applications/app/dependencies.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="1 5-7"
{!> ../../../docs_src/bigger_applications/app_an/dependencies.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="3 6-8"
{!> ../../../docs_src/bigger_applications/app_an_py39/dependencies.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1 4-6"
{!> ../../../docs_src/bigger_applications/app/dependencies.py!}
```
!!! tip
We are using an invented header to simplify this example.

52
docs/en/docs/tutorial/body-fields.md

@ -9,11 +9,35 @@ First, you have to import it:
=== "Python 3.6 and above"
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001.py!}
{!> ../../../docs_src/body_fields/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="4"
{!> ../../../docs_src/body_fields/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="2"
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
```
@ -27,12 +51,36 @@ You can then use `Field` with model attributes:
=== "Python 3.6 and above"
```Python hl_lines="12-15"
{!> ../../../docs_src/body_fields/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="11-14"
{!> ../../../docs_src/body_fields/tutorial001.py!}
{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="11-14"
{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="11-14"
{!> ../../../docs_src/body_fields/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9-12"
{!> ../../../docs_src/body_fields/tutorial001_py310.py!}
```

106
docs/en/docs/tutorial/body-multiple-params.md

@ -11,11 +11,35 @@ And you can also declare body parameters as optional, by setting the default to
=== "Python 3.6 and above"
```Python hl_lines="19-21"
{!> ../../../docs_src/body_multiple_params/tutorial001.py!}
{!> ../../../docs_src/body_multiple_params/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="18-20"
{!> ../../../docs_src/body_multiple_params/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="18-20"
{!> ../../../docs_src/body_multiple_params/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="19-21"
{!> ../../../docs_src/body_multiple_params/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="17-19"
{!> ../../../docs_src/body_multiple_params/tutorial001_py310.py!}
```
@ -89,11 +113,35 @@ But you can instruct **FastAPI** to treat it as another body key using `Body`:
=== "Python 3.6 and above"
```Python hl_lines="24"
{!> ../../../docs_src/body_multiple_params/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="23"
{!> ../../../docs_src/body_multiple_params/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="23"
{!> ../../../docs_src/body_multiple_params/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="22"
{!> ../../../docs_src/body_multiple_params/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="20"
{!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!}
@ -139,13 +187,37 @@ For example:
=== "Python 3.6 and above"
```Python hl_lines="28"
{!> ../../../docs_src/body_multiple_params/tutorial004_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="27"
{!> ../../../docs_src/body_multiple_params/tutorial004.py!}
{!> ../../../docs_src/body_multiple_params/tutorial004_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="26"
```Python hl_lines="27"
{!> ../../../docs_src/body_multiple_params/tutorial004_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="27"
{!> ../../../docs_src/body_multiple_params/tutorial004.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="25"
{!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!}
```
@ -168,12 +240,36 @@ as in:
=== "Python 3.6 and above"
```Python hl_lines="18"
{!> ../../../docs_src/body_multiple_params/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="17"
{!> ../../../docs_src/body_multiple_params/tutorial005.py!}
{!> ../../../docs_src/body_multiple_params/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="17"
{!> ../../../docs_src/body_multiple_params/tutorial005_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="17"
{!> ../../../docs_src/body_multiple_params/tutorial005.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="15"
{!> ../../../docs_src/body_multiple_params/tutorial005_py310.py!}
```

52
docs/en/docs/tutorial/cookie-params.md

@ -9,11 +9,35 @@ First import `Cookie`:
=== "Python 3.6 and above"
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
{!> ../../../docs_src/cookie_params/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="3"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1"
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
```
@ -26,12 +50,36 @@ The first value is the default value, you can pass all the extra validation or a
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/cookie_params/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
{!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="9"
{!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/cookie_params/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/cookie_params/tutorial001_py310.py!}
```

291
docs/en/docs/tutorial/dependencies/classes-as-dependencies.md

@ -8,11 +8,35 @@ In the previous example, we were returning a `dict` from our dependency ("depend
=== "Python 3.6 and above"
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="11"
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="11"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
@ -81,12 +105,36 @@ Then, we can change the dependency "dependable" `common_parameters` from above t
=== "Python 3.6 and above"
```Python hl_lines="12-16"
{!> ../../../docs_src/dependencies/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="11-15"
{!> ../../../docs_src/dependencies/tutorial002.py!}
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="11-15"
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="11-15"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9-13"
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
```
@ -95,12 +143,36 @@ Pay attention to the `__init__` method used to create the instance of the class:
=== "Python 3.6 and above"
```Python hl_lines="13"
{!> ../../../docs_src/dependencies/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial002.py!}
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="12"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10"
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
```
@ -109,12 +181,36 @@ Pay attention to the `__init__` method used to create the instance of the class:
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001.py!}
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="8"
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="6"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
@ -133,14 +229,39 @@ In both cases the data will be converted, validated, documented on the OpenAPI s
Now you can declare your dependency using this class.
=== "Python 3.6 and above"
```Python hl_lines="20"
{!> ../../../docs_src/dependencies/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial002.py!}
{!> ../../../docs_src/dependencies/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial002_py310.py!}
```
@ -151,14 +272,25 @@ Now you can declare your dependency using this class.
Notice how we write `CommonQueryParams` twice in the above code:
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
=== "Python 3.6 and above"
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
The last `CommonQueryParams`, in:
```Python
... = Depends(CommonQueryParams)
... Depends(CommonQueryParams)
```
...is what **FastAPI** will actually use to know what is the dependency.
@ -169,28 +301,74 @@ From it is that FastAPI will extract the declared parameters and that is what Fa
In this case, the first `CommonQueryParams`, in:
```Python
commons: CommonQueryParams ...
```
=== "Python 3.6 and above"
```Python
commons: Annotated[CommonQueryParams, ...
```
=== "Python 3.6 and above - non-Annotated"
...doesn't have any special meaning for **FastAPI**. FastAPI won't use it for data conversion, validation, etc. (as it is using the `= Depends(CommonQueryParams)` for that).
!!! tip
Try to use the main, `Annotated` version better.
```Python
commons: CommonQueryParams ...
```
...doesn't have any special meaning for **FastAPI**. FastAPI won't use it for data conversion, validation, etc. (as it is using the `Depends(CommonQueryParams)` for that).
You could actually write just:
```Python
commons = Depends(CommonQueryParams)
```
=== "Python 3.6 and above"
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python
commons = Depends(CommonQueryParams)
```
..as in:
=== "Python 3.6 and above"
```Python hl_lines="20"
{!> ../../../docs_src/dependencies/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial003.py!}
{!> ../../../docs_src/dependencies/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial003.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial003_py310.py!}
```
@ -203,9 +381,20 @@ But declaring the type is encouraged as that way your editor will know what will
But you see that we are having some code repetition here, writing `CommonQueryParams` twice:
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
=== "Python 3.6 and above"
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
**FastAPI** provides a shortcut for these cases, in where the dependency is *specifically* a class that **FastAPI** will "call" to create an instance of the class itself.
@ -213,28 +402,74 @@ For those specific cases, you can do the following:
Instead of writing:
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
=== "Python 3.6 and above"
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
...you write:
```Python
commons: CommonQueryParams = Depends()
```
=== "Python 3.6 and above"
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
You declare the dependency as the type of the parameter, and you use `Depends()` as its "default" value (that after the `=`) for that function's parameter, without any parameter in `Depends()`, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`.
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python
commons: CommonQueryParams = Depends()
```
You declare the dependency as the type of the parameter, and you use `Depends()` without any parameter, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`.
The same example would then look like:
=== "Python 3.6 and above"
```Python hl_lines="20"
{!> ../../../docs_src/dependencies/tutorial004_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial004.py!}
{!> ../../../docs_src/dependencies/tutorial004_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial004_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial004.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial004_py310.py!}
```

92
docs/en/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md

@ -14,9 +14,26 @@ The *path operation decorator* receives an optional argument `dependencies`.
It should be a `list` of `Depends()`:
```Python hl_lines="17"
{!../../../docs_src/dependencies/tutorial006.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="18"
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
```
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="17"
{!> ../../../docs_src/dependencies/tutorial006.py!}
```
These dependencies will be executed/solved the same way normal dependencies. But their value (if they return any) won't be passed to your *path operation function*.
@ -40,17 +57,51 @@ You can use the same dependency *functions* you use normally.
They can declare request requirements (like headers) or other sub-dependencies:
```Python hl_lines="6 11"
{!../../../docs_src/dependencies/tutorial006.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="7 12"
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="8 13"
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
```
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="6 11"
{!> ../../../docs_src/dependencies/tutorial006.py!}
```
### Raise exceptions
These dependencies can `raise` exceptions, the same as normal dependencies:
```Python hl_lines="8 13"
{!../../../docs_src/dependencies/tutorial006.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="9 14"
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10 15"
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
```
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8 13"
{!> ../../../docs_src/dependencies/tutorial006.py!}
```
### Return values
@ -58,9 +109,26 @@ And they can return values or not, the values won't be used.
So, you can re-use a normal dependency (that returns a value) you already use somewhere else, and even though the value won't be used, the dependency will be executed:
```Python hl_lines="9 14"
{!../../../docs_src/dependencies/tutorial006.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="10 15"
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="11 16"
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
```
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9 14"
{!> ../../../docs_src/dependencies/tutorial006.py!}
```
## Dependencies for a group of *path operations*

46
docs/en/docs/tutorial/dependencies/dependencies-with-yield.md

@ -66,9 +66,26 @@ You can have sub-dependencies and "trees" of sub-dependencies of any size and sh
For example, `dependency_c` can have a dependency on `dependency_b`, and `dependency_b` on `dependency_a`:
```Python hl_lines="4 12 20"
{!../../../docs_src/dependencies/tutorial008.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="5 13 21"
{!> ../../../docs_src/dependencies/tutorial008_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="6 14 22"
{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="4 12 20"
{!> ../../../docs_src/dependencies/tutorial008.py!}
```
And all of them can use `yield`.
@ -76,9 +93,26 @@ In this case `dependency_c`, to execute its exit code, needs the value from `dep
And, in turn, `dependency_b` needs the value from `dependency_a` (here named `dep_a`) to be available for its exit code.
```Python hl_lines="16-17 24-25"
{!../../../docs_src/dependencies/tutorial008.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="17-18 25-26"
{!> ../../../docs_src/dependencies/tutorial008_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="18-19 26-27"
{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="16-17 24-25"
{!> ../../../docs_src/dependencies/tutorial008.py!}
```
The same way, you could have dependencies with `yield` and `return` mixed.

24
docs/en/docs/tutorial/dependencies/global-dependencies.md

@ -6,9 +6,27 @@ Similar to the way you can [add `dependencies` to the *path operation decorators
In that case, they will be applied to all the *path operations* in the application:
```Python hl_lines="15"
{!../../../docs_src/dependencies/tutorial012.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="16"
{!> ../../../docs_src/dependencies/tutorial012_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="16"
{!> ../../../docs_src/dependencies/tutorial012_an_py39.py!}
```
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="15"
{!> ../../../docs_src/dependencies/tutorial012.py!}
```
And all the ideas in the section about [adding `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} still apply, but in this case, to all of the *path operations* in the app.

119
docs/en/docs/tutorial/dependencies/index.md

@ -33,12 +33,36 @@ It is just a function that can take all the same parameters that a *path operati
=== "Python 3.6 and above"
```Python hl_lines="9-12"
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="8-11"
{!> ../../../docs_src/dependencies/tutorial001.py!}
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="8-9"
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8-11"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="6-7"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
@ -66,11 +90,35 @@ And then it just returns a `dict` containing those values.
=== "Python 3.6 and above"
```Python hl_lines="3"
{!> ../../../docs_src/dependencies/tutorial001.py!}
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="3"
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="3"
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="3"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
@ -81,12 +129,36 @@ The same way you use `Body`, `Query`, etc. with your *path operation function* p
=== "Python 3.6 and above"
```Python hl_lines="16 21"
{!> ../../../docs_src/dependencies/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="15 20"
{!> ../../../docs_src/dependencies/tutorial001.py!}
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="13 18"
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="15 20"
{!> ../../../docs_src/dependencies/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="11 16"
{!> ../../../docs_src/dependencies/tutorial001_py310.py!}
```
@ -97,6 +169,8 @@ You only give `Depends` a single parameter.
This parameter must be something like a function.
You **don't call it** directly (don't add the parenthesis at the end), you just pass it as a parameter to `Depends()`.
And that function takes parameters in the same way that *path operation functions* do.
!!! tip
@ -126,6 +200,45 @@ This way you write shared code once and **FastAPI** takes care of calling it for
You just pass it to `Depends` and **FastAPI** knows how to do the rest.
## Share `Annotated` dependencies
In the examples above, you see that there's a tiny bit of **code duplication**.
When you need to use the `common_parameters()` dependency, you have to write the whole parameter with the type annotation and `Depends()`:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
But because we are using `Annotated`, we can store that `Annotated` value in a variable and use it in multiple places:
=== "Python 3.6 and above"
```Python hl_lines="15 19 24"
{!> ../../../docs_src/dependencies/tutorial001_02_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="14 18 23"
{!> ../../../docs_src/dependencies/tutorial001_02_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="12 16 21"
{!> ../../../docs_src/dependencies/tutorial001_02_an_py310.py!}
```
!!! tip
This is just standard Python, it's called a "type alias", it's actually not specific to **FastAPI**.
But because **FastAPI** is based on the Python standards, including `Annotated`, you can use this trick in your code. 😎
The dependencies will keep working as expected, and the **best part** is that the **type information will be preserved**, which means that your editor will be able to keep providing you with **autocompletion**, **inline errors**, etc. The same for other tools like `mypy`.
This will be especially useful when you use it in a **large code base** where you use **the same dependencies** over and over again in **many *path operations***.
## To `async` or not to `async`
As dependencies will also be called by **FastAPI** (the same as your *path operation functions*), the same rules apply while defining your functions.

98
docs/en/docs/tutorial/dependencies/sub-dependencies.md

@ -12,12 +12,36 @@ You could create a first dependency ("dependable") like:
=== "Python 3.6 and above"
```Python hl_lines="9-10"
{!> ../../../docs_src/dependencies/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="8-9"
{!> ../../../docs_src/dependencies/tutorial005.py!}
{!> ../../../docs_src/dependencies/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="8-9"
{!> ../../../docs_src/dependencies/tutorial005_an_py310.py!}
```
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8-9"
{!> ../../../docs_src/dependencies/tutorial005.py!}
```
=== "Python 3.10 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="6-7"
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
```
@ -32,12 +56,36 @@ Then you can create another dependency function (a "dependable") that at the sam
=== "Python 3.6 and above"
```Python hl_lines="14"
{!> ../../../docs_src/dependencies/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="13"
{!> ../../../docs_src/dependencies/tutorial005.py!}
{!> ../../../docs_src/dependencies/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="13"
{!> ../../../docs_src/dependencies/tutorial005_an_py310.py!}
```
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="13"
{!> ../../../docs_src/dependencies/tutorial005.py!}
```
=== "Python 3.10 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="11"
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
```
@ -55,11 +103,35 @@ Then we can use the dependency with:
=== "Python 3.6 and above"
```Python hl_lines="24"
{!> ../../../docs_src/dependencies/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="23"
{!> ../../../docs_src/dependencies/tutorial005_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="23"
{!> ../../../docs_src/dependencies/tutorial005_an_py310.py!}
```
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="22"
{!> ../../../docs_src/dependencies/tutorial005.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="19"
{!> ../../../docs_src/dependencies/tutorial005_py310.py!}
@ -89,10 +161,22 @@ And it will save the returned value in a <abbr title="A utility/system to store
In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`:
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
=== "Python 3.6 and above"
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
## Recap

52
docs/en/docs/tutorial/extra-data-types.md

@ -57,12 +57,36 @@ Here's an example *path operation* with parameters using some of the above types
=== "Python 3.6 and above"
```Python hl_lines="1 3 13-17"
{!> ../../../docs_src/extra_data_types/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="1 3 12-16"
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
{!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="1 3 12-16"
{!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1 2 12-16"
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1 2 11-15"
{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
```
@ -71,12 +95,36 @@ Note that the parameters inside the function have their natural data type, and y
=== "Python 3.6 and above"
```Python hl_lines="19-20"
{!> ../../../docs_src/extra_data_types/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="18-19"
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
{!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="18-19"
{!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="18-19"
{!> ../../../docs_src/extra_data_types/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="17-18"
{!> ../../../docs_src/extra_data_types/tutorial001_py310.py!}
```

109
docs/en/docs/tutorial/header-params.md

@ -9,11 +9,35 @@ First import `Header`:
=== "Python 3.6 and above"
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001.py!}
{!> ../../../docs_src/header_params/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="3"
{!> ../../../docs_src/header_params/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1"
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
```
@ -26,12 +50,36 @@ The first value is the default value, you can pass all the extra validation or a
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial001.py!}
{!> ../../../docs_src/header_params/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/header_params/tutorial001_py310.py!}
```
@ -62,11 +110,35 @@ If for some reason you need to disable automatic conversion of underscores to hy
=== "Python 3.6 and above"
```Python hl_lines="12"
{!> ../../../docs_src/header_params/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="11"
{!> ../../../docs_src/header_params/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial002.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8"
{!> ../../../docs_src/header_params/tutorial002_py310.py!}
@ -87,17 +159,44 @@ For example, to declare a header of `X-Token` that can appear more than once, yo
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/header_params/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/header_params/tutorial003_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/header_params/tutorial003_py310.py!}

183
docs/en/docs/tutorial/path-params-numeric-validations.md

@ -4,15 +4,39 @@ In the same way that you can declare more validations and metadata for query par
## Import Path
First, import `Path` from `fastapi`:
First, import `Path` from `fastapi`, and import `Annotated`:
=== "Python 3.6 and above"
```Python hl_lines="3-4"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="1 3"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="1 3"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="3"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
@ -26,12 +50,36 @@ For example, to declare a `title` metadata value for the path parameter `item_id
=== "Python 3.6 and above"
```Python hl_lines="11"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8"
{!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!}
```
@ -45,11 +93,14 @@ For example, to declare a `title` metadata value for the path parameter `item_id
## Order the parameters as you need
!!! tip
This is probably not as important or necessary if you use `Annotated`.
Let's say that you want to declare the query parameter `q` as a required `str`.
And you don't need to declare anything else for that parameter, so you don't really need to use `Query`.
But you still need to use `Path` for the `item_id` path parameter.
But you still need to use `Path` for the `item_id` path parameter. And you don't want to use `Annotated` for some reason.
Python will complain if you put a value with a "default" before a value that doesn't have a "default".
@ -59,13 +110,44 @@ It doesn't matter for **FastAPI**. It will detect the parameters by their names,
So, you can declare your function as:
```Python hl_lines="7"
{!../../../docs_src/path_params_numeric_validations/tutorial002.py!}
```
=== "Python 3.6 - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/path_params_numeric_validations/tutorial002.py!}
```
But have in mind that if you use `Annotated`, you won't have this problem, it won't matter as you're not using the function parameter default values for `Query()` or `Path()`.
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py!}
```
## Order the parameters as you need, tricks
If you want to declare the `q` query parameter without a `Query` nor any default value, and the path parameter `item_id` using `Path`, and have them in a different order, Python has a little special syntax for that.
!!! tip
This is probably not as important or necessary if you use `Annotated`.
Here's a **small trick** that can be handy, but you won't need it often.
If you want to:
* declare the `q` query parameter without a `Query` nor any default value
* declare the path parameter `item_id` using `Path`
* have them in a different order
* not use `Annotated`
...Python has a little special syntax for that.
Pass `*`, as the first parameter of the function.
@ -75,15 +157,48 @@ Python won't do anything with that `*`, but it will know that all the following
{!../../../docs_src/path_params_numeric_validations/tutorial003.py!}
```
### Better with `Annotated`
Have in mind that if you use `Annotated`, as you are not using function parameter default values, you won't have this problem, and yo probably won't need to use `*`.
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py!}
```
## Number validations: greater than or equal
With `Query` and `Path` (and others you'll see later) you can declare number constraints.
Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than or `e`qual" to `1`.
```Python hl_lines="8"
{!../../../docs_src/path_params_numeric_validations/tutorial004.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial004_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8"
{!> ../../../docs_src/path_params_numeric_validations/tutorial004.py!}
```
## Number validations: greater than and less than or equal
@ -92,9 +207,26 @@ The same applies for:
* `gt`: `g`reater `t`han
* `le`: `l`ess than or `e`qual
```Python hl_lines="9"
{!../../../docs_src/path_params_numeric_validations/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10"
{!> ../../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/path_params_numeric_validations/tutorial005.py!}
```
## Number validations: floats, greater than and less than
@ -106,9 +238,26 @@ So, `0.5` would be a valid value. But `0.0` or `0` would not.
And the same for <abbr title="less than"><code>lt</code></abbr>.
```Python hl_lines="11"
{!../../../docs_src/path_params_numeric_validations/tutorial006.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="12"
{!> ../../../docs_src/path_params_numeric_validations/tutorial006_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="13"
{!> ../../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="11"
{!> ../../../docs_src/path_params_numeric_validations/tutorial006.py!}
```
## Recap

542
docs/en/docs/tutorial/query-params-str-validations.md

@ -27,25 +27,103 @@ The query parameter `q` is of type `Union[str, None]` (or `str | None` in Python
We are going to enforce that even though `q` is optional, whenever it is provided, **its length doesn't exceed 50 characters**.
### Import `Query`
### Import `Query` and `Annotated`
To achieve that, first import `Query` from `fastapi`:
To achieve that, first import:
* `Query` from `fastapi`
* `Annotated` from `typing` (or from `typing_extensions` in Python below 3.9)
=== "Python 3.6 and above"
```Python hl_lines="3"
{!> ../../../docs_src/query_params_str_validations/tutorial002.py!}
In versions of Python below Python 3.9 you import `Annotation` from `typing_extensions`.
It will already be installed with FastAPI.
```Python hl_lines="3-4"
{!> ../../../docs_src/query_params_str_validations/tutorial002_an.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="1"
{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
In Python 3.9 or above, `Annotated` is part of the standard library, so you can import it from `typing`.
```Python hl_lines="1 3"
{!> ../../../docs_src/query_params_str_validations/tutorial002_an_py310.py!}
```
## Use `Annotated` in the type for the `q` parameter
Remember I told you before that `Annotated` can be used to add metadata to your parameters in the [Python Types Intro](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank}?
Now it's the time to use it with FastAPI. 🚀
We had this type annotation:
=== "Python 3.6 and above"
```Python
q: Union[str, None] = None
```
=== "Python 3.10 and above"
```Python
q: str | None = None
```
What we will do is wrap that with `Annotated`, so it becomes:
=== "Python 3.6 and above"
```Python
q: Annotated[Union[str, None]] = None
```
=== "Python 3.10 and above"
```Python
q: Annotated[str | None] = None
```
Both of those versions mean the same thing, `q` is a parameter that can be a `str` or `None`, and by default, it is `None`.
Now let's jump to the fun stuff. 🎉
## Add `Query` to `Annotated` in the `q` parameter
Now that we have this `Annotated` where we can put more metadata, add `Query` to it, and set the parameter `max_length` to 50:
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial002_an.py!}
```
## Use `Query` as the default value
=== "Python 3.10 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial002_an_py310.py!}
```
Notice that the default value is still `None`, so the parameter is still optional.
But now, having `Query(max_length=50)` inside of `Annotated`, we are telling FastAPI that we want it to extract this value from the query parameters (this would have been the default anyway 🤷) and that we want to have **additional validation** for this value (that's why we do this, to get the additional validation). 😎
FastAPI wll now:
* **Validate** the data making sure that the max length is 50 characters
* Show a **clear error** for the client when the data is not valid
* **Document** the parameter in the OpenAPI schema *path operation* (so it will show up in the **automatic docs UI**)
## Alternative (old) `Query` as the default value
Previous versions of FastAPI (before <abbr title="before 2023-03">0.95.0</abbr>) required you to use `Query` as the default value of your parameter, instead of putting it in `Annotated`, there's a high chance that you will see code using it around, so I'll explain it to you.
And now use it as the default value of your parameter, setting the parameter `max_length` to 50:
!!! tip
For new code and whenever possible, use `Annotated` as explained above. There are multiple advantages (explained below) and no disadvantages. 🍰
This is how you would use `Query()` as the default value of your function parameter, setting the parameter `max_length` to 50:
=== "Python 3.6 and above"
@ -59,7 +137,7 @@ And now use it as the default value of your parameter, setting the parameter `ma
{!> ../../../docs_src/query_params_str_validations/tutorial002_py310.py!}
```
As we have to replace the default value `None` in the function with `Query()`, we can now set the default value with the parameter `Query(default=None)`, it serves the same purpose of defining that default value.
As in this case (without using `Annotated`) we have to replace the default value `None` in the function with `Query()`, we now need to set the default value with the parameter `Query(default=None)`, it serves the same purpose of defining that default value (at least for FastAPI).
So:
@ -67,7 +145,7 @@ So:
q: Union[str, None] = Query(default=None)
```
...makes the parameter optional, the same as:
...makes the parameter optional, with a default value of `None`, the same as:
```Python
q: Union[str, None] = None
@ -79,7 +157,7 @@ And in Python 3.10 and above:
q: str | None = Query(default=None)
```
...makes the parameter optional, the same as:
...makes the parameter optional, with a default value of `None`, the same as:
```Python
q: str | None = None
@ -112,18 +190,80 @@ q: Union[str, None] = Query(default=None, max_length=50)
This will validate the data, show a clear error when the data is not valid, and document the parameter in the OpenAPI schema *path operation*.
### `Query` as the default value or in `Annotated`
Have in mind that when using `Query` inside of `Annotated` you cannot use the `default` parameter for `Query`.
Instead use the actual default value of the function parameter. Otherwise, it would be inconsistent.
For example, this is not allowed:
```Python
q: Annotated[str Query(default="rick")] = "morty"
```
...because it's not clear if the default value should be `"rick"` or `"morty"`.
So, you would use (preferably):
```Python
q: Annotated[str, Query()] = "rick"
```
...or in older code bases you will find:
```Python
q: str = Query(default="rick")
```
### Advantages of `Annotated`
**Using `Annotated` is recommended** instead of the default value in function parameters, it is **better** for multiple reasons. 🤓
The **default** value of the **function parameter** is the **actual default** value, that's more intuitive with Python in general. 😌
You could **call** that same function in **other places** without FastAPI, and it would **work as expected**. If there's a **required** parameter (without a default value), your **editor** will let you know with an error, **Python** will also complain if you run it without passing the required parameter.
When you don't use `Annotated` and instead use the **(old) default value style**, if you call that function without FastAPI in **other place**, you have to **remember** to pass the arguments to the function for it to work correctly, otherwise the values will be different from what you expect (e.g. `QueryInfo` or something similar instead of `str`). And your editor won't complain, and Python won't complain running that function, only when the operations inside error out.
Because `Annotated` can have more than one metadata annotation, you could now even use the same function with other tools, like <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
## Add more validations
You can also add a parameter `min_length`:
=== "Python 3.6 and above"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial003.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial003.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial003_py310.py!}
```
@ -134,12 +274,36 @@ You can define a <abbr title="A regular expression, regex or regexp is a sequenc
=== "Python 3.6 and above"
```Python hl_lines="12"
{!> ../../../docs_src/query_params_str_validations/tutorial004_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial004.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial004_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial004_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial004.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial004_py310.py!}
```
@ -156,16 +320,33 @@ But whenever you need them and go and learn them, know that you can already use
## Default values
The same way that you can pass `None` as the value for the `default` parameter, you can pass other values.
You can, of course, use default values other than `None`.
Let's say that you want to declare the `q` query parameter to have a `min_length` of `3`, and to have a default value of `"fixedquery"`:
```Python hl_lines="7"
{!../../../docs_src/query_params_str_validations/tutorial005.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial005_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial005_an_py39.py!}
```
=== "non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial005.py!}
```
!!! note
Having a default value also makes the parameter optional.
Having a default value of any type, including `None`, makes the parameter optional (not required).
## Make it required
@ -183,23 +364,70 @@ q: Union[str, None] = None
But we are now declaring it with `Query`, for example like:
```Python
q: Union[str, None] = Query(default=None, min_length=3)
```
=== "Annotated"
```Python
q: Annotated[Union[str, None], Query(min_length=3)] = None
```
=== "non-Annotated"
```Python
q: Union[str, None] = Query(default=None, min_length=3)
```
So, when you need to declare a value as required while using `Query`, you can simply not declare a default value:
```Python hl_lines="7"
{!../../../docs_src/query_params_str_validations/tutorial006.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial006_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial006.py!}
```
!!! tip
Notice that, even though in this case the `Query()` is used as the function parameter default value, we don't pass the `default=None` to `Query()`.
Still, probably better to use the `Annotated` version. 😉
### Required with Ellipsis (`...`)
There's an alternative way to explicitly declare that a value is required. You can set the `default` parameter to the literal value `...`:
There's an alternative way to explicitly declare that a value is required. You can set the default to the literal value `...`:
```Python hl_lines="7"
{!../../../docs_src/query_params_str_validations/tutorial006b.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial006b_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006b_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial006b.py!}
```
!!! info
If you hadn't seen that `...` before: it is a special single value, it is <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">part of Python and is called "Ellipsis"</a>.
@ -212,16 +440,40 @@ This will let **FastAPI** know that this parameter is required.
You can declare that a parameter can accept `None`, but that it's still required. This would force clients to send a value, even if the value is `None`.
To do that, you can declare that `None` is a valid type but still use `default=...`:
To do that, you can declare that `None` is a valid type but still use `...` as the default:
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial006c_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006c.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial006c_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006c_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial006c.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial006c_py310.py!}
```
@ -233,12 +485,29 @@ To do that, you can declare that `None` is a valid type but still use `default=.
If you feel uncomfortable using `...`, you can also import and use `Required` from Pydantic:
```Python hl_lines="2 8"
{!../../../docs_src/query_params_str_validations/tutorial006d.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="2 9"
{!> ../../../docs_src/query_params_str_validations/tutorial006d_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="4 10"
{!> ../../../docs_src/query_params_str_validations/tutorial006d_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="2 8"
{!> ../../../docs_src/query_params_str_validations/tutorial006d.py!}
```
!!! tip
Remember that in most of the cases, when something is required, you can simply omit the `default` parameter, so you normally don't have to use `...` nor `Required`.
Remember that in most of the cases, when something is required, you can simply omit the default, so you normally don't have to use `...` nor `Required`.
## Query parameter list / multiple values
@ -248,17 +517,44 @@ For example, to declare a query parameter `q` that can appear multiple times in
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial011_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial011_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial011_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial011.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial011_py39.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial011_py310.py!}
@ -296,11 +592,29 @@ And you can also define a default `list` of values if none are provided:
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial012_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial012_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial012.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial012_py39.py!}
@ -327,9 +641,26 @@ the default of `q` will be: `["foo", "bar"]` and your response will be:
You can also use `list` directly instead of `List[str]` (or `list[str]` in Python 3.9+):
```Python hl_lines="7"
{!../../../docs_src/query_params_str_validations/tutorial013.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial013_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial013_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial013.py!}
```
!!! note
Have in mind that in this case, FastAPI won't check the contents of the list.
@ -351,25 +682,74 @@ You can add a `title`:
=== "Python 3.6 and above"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial007_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial007.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial007_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial007_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial007.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial007_py310.py!}
```
And a `description`:
=== "Python 3.6 and above"
```Python hl_lines="15"
{!> ../../../docs_src/query_params_str_validations/tutorial008_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="14"
{!> ../../../docs_src/query_params_str_validations/tutorial008_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="14"
{!> ../../../docs_src/query_params_str_validations/tutorial008_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="13"
{!> ../../../docs_src/query_params_str_validations/tutorial008.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="12"
{!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!}
@ -395,12 +775,36 @@ Then you can declare an `alias`, and that alias is what will be used to find the
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial009_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial009.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial009_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial009_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9"
{!> ../../../docs_src/query_params_str_validations/tutorial009.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/query_params_str_validations/tutorial009_py310.py!}
```
@ -415,11 +819,35 @@ Then pass the parameter `deprecated=True` to `Query`:
=== "Python 3.6 and above"
```Python hl_lines="20"
{!> ../../../docs_src/query_params_str_validations/tutorial010_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="19"
{!> ../../../docs_src/query_params_str_validations/tutorial010_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="19"
{!> ../../../docs_src/query_params_str_validations/tutorial010_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="18"
{!> ../../../docs_src/query_params_str_validations/tutorial010.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="17"
{!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!}
@ -435,12 +863,36 @@ To exclude a query parameter from the generated OpenAPI schema (and thus, from t
=== "Python 3.6 and above"
```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial014_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial014.py!}
{!> ../../../docs_src/query_params_str_validations/tutorial014_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial014_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10"
{!> ../../../docs_src/query_params_str_validations/tutorial014.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8"
{!> ../../../docs_src/query_params_str_validations/tutorial014_py310.py!}
```

165
docs/en/docs/tutorial/request-files.md

@ -13,17 +13,51 @@ You can define files to be uploaded by the client using `File`.
Import `File` and `UploadFile` from `fastapi`:
```Python hl_lines="1"
{!../../../docs_src/request_files/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="1"
{!> ../../../docs_src/request_files/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="3"
{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1"
{!> ../../../docs_src/request_files/tutorial001.py!}
```
## Define `File` Parameters
Create file parameters the same way you would for `Body` or `Form`:
```Python hl_lines="7"
{!../../../docs_src/request_files/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="8"
{!> ../../../docs_src/request_files/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/request_files/tutorial001.py!}
```
!!! info
`File` is a class that inherits directly from `Form`.
@ -45,9 +79,26 @@ But there are several cases in which you might benefit from using `UploadFile`.
Define a file parameter with a type of `UploadFile`:
```Python hl_lines="12"
{!../../../docs_src/request_files/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="13"
{!> ../../../docs_src/request_files/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="14"
{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="12"
{!> ../../../docs_src/request_files/tutorial001.py!}
```
Using `UploadFile` has several advantages over `bytes`:
@ -120,23 +171,65 @@ You can make a file optional by using standard type annotations and setting a de
=== "Python 3.6 and above"
```Python hl_lines="10 18"
{!> ../../../docs_src/request_files/tutorial001_02_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9 17"
{!> ../../../docs_src/request_files/tutorial001_02.py!}
{!> ../../../docs_src/request_files/tutorial001_02_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="7 14"
```Python hl_lines="9 17"
{!> ../../../docs_src/request_files/tutorial001_02_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9 17"
{!> ../../../docs_src/request_files/tutorial001_02.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7 15"
{!> ../../../docs_src/request_files/tutorial001_02_py310.py!}
```
## `UploadFile` with Additional Metadata
You can also use `File()` with `UploadFile`, for example, to set additional metadata:
```Python hl_lines="13"
{!../../../docs_src/request_files/tutorial001_03.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="8 14"
{!> ../../../docs_src/request_files/tutorial001_03_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9 15"
{!> ../../../docs_src/request_files/tutorial001_03_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7 13"
{!> ../../../docs_src/request_files/tutorial001_03.py!}
```
## Multiple File Uploads
@ -148,11 +241,29 @@ To use that, declare a list of `bytes` or `UploadFile`:
=== "Python 3.6 and above"
```Python hl_lines="11 16"
{!> ../../../docs_src/request_files/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10 15"
{!> ../../../docs_src/request_files/tutorial002_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10 15"
{!> ../../../docs_src/request_files/tutorial002.py!}
```
=== "Python 3.9 and above"
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8 13"
{!> ../../../docs_src/request_files/tutorial002_py39.py!}
@ -171,13 +282,31 @@ And the same way as before, you can use `File()` to set additional parameters, e
=== "Python 3.6 and above"
```Python hl_lines="18"
{!> ../../../docs_src/request_files/tutorial003.py!}
```Python hl_lines="12 19-21"
{!> ../../../docs_src/request_files/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="16"
```Python hl_lines="11 18-20"
{!> ../../../docs_src/request_files/tutorial003_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="11 18"
{!> ../../../docs_src/request_files/tutorial003.py!}
```
=== "Python 3.9 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="9 16"
{!> ../../../docs_src/request_files/tutorial003_py39.py!}
```

46
docs/en/docs/tutorial/request-forms-and-files.md

@ -9,17 +9,51 @@ You can define files and form fields at the same time using `File` and `Form`.
## Import `File` and `Form`
```Python hl_lines="1"
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="1"
{!> ../../../docs_src/request_forms_and_files/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="3"
{!> ../../../docs_src/request_forms_and_files/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1"
{!> ../../../docs_src/request_forms_and_files/tutorial001.py!}
```
## Define `File` and `Form` parameters
Create file and form parameters the same way you would for `Body` or `Query`:
```Python hl_lines="8"
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="9-11"
{!> ../../../docs_src/request_forms_and_files/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="10-12"
{!> ../../../docs_src/request_forms_and_files/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="8"
{!> ../../../docs_src/request_forms_and_files/tutorial001.py!}
```
The files and form fields will be uploaded as form data and you will receive the files and form fields.

46
docs/en/docs/tutorial/request-forms.md

@ -11,17 +11,51 @@ When you need to receive form fields instead of JSON, you can use `Form`.
Import `Form` from `fastapi`:
```Python hl_lines="1"
{!../../../docs_src/request_forms/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="1"
{!> ../../../docs_src/request_forms/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="3"
{!> ../../../docs_src/request_forms/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1"
{!> ../../../docs_src/request_forms/tutorial001.py!}
```
## Define `Form` parameters
Create form parameters the same way you would for `Body` or `Query`:
```Python hl_lines="7"
{!../../../docs_src/request_forms/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="8"
{!> ../../../docs_src/request_forms/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="9"
{!> ../../../docs_src/request_forms/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7"
{!> ../../../docs_src/request_forms/tutorial001.py!}
```
For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is required to send a `username` and `password` as form fields.

46
docs/en/docs/tutorial/schema-extra-example.md

@ -68,11 +68,32 @@ Here we pass an `example` of the data expected in `Body()`:
=== "Python 3.6 and above"
```Python hl_lines="23-28"
{!> ../../../docs_src/schema_extra_example/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="22-27"
{!> ../../../docs_src/schema_extra_example/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="22-27"
{!> ../../../docs_src/schema_extra_example/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="20-25"
{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
```Python hl_lines="18-23"
{!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!}
@ -99,11 +120,32 @@ Each specific example `dict` in the `examples` can contain:
=== "Python 3.6 and above"
```Python hl_lines="24-50"
{!> ../../../docs_src/schema_extra_example/tutorial004_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="23-49"
{!> ../../../docs_src/schema_extra_example/tutorial004_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="23-49"
{!> ../../../docs_src/schema_extra_example/tutorial004_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="21-47"
{!> ../../../docs_src/schema_extra_example/tutorial004.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
```Python hl_lines="19-45"
{!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!}

70
docs/en/docs/tutorial/security/first-steps.md

@ -20,9 +20,27 @@ Let's first just use the code and see how it works, and then we'll come back to
Copy the example in a file `main.py`:
```Python
{!../../../docs_src/security/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python
{!> ../../../docs_src/security/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python
{!> ../../../docs_src/security/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python
{!> ../../../docs_src/security/tutorial001.py!}
```
## Run it
@ -116,9 +134,26 @@ In this example we are going to use **OAuth2**, with the **Password** flow, usin
When we create an instance of the `OAuth2PasswordBearer` class we pass in the `tokenUrl` parameter. This parameter contains the URL that the client (the frontend running in the user's browser) will use to send the `username` and `password` in order to get a token.
```Python hl_lines="6"
{!../../../docs_src/security/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="7"
{!> ../../../docs_src/security/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="8"
{!> ../../../docs_src/security/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="6"
{!> ../../../docs_src/security/tutorial001.py!}
```
!!! tip
Here `tokenUrl="token"` refers to a relative URL `token` that we haven't created yet. As it's a relative URL, it's equivalent to `./token`.
@ -150,9 +185,26 @@ So, it can be used with `Depends`.
Now you can pass that `oauth2_scheme` in a dependency with `Depends`.
```Python hl_lines="10"
{!../../../docs_src/security/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="11"
{!> ../../../docs_src/security/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="12"
{!> ../../../docs_src/security/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10"
{!> ../../../docs_src/security/tutorial001.py!}
```
This dependency will provide a `str` that is assigned to the parameter `token` of the *path operation function*.

153
docs/en/docs/tutorial/security/get-current-user.md

@ -2,9 +2,26 @@
In the previous chapter the security system (which is based on the dependency injection system) was giving the *path operation function* a `token` as a `str`:
```Python hl_lines="10"
{!../../../docs_src/security/tutorial001.py!}
```
=== "Python 3.6 and above"
```Python hl_lines="11"
{!> ../../../docs_src/security/tutorial001_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="12"
{!> ../../../docs_src/security/tutorial001_an_py39.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="10"
{!> ../../../docs_src/security/tutorial001.py!}
```
But that is still not that useful.
@ -18,12 +35,36 @@ The same way we use Pydantic to declare bodies, we can use it anywhere else:
=== "Python 3.6 and above"
```Python hl_lines="5 13-17"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="5 12-16"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="5 12-16"
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="5 12-16"
{!> ../../../docs_src/security/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="3 10-14"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```
@ -40,12 +81,36 @@ The same as we were doing before in the *path operation* directly, our new depen
=== "Python 3.6 and above"
```Python hl_lines="26"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="25"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="25"
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="25"
{!> ../../../docs_src/security/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="23"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```
@ -56,12 +121,36 @@ The same as we were doing before in the *path operation* directly, our new depen
=== "Python 3.6 and above"
```Python hl_lines="20-23 27-28"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="19-22 26-27"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="19-22 26-27"
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="19-22 26-27"
{!> ../../../docs_src/security/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="17-20 24-25"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```
@ -72,12 +161,36 @@ So now we can use the same `Depends` with our `get_current_user` in the *path op
=== "Python 3.6 and above"
```Python hl_lines="32"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="31"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="31"
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="31"
{!> ../../../docs_src/security/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="29"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```
@ -130,12 +243,36 @@ And all these thousands of *path operations* can be as small as 3 lines:
=== "Python 3.6 and above"
```Python hl_lines="31-33"
{!> ../../../docs_src/security/tutorial002_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="30-32"
{!> ../../../docs_src/security/tutorial002.py!}
{!> ../../../docs_src/security/tutorial002_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="30-32"
{!> ../../../docs_src/security/tutorial002_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="30-32"
{!> ../../../docs_src/security/tutorial002.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="28-30"
{!> ../../../docs_src/security/tutorial002_py310.py!}
```

104
docs/en/docs/tutorial/security/oauth2-jwt.md

@ -111,12 +111,36 @@ And another one to authenticate and return a user.
=== "Python 3.6 and above"
```Python hl_lines="7 49 56-57 60-61 70-76"
{!> ../../../docs_src/security/tutorial004_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="7 48 55-56 59-60 69-75"
{!> ../../../docs_src/security/tutorial004.py!}
{!> ../../../docs_src/security/tutorial004_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="7 48 55-56 59-60 69-75"
{!> ../../../docs_src/security/tutorial004_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="7 48 55-56 59-60 69-75"
{!> ../../../docs_src/security/tutorial004.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="6 47 54-55 58-59 68-74"
{!> ../../../docs_src/security/tutorial004_py310.py!}
```
@ -154,12 +178,36 @@ Create a utility function to generate a new access token.
=== "Python 3.6 and above"
```Python hl_lines="6 13-15 29-31 79-87"
{!> ../../../docs_src/security/tutorial004_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="6 12-14 28-30 78-86"
{!> ../../../docs_src/security/tutorial004.py!}
{!> ../../../docs_src/security/tutorial004_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="6 12-14 28-30 78-86"
{!> ../../../docs_src/security/tutorial004_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="6 12-14 28-30 78-86"
{!> ../../../docs_src/security/tutorial004.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="5 11-13 27-29 77-85"
{!> ../../../docs_src/security/tutorial004_py310.py!}
```
@ -174,12 +222,36 @@ If the token is invalid, return an HTTP error right away.
=== "Python 3.6 and above"
```Python hl_lines="90-107"
{!> ../../../docs_src/security/tutorial004_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="89-106"
{!> ../../../docs_src/security/tutorial004.py!}
{!> ../../../docs_src/security/tutorial004_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="89-106"
{!> ../../../docs_src/security/tutorial004_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="89-106"
{!> ../../../docs_src/security/tutorial004.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="88-105"
{!> ../../../docs_src/security/tutorial004_py310.py!}
```
@ -192,11 +264,35 @@ Create a real JWT access token and return it.
=== "Python 3.6 and above"
```Python hl_lines="118-133"
{!> ../../../docs_src/security/tutorial004_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="117-132"
{!> ../../../docs_src/security/tutorial004_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="117-132"
{!> ../../../docs_src/security/tutorial004_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="115-128"
{!> ../../../docs_src/security/tutorial004.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="114-127"
{!> ../../../docs_src/security/tutorial004_py310.py!}

132
docs/en/docs/tutorial/security/simple-oauth2.md

@ -51,11 +51,35 @@ First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depe
=== "Python 3.6 and above"
```Python hl_lines="4 79"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="4 78"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="4 78"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="4 76"
{!> ../../../docs_src/security/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="2 74"
{!> ../../../docs_src/security/tutorial003_py310.py!}
@ -100,11 +124,35 @@ For the error, we use the exception `HTTPException`:
=== "Python 3.6 and above"
```Python hl_lines="3 80-82"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="3 79-81"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="3 79-81"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="3 77-79"
{!> ../../../docs_src/security/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="1 75-77"
{!> ../../../docs_src/security/tutorial003_py310.py!}
@ -136,11 +184,35 @@ So, the thief won't be able to try to use those same passwords in another system
=== "Python 3.6 and above"
```Python hl_lines="83-86"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="82-85"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="82-85"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="80-83"
{!> ../../../docs_src/security/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="78-81"
{!> ../../../docs_src/security/tutorial003_py310.py!}
@ -182,11 +254,35 @@ For this simple example, we are going to just be completely insecure and return
=== "Python 3.6 and above"
```Python hl_lines="88"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="87"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="87"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="85"
{!> ../../../docs_src/security/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="83"
{!> ../../../docs_src/security/tutorial003_py310.py!}
@ -215,13 +311,37 @@ So, in our endpoint, we will only get a user if the user exists, was correctly a
=== "Python 3.6 and above"
```Python hl_lines="59-67 70-75 95"
{!> ../../../docs_src/security/tutorial003_an.py!}
```
=== "Python 3.9 and above"
```Python hl_lines="58-66 69-74 94"
{!> ../../../docs_src/security/tutorial003_an_py39.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="58-66 69-74 94"
{!> ../../../docs_src/security/tutorial003_an_py310.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="58-66 69-72 90"
{!> ../../../docs_src/security/tutorial003.py!}
```
=== "Python 3.10 and above"
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python hl_lines="55-64 67-70 88"
```Python hl_lines="56-64 67-70 88"
{!> ../../../docs_src/security/tutorial003_py310.py!}
```

26
docs/en/docs/tutorial/testing.md

@ -113,11 +113,35 @@ Both *path operations* require an `X-Token` header.
=== "Python 3.6 and above"
```Python
{!> ../../../docs_src/app_testing/app_b/main.py!}
{!> ../../../docs_src/app_testing/app_b_an/main.py!}
```
=== "Python 3.9 and above"
```Python
{!> ../../../docs_src/app_testing/app_b_an_py39/main.py!}
```
=== "Python 3.10 and above"
```Python
{!> ../../../docs_src/app_testing/app_b_an_py310/main.py!}
```
=== "Python 3.6 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python
{!> ../../../docs_src/app_testing/app_b/main.py!}
```
=== "Python 3.10 and above - non-Annotated"
!!! tip
Try to use the main, `Annotated` version better.
```Python
{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
```

26
docs_src/additional_status_codes/tutorial001_an.py

@ -0,0 +1,26 @@
from typing import Union
from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse
from typing_extensions import Annotated
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(
item_id: str,
name: Annotated[Union[str, None], Body()] = None,
size: Annotated[Union[int, None], Body()] = None,
):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)

25
docs_src/additional_status_codes/tutorial001_an_py310.py

@ -0,0 +1,25 @@
from typing import Annotated
from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(
item_id: str,
name: Annotated[str | None, Body()] = None,
size: Annotated[int | None, Body()] = None,
):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)

25
docs_src/additional_status_codes/tutorial001_an_py39.py

@ -0,0 +1,25 @@
from typing import Annotated, Union
from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(
item_id: str,
name: Annotated[Union[str, None], Body()] = None,
size: Annotated[Union[int, None], Body()] = None,
):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)

23
docs_src/additional_status_codes/tutorial001_py310.py

@ -0,0 +1,23 @@
from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(
item_id: str,
name: str | None = Body(default=None),
size: int | None = Body(default=None),
):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)

18
docs_src/annotated/tutorial001.py

@ -1,18 +0,0 @@
from typing import Optional
from fastapi import Depends, FastAPI
from typing_extensions import Annotated
app = FastAPI()
async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
CommonParamsDepends = Annotated[dict, Depends(common_parameters)]
@app.get("/items/")
async def read_items(commons: CommonParamsDepends):
return commons

17
docs_src/annotated/tutorial001_py39.py

@ -1,17 +0,0 @@
from typing import Annotated, Optional
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
CommonParamsDepends = Annotated[dict, Depends(common_parameters)]
@app.get("/items/")
async def read_items(commons: CommonParamsDepends):
return commons

21
docs_src/annotated/tutorial002.py

@ -1,21 +0,0 @@
from typing import Optional
from fastapi import Depends, FastAPI
from typing_extensions import Annotated
app = FastAPI()
class CommonQueryParams:
def __init__(self, q: Optional[str] = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
CommonQueryParamsDepends = Annotated[CommonQueryParams, Depends()]
@app.get("/items/")
async def read_items(commons: CommonQueryParamsDepends):
return commons

20
docs_src/annotated/tutorial002_py39.py

@ -1,20 +0,0 @@
from typing import Annotated, Optional
from fastapi import Depends, FastAPI
app = FastAPI()
class CommonQueryParams:
def __init__(self, q: Optional[str] = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
CommonQueryParamsDepends = Annotated[CommonQueryParams, Depends()]
@app.get("/items/")
async def read_items(commons: CommonQueryParamsDepends):
return commons

15
docs_src/annotated/tutorial003.py

@ -1,15 +0,0 @@
from fastapi import FastAPI, Path
from fastapi.param_functions import Query
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(item_id: Annotated[int, Path(gt=0)]):
return {"item_id": item_id}
@app.get("/users")
async def read_users(user_id: Annotated[str, Query(min_length=1)] = "me"):
return {"user_id": user_id}

16
docs_src/annotated/tutorial003_py39.py

@ -1,16 +0,0 @@
from typing import Annotated
from fastapi import FastAPI, Path
from fastapi.param_functions import Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(item_id: Annotated[int, Path(gt=0)]):
return {"item_id": item_id}
@app.get("/users")
async def read_users(user_id: Annotated[str, Query(min_length=1)] = "me"):
return {"user_id": user_id}

0
tests/test_tutorial/test_annotated/__init__.py → docs_src/app_testing/app_b_an/__init__.py

39
docs_src/app_testing/app_b_an/main.py

@ -0,0 +1,39 @@
from typing import Union
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
from typing_extensions import Annotated
fake_secret_token = "coneofsilence"
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
app = FastAPI()
class Item(BaseModel):
id: str
title: str
description: Union[str, None] = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/", response_model=Item)
async def create_item(item: Item, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=400, detail="Item already exists")
fake_db[item.id] = item
return item

65
docs_src/app_testing/app_b_an/test_main.py

@ -0,0 +1,65 @@
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}
def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_read_inexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}
def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)
assert response.status_code == 400
assert response.json() == {"detail": "Item already exists"}

0
docs_src/app_testing/app_b_an_py310/__init__.py

38
docs_src/app_testing/app_b_an_py310/main.py

@ -0,0 +1,38 @@
from typing import Annotated
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
fake_secret_token = "coneofsilence"
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
app = FastAPI()
class Item(BaseModel):
id: str
title: str
description: str | None = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/", response_model=Item)
async def create_item(item: Item, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=400, detail="Item already exists")
fake_db[item.id] = item
return item

65
docs_src/app_testing/app_b_an_py310/test_main.py

@ -0,0 +1,65 @@
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}
def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_read_inexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}
def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)
assert response.status_code == 400
assert response.json() == {"detail": "Item already exists"}

0
docs_src/app_testing/app_b_an_py39/__init__.py

38
docs_src/app_testing/app_b_an_py39/main.py

@ -0,0 +1,38 @@
from typing import Annotated, Union
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
fake_secret_token = "coneofsilence"
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
app = FastAPI()
class Item(BaseModel):
id: str
title: str
description: Union[str, None] = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/", response_model=Item)
async def create_item(item: Item, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=400, detail="Item already exists")
fake_db[item.id] = item
return item

65
docs_src/app_testing/app_b_an_py39/test_main.py

@ -0,0 +1,65 @@
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}
def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_read_inexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}
def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)
assert response.status_code == 400
assert response.json() == {"detail": "Item already exists"}

27
docs_src/background_tasks/tutorial002_an.py

@ -0,0 +1,27 @@
from typing import Union
from fastapi import BackgroundTasks, Depends, FastAPI
from typing_extensions import Annotated
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: Union[str, None] = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}

26
docs_src/background_tasks/tutorial002_an_py310.py

@ -0,0 +1,26 @@
from typing import Annotated
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: str | None = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}

26
docs_src/background_tasks/tutorial002_an_py39.py

@ -0,0 +1,26 @@
from typing import Annotated, Union
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: Union[str, None] = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}

0
docs_src/bigger_applications/app_an/__init__.py

12
docs_src/bigger_applications/app_an/dependencies.py

@ -0,0 +1,12 @@
from fastapi import Header, HTTPException
from typing_extensions import Annotated
async def get_token_header(x_token: Annotated[str, Header()]):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def get_query_token(token: str):
if token != "jessica":
raise HTTPException(status_code=400, detail="No Jessica token provided")

0
docs_src/bigger_applications/app_an/internal/__init__.py

8
docs_src/bigger_applications/app_an/internal/admin.py

@ -0,0 +1,8 @@
from fastapi import APIRouter
router = APIRouter()
@router.post("/")
async def update_admin():
return {"message": "Admin getting schwifty"}

23
docs_src/bigger_applications/app_an/main.py

@ -0,0 +1,23 @@
from fastapi import Depends, FastAPI
from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users
app = FastAPI(dependencies=[Depends(get_query_token)])
app.include_router(users.router)
app.include_router(items.router)
app.include_router(
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={418: {"description": "I'm a teapot"}},
)
@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}

0
docs_src/bigger_applications/app_an/routers/__init__.py

38
docs_src/bigger_applications/app_an/routers/items.py

@ -0,0 +1,38 @@
from fastapi import APIRouter, Depends, HTTPException
from ..dependencies import get_token_header
router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}
@router.get("/")
async def read_items():
return fake_items_db
@router.get("/{item_id}")
async def read_item(item_id: str):
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
return {"name": fake_items_db[item_id]["name"], "item_id": item_id}
@router.put(
"/{item_id}",
tags=["custom"],
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "plumbus":
raise HTTPException(
status_code=403, detail="You can only update the item: plumbus"
)
return {"item_id": item_id, "name": "The great Plumbus"}

18
docs_src/bigger_applications/app_an/routers/users.py

@ -0,0 +1,18 @@
from fastapi import APIRouter
router = APIRouter()
@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]
@router.get("/users/me", tags=["users"])
async def read_user_me():
return {"username": "fakecurrentuser"}
@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
return {"username": username}

0
docs_src/bigger_applications/app_an_py39/__init__.py

13
docs_src/bigger_applications/app_an_py39/dependencies.py

@ -0,0 +1,13 @@
from typing import Annotated
from fastapi import Header, HTTPException
async def get_token_header(x_token: Annotated[str, Header()]):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def get_query_token(token: str):
if token != "jessica":
raise HTTPException(status_code=400, detail="No Jessica token provided")

0
docs_src/bigger_applications/app_an_py39/internal/__init__.py

8
docs_src/bigger_applications/app_an_py39/internal/admin.py

@ -0,0 +1,8 @@
from fastapi import APIRouter
router = APIRouter()
@router.post("/")
async def update_admin():
return {"message": "Admin getting schwifty"}

23
docs_src/bigger_applications/app_an_py39/main.py

@ -0,0 +1,23 @@
from fastapi import Depends, FastAPI
from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users
app = FastAPI(dependencies=[Depends(get_query_token)])
app.include_router(users.router)
app.include_router(items.router)
app.include_router(
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={418: {"description": "I'm a teapot"}},
)
@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}

0
docs_src/bigger_applications/app_an_py39/routers/__init__.py

38
docs_src/bigger_applications/app_an_py39/routers/items.py

@ -0,0 +1,38 @@
from fastapi import APIRouter, Depends, HTTPException
from ..dependencies import get_token_header
router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}
@router.get("/")
async def read_items():
return fake_items_db
@router.get("/{item_id}")
async def read_item(item_id: str):
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
return {"name": fake_items_db[item_id]["name"], "item_id": item_id}
@router.put(
"/{item_id}",
tags=["custom"],
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "plumbus":
raise HTTPException(
status_code=403, detail="You can only update the item: plumbus"
)
return {"item_id": item_id, "name": "The great Plumbus"}

18
docs_src/bigger_applications/app_an_py39/routers/users.py

@ -0,0 +1,18 @@
from fastapi import APIRouter
router = APIRouter()
@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]
@router.get("/users/me", tags=["users"])
async def read_user_me():
return {"username": "fakecurrentuser"}
@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
return {"username": username}

22
docs_src/body_fields/tutorial001_an.py

@ -0,0 +1,22 @@
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results

21
docs_src/body_fields/tutorial001_an_py310.py

@ -0,0 +1,21 @@
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results

21
docs_src/body_fields/tutorial001_an_py39.py

@ -0,0 +1,21 @@
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results

28
docs_src/body_multiple_params/tutorial001_an.py

@ -0,0 +1,28 @@
from typing import Union
from fastapi import FastAPI, Path
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: Union[str, None] = None,
item: Union[Item, None] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if item:
results.update({"item": item})
return results

27
docs_src/body_multiple_params/tutorial001_an_py310.py

@ -0,0 +1,27 @@
from typing import Annotated
from fastapi import FastAPI, Path
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: str | None = None,
item: Item | None = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if item:
results.update({"item": item})
return results

27
docs_src/body_multiple_params/tutorial001_an_py39.py

@ -0,0 +1,27 @@
from typing import Annotated, Union
from fastapi import FastAPI, Path
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: Union[str, None] = None,
item: Union[Item, None] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if item:
results.update({"item": item})
return results

27
docs_src/body_multiple_params/tutorial003_an.py

@ -0,0 +1,27 @@
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
class User(BaseModel):
username: str
full_name: Union[str, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results

26
docs_src/body_multiple_params/tutorial003_an_py310.py

@ -0,0 +1,26 @@
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class User(BaseModel):
username: str
full_name: str | None = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results

26
docs_src/body_multiple_params/tutorial003_an_py39.py

@ -0,0 +1,26 @@
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
class User(BaseModel):
username: str
full_name: Union[str, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results

34
docs_src/body_multiple_params/tutorial004_an.py

@ -0,0 +1,34 @@
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
class User(BaseModel):
username: str
full_name: Union[str, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item,
user: User,
importance: Annotated[int, Body(gt=0)],
q: Union[str, None] = None,
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
if q:
results.update({"q": q})
return results

33
docs_src/body_multiple_params/tutorial004_an_py310.py

@ -0,0 +1,33 @@
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class User(BaseModel):
username: str
full_name: str | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item,
user: User,
importance: Annotated[int, Body(gt=0)],
q: str | None = None,
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
if q:
results.update({"q": q})
return results

33
docs_src/body_multiple_params/tutorial004_an_py39.py

@ -0,0 +1,33 @@
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
class User(BaseModel):
username: str
full_name: Union[str, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item,
user: User,
importance: Annotated[int, Body(gt=0)],
q: Union[str, None] = None,
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
if q:
results.update({"q": q})
return results

20
docs_src/body_multiple_params/tutorial005_an.py

@ -0,0 +1,20 @@
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results

19
docs_src/body_multiple_params/tutorial005_an_py310.py

@ -0,0 +1,19 @@
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results

19
docs_src/body_multiple_params/tutorial005_an_py39.py

@ -0,0 +1,19 @@
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results

11
docs_src/cookie_params/tutorial001_an.py

@ -0,0 +1,11 @@
from typing import Union
from fastapi import Cookie, FastAPI
from typing_extensions import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(ads_id: Annotated[Union[str, None], Cookie()] = None):
return {"ads_id": ads_id}

10
docs_src/cookie_params/tutorial001_an_py310.py

@ -0,0 +1,10 @@
from typing import Annotated
from fastapi import Cookie, FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
return {"ads_id": ads_id}

10
docs_src/cookie_params/tutorial001_an_py39.py

@ -0,0 +1,10 @@
from typing import Annotated, Union
from fastapi import Cookie, FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(ads_id: Annotated[Union[str, None], Cookie()] = None):
return {"ads_id": ads_id}

25
docs_src/dependencies/tutorial001_02_an.py

@ -0,0 +1,25 @@
from typing import Union
from fastapi import Depends, FastAPI
from typing_extensions import Annotated
app = FastAPI()
async def common_parameters(
q: Union[str, None] = None, skip: int = 0, limit: int = 100
):
return {"q": q, "skip": skip, "limit": limit}
CommonsDep = Annotated[dict, Depends(common_parameters)]
@app.get("/items/")
async def read_items(commons: CommonsDep):
return commons
@app.get("/users/")
async def read_users(commons: CommonsDep):
return commons

22
docs_src/dependencies/tutorial001_02_an_py310.py

@ -0,0 +1,22 @@
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
CommonsDep = Annotated[dict, Depends(common_parameters)]
@app.get("/items/")
async def read_items(commons: CommonsDep):
return commons
@app.get("/users/")
async def read_users(commons: CommonsDep):
return commons

24
docs_src/dependencies/tutorial001_02_an_py39.py

@ -0,0 +1,24 @@
from typing import Annotated, Union
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(
q: Union[str, None] = None, skip: int = 0, limit: int = 100
):
return {"q": q, "skip": skip, "limit": limit}
CommonsDep = Annotated[dict, Depends(common_parameters)]
@app.get("/items/")
async def read_items(commons: CommonsDep):
return commons
@app.get("/users/")
async def read_users(commons: CommonsDep):
return commons

22
docs_src/dependencies/tutorial001_an.py

@ -0,0 +1,22 @@
from typing import Union
from fastapi import Depends, FastAPI
from typing_extensions import Annotated
app = FastAPI()
async def common_parameters(
q: Union[str, None] = None, skip: int = 0, limit: int = 100
):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons
@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons

19
docs_src/dependencies/tutorial001_an_py310.py

@ -0,0 +1,19 @@
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons
@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons

21
docs_src/dependencies/tutorial001_an_py39.py

@ -0,0 +1,21 @@
from typing import Annotated, Union
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(
q: Union[str, None] = None, skip: int = 0, limit: int = 100
):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons
@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons

26
docs_src/dependencies/tutorial002_an.py

@ -0,0 +1,26 @@
from typing import Union
from fastapi import Depends, FastAPI
from typing_extensions import Annotated
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

25
docs_src/dependencies/tutorial002_an_py310.py

@ -0,0 +1,25 @@
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

25
docs_src/dependencies/tutorial002_an_py39.py

@ -0,0 +1,25 @@
from typing import Annotated, Union
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

26
docs_src/dependencies/tutorial003_an.py

@ -0,0 +1,26 @@
from typing import Any, Union
from fastapi import Depends, FastAPI
from typing_extensions import Annotated
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[Any, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

25
docs_src/dependencies/tutorial003_an_py310.py

@ -0,0 +1,25 @@
from typing import Annotated, Any
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[Any, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

25
docs_src/dependencies/tutorial003_an_py39.py

@ -0,0 +1,25 @@
from typing import Annotated, Any, Union
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[Any, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

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

Loading…
Cancel
Save