Browse Source

Merge branch 'master' into feat/exception-on-duplicate-path

pull/5595/head
Irfanuddin Shafi Ahmed 8 months ago
committed by GitHub
parent
commit
a7aeba2923
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      .github/workflows/build-docs.yml
  2. 4
      .github/workflows/deploy-docs.yml
  3. 2
      .github/workflows/label-approved.yml
  4. 3
      .github/workflows/notify-translations.yml
  5. 2
      .github/workflows/publish.yml
  6. 2
      .github/workflows/smokeshow.yml
  7. 6
      .github/workflows/test.yml
  8. 4
      README.md
  9. 6
      docs/en/data/sponsors.yml
  10. 1
      docs/en/data/sponsors_badge.yml
  11. 4
      docs/en/docs/contributing.md
  12. BIN
      docs/en/docs/img/sponsors/blockbee-banner.png
  13. BIN
      docs/en/docs/img/sponsors/blockbee.png
  14. 2
      docs/en/docs/index.md
  15. 55
      docs/en/docs/release-notes.md
  16. 3
      docs/en/docs/tutorial/sql-databases.md
  17. 4
      docs/en/overrides/main.html
  18. 332
      docs/id/docs/tutorial/first-steps.md
  19. 258
      docs/id/docs/tutorial/path-params.md
  20. 41
      docs/ko/docs/advanced/additional-status-codes.md
  21. 108
      docs/ko/docs/advanced/async-tests.md
  22. 127
      docs/ko/docs/advanced/templates.md
  23. 70
      docs/ko/docs/how-to/configure-swagger-ui.md
  24. 3
      docs/ko/docs/resources/index.md
  25. 76
      docs/ko/docs/tutorial/cookie-param-models.md
  26. 56
      docs/ko/docs/tutorial/header-param-models.md
  27. 78
      docs/ko/docs/tutorial/request-form-models.md
  28. 74
      docs/ko/docs/tutorial/request-forms.md
  29. 360
      docs/ko/docs/tutorial/sql-databases.md
  30. 243
      docs/ko/docs/tutorial/testing.md
  31. 261
      docs/pt/docs/advanced/generate-clients.md
  32. 6
      docs/ru/docs/deployment/docker.md
  33. 75
      docs/ru/docs/fastapi-cli.md
  34. 132
      docs/ru/docs/tutorial/query-param-models.md
  35. 99
      docs/ru/docs/tutorial/security/get-current-user.md
  36. 261
      docs/ru/docs/tutorial/security/oauth2-jwt.md
  37. 272
      docs/ru/docs/tutorial/security/simple-oauth2.md
  38. 358
      docs/ru/docs/tutorial/sql-databases.md
  39. 839
      docs/ru/docs/virtual-environments.md
  40. 83
      docs/uk/docs/fastapi-cli.md
  41. 207
      docs/zh-hant/docs/features.md
  42. 331
      docs/zh-hant/docs/tutorial/first-steps.md
  43. 4
      docs/zh-hant/docs/tutorial/index.md
  44. 844
      docs/zh-hant/docs/virtual-environments.md
  45. 99
      docs/zh/docs/advanced/async-tests.md
  46. 132
      docs/zh/docs/advanced/events.md
  47. 55
      docs/zh/docs/advanced/openapi-webhooks.md
  48. 2
      docs/zh/docs/advanced/testing-dependencies.md
  49. 34
      docs/zh/docs/async.md
  50. 76
      docs/zh/docs/tutorial/cookie-param-models.md
  51. 2
      docs/zh/docs/tutorial/first-steps.md
  52. 56
      docs/zh/docs/tutorial/header-param-models.md
  53. 2
      docs/zh/docs/tutorial/path-params.md
  54. 2
      docs/zh/docs/tutorial/query-params-str-validations.md
  55. 78
      docs/zh/docs/tutorial/request-form-models.md
  56. 360
      docs/zh/docs/tutorial/sql-databases.md
  57. 2
      requirements-docs.txt
  58. 6
      requirements-tests.txt

4
.github/workflows/build-docs.yml

@ -53,7 +53,7 @@ jobs:
with: with:
python-version: "3.11" python-version: "3.11"
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v3 uses: astral-sh/setup-uv@v5
with: with:
version: "0.4.15" version: "0.4.15"
enable-cache: true enable-cache: true
@ -95,7 +95,7 @@ jobs:
with: with:
python-version: "3.11" python-version: "3.11"
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v3 uses: astral-sh/setup-uv@v5
with: with:
version: "0.4.15" version: "0.4.15"
enable-cache: true enable-cache: true

4
.github/workflows/deploy-docs.yml

@ -29,7 +29,7 @@ jobs:
with: with:
python-version: "3.11" python-version: "3.11"
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v3 uses: astral-sh/setup-uv@v5
with: with:
version: "0.4.15" version: "0.4.15"
enable-cache: true enable-cache: true
@ -64,7 +64,7 @@ jobs:
BRANCH: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }} BRANCH: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }}
# TODO: Use v3 when it's fixed, probably in v3.11 # TODO: Use v3 when it's fixed, probably in v3.11
# https://github.com/cloudflare/wrangler-action/issues/307 # https://github.com/cloudflare/wrangler-action/issues/307
uses: cloudflare/wrangler-action@v3.12 uses: cloudflare/wrangler-action@v3.13
# uses: cloudflare/wrangler-action@v3 # uses: cloudflare/wrangler-action@v3
with: with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}

2
.github/workflows/label-approved.yml

@ -26,7 +26,7 @@ jobs:
with: with:
python-version: "3.11" python-version: "3.11"
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v3 uses: astral-sh/setup-uv@v5
with: with:
version: "0.4.15" version: "0.4.15"
enable-cache: true enable-cache: true

3
.github/workflows/notify-translations.yml

@ -30,13 +30,12 @@ jobs:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: "3.11" python-version: "3.11"
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v3 uses: astral-sh/setup-uv@v5
with: with:
version: "0.4.15" version: "0.4.15"
enable-cache: true enable-cache: true

2
.github/workflows/publish.yml

@ -35,7 +35,7 @@ jobs:
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }} TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
run: python -m build run: python -m build
- name: Publish - name: Publish
uses: pypa/gh-action-pypi-publish@v1.12.2 uses: pypa/gh-action-pypi-publish@v1.12.3
- name: Dump GitHub context - name: Dump GitHub context
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}

2
.github/workflows/smokeshow.yml

@ -26,7 +26,7 @@ jobs:
with: with:
python-version: '3.9' python-version: '3.9'
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v3 uses: astral-sh/setup-uv@v5
with: with:
version: "0.4.15" version: "0.4.15"
enable-cache: true enable-cache: true

6
.github/workflows/test.yml

@ -29,7 +29,7 @@ jobs:
with: with:
python-version: "3.11" python-version: "3.11"
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v3 uses: astral-sh/setup-uv@v5
with: with:
version: "0.4.15" version: "0.4.15"
enable-cache: true enable-cache: true
@ -66,7 +66,7 @@ jobs:
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v3 uses: astral-sh/setup-uv@v5
with: with:
version: "0.4.15" version: "0.4.15"
enable-cache: true enable-cache: true
@ -107,7 +107,7 @@ jobs:
with: with:
python-version: '3.8' python-version: '3.8'
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v3 uses: astral-sh/setup-uv@v5
with: with:
version: "0.4.15" version: "0.4.15"
enable-cache: true enable-cache: true

4
README.md

@ -46,7 +46,7 @@ The key features are:
<!-- sponsors --> <!-- sponsors -->
<a href="https://cryptapi.io/" target="_blank" title="CryptAPI: Your easy to use, secure and privacy oriented payment gateway."><img src="https://fastapi.tiangolo.com/img/sponsors/cryptapi.svg"></a> <a href="https://blockbee.io?ref=fastapi" target="_blank" title="BlockBee Cryptocurrency Payment Gateway"><img src="https://fastapi.tiangolo.com/img/sponsors/blockbee.png"></a>
<a href="https://platform.sh/try-it-now/?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023" target="_blank" title="Build, run and scale your apps on a modern, reliable, and secure PaaS."><img src="https://fastapi.tiangolo.com/img/sponsors/platform-sh.png"></a> <a href="https://platform.sh/try-it-now/?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023" target="_blank" title="Build, run and scale your apps on a modern, reliable, and secure PaaS."><img src="https://fastapi.tiangolo.com/img/sponsors/platform-sh.png"></a>
<a href="https://www.porter.run" target="_blank" title="Deploy FastAPI on AWS with a few clicks"><img src="https://fastapi.tiangolo.com/img/sponsors/porter.png"></a> <a href="https://www.porter.run" target="_blank" title="Deploy FastAPI on AWS with a few clicks"><img src="https://fastapi.tiangolo.com/img/sponsors/porter.png"></a>
<a href="https://bump.sh/fastapi?utm_source=fastapi&utm_medium=referral&utm_campaign=sponsor" target="_blank" title="Automate FastAPI documentation generation with Bump.sh"><img src="https://fastapi.tiangolo.com/img/sponsors/bump-sh.svg"></a> <a href="https://bump.sh/fastapi?utm_source=fastapi&utm_medium=referral&utm_campaign=sponsor" target="_blank" title="Automate FastAPI documentation generation with Bump.sh"><img src="https://fastapi.tiangolo.com/img/sponsors/bump-sh.svg"></a>
@ -458,7 +458,7 @@ FastAPI depends on Pydantic and Starlette.
### `standard` Dependencies ### `standard` Dependencies
When you install FastAPI with `pip install "fastapi[standard]"` it comes the `standard` group of optional dependencies: When you install FastAPI with `pip install "fastapi[standard]"` it comes with the `standard` group of optional dependencies:
Used by Pydantic: Used by Pydantic:

6
docs/en/data/sponsors.yml

@ -1,7 +1,7 @@
gold: gold:
- url: https://cryptapi.io/ - url: https://blockbee.io?ref=fastapi
title: "CryptAPI: Your easy to use, secure and privacy oriented payment gateway." title: BlockBee Cryptocurrency Payment Gateway
img: https://fastapi.tiangolo.com/img/sponsors/cryptapi.svg img: https://fastapi.tiangolo.com/img/sponsors/blockbee.png
- url: https://platform.sh/try-it-now/?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023 - url: https://platform.sh/try-it-now/?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023
title: "Build, run and scale your apps on a modern, reliable, and secure PaaS." title: "Build, run and scale your apps on a modern, reliable, and secure PaaS."
img: https://fastapi.tiangolo.com/img/sponsors/platform-sh.png img: https://fastapi.tiangolo.com/img/sponsors/platform-sh.png

1
docs/en/data/sponsors_badge.yml

@ -32,3 +32,4 @@ logins:
- Kong - Kong
- speakeasy-api - speakeasy-api
- jess-render - jess-render
- blockbee-io

4
docs/en/docs/contributing.md

@ -107,7 +107,7 @@ $ cd docs/en/
Then run `mkdocs` in that directory: Then run `mkdocs` in that directory:
```console ```console
$ mkdocs serve --dev-addr 8008 $ mkdocs serve --dev-addr 127.0.0.1:8008
``` ```
/// ///
@ -245,7 +245,7 @@ $ cd docs/es/
Then run `mkdocs` in that directory: Then run `mkdocs` in that directory:
```console ```console
$ mkdocs serve --dev-addr 8008 $ mkdocs serve --dev-addr 127.0.0.1:8008
``` ```
/// ///

BIN
docs/en/docs/img/sponsors/blockbee-banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/en/docs/img/sponsors/blockbee.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

2
docs/en/docs/index.md

@ -456,7 +456,7 @@ FastAPI depends on Pydantic and Starlette.
### `standard` Dependencies ### `standard` Dependencies
When you install FastAPI with `pip install "fastapi[standard]"` it comes the `standard` group of optional dependencies: When you install FastAPI with `pip install "fastapi[standard]"` it comes with the `standard` group of optional dependencies:
Used by Pydantic: Used by Pydantic:

55
docs/en/docs/release-notes.md

@ -9,10 +9,65 @@ hide:
### Docs ### Docs
* ✏️ Fix error in `docs/en/docs/contributing.md`. PR [#12899](https://github.com/fastapi/fastapi/pull/12899) by [@kingsubin](https://github.com/kingsubin).
* 📝 Minor corrections in `docs/en/docs/tutorial/sql-databases.md`. PR [#13081](https://github.com/fastapi/fastapi/pull/13081) by [@alv2017](https://github.com/alv2017).
* 📝 Update includes in `docs/ru/docs/tutorial/query-param-models.md`. PR [#12994](https://github.com/fastapi/fastapi/pull/12994) by [@alejsdev](https://github.com/alejsdev).
* ✏️ Fix typo in README installation instructions. PR [#13011](https://github.com/fastapi/fastapi/pull/13011) by [@dave-hay](https://github.com/dave-hay).
* 📝 Update docs for `fastapi-cli`. PR [#13031](https://github.com/fastapi/fastapi/pull/13031) by [@tiangolo](https://github.com/tiangolo). * 📝 Update docs for `fastapi-cli`. PR [#13031](https://github.com/fastapi/fastapi/pull/13031) by [@tiangolo](https://github.com/tiangolo).
### Translations
* 🌐 Add Indonesian translation for `docs/id/docs/tutorial/path-params.md`. PR [#13086](https://github.com/fastapi/fastapi/pull/13086) by [@gerry-sabar](https://github.com/gerry-sabar).
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/sql-databases.md`. PR [#13093](https://github.com/fastapi/fastapi/pull/13093) by [@GeumBinLee](https://github.com/GeumBinLee).
* 🌐 Update Chinese translation for `docs/zh/docs/async.md`. PR [#13095](https://github.com/fastapi/fastapi/pull/13095) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Add Chinese translation for `docs/zh/docs/advanced/openapi-webhooks.md`. PR [#13091](https://github.com/fastapi/fastapi/pull/13091) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Add Chinese translation for `docs/zh/docs/advanced/async-tests.md`. PR [#13074](https://github.com/fastapi/fastapi/pull/13074) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Add Ukrainian translation for `docs/uk/docs/fastapi-cli.md`. PR [#13020](https://github.com/fastapi/fastapi/pull/13020) by [@ykertytsky](https://github.com/ykertytsky).
* 🌐 Add Chinese translation for `docs/zh/docs/advanced/events.md`. PR [#12512](https://github.com/fastapi/fastapi/pull/12512) by [@ZhibangYue](https://github.com/ZhibangYue).
* 🌐 Add Russian translation for `/docs/ru/docs/tutorial/sql-databases.md`. PR [#13079](https://github.com/fastapi/fastapi/pull/13079) by [@alv2017](https://github.com/alv2017).
* 🌐 Update Chinese translation for `docs/zh/docs/advanced/testing-dependencies.md`. PR [#13066](https://github.com/fastapi/fastapi/pull/13066) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Update Traditional Chinese translation for `docs/zh-hant/docs/tutorial/index.md`. PR [#13075](https://github.com/fastapi/fastapi/pull/13075) by [@codingjenny](https://github.com/codingjenny).
* 🌐 Add Chinese translation for `docs/zh/docs/tutorial/sql-databases.md`. PR [#13051](https://github.com/fastapi/fastapi/pull/13051) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Update Chinese translation for `docs/zh/docs/tutorial/query-params-str-validations.md`. PR [#12928](https://github.com/fastapi/fastapi/pull/12928) by [@Vincy1230](https://github.com/Vincy1230).
* 🌐 Add Chinese translation for `docs/zh/docs/tutorial/header-param-models.md`. PR [#13040](https://github.com/fastapi/fastapi/pull/13040) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Update Chinese translation for `docs/zh/docs/tutorial/path-params.md`. PR [#12926](https://github.com/fastapi/fastapi/pull/12926) by [@Vincy1230](https://github.com/Vincy1230).
* 🌐 Update Chinese translation for `docs/zh/docs/tutorial/first-steps.md`. PR [#12923](https://github.com/fastapi/fastapi/pull/12923) by [@Vincy1230](https://github.com/Vincy1230).
* 🌐 Update Russian translation for `docs/ru/docs/deployment/docker.md`. PR [#13048](https://github.com/fastapi/fastapi/pull/13048) by [@anklav24](https://github.com/anklav24).
* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/generate-clients.md`. PR [#13030](https://github.com/fastapi/fastapi/pull/13030) by [@vitumenezes](https://github.com/vitumenezes).
* 🌐 Add Indonesian translation for `docs/id/docs/tutorial/first-steps.md`. PR [#13042](https://github.com/fastapi/fastapi/pull/13042) by [@gerry-sabar](https://github.com/gerry-sabar).
* 🌐 Add Chinese translation for `docs/zh/docs/tutorial/cookie-param-models.md`. PR [#13038](https://github.com/fastapi/fastapi/pull/13038) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Add Chinese translation for `docs/zh/docs/tutorial/request-form-models.md`. PR [#13045](https://github.com/fastapi/fastapi/pull/13045) by [@Zhongheng-Cheng](https://github.com/Zhongheng-Cheng).
* 🌐 Add Russian translation for `docs/ru/docs/virtual-environments.md`. PR [#13026](https://github.com/fastapi/fastapi/pull/13026) by [@alv2017](https://github.com/alv2017).
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/testing.md`. PR [#12968](https://github.com/fastapi/fastapi/pull/12968) by [@jts8257](https://github.com/jts8257).
* 🌐 Add Korean translation for `docs/ko/docs/advanced/async-test.md`. PR [#12918](https://github.com/fastapi/fastapi/pull/12918) by [@icehongssii](https://github.com/icehongssii).
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/oauth2-jwt.md`. PR [#10601](https://github.com/fastapi/fastapi/pull/10601) by [@AlertRED](https://github.com/AlertRED).
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/simple-oauth2.md`. PR [#10599](https://github.com/fastapi/fastapi/pull/10599) by [@AlertRED](https://github.com/AlertRED).
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/get-current-user.md`. PR [#10594](https://github.com/fastapi/fastapi/pull/10594) by [@AlertRED](https://github.com/AlertRED).
* 🌐 Add Traditional Chinese translation for `docs/zh-hant/docs/features.md`. PR [#12441](https://github.com/fastapi/fastapi/pull/12441) by [@codingjenny](https://github.com/codingjenny).
* 🌐 Add Traditional Chinese translation for `docs/zh-hant/docs/virtual-environments.md`. PR [#12791](https://github.com/fastapi/fastapi/pull/12791) by [@Vincy1230](https://github.com/Vincy1230).
* 🌐 Add Korean translation for `docs/ko/docs/advanced/templates.md`. PR [#12726](https://github.com/fastapi/fastapi/pull/12726) by [@Heumhub](https://github.com/Heumhub).
* 🌐 Add Russian translation for `docs/ru/docs/fastapi-cli.md`. PR [#13041](https://github.com/fastapi/fastapi/pull/13041) by [@alv2017](https://github.com/alv2017).
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/cookie-param-models.md`. PR [#13000](https://github.com/fastapi/fastapi/pull/13000) by [@hard-coders](https://github.com/hard-coders).
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/header-param-models.md`. PR [#13001](https://github.com/fastapi/fastapi/pull/13001) by [@hard-coders](https://github.com/hard-coders).
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/request-form-models.md`. PR [#13002](https://github.com/fastapi/fastapi/pull/13002) by [@hard-coders](https://github.com/hard-coders).
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/request-forms.md`. PR [#13003](https://github.com/fastapi/fastapi/pull/13003) by [@hard-coders](https://github.com/hard-coders).
* 🌐 Add Korean translation for `docs/ko/docs/resources/index.md`. PR [#13004](https://github.com/fastapi/fastapi/pull/13004) by [@hard-coders](https://github.com/hard-coders).
* 🌐 Add Korean translation for `docs/ko/docs/how-to/configure-swagger-ui.md`. PR [#12898](https://github.com/fastapi/fastapi/pull/12898) by [@nahyunkeem](https://github.com/nahyunkeem).
* 🌐 Add Korean translation to `docs/ko/docs/advanced/additional-status-codes.md`. PR [#12715](https://github.com/fastapi/fastapi/pull/12715) by [@nahyunkeem](https://github.com/nahyunkeem).
* 🌐 Add Traditional Chinese translation for `docs/zh-hant/docs/tutorial/first-steps.md`. PR [#12467](https://github.com/fastapi/fastapi/pull/12467) by [@codingjenny](https://github.com/codingjenny).
### Internal ### Internal
* ⬆ Bump astral-sh/setup-uv from 4 to 5. PR [#13096](https://github.com/fastapi/fastapi/pull/13096) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 🔧 Update sponsors: rename CryptAPI to BlockBee. PR [#13078](https://github.com/fastapi/fastapi/pull/13078) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump pypa/gh-action-pypi-publish from 1.12.2 to 1.12.3. PR [#13055](https://github.com/fastapi/fastapi/pull/13055) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump types-ujson from 5.7.0.1 to 5.10.0.20240515. PR [#13018](https://github.com/fastapi/fastapi/pull/13018) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump black from 24.3.0 to 24.10.0. PR [#13014](https://github.com/fastapi/fastapi/pull/13014) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump inline-snapshot from 0.13.0 to 0.14.0. PR [#13017](https://github.com/fastapi/fastapi/pull/13017) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump dirty-equals from 0.6.0 to 0.8.0. PR [#13015](https://github.com/fastapi/fastapi/pull/13015) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump cloudflare/wrangler-action from 3.12 to 3.13. PR [#12996](https://github.com/fastapi/fastapi/pull/12996) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump astral-sh/setup-uv from 3 to 4. PR [#12982](https://github.com/fastapi/fastapi/pull/12982) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 🔧 Remove duplicate actions/checkout in `notify-translations.yml`. PR [#12915](https://github.com/fastapi/fastapi/pull/12915) by [@tinyboxvk](https://github.com/tinyboxvk).
* 🔧 Update team members. PR [#13033](https://github.com/fastapi/fastapi/pull/13033) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update team members. PR [#13033](https://github.com/fastapi/fastapi/pull/13033) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update sponsors: remove Codacy. PR [#13032](https://github.com/fastapi/fastapi/pull/13032) by [@tiangolo](https://github.com/tiangolo). * 📝 Update sponsors: remove Codacy. PR [#13032](https://github.com/fastapi/fastapi/pull/13032) by [@tiangolo](https://github.com/tiangolo).

3
docs/en/docs/tutorial/sql-databases.md

@ -125,8 +125,6 @@ The same way, you can declare it as the function's **return type**, and then the
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *} {* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
</details>
Here we use the `SessionDep` dependency (a `Session`) to add the new `Hero` to the `Session` instance, commit the changes to the database, refresh the data in the `hero`, and then return it. Here we use the `SessionDep` dependency (a `Session`) to add the new `Hero` to the `Session` instance, commit the changes to the database, refresh the data in the `hero`, and then return it.
### Read Heroes ### Read Heroes
@ -235,7 +233,6 @@ All the fields in `HeroPublic` are the same as in `HeroBase`, with `id` declared
* `id` * `id`
* `name` * `name`
* `age` * `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *} {* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}

4
docs/en/overrides/main.html

@ -23,9 +23,9 @@
</div> </div>
<div id="announce-right" style="position: relative;"> <div id="announce-right" style="position: relative;">
<div class="item"> <div class="item">
<a title="CryptAPI: Your easy to use, secure and privacy oriented payment gateway." style="display: block; position: relative;" href="https://cryptapi.io/" target="_blank"> <a title="BlockBee Cryptocurrency Payment Gateway" style="display: block; position: relative;" href="https://blockbee.io?ref=fastapi" target="_blank">
<span class="sponsor-badge">sponsor</span> <span class="sponsor-badge">sponsor</span>
<img class="sponsor-image" src="/img/sponsors/cryptapi-banner.svg" /> <img class="sponsor-image" src="/img/sponsors/blockbee-banner.png" />
</a> </a>
</div> </div>
<div class="item"> <div class="item">

332
docs/id/docs/tutorial/first-steps.md

@ -0,0 +1,332 @@
# Langkah Pertama
File FastAPI yang paling sederhana bisa seperti berikut:
{* ../../docs_src/first_steps/tutorial001.py *}
Salin file tersebut ke `main.py`.
Jalankan di server:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:single">main.py</u>
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
│ │
│ 🐍 main.py │
│ │
╰──────────────────────╯
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
<font color="#3465A4">INFO </font> Found importable FastAPI app
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
│ │
<span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span>
│ │
╰──────────────────────────╯
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
<span style="background-color:#C4A000"><font color="#2E3436">╭────────── FastAPI CLI - Development mode ───────────╮</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ Serving at: http://127.0.0.1:8000 │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ API docs: http://127.0.0.1:8000/docs │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ Running in development mode, for production use: │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436"></font></span><span style="background-color:#C4A000"><font color="#555753"><b>fastapi run</b></font></span><span style="background-color:#C4A000"><font color="#2E3436"></font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">╰─────────────────────────────────────────────────────╯</font></span>
<font color="#4E9A06">INFO</font>: Will watch for changes in these directories: [&apos;/home/user/code/awesomeapp&apos;]
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://127.0.0.1:8000</b> (Press CTRL+C to quit)
<font color="#4E9A06">INFO</font>: Started reloader process [<font color="#34E2E2"><b>2265862</b></font>] using <font color="#34E2E2"><b>WatchFiles</b></font>
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">2265873</font>]
<font color="#4E9A06">INFO</font>: Waiting for application startup.
<font color="#4E9A06">INFO</font>: Application startup complete.
```
</div>
Di output, terdapat sebaris pesan:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Baris tersebut menunjukan URL dimana app aktif di komputer anda.
### Mencoba aplikasi
Buka browser di <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Anda akan melihat response JSON sebagai berikut:
```JSON
{"message": "Hello World"}
```
### Dokumen API interaktif
Sekarang kunjungi <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Anda akan melihat dokumentasi API interaktif otomatis (dibuat oleh <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Dokumen API alternatif
Dan sekarang, kunjungi <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Anda akan melihat dokumentasi alternatif otomatis (dibuat oleh <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
### OpenAPI
**FastAPI** membuat sebuah "schema" dimana semua API anda menggunakan standar **OpenAPI** untuk mendefinisikan API.
#### "Schema"
"schema" adalah suatu definisi atau deskripsi dari sesuatu. Bukan kode yang mengimplementasi definisi tersebut. Ini hanyalah sebuah deskripsi abstrak.
#### "schema" API
Dalam hal ini, <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> adalah spesifikasi yang menunjukan bagaimana untuk mendefinisikan sebuah skema di API anda.
Definisi skema ini termasuk jalur API anda, parameter yang bisa diterima, dll.
#### "schema" Data
Istilah "schema" bisa juga merujuk ke struktur data, seperti konten JSON.
Dalam kondisi ini, ini berarti attribut JSON dan tipe data yang dimiliki, dll.
#### Schema OpenAPI and JSON
"schema" OpenAPI mendefinisikan skema API dari API yang anda buat. Skema tersebut termasuk definisi (atau "schema") dari data yang dikirim atau diterima oleh API dari **JSON Schema**, skema data standar JSON.
#### Lihat `openapi.json`
Jika anda penasaran bagaimana skema OpenAPI polos seperti apa, FastAPI secara otomatis membuat JSON (schema) dengan deksripsi API anda.
anda bisa melihatnya di: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
Anda akan melihat JSON yang dimulai seperti:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### Kegunaan OpenAPI
Skema OpenAPI adalah tulang punggung dua sistem dokumentasi API interaktif yang ada di FastAPI.
Ada banyak alternatif sistem dokumentasi lainnya yang semuanya berdasarkan OpenAPI. Anda bisa menambahkannya ke aplikasi **FastAPI** anda.
Anda juga bisa menggunakan OpenAPI untuk membuat kode secara otomatis, untuk klien yang menggunakan API anda. Sebagai contoh, frontend, aplikasi mobile atau IoT.
## Ringkasan, secara bertahap
### Langkah 1: impor `FastAPI`
{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
`FastAPI` adalah class Python yang menyediakan semua fungsionalitas API anda.
/// note | Detail Teknis
`FastAPI` adalah class turunan langsung dari `Starlette`.
Anda bisa menggunakan semua fungsionalitas <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> dengan `FastAPI` juga.
///
### Langkah 2: buat "instance" dari `FastAPI`
{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
Di sini variabel `app` akan menjadi sebuah "instance" dari class `FastAPI`.
Ini akan menjadi gerbang utama untuk membuat semua API anda.
### Langkah 3: Buat *operasi path*
#### Path
"Path" atau jalur di sini merujuk ke bagian URL terakhir dimulai dari `/` pertama.
Sehingga, URL seperti:
```
https://example.com/items/foo
```
...path-nya adalah:
```
/items/foo
```
/// info
"path" juga biasa disebut "endpoint" atau "route".
///
ketika membuat API, "path" adalah jalan utama untuk memisahkan "concern" dan "resources".
#### Operasi
"Operasi" di sini merujuk ke salah satu dari metode HTTP berikut.
Salah satu dari:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...dan operasi lainnya yang unik:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
Dalam protokol HTTP, anda bisa berkomunikasi ke setiap path menggunakan satu (atau lebih) metode di atas.
---
Ketika membuat API, anda umumnya menggunakan metode HTTP tertentu untuk proses tertentu.
Umumnya menggunakan:
* `POST`: untuk membuat data.
* `GET`: untuk membaca data.
* `PUT`: untuk memperbarui data.
* `DELETE`: untuk menghapus data.
Sehingga, di OpanAPI, setiap metode HTTP ini disebut sebuah "operasi".
Kita akan menyebut mereka juga "**operasi**".
#### Mendefinisikan *dekorator operasi path*
{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
`@app.get("/")` memberitahu **FastAPI** bahwa fungsi di bawahnya mengurusi request yang menuju ke:
* path `/`
* menggunakan <abbr title="an HTTP GET method">operasi <code>get</code></abbr>
/// info | `@decorator` Info
Sintaksis `@sesuatu` di Python disebut "dekorator".
Dekorator ditempatkan di atas fungsi. Seperti sebuah topi cantik (Saya pikir istilah ini berasal dari situ).
"dekorator" memanggil dan bekerja dengan fungsi yang ada di bawahnya
Pada kondisi ini, dekorator ini memberi tahu **FastAPI** bahwa fungsi di bawah nya berhubungan dengan **path** `/` dengan **operasi** `get`.
Sehingga disebut **dekorator operasi path**.
///
Operasi lainnya yang bisa digunakan:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
Dan operasi unik lainnya:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip | Tips
Jika anda bisa menggunakan operasi apa saja (metode HTTP).
**FastAPI** tidak mengharuskan anda menggunakan operasi tertentu.
Informasi di sini hanyalah sebagai panduan, bukan keharusan.
Sebagai contoh, ketika menggunakan GraphQL, semua operasi umumnya hanya menggunakan `POST`.
///
### Langkah 4: mendefinisikan **fungsi operasi path**
Ini "**fungsi operasi path**" kita:
* **path**: adalah `/`.
* **operasi**: adalah `get`.
* **fungsi**: adalah fungsi yang ada di bawah dekorator (di bawah `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
Ini adalah fungsi Python.
Fungsi ini dipanggil **FastAPI** setiap kali menerima request ke URL "`/`" dengan operasi `GET`.
Di kondisi ini, ini adalah sebuah fungsi `async`.
---
Anda bisa mendefinisikan fungsi ini sebagai fungsi normal daripada `async def`:
{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
/// note | Catatan
Jika anda tidak tahu perbedaannya, kunjungi [Async: *"Panduan cepat"*](../async.md#in-a-hurry){.internal-link target=_blank}.
///
### Langkah 5: hasilkan konten
{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
Anda bisa menghasilkan `dict`, `list`, nilai singular seperti `str`, `int`, dll.
Anda juga bisa menghasilkan model Pydantic (anda akan belajar mengenai ini nanti).
Ada banyak objek dan model yang secara otomatis dikonversi ke JSON (termasuk ORM, dll). Anda bisa menggunakan yang anda suka, kemungkinan sudah didukung.
## Ringkasan
* Impor `FastAPI`.
* Buat sebuah instance `app`.
* Tulis **dekorator operasi path** menggunakan dekorator seperti `@app.get("/")`.
* Definisikan **fungsi operasi path**; sebagai contoh, `def root(): ...`.
* Jalankan server development dengan perintah `fastapi dev`.

258
docs/id/docs/tutorial/path-params.md

@ -0,0 +1,258 @@
# Parameter Path
"parameter" atau "variabel" path didefinisikan dengan sintaksis Python format string:
{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
Nilai parameter path `item_id` akan dikirim ke fungsi sebagai argument `item_id`:
Jika anda menjalankan contoh berikut dan kunjungi <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, anda akan melihat respon:
```JSON
{"item_id":"foo"}
```
## Parameter path dengan tipe data
Tipe data parameter path bisa didefinisikan di dalam fungsi, menggunakan anotasi tipe data standar Python:
{* ../../docs_src/path_params/tutorial002.py hl[7] *}
Dalam hal ini `item_id` didefinisikan sebagai `int`.
/// check | Periksa
Penyunting kode anda bisa membantu periksa di dalam fungsi seperti pemeriksaan kesalahan, kelengkapan kode, dll.
///
## <abbr title="juga disebut: serialization, parsing, marshalling">Konversi</abbr> data
Jika contoh berikut dijalankan dan diakses browser melalui <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, anda akan melihat respon:
```JSON
{"item_id":3}
```
/// check | Periksa
Perhatikan nilai fungsi yang diterima (dan dihasilkan) adalah `3`, sebagai `int` di Python, dan bukan string `"3"`.
Sehingga dengan deklarasi tipe data **FastAPI** memberikan request otomatis <abbr title="konversi string dari request HTTP menjadi data Python">"parsing"</abbr>.
///
## Validasi Data
Tetapi jika di browser anda akses <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, anda akan melihat pesan kesalahan HTTP:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo",
"url": "https://errors.pydantic.dev/2.1/v/int_parsing"
}
]
}
```
Karena parameter path `item_id` bernilai `"foo"` yang bukan tipe data `int`.
Kesalahan yang sama akan muncul jika menggunakan `float` daripada `int`, seperti di: <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>
/// check | Periksa
Dengan deklarasi tipe data Python, **FastAPI** melakukan validasi data.
Perhatikan kesalahan tersebut juga menjelaskan validasi apa yang tidak sesuai.
Validasi ini sangat membantu ketika mengembangkan dan men-*debug* kode yang berhubungan dengan API anda.
///
## Dokumentasi
Ketika anda membuka browser di <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, anda melihat dokumentasi API interaktif otomatis berikut:
<img src="/img/tutorial/path-params/image01.png">
/// check | Periksa
Dengan deklarasi tipe data Python yang sama, **FastAPI** membuat dokumentasi interaktif otomatis (terintegrasi Swagger UI).
Perhatikan parameter path dideklarasikan sebagai integer.
///
## Keuntungan basis-standar, dokumentasi alternatif
Karena skema yang dibuat berasal dari standar <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a>, maka banyak alat lain yang kompatibel.
Sehingga **FastAPI** menyediakan dokumentasi alternatif (menggunakan ReDoc), yang bisa diakses di <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>:
<img src="/img/tutorial/path-params/image02.png">
Cara yang sama untuk menggunakan tools kompatibel lainnya. Termasuk alat membuat kode otomatis untuk banyak bahasa.
## Pydantic
Semua validasi data dikerjakan di belakang layar oleh <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>, sehingga anda mendapatkan banyak kemudahan. Anda juga tahu proses ini akan ditangani dengan baik.
Anda bisa mendeklarasikan tipe data dengan `str`, `float`, `bool` dan banyak tipe data kompleks lainnya.
Beberapa tipe di atas akan dibahas pada bab berikutnya tutorial ini.
## Urutan berpengaruh
Ketika membuat *operasi path*, anda bisa menghadapi kondisi dimana *path* nya sudah tetap.
Seperti `/users/me`, untuk mendapatkan data user yang sedang aktif.
Kemudian anda bisa memiliki path `/users/{user_id}` untuk mendapatkan data user tertentu melalui user ID.
karena *operasi path* dievaluasi melalui urutan, anda harus memastikan path untuk `/users/me` dideklarasikan sebelum `/user/{user_id}`:
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
Sebaliknya, path `/users/{user_id}` juga akan sesuai dengan `/users/me`, "menganggap" menerima parameter `user_id` dengan nilai `"me"`.
Serupa, anda juga tidak bisa mendefinisikan operasi path:
{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
Path pertama akan selalu digunakan karena path sesuai dengan yang pertama.
## Nilai terdefinisi
Jika ada *operasi path* yang menerima *parameter path*, tetapi anda ingin nilai valid *parameter path* sudah terdefinisi, anda bisa menggunakan standar Python <abbr title="Enumeration">`Enum`</abbr>.
### Membuat class `Enum`
Import `Enum` dan buat *sub-class* warisan dari `str` dan `Enum`.
Dengan warisan dari `str` dokumen API mengetahui nilai nya harus berjenis `string` supaya bisa digunakan dengan benar.
Kemudian buat atribut *class* dengan nilai tetap *string* yang benar:
{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
/// info
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Enumerasi (atau enum) tersedia di Python</a> sejak versi 3.4.
///
/// tip | Tips
"AlxexNet", "ResNet", dan "LeNet" adalah nama <abbr title="Secara teknis, arsitektur model Deep Learning">model</abbr> *Machine Learning*.
///
### Mendeklarasikan *parameter path*
Kemudian buat *parameter path* dengan tipe anotasi menggunakan *class* enum dari (`ModelName`)
{* ../../docs_src/path_params/tutorial005.py hl[16] *}
### Periksa dokumentasi
Karena nilai yang tersedia untuk *parameter path* telah terdefinisi, dokumen interatik bisa memunculkan:
<img src="/img/tutorial/path-params/image03.png">
### Bekerja dengan *enumarasi* Python
Nilai *parameter path* akan menjadi *anggota enumerasi*.
#### Membandingkan *anggota enumerasi*
Anda bisa membandingkan parameter *path* dengan *anggota enumerasi* di enum `ModelName` yang anda buat:
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
#### Mendapatkan *nilai enumerasi*
Anda bisa mendapatkan nilai (`str` dalam kasus ini) menggunakan `model_name.value`, atau secara umum `anggota_enum_anda.value`:
{* ../../docs_src/path_params/tutorial005.py hl[20] *}
/// tip | Tips
Anda bisa mengakses nilai `"lenet"` dnegan `ModelName.lenet.value`.
///
#### Menghasilkan *anggota enumerasi*
Anda bisa menghasilkan *anggota enumerasi* dari *operasi path* bahkan di body JSON bersarang (contoh `dict`).
They will be converted to their corresponding values (strings in this case) before returning them to the client:
{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
Klien akan mendapatkan respon JSON seperti berikut:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Parameter path berisi path
Misalkan terdapat *operasi path* dengan path `/files/{file_path}`.
Tetapi anda memerlukan `file_path` itu berisi *path*, seperti like `home/johndoe/myfile.txt`.
Sehingga URL untuk file tersebut akan seperti: `/files/home/johndoe/myfile.txt`.
### Dukungan OpenAPI
OpenAPI tidak bisa mendeklarasikan *parameter path* berisi *path* di dalamnya, karena menyebabkan kondisi yang sulit di*test* dan didefinisikan.
Tetapi, di **FastAPI** anda tetap bisa melakukannya dengan menggunakan *tools* internal dari Starlette.
Dan dokumentasi tetap berfungsi walaupun tidak menambahkan keterangan bahwa parameter harus berisi *path*.
### Konverter path
Melalui Starlette anda bisa mendeklarasikan *parameter path* berisi *path* dengan URL seperti:
```
/files/{file_path:path}
```
Dikondisi ini nama parameter adalah `file_path` dan bagian terakhir `:path` menginformasikan parameter harus sesuai dengan setiap *path*.
Sehingga anda bisa menggunakan:
{* ../../docs_src/path_params/tutorial004.py hl[6] *}
/// tip | Tips
Anda mungkin perlu parameter berisi `/home/johndoe/myfile.txt` di awali garis belakang (`/`).
Di kondisi ini, URL nya menjadi: `/files//home/johndoe/myfile.txt`, dengan dua garis belakang (`//`) di antara `files` dan `home`.
///
## Ringkasan
Di **FastAPI** dengan menggunakan deklarasi tipe Python standar, pendek, intuitif, anda mendapatkan:
* Dukungan editor: pemeriksaan kesalahan, autocompletion, dll.
* "<abbr title="konversi string dari request HTTP menjadi data Python">Parsing</abbr>" data.
* Validasi data.
* Annotasi API dan dokumentasi otomatis.
Semua itu anda hanya perlu mendeklarasikan sekali saja.
Ini adalah salah satu keunggulan **FastAPI** dibandingkan dengan *framework* lainnya (selain dari performa Python *native*c)

41
docs/ko/docs/advanced/additional-status-codes.md

@ -0,0 +1,41 @@
# 추가 상태 코드
기본적으로 **FastAPI**는 응답을 `JSONResponse`를 사용하여 반환하며, *경로 작업(path operation)*에서 반환한 내용을 해당 `JSONResponse` 안에 넣어 반환합니다.
기본 상태 코드 또는 *경로 작업*에서 설정한 상태 코드를 사용합니다.
## 추가 상태 코드
기본 상태 코드와 별도로 추가 상태 코드를 반환하려면 `JSONResponse`와 같이 `Response`를 직접 반환하고 추가 상태 코드를 직접 설정할 수 있습니다.
예를 들어 항목을 업데이트할 수 있는 *경로 작업*이 있고 성공 시 200 “OK”의 HTTP 상태 코드를 반환한다고 가정해 보겠습니다.
하지만 새로운 항목을 허용하기를 원할 것입니다. 항목이 이전에 존재하지 않았다면 이를 생성하고 HTTP 상태 코드 201 "Created"를 반환합니다.
이를 위해서는 `JSONResponse`를 가져와서 원하는 `status_code`를 설정하여 콘텐츠를 직접 반환합니다:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | 경고
위의 예제처럼 `Response`를 직접 반환하면 바로 반환됩니다.
모델 등과 함께 직렬화되지 않습니다.
원하는 데이터가 있는지, 값이 유효한 JSON인지 확인합니다(`JSONResponse`를 사용하는 경우).
///
/// note | 기술적 세부 정보
`from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자 여러분을 위한 편의성으로 `fastapi.responses`와 동일한 `starlette.responses`를 제공합니다. 그러나 사용 가능한 응답의 대부분은 Starlette에서 직접 제공됩니다. `status` 또한 마찬가지입니다.
///
## OpenAPI 및 API 문서
추가 상태 코드와 응답을 직접 반환하는 경우, FastAPI는 반환할 내용을 미리 알 수 있는 방법이 없기 때문에 OpenAPI 스키마(API 문서)에 포함되지 않습니다.
하지만 다음을 사용하여 코드에 이를 문서화할 수 있습니다: [추가 응답](additional-responses.md){.internal-link target=_blank}.

108
docs/ko/docs/advanced/async-tests.md

@ -0,0 +1,108 @@
# 비동기 테스트 코드 작성
이전 장에서 `TestClient` 를 이용해 **FastAPI** 어플리케이션 테스트를 작성하는 법을 배우셨을텐데요.
지금까지는 `async` 키워드 사용없이 동기 함수의 테스트 코드를 작성하는 법만 익혔습니다.
하지만 비동기 함수를 사용하여 테스트 코드를 작성하는 것은 매우 유용할 수 있습니다.
예를 들면 데이터베이스에 비동기로 쿼리하는 경우를 생각해봅시다.
FastAPI 애플리케이션에 요청을 보내고, 비동기 데이터베이스 라이브러리를 사용하여 백엔드가 데이터베이스에 올바르게 데이터를 기록했는지 확인하고 싶을 때가 있을 겁니다.
이런 경우의 테스트 코드를 어떻게 비동기로 작성하는지 알아봅시다.
## pytest.mark.anyio
앞에서 작성한 테스트 함수에서 비동기 함수를 호출하고 싶다면, 테스트 코드도 비동기 함수여야합니다.
AnyIO는 특정 테스트 함수를 비동기 함수로 호출 할 수 있는 깔끔한 플러그인을 제공합니다.
## HTTPX
**FastAPI** 애플리케이션이 `async def` 대신 `def` 키워드로 선언된 함수를 사용하더라도, 내부적으로는 여전히 `비동기` 애플리케이션입니다.
`TestClient`는 pytest 표준을 사용하여 비동기 FastAPI 애플리케이션을 일반적인 `def` 테스트 함수 내에서 호출할 수 있도록 내부에서 마술을 부립니다. 하지만 이 마술은 비동기 함수 내부에서 사용할 때는 더 이상 작동하지 않습니다. 테스트를 비동기로 실행하면, 더 이상 테스트 함수 내부에서 `TestClient`를 사용할 수 없습니다.
`TestClient`<a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>를 기반으로 하고 있으며, 다행히 이를 직접 사용하여 API를 테스트할 수 있습니다.
## 예시
간단한 예시를 위해 [더 큰 어플리케이션 만들기](../ko/tutorial/bigger-applications.md){.internal-link target=_blank} 와 [테스트](../ko/tutorial/testing.md){.internal-link target=_blank}:에서 다룬 파일 구조와 비슷한 형태를 확인해봅시다:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py
```
`main.py`는 아래와 같아야 합니다:
{* ../../docs_src/async_tests/main.py *}
`test_main.py` 파일은 `main.py`에 대한 테스트가 있을 텐데, 다음과 같을 수 있습니다:
{* ../../docs_src/async_tests/test_main.py *}
## 실행하기
아래의 명령어로 테스트 코드를 실행합니다:
<div class="termy">
```console
$ pytest
---> 100%
```
</div>
## 자세히 보기
`@pytest.mark.anyio` 마커는 pytest에게 이 테스트 함수가 비동기로 호출되어야 함을 알려줍니다:
{* ../../docs_src/async_tests/test_main.py hl[7] *}
/// tip | 팁
테스트 함수가 이제 `TestClient`를 사용할 때처럼 단순히 `def`가 아니라 `async def`로 작성된 점에 주목해주세요.
///
그 다음에 `AsyncClient` 로 앱을 만들고 비동기 요청을 `await` 키워드로 보낼 수 있습니다:
{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
위의 코드는:
```Python
response = client.get('/')
```
`TestClient` 에 요청을 보내던 것과 동일합니다.
/// tip | 팁
새로운 `AsyncClient`를 사용할 때 async/await를 사용하고 있다는 점에 주목하세요. 이 요청은 비동기적으로 처리됩니다.
///
/// warning | 경고
만약의 어플리케이션이 Lifespan 이벤트에 의존성을 갖고 있다면 `AsyncClient` 가 이러한 이벤트를 실행시키지 않습니다.
`AsyncClient` 가 테스트를 실행시켰다는 것을 확인하기 위해
`LifespanManager` from <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a>.확인해주세요.
///
## 그 외의 비동기 함수 호출
테스트 함수가 이제 비동기 함수이므로, FastAPI 애플리케이션에 요청을 보내는 것 외에도 다른 `async` 함수를 호출하고 `await` 키워드를 사용 할 수 있습니다.
/// tip | 팁
테스트에 비동기 함수 호출을 통합할 때 (예: <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB의 MotorClient</a>를 사용할 때) `RuntimeError: Task attached to a different loop` 오류가 발생한다면, 이벤트 루프가 필요한 객체는 반드시 비동기 함수 내에서만 인스턴스화해야 한다는 점을 주의하세요!
예를 들어 `@app.on_event("startup")` 콜백 내에서 인스턴스화하는 것이 좋습니다.
///

127
docs/ko/docs/advanced/templates.md

@ -0,0 +1,127 @@
# 템플릿
**FastAPI**와 함께 원하는 어떤 템플릿 엔진도 사용할 수 있습니다.
일반적인 선택은 Jinja2로, Flask와 다른 도구에서도 사용됩니다.
설정을 쉽게 할 수 있는 유틸리티가 있으며, 이를 **FastAPI** 애플리케이션에서 직접 사용할 수 있습니다(Starlette 제공).
## 의존성 설치
가상 환경을 생성하고(virtual environment{.internal-link target=_blank}), 활성화한 후 jinja2를 설치해야 합니다:
<div class="termy">
```console
$ pip install jinja2
---> 100%
```
</div>
## 사용하기 `Jinja2Templates`
* `Jinja2Templates`를 가져옵니다.
* 나중에 재사용할 수 있는 `templates` 객체를 생성합니다.
* 템플릿을 반환할 경로 작업에 `Request` 매개변수를 선언합니다.
* 생성한 `templates`를 사용하여 `TemplateResponse`를 렌더링하고 반환합니다. 템플릿의 이름, 요청 객체 및 Jinja2 템플릿 내에서 사용될 키-값 쌍이 포함된 "컨텍스트" 딕셔너리도 전달합니다.
```Python hl_lines="4 11 15-18"
{!../../docs_src/templates/tutorial001.py!}
```
/// note | 참고
FastAPI 0.108.0 이전과 Starlette 0.29.0에서는 `name`이 첫 번째 매개변수였습니다.
또한 이전 버전에서는 `request` 객체가 Jinja2의 컨텍스트에서 키-값 쌍의 일부로 전달되었습니다.
///
/// tip | 팁
`response_class=HTMLResponse`를 선언하면 문서 UI 응답이 HTML임을 알 수 있습니다.
///
/// note | 기술 세부 사항
`from starlette.templating import Jinja2Templates`를 사용할 수도 있습니다.
**FastAPI**는 개발자를 위한 편리함으로 `fastapi.templating` 대신 `starlette.templating`을 제공합니다. 하지만 대부분의 사용 가능한 응답은 Starlette에서 직접 옵니다. `Request``StaticFiles`도 마찬가지입니다.
///
## 템플릿 작성하기
그런 다음 `templates/item.html`에 템플릿을 작성할 수 있습니다. 예를 들면:
```jinja hl_lines="7"
{!../../docs_src/templates/templates/item.html!}
```
### 템플릿 컨텍스트 값
다음과 같은 HTML에서:
{% raw %}
```jinja
Item ID: {{ id }}
```
{% endraw %}
...이는 전달한 "컨텍스트" `dict`에서 가져온 `id`를 표시합니다:
```Python
{"id": id}
```
예를 들어, ID가 `42`일 경우, 이는 다음과 같이 렌더링됩니다:
```html
Item ID: 42
```
### 템플릿 `url_for` 인수
템플릿 내에서 `url_for()`를 사용할 수도 있으며, 이는 *경로 작업 함수*에서 사용될 인수와 동일한 인수를 받습니다.
따라서 다음과 같은 부분에서:
{% raw %}
```jinja
<a href="{{ url_for('read_item', id=id) }}">
```
{% endraw %}
...이는 *경로 작업 함수* `read_item(id=id)`가 처리할 동일한 URL로 링크를 생성합니다.
예를 들어, ID가 `42`일 경우, 이는 다음과 같이 렌더링됩니다:
```html
<a href="/items/42">
```
## 템플릿과 정적 파일
템플릿 내에서 `url_for()`를 사용할 수 있으며, 예를 들어 `name="static"`으로 마운트한 `StaticFiles`와 함께 사용할 수 있습니다.
```jinja hl_lines="4"
{!../../docs_src/templates/templates/item.html!}
```
이 예제에서는 `static/styles.css`에 있는 CSS 파일에 연결될 것입니다:
```CSS hl_lines="4"
{!../../docs_src/templates/static/styles.css!}
```
그리고 `StaticFiles`를 사용하고 있으므로, 해당 CSS 파일은 **FastAPI** 애플리케이션에서 `/static/styles.css` URL로 자동 제공됩니다.
## 더 많은 세부 사항
템플릿 테스트를 포함한 더 많은 세부 사항은 <a href="https://www.starlette.io/templates/" class="external-link" target="_blank">Starlette의 템플릿 문서</a>를 확인하세요.

70
docs/ko/docs/how-to/configure-swagger-ui.md

@ -0,0 +1,70 @@
# Swagger UI 구성
추가적인 <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">Swagger UI 매개변수</a>를 구성할 수 있습니다.
구성을 하려면, `FastAPI()` 앱 객체를 생성할 때 또는 `get_swagger_ui_html()` 함수에 `swagger_ui_parameters` 인수를 전달하십시오.
`swagger_ui_parameters`는 Swagger UI에 직접 전달된 구성을 포함하는 딕셔너리를 받습니다.
FastAPI는 이 구성을 **JSON** 형식으로 변환하여 JavaScript와 호환되도록 합니다. 이는 Swagger UI에서 필요로 하는 형식입니다.
## 구문 강조 비활성화
예를 들어, Swagger UI에서 구문 강조 기능을 비활성화할 수 있습니다.
설정을 변경하지 않으면, 기본적으로 구문 강조 기능이 활성화되어 있습니다:
<img src="/img/tutorial/extending-openapi/image02.png">
그러나 `syntaxHighlight``False`로 설정하여 구문 강조 기능을 비활성화할 수 있습니다:
{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
...그럼 Swagger UI에서 더 이상 구문 강조 기능이 표시되지 않습니다:
<img src="/img/tutorial/extending-openapi/image03.png">
## 테마 변경
동일한 방식으로 `"syntaxHighlight.theme"` 키를 사용하여 구문 강조 테마를 설정할 수 있습니다 (중간에 점이 포함된 것을 참고하십시오).
{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
이 설정은 구문 강조 색상 테마를 변경합니다:
<img src="/img/tutorial/extending-openapi/image04.png">
## 기본 Swagger UI 매개변수 변경
FastAPI는 대부분의 사용 사례에 적합한 몇 가지 기본 구성 매개변수를 포함하고 있습니다.
기본 구성에는 다음이 포함됩니다:
{* ../../fastapi/openapi/docs.py ln[8:23] hl[17:23] *}
`swagger_ui_parameters` 인수에 다른 값을 설정하여 이러한 기본값 중 일부를 재정의할 수 있습니다.
예를 들어, `deepLinking`을 비활성화하려면 `swagger_ui_parameters`에 다음 설정을 전달할 수 있습니다:
{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
## 기타 Swagger UI 매개변수
사용할 수 있는 다른 모든 구성 옵션을 확인하려면, Swagger UI 매개변수에 대한 공식 <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">문서</a>를 참조하십시오.
## JavaScript 전용 설정
Swagger UI는 **JavaScript 전용** 객체(예: JavaScript 함수)로 다른 구성을 허용하기도 합니다.
FastAPI는 이러한 JavaScript 전용 `presets` 설정을 포함하고 있습니다:
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
이들은 문자열이 아닌 **JavaScript** 객체이므로 Python 코드에서 직접 전달할 수 없습니다.
이와 같은 JavaScript 전용 구성을 사용해야 하는 경우, 위의 방법 중 하나를 사용하여 모든 Swagger UI 경로 작업을 재정의하고 필요한 JavaScript를 수동으로 작성할 수 있습니다.

3
docs/ko/docs/resources/index.md

@ -0,0 +1,3 @@
# 리소스
추가 리소스, 외부 링크, 기사 등. ✈️

76
docs/ko/docs/tutorial/cookie-param-models.md

@ -0,0 +1,76 @@
# 쿠키 매개변수 모델
관련있는 **쿠키**들의 그룹이 있는 경우, **Pydantic 모델**을 생성하여 선언할 수 있습니다. 🍪
이를 통해 **여러 위치**에서 **모델을 재사용** 할 수 있고 모든 매개변수에 대한 유효성 검사 및 메타데이터를 한 번에 선언할 수도 있습니다. 😍
/// note | 참고
이 기능은 FastAPI 버전 `0.115.0` 이후부터 지원됩니다. 🤓
///
/// tip | 팁
동일한 기술이 `Query`, `Cookie`, 그리고 `Header`에 적용됩니다. 😎
///
## Pydantic 모델을 사용한 쿠키
**Pydantic 모델**에 필요한 **쿠키** 매개변수를 선언한 다음, 해당 매개변수를 `Cookie`로 선언합니다:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI**는 요청에서 받은 **쿠키**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
## 문서 확인하기
문서 UI `/docs`에서 정의한 쿠키를 볼 수 있습니다:
<div class="screenshot">
<img src="/img/tutorial/cookie-param-models/image01.png">
</div>
/// info | 정보
명심하세요, 내부적으로 **브라우저는 쿠키를 특별한 방식으로 처리**하기 때문에 **자바스크립트**가 쉽게 쿠키를 건드릴 수 **없습니다**.
`/docs`에서 **API 문서 UI**로 이동하면 *경로 작업*에 대한 쿠키의 **문서**를 볼 수 있습니다.
하지만 아무리 **데이터를 입력**하고 "실행(Execute)"을 클릭해도, 문서 UI는 **자바스크립트**로 작동하기 때문에 쿠키는 전송되지 않고, 아무 값도 쓰지 않은 것처럼 **오류** 메시지를 보게 됩니다.
///
## 추가 쿠키 금지하기
일부 특별한 사용 사례(흔하지는 않겠지만)에서는 수신하려는 쿠키를 **제한**할 수 있습니다.
이제 API는 자신의 <abbr title="농담입니다, 혹시나 해서요. 쿠키 동의와 관련해서 할 수 있는 것은 없지만, 이제 API조차도 잘못된 쿠키를 거부할 수 있다는 점이 재밌습니다. 쿠키 드세요. 🍪">쿠키 동의</abbr>를 제어할 수 있는 권한을 갖게 되었습니다. 🤪🍪
Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forbid`)할 수 있습니다:
{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *}
클라이언트가 **추가 쿠키**를 보내려고 시도하면, **오류** 응답을 받게 됩니다.
<abbr title="이건 또 다른 농담입니다. 제 말에 귀 기울이지 마세요. 커피랑 쿠키 좀 드세요. ☕">API가 거부</abbr>하는데도 동의를 얻기 위해 애쓰는 불쌍한 쿠키 배너(팝업)들. 🍪
예를 들어, 클라이언트가 `good-list-please` 값으로 `santa_tracker` 쿠키를 보내려고 하면 클라이언트는 `santa_tracker` <abbr title="산타는 쿠키가 부족한 것을 못마땅해합니다. 🎅 알겠습니다, 쿠키 농담은 이제 없습니다.">쿠키가 허용되지 않는다</abbr>**오류** 응답을 받게 됩니다:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## 요약
**Pydantic 모델**을 사용하여 **FastAPI**에서 <abbr title="가기 전에 마지막 쿠키를 드세요. 🍪">**쿠키**</abbr>를 선언할 수 있습니다. 😍

56
docs/ko/docs/tutorial/header-param-models.md

@ -0,0 +1,56 @@
# 헤더 매개변수 모델
관련 있는 **헤더 매개변수** 그룹이 있는 경우, **Pydantic 모델**을 생성하여 선언할 수 있습니다.
이를 통해 **여러 위치**에서 **모델을 재사용** 할 수 있고 모든 매개변수에 대한 유효성 검사 및 메타데이터를 한 번에 선언할 수도 있습니다. 😎
/// note | 참고
이 기능은 FastAPI 버전 `0.115.0` 이후부터 지원됩니다. 🤓
///
## Pydantic 모델을 사용한 헤더 매개변수
**Pydantic 모델**에 필요한 **헤더 매개변수**를 선언한 다음, 해당 매개변수를 `Header`로 선언합니다:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI**는 요청에서 받은 **헤더**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
## 문서 확인하기
문서 UI `/docs`에서 필요한 헤더를 볼 수 있습니다:
<div class="screenshot">
<img src="/img/tutorial/header-param-models/image01.png">
</div>
## 추가 헤더 금지하기
일부 특별한 사용 사례(흔하지는 않겠지만)에서는 수신하려는 헤더를 **제한**할 수 있습니다.
Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forbid`)할 수 있습니다:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
클라이언트가 **추가 헤더**를 보내려고 시도하면, **오류** 응답을 받게 됩니다.
예를 들어, 클라이언트가 `plumbus` 값으로 `tool` 헤더를 보내려고 하면, 클라이언트는 헤더 매개변수 `tool`이 허용 되지 않는다는 **오류** 응답을 받게 됩니다:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## 요약
**Pydantic 모델**을 사용하여 **FastAPI**에서 **헤더**를 선언할 수 있습니다. 😎

78
docs/ko/docs/tutorial/request-form-models.md

@ -0,0 +1,78 @@
# 폼 모델
FastAPI에서 **Pydantic 모델**을 이용하여 **폼 필드**를 선언할 수 있습니다.
/// info | 정보
폼(Form)을 사용하려면, 먼저 <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>를 설치하세요.
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, 아래와 같이 설치할 수 있습니다:
```console
$ pip install python-multipart
```
///
/// note | 참고
이 기능은 FastAPI 버전 `0.113.0` 이후부터 지원됩니다. 🤓
///
## Pydantic 모델을 사용한 폼
**폼 필드**로 받고 싶은 필드를 **Pydantic 모델**로 선언한 다음, 매개변수를 `Form`으로 선언하면 됩니다:
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
**FastAPI**는 요청에서 받은 **폼 데이터**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
## 문서 확인하기
문서 UI `/docs`에서 확인할 수 있습니다:
<div class="screenshot">
<img src="/img/tutorial/request-form-models/image01.png">
</div>
## 추가 폼 필드 금지하기
일부 특별한 사용 사례(흔하지는 않겠지만)에서는 Pydantic 모델에서 정의한 폼 필드를 **제한**하길 원할 수도 있습니다. 그리고 **추가** 필드를 **금지**할 수도 있습니다.
/// note | 참고
이 기능은 FastAPI 버전 `0.114.0` 이후부터 지원됩니다. 🤓
///
Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forbid`)할 수 있습니다:
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
클라이언트가 추가 데이터를 보내려고 하면 **오류** 응답을 받게 됩니다.
예를 들어, 클라이언트가 폼 필드를 보내려고 하면:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
`extra` 필드가 허용되지 않는다는 오류 응답을 받게 됩니다:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## 요약
Pydantic 모델을 사용하여 FastAPI에서 폼 필드를 선언할 수 있습니다. 😎

74
docs/ko/docs/tutorial/request-forms.md

@ -0,0 +1,74 @@
# 폼 데이터
JSON 대신 폼 필드를 받아야 하는 경우 `Form`을 사용할 수 있습니다.
/// info | 정보
폼을 사용하려면, 먼저 <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>를 설치하세요.
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, 아래와 같이 설치할 수 있습니다:
```console
$ pip install python-multipart
```
///
## `Form` 임포트하기
`fastapi`에서 `Form`을 임포트합니다:
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
## `Form` 매개변수 정의하기
`Body` 또는 `Query`와 동일한 방식으로 폼 매개변수를 만듭니다:
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
예를 들어, OAuth2 사양을 사용할 수 있는 방법 중 하나("패스워드 플로우"라고 함)로 `username``password`를 폼 필드로 보내야 합니다.
<abbr title="specification">사양</abbr>에서는 필드 이름이 `username``password`로 정확하게 명명되어야 하고, JSON이 아닌 폼 필드로 전송해야 합니다.
`Form`을 사용하면 유효성 검사, 예제, 별칭(예: `username` 대신 `user-name`) 등을 포함하여 `Body`(및 `Query`, `Path`, `Cookie`)와 동일한 구성을 선언할 수 있습니다.
/// info | 정보
`Form``Body`에서 직접 상속되는 클래스입니다.
///
/// tip | 팁
폼 본문을 선언할 때, 폼이 없으면 매개변수가 쿼리 매개변수나 본문(JSON) 매개변수로 해석(interpret)되기 때문에 `Form`을 명시적으로 사용해야 합니다.
///
## "폼 필드"에 대해
HTML 폼(`<form></form>`)이 데이터를 서버로 보내는 방식은 일반적으로 해당 데이터에 대해 "특수" 인코딩을 사용하며, 이는 JSON과 다릅니다.
**FastAPI**는 JSON 대신 올바른 위치에서 해당 데이터를 읽습니다.
/// note | 기술 세부사항
폼의 데이터는 일반적으로 "미디어 유형(media type)" `application/x-www-form-urlencoded`를 사용하여 인코딩합니다.
그러나 폼에 파일이 포함된 경우, `multipart/form-data`로 인코딩합니다. 다음 장에서 파일 처리에 대해 읽을 겁니다.
이러한 인코딩 및 폼 필드에 대해 더 읽고 싶다면, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><code>POST</code>에 대한 <abbr title="Mozilla Developer Network">MDN</a> 웹 문서를 참조하세요.
///
/// warning | 경고
*경로 작업*에서 여러 `Form` 매개변수를 선언할 수 있지만, JSON으로 수신할 것으로 예상되는 `Body` 필드와 함께 선언할 수 없습니다. 요청 본문은 `application/json` 대신에 `application/x-www-form-urlencoded`를 사용하여 인코딩되기 때문입니다.
이는 **FastAPI**의 제한 사항이 아니며 HTTP 프로토콜의 일부입니다.
///
## 요약
폼 데이터 입력 매개변수를 선언하려면 `Form`을 사용하세요.

360
docs/ko/docs/tutorial/sql-databases.md

@ -0,0 +1,360 @@
# SQL (관계형) 데이터베이스
**FastAPI**에서 SQL(관계형) 데이터베이스 사용은 필수가 아닙니다. 여러분이 원하는 **어떤 데이터베이스든** 사용할 수 있습니다.
여기서는 <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel</a>을 사용하는 예제를 살펴보겠습니다.
**SQLModel**은 <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a>와 Pydantic을 기반으로 구축되었습니다.SQLModel은 **SQL 데이터베이스**를 사용하는 FastAPI 애플리케이션에 완벽히 어울리도록 **FastAPI**의 제작자가 설계한 도구입니다.
/// tip | 팁
다른 SQL 또는 NoSQL 데이터베이스 라이브러리를 사용할 수도 있습니다 (일부는 <abbr title="객체 관계 매퍼(Object Relational Mapper), SQL 테이블을 나타내는 클래스를 제공하고 테이블의 행을 인스턴스로 표현하는 라이브러리를 지칭하는 용어">"ORM"</abbr>이라고도 불립니다), FastAPI는 특정 라이브러리의 사용을 강요하지 않습니다. 😎
///
SQLModel은 SQLAlchemy를 기반으로 하므로, SQLAlchemy에서 **지원하는 모든 데이터베이스**를 손쉽게 사용할 수 있습니다(SQLModel에서도 동일하게 지원됩니다). 예를 들면:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server 등.
이 예제에서는 **SQLite**를 사용합니다. SQLite는 단일 파일을 사용하고 파이썬에서 기본적으로 지원하기 때문입니다. 따라서 이 예제를 그대로 복사하여 실행할 수 있습니다.
나중에 실제 프로덕션 애플리케이션에서는 **PostgreSQL**과 같은 데이터베이스 서버를 사용하는 것이 좋습니다.
/// tip | 팁
**FastAPI**와 **PostgreSQL**를 포함하여 프론트엔드와 다양한 도구를 제공하는 공식 프로젝트 생성기가 있습니다: <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
///
이 튜토리얼은 매우 간단하고 짧습니다. 데이터베이스 기본 개념, SQL, 또는 더 복잡한 기능에 대해 배우고 싶다면, <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel 문서</a>를 참고하세요.
## `SQLModel` 설치하기
먼저, [가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, `sqlmodel`을 설치하세요:
<div class="termy">
```console
$ pip install sqlmodel
---> 100%
```
</div>
## 단일 모델로 애플리케이션 생성하기
우선 단일 **SQLModel** 모델을 사용하여 애플리케이션의 가장 간단한 첫 번째 버전을 생성해보겠습니다.
이후 **다중 모델**을 추가하여 보안과 유연성을 강화할 것입니다. 🤓
### 모델 생성하기
`SQLModel`을 가져오고 데이터베이스 모델을 생성합니다:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
`Hero` 클래스는 Pydantic 모델과 매우 유사합니다 (실제로 내부적으로 *Pydantic 모델이기도 합니다*).
몇 가지 차이점이 있습니다:
* `table=True`는 SQLModel에 이 모델이 *테이블 모델*이며, 단순한 데이터 모델이 아니라 SQL 데이터베이스의 **테이블**을 나타낸다는 것을 알려줍니다. (다른 일반적인 Pydantic 클래스처럼) 단순한 *데이터 모델*이 아닙니다.
* `Field(primary_key=True)`는 SQLModel에 `id`가 SQL 데이터베이스의 **기본 키**임을 알려줍니다 (SQL 기본 키에 대한 자세한 내용은 SQLModel 문서를 참고하세요).
`int | None` 유형으로 설정하면, SQLModel은 해당 열이 SQL 데이터베이스에서 `INTEGER` 유형이며 `NULLABLE` 값이어야 한다는 것을 알 수 있습니다.
* `Field(index=True)`는 SQLModel에 해당 열에 대해 **SQL 인덱스**를 생성하도록 지시합니다. 이를 통해 데이터베이스에서 이 열으로 필터링된 데이터를 읽을 때 더 빠르게 조회할 수 있습니다.
SQLModel은 `str`으로 선언된 항목이 SQL 데이터베이스에서 `TEXT` (또는 데이터베이스에 따라 `VARCHAR`) 유형의 열로 저장된다는 것을 인식합니다.
### 엔진 생성하기
SQLModel의 `engine` (내부적으로는 SQLAlchemy `engine`)은 데이터베이스에 대한 **연결을 유지**하는 역할을 합니다.
**하나의 단일 engine 객체**를 통해 코드 전체에서 동일한 데이터베이스에 연결할 수 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
`check_same_thread=False`를 사용하면 FastAPI에서 여러 스레드에서 동일한 SQLite 데이터베이스를 사용할 수 있습니다. 이는 **하나의 단일 요청**이 **여러 스레드**를 사용할 수 있기 때문에 필요합니다(예: 의존성에서 사용되는 경우).
걱정하지 마세요. 코드가 구조화된 방식으로 인해, 이후에 **각 요청마다 단일 SQLModel *세션*을 사용**하도록 보장할 것입니다. 실제로 그것이 `check_same_thread`가 하려는 것입니다.
### 테이블 생성하기
그 다음 `SQLModel.metadata.create_all(engine)`을 사용하여 모든 *테이블 모델*의 **테이블을 생성**하는 함수를 추가합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### 세션 의존성 생성하기
**`Session`**은 **메모리에 객체**를 저장하고 데이터에 필요한 모든 변경 사항을 추적한 후, **`engine`을 통해** 데이터베이스와 통신합니다.
`yield`를 사용해 FastAPI의 **의존성**을 생성하여 각 요청마다 새로운 `Session`을 제공합니다. 이는 요청당 하나의 세션만 사용되도록 보장합니다. 🤓
그런 다음 이 의존성을 사용하는 코드를 간소화하기 위해 `Annotated` 의존성 `SessionDep`을 생성합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### 시작 시 데이터베이스 테이블 생성하기
애플리케이션 시작 시 데이터베이스 테이블을 생성합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
여기서는 애플리케이션 시작 이벤트 시 테이블을 생성합니다.
프로덕션 환경에서는 애플리케이션을 시작하기 전에 실행되는 마이그레이션 스크립트를 사용할 가능성이 높습니다. 🤓
/// tip | 팁
SQLModel은 Alembic을 감싸는 마이그레이션 유틸리티를 제공할 예정입니다. 하지만 현재 <a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a>을 직접 사용할 수 있습니다.
///
### Hero 생성하기
각 SQLModel 모델은 Pydantic 모델이기도 하므로, Pydantic 모델을 사용할 수 있는 **타입 어노테이**션에서 동일하게 사용할 수 있습니다.
예를 들어, 파라미터를 `Hero` 타입으로 선언하면 **JSON 본문**에서 값을 읽어옵니다.
마찬가지로, 함수의 **반환 타입**으로 선언하면 해당 데이터의 구조가 자동으로 생성되는 API 문서의 UI에 나타납니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
</details>
여기서 `SessionDep` 의존성 (즉, `Session`)을 사용하여 새로운 `Hero``Session` 인스턴스에 추가하고, 데이터베이스에 변경 사항을 커밋하고, `hero` 데이터의 최신 상태를 갱신한 다음 이를 반환합니다.
### Heroes 조회하기
`select()`를 사용하여 데이터베이스에서 `Hero`를 **조회**할 수 있습니다. 결과에 페이지네이션을 적용하기 위해 `limit``offset`을 포함할 수 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### 단일 Hero 조회하기
단일 `Hero`를 **조회**할 수도 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Hero 삭제하기
`Hero`를 **삭제**하는 것도 가능합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### 애플리케이션 실행하기
애플리케이션을 실행하려면 다음 명령을 사용합니다:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
그런 다음 `/docs` UI로 이동하면, **FastAPI**가 해당 **model들**을 사용하여 API **문서를 생성**하는 것으르 확인할 수 있습니다. 또한 이 모델들은 데이터를 직렬화하고 검증하는 데에도 사용됩니다.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image01.png">
</div>
## 여러 모델로 애플리케이션 업데이트
이제 애플리케이션을 약간 **리팩토링**하여 **보안**과 **유연성**을 개선해 보겠습니다.
이전 애플리케이션의 UI를 보면, 지금까지는 클라이언트가 생성할 `Hero``id`를 직접 지정할 수 있다는 것을 알 수 있습니다. 😱
이는 허용되어선 안 됩니다. 클라이언트가 이미 데이터베이스에 저장된 `id`를 덮어쓸 위험이 있기 때문입니다. `id`**백엔드** 또는 **데이터베이스**가 결정해야 하며, **클라이언트**가 결정해서는 안 됩니다.
또한 hero의 `secret_name`을 생성하긴 했지만, 지금까지는 이 값을 어디에서나 반환하고 있습니다. 이는 그다지 **비밀스럽지** 않습니다... 😅
이러한 문제를 해결하기 위해 몇 가지 **추가 모델**을 추가할 것입니다. 바로 여기서 SQLModel이 빛을 발하게 됩니다. ✨
### 여러 모델 생성하기
**SQLModel**에서 `table=True`가 설정된 모델 클래스는 **테이블 모델**입니다.
`table=True`가 없는 모델 클래스는 **데이터 모델**로, 이는 실제로 몇 가지 추가 기능이 포함된 Pydantic 모델에 불과합니다. 🤓
SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중복 선언하지 않아도** 됩니다.
#### `HeroBase` - 기본 클래스
모든 모델에서 **공유되는 필드**를 가진 `HeroBase` 모델을 시작해 봅시다:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - *테이블 모델*
다음으로 실제 *테이블 모델*인 `Hero`를 생성합니다. 이 모델은 다른 모델에는 항상 포함되는 건 아닌 **추가 필드**를 포함합니다:
* `id`
* `secret_name`
`Hero``HeroBase`를 상속하므로 `HeroBase`에 선언된 필드도 포함합니다. 따라서 `Hero`는 다음 **필드들도** 가지게 됩니다:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - 공개 *데이터 모델*
다음으로 `HeroPublic` 모델을 생성합니다. 이 모델은 API 클라이언트에 **반환**되는 모델입니다.
`HeroPublic``HeroBase`와 동일한 필드를 가지며, `secret_name`은 포함하지 않습니다.
마침내 우리의 heroes의 정체가 보호됩니다! 🥷
또한 `id: int`를 다시 선언합니다. 이를 통해, API 클라이언트와 **계약**을 맺어 `id`가 항상 존재하며 항상 `int` 타입이라는 것을 보장합니다(`None`이 될 수 없습니다).
/// tip | 팁
반환 모델이 값이 항상 존재하고 항상 `int`(`None`이 아님)를 보장하는 것은 API 클라이언트에게 매우 유용합니다. 이를 통해 API와 통신하는 개발자가 훨씬 더 간단한 코드를 작성할 수 있습니다.
또한 **자동으로 생성된 클라이언트**는 더 단순한 인터페이스를 제공하므로, API와 소통하는 개발자들이 훨씬 수월하게 작업할 수 있습니다. 😎
///
`HeroPublic`의 모든 필드는 `HeroBase`와 동일하며, `id``int`로 선언됩니다(`None`이 아님):
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - hero 생성용 *데이터 모델*
이제 `HeroCreate` 모델을 생성합니다. 이 모델은 클라이언트로부터 받은 데이터를 **검증**하는 역할을 합니다.
`HeroCreate``HeroBase와` 동일한 필드를 가지며, 추가로 `secret_name을` 포함합니다.
클라이언트가 **새 hero을 생성**할 때 `secret_name`을 보내고, 이는 데이터베이스에 저장되지만, 해당 비밀 이름은 API를 통해 클라이언트에게 반환되지 않습니다.
/// tip | 팁
이 방식은 **비밀번호**를 처리하는 방법과 동일합니다. 비밀번호를 받지만, 이를 API에서 반환하지는 않습니다.
비밀번호 값을 저장하기 전에 **해싱**하여 저장하고, **평문으로 저장하지 마십시오**.
///
`HeroCreate`의 필드는 다음과 같습니다:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - hero 수정용 *데이터 모델*
이전 애플리케이션에서는 **hero를 수정**할 방법이 없었지만, 이제 **다중 모델**을 통해 수정 기능을 추가할 수 있습니다. 🎉
`HeroUpdate` *데이터 모델*은 약간 특별한데, 새 hero을 생성할 때 필요한 **모든 동일한 필드**를 가지지만, 모든 필드가 **선택적**(기본값이 있음)입니다. 이렇게 하면 hero을 수정할 때 수정하려는 필드만 보낼 수 있습니다.
모든 **필드가 변경되기** 때문에(타입이 `None`을 포함하고, 기본값이 `None`으로 설정됨), 모든 필드를 **다시 선언**해야 합니다.
엄밀히 말하면 `HeroBase`를 상속할 필요는 없습니다. 모든 필드를 다시 선언하기 때문입니다. 일관성을 위해 상속을 유지하긴 했지만, 필수는 아닙니다. 이는 개인적인 취향의 문제입니다. 🤷
`HeroUpdate`의 필드는 다음과 같습니다:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### `HeroCreate`로 생성하고 `HeroPublic` 반환하기
이제 **다중 모델**을 사용하므로 애플리케이션의 관련 부분을 업데이트할 수 있습니다.
요청에서 `HeroCreate` *데이터 모델*을 받아 이를 기반으로 `Hero` *테이블 모델*을 생성합니다.
이 새 *테이블 모델* `Hero`는 클라이언트에서 보낸 필드를 가지며, 데이터베이스에서 생성된 `id`도 포함합니다.
그런 다음 함수를 통해 동일한 *테이블 모델* `Hero`를 반환합니다. 하지만 `response_model``HeroPublic` *데이터 모델*을 선언했기 때문에, **FastAPI**는 `HeroPublic`을 사용하여 데이터를 검증하고 직렬화합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | 팁
이제 **반환 타입 주석** `-> HeroPublic` 대신 `response_model=HeroPublic`을 사용합니다. 반환하는 값이 실제로 `HeroPublic`*아니기* 때문입니다.
만약 `-> HeroPublic`으로 선언했다면, 에디터와 린터에서 반환값이 `HeroPublic`이 아니라 `Hero`라고 경고했을 것입니다. 이는 적절한 경고입니다.
`response_model`에 선언함으로써 **FastAPI**가 이를 처리하도록 하고, 타입 어노테이션과 에디터 및 다른 도구의 도움에는 영향을 미치지 않도록 설정합니다.
///
### `HeroPublic`으로 Heroes 조회하기
이전과 동일하게 `Hero`를 **조회**할 수 있습니다. 이번에도 `response_model=list[HeroPublic]`을 사용하여 데이터가 올바르게 검증되고 직렬화되도록 보장합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### `HeroPublic`으로 단일 Hero 조회하기
단일 hero을 **조회**할 수도 있습니다:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### `HeroUpdate`로 Hero 수정하기
**hero를 수정**할 수도 있습니다. 이를 위해 HTTP `PATCH` 작업을 사용합니다.
코드에서는 클라이언트가 보낸 데이터를 딕셔너리 형태(`dict`)로 가져옵니다. 이는 **클라이언트가 보낸 데이터만 포함**하며, 기본값으로 들어가는 값은 제외합니다. 이를 위해 `exclude_unset=True`를 사용합니다. 이것이 주요 핵심입니다. 🪄
그런 다음, `hero_db.sqlmodel_update(hero_data)`를 사용하여 `hero_data`의 데이터를 `hero_db`에 업데이트합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Hero 다시 삭제하기
hero **삭제**는 이전과 거의 동일합니다.
이번에는 모든 것을 리팩토링하고 싶은 욕구를 만족시키지 못할 것 같습니다. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### 애플리케이션 다시 실행하기
다음 명령을 사용해 애플리케이션을 다시 실행할 수 있습니다:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
`/docs` API UI로 이동하면 업데이트된 것을 확인할 수 있습니다. 클라이언트가 영웅을 생성할 때 `id`를 제공할 필요가 없게 되는 등의 변화도 보입니다.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image02.png">
</div>
## 요약
<a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a>을 사용하여 SQL 데이터베이스와 상호작용하고, *데이터 모델* 및 *테이블 모델*로 코드를 간소화할 수 있습니다.
더 많은 내용을 배우고 싶다면, **SQLModel** 문서를 참고하세요. <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">SQLModel을 **FastAPI**와 함께 사용하는 것에 대한 더 긴 미니 튜토리얼</a>도 제공합니다. 🚀

243
docs/ko/docs/tutorial/testing.md

@ -0,0 +1,243 @@
# 테스팅
<a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a> 덕분에 **FastAPI** 를 테스트하는 일은 쉽고 즐거운 일이 되었습니다.
Starlette는 <a href="https://www.python-httpx.org\" class="external-link" target="_blank">HTTPX</a>를 기반으로 하며, 이는 Requests를 기반으로 설계되었기 때문에 매우 친숙하고 직관적입니다.
이를 사용하면 FastAPI에서 <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a>를 직접 사용할 수 있습니다.
## `TestClient` 사용하기
/// info | 정보
`TestClient` 사용하려면, 우선 <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a> 를 설치해야 합니다.
[virtual environment](../virtual-environments.md){.internal-link target=_blank} 를 만들고, 활성화 시킨 뒤에 설치하세요. 예시:
```console
$ pip install httpx
```
///
`TestClient` 를 임포트하세요.
**FastAPI** 어플리케이션을 전달하여 `TestClient` 를 만드세요.
이름이 `test_` 로 시작하는 함수를 만드세요(`pytest` 의 표준적인 관례입니다).
`httpx` 를 사용하는 것과 같은 방식으로 `TestClient` 객체를 사용하세요.
표준적인 파이썬 문법을 이용하여 확인이 필요한 곳에 간단한 `assert` 문장을 작성하세요(역시 표준적인 `pytest` 관례입니다).
{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
/// tip | 팁
테스트를 위한 함수는 `async def` 가 아니라 `def` 로 작성됨에 주의하세요.
그리고 클라이언트에 대한 호출도 `await` 를 사용하지 않는 일반 호출입니다.
이렇게 하여 복잡한 과정 없이 `pytest` 를 직접적으로 사용할 수 있습니다.
///
/// note | 기술 세부사항
`from starlette.testclient import TestClient` 역시 사용할 수 있습니다.
**FastAPI** 는 개발자의 편의를 위해 `starlette.testclient``fastapi.testclient` 로도 제공할 뿐입니다. 이는 단지 `Starlette` 에서 직접 가져오는지의 차이일 뿐입니다.
///
/// tip | 팁
FastAPI 애플리케이션에 요청을 보내는 것 외에도 테스트에서 `async` 함수를 호출하고 싶다면 (예: 비동기 데이터베이스 함수), 심화 튜토리얼의 [Async Tests](../advanced/async-tests.md){.internal-link target=_blank} 를 참조하세요.
///
## 테스트 분리하기
실제 애플리케이션에서는 테스트를 별도의 파일로 나누는 경우가 많습니다.
그리고 **FastAPI** 애플리케이션도 여러 파일이나 모듈 등으로 구성될 수 있습니다.
### **FastAPI** app 파일
[Bigger Applications](bigger-applications.md){.internal-link target=_blank} 에 묘사된 파일 구조를 가지고 있는 것으로 가정해봅시다.
```
.
├── app
│   ├── __init__.py
│   └── main.py
```
`main.py` 파일 안에 **FastAPI** app 을 만들었습니다:
{* ../../docs_src/app_testing/main.py *}
### 테스트 파일
테스트를 위해 `test_main.py` 라는 파일을 생성할 수 있습니다. 이 파일은 동일한 Python 패키지(즉, `__init__.py` 파일이 있는 동일한 디렉터리)에 위치할 수 있습니다.
``` hl_lines="5"
.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py
```
파일들이 동일한 패키지에 위치해 있으므로, 상대 참조를 사용하여 `main` 에서 `app` 객체를 임포트 해올 수 있습니다.
{* ../../docs_src/app_testing/test_main.py hl[3] *}
...그리고 이전에 작성했던 것과 같은 테스트 코드를 작성할 수 있습니다.
## 테스트: 확장된 예시
이제 위의 예시를 확장하고 더 많은 세부 사항을 추가하여 다양한 부분을 어떻게 테스트하는지 살펴보겠습니다.
### 확장된 FastAPI 애플리케이션 파일
이전과 같은 파일 구조를 계속 사용해 보겠습니다.
```
.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py
```
이제 **FastAPI** 앱이 있는 `main.py` 파일에 몇 가지 다른 **경로 작업** 이 추가된 경우를 생각해봅시다.
단일 오류를 반환할 수 있는 `GET` 작업이 있습니다.
여러 다른 오류를 반환할 수 있는 `POST` 작업이 있습니다.
*경로 작업* 모두 `X-Token` 헤더를 요구합니다.
//// tab | Python 3.10+
```Python
{!> ../../docs_src/app_testing/app_b_an_py310/main.py!}
```
////
//// tab | Python 3.9+
```Python
{!> ../../docs_src/app_testing/app_b_an_py39/main.py!}
```
////
//// tab | Python 3.8+
```Python
{!> ../../docs_src/app_testing/app_b_an/main.py!}
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | 팁
될 수 있으면 `Annotated` 버전 사용을 권장합나다.
///
```Python
{!> ../../docs_src/app_testing/app_b_py310/main.py!}
```
////
//// tab | Python 3.8+ non-Annotated
/// tip | 팁
될 수 있으면 `Annotated` 버전 사용을 권장합나다.
///
```Python
{!> ../../docs_src/app_testing/app_b/main.py!}
```
////
### 확장된 테스트 파일
이제는 `test_main.py` 를 확장된 테스트들로 수정할 수 있습니다:
{* ../../docs_src/app_testing/app_b/test_main.py *}
클라이언트가 요청에 정보를 전달해야 하는데 방법을 모르겠다면, `httpx`에서 해당 작업을 수행하는 방법을 검색(Google)하거나, `requests`에서의 방법을 검색해보세요. HTTPX는 Requests의 디자인을 기반으로 설계되었습니다.
그 후, 테스트에서도 동일하게 적용하면 됩니다.
예시:
* *경로* 혹은 *쿼리* 매개변수를 전달하려면, URL 자체에 추가한다.
* JSON 본문을 전달하려면, 파이썬 객체 (예를들면 `dict`) 를 `json` 파라미터로 전달한다.
* JSON 대신 *폼 데이터* 를 보내야한다면, `data` 파라미터를 대신 전달한다.
* *헤더* 를 전달하려면, `headers` 파라미터에 `dict` 를 전달한다.
* *쿠키* 를 전달하려면, `cookies` 파라미터에 `dict` 를 전달한다.
백엔드로 데이터를 어떻게 보내는지 정보를 더 얻으려면 (`httpx` 혹은 `TestClient` 를 이용해서) <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX documentation</a> 를 확인하세요.
/// info | 정보
`TestClient` 는 Pydantic 모델이 아니라 JSON 으로 변환될 수 있는 데이터를 받습니다.
만약 테스트중 Pydantic 모델을 어플리케이션으로에 보내고 싶다면, [JSON 호환 가능 인코더](encoder.md){.internal-link target=_blank} 에 설명되어 있는 `jsonable_encoder` 를 사용할 수 있습니다.
///
## 실행하기
테스트 코드를 작성하고, `pytest` 를 설치해야합니다.
[virtual environment](../virtual-environments.md){.internal-link target=_blank} 를 만들고, 활성화 시킨 뒤에 설치하세요. 예시:
<div class="termy">
```console
$ pip install pytest
---> 100%
```
</div>
`pytest` 파일과 테스트를 자동으로 감지하고 실행한 다음, 결과를 보고할 것입니다.
테스트를 다음 명령어로 실행하세요.
<div class="termy">
```console
$ pytest
================ test session starts ================
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: /home/user/code/superawesome-cli/app
plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1
collected 6 items
---> 100%
test_main.py <span style="color: green; white-space: pre;">...... [100%]</span>
<span style="color: green;">================= 1 passed in 0.03s =================</span>
```
</div>

261
docs/pt/docs/advanced/generate-clients.md

@ -0,0 +1,261 @@
# Generate Clients
Como o **FastAPI** é baseado na especificação **OpenAPI**, você obtém compatibilidade automática com muitas ferramentas, incluindo a documentação automática da API (fornecida pelo Swagger UI).
Uma vantagem particular que nem sempre é óbvia é que você pode **gerar clientes** (às vezes chamados de <abbr title="Software Development Kits">**SDKs**</abbr>) para a sua API, para muitas **linguagens de programação** diferentes.
## Geradores de Clientes OpenAPI
Existem muitas ferramentas para gerar clientes a partir do **OpenAPI**.
Uma ferramenta comum é o <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>.
Se voce está construindo um **frontend**, uma alternativa muito interessante é o <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">openapi-ts</a>.
## Geradores de Clientes e SDKs - Patrocinadores
Existem também alguns geradores de clientes e SDKs baseados na OpenAPI (FastAPI) **patrocinados por empresas**, em alguns casos eles podem oferecer **recursos adicionais** além de SDKs/clientes gerados de alta qualidade.
Alguns deles também ✨ [**patrocinam o FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, isso garante o **desenvolvimento** contínuo e saudável do FastAPI e seu **ecossistema**.
E isso mostra o verdadeiro compromisso deles com o FastAPI e sua **comunidade** (você), pois eles não apenas querem fornecer um **bom serviço**, mas também querem garantir que você tenha um **framework bom e saudável**, o FastAPI. 🙇
Por exemplo, você pode querer experimentar:
* <a href="https://speakeasy.com/?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
* <a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi/?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
Existem também várias outras empresas que oferecem serviços semelhantes que você pode pesquisar e encontrar online. 🤓
## Gerar um Cliente Frontend TypeScript
Vamos começar com um aplicativo **FastAPI** simples:
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
Note que as *operações de rota* definem os modelos que usam para o corpo da requisição e o corpo da resposta, usando os modelos `Item` e `ResponseMessage`.
### Documentação da API
Se você acessar a documentação da API, verá que ela tem os **schemas** para os dados a serem enviados nas requisições e recebidos nas respostas:
<img src="/img/tutorial/generate-clients/image01.png">
Você pode ver esses schemas porque eles foram declarados com os modelos no app.
Essas informações estão disponíveis no **OpenAPI schema** do app e são mostradas na documentação da API (pelo Swagger UI).
E essas mesmas informações dos modelos que estão incluídas no OpenAPI são o que pode ser usado para **gerar o código do cliente**.
### Gerar um Cliente TypeScript
Agora que temos o app com os modelos, podemos gerar o código do cliente para o frontend.
#### Instalar o `openapi-ts`
Você pode instalar o `openapi-ts` no seu código frontend com:
<div class="termy">
```console
$ npm install @hey-api/openapi-ts --save-dev
---> 100%
```
</div>
#### Gerar o Código do Cliente
Para gerar o código do cliente, você pode usar a aplicação de linha de comando `openapi-ts` que agora está instalada.
Como ela está instalada no projeto local, você provavelmente não conseguiria chamar esse comando diretamente, mas você o colocaria no seu arquivo `package.json`.
Poderia ser assim:
```JSON hl_lines="7"
{
"name": "frontend-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"
},
"author": "",
"license": "",
"devDependencies": {
"@hey-api/openapi-ts": "^0.27.38",
"typescript": "^4.6.2"
}
}
```
Depois de ter esse script NPM `generate-client` lá, você pode executá-lo com:
<div class="termy">
```console
$ npm run generate-client
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios
```
</div>
Esse comando gerará o código em `./src/client` e usará o `axios` (a biblioteca HTTP frontend) internamente.
### Experimente o Código do Cliente
Agora você pode importar e usar o código do cliente, ele poderia ser assim, observe que você obtém preenchimento automático para os métodos:
<img src="/img/tutorial/generate-clients/image02.png">
Você também obterá preenchimento automático para o corpo a ser enviado:
<img src="/img/tutorial/generate-clients/image03.png">
/// tip | Dica
Observe o preenchimento automático para `name` e `price`, que foi definido no aplicativo FastAPI, no modelo `Item`.
///
Você terá erros em linha para os dados que você envia:
<img src="/img/tutorial/generate-clients/image04.png">
O objeto de resposta também terá preenchimento automático:
<img src="/img/tutorial/generate-clients/image05.png">
## App FastAPI com Tags
Em muitos casos seu app FastAPI será maior, e você provavelmente usará tags para separar diferentes grupos de *operações de rota*.
Por exemplo, você poderia ter uma seção para **items** e outra seção para **users**, e elas poderiam ser separadas por tags:
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
### Gerar um Cliente TypeScript com Tags
Se você gerar um cliente para um app FastAPI usando tags, normalmente também separará o código do cliente com base nas tags.
Dessa forma, você poderá ter as coisas ordenadas e agrupadas corretamente para o código do cliente:
<img src="/img/tutorial/generate-clients/image06.png">
Nesse caso você tem:
* `ItemsService`
* `UsersService`
### Nomes dos Métodos do Cliente
Agora os nomes dos métodos gerados como `createItemItemsPost` não parecem muito "limpos":
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...isto ocorre porque o gerador de clientes usa o **operation ID** interno do OpenAPI para cada *operação de rota*.
O OpenAPI exige que cada operation ID seja único em todas as *operações de rota*, então o FastAPI usa o **nome da função**, o **caminho** e o **método/operacao HTTP** para gerar esse operation ID, porque dessa forma ele pode garantir que os operation IDs sejam únicos.
Mas eu vou te mostrar como melhorar isso a seguir. 🤓
### IDs de Operação Personalizados e Melhores Nomes de Método
Você pode **modificar** a maneira como esses IDs de operação são **gerados** para torná-los mais simples e ter **nomes de método mais simples** nos clientes.
Neste caso, você terá que garantir que cada ID de operação seja **único** de alguma outra maneira.
Por exemplo, você poderia garantir que cada *operação de rota* tenha uma tag, e então gerar o ID da operação com base na **tag** e no **nome** da *operação de rota* (o nome da função).
### Função Personalizada para Gerar IDs de Operação Únicos
O FastAPI usa um **ID único** para cada *operação de rota*, ele é usado para o **ID da operação** e também para os nomes de quaisquer modelos personalizados necessários, para requisições ou respostas.
Você pode personalizar essa função. Ela recebe uma `APIRoute` e gera uma string.
Por exemplo, aqui está usando a primeira tag (você provavelmente terá apenas uma tag) e o nome da *operação de rota* (o nome da função).
Você pode então passar essa função personalizada para o **FastAPI** como o parâmetro `generate_unique_id_function`:
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
### Gerar um Cliente TypeScript com IDs de Operação Personalizados
Agora, se você gerar o cliente novamente, verá que ele tem os nomes dos métodos melhorados:
<img src="/img/tutorial/generate-clients/image07.png">
Como você pode ver, os nomes dos métodos agora têm a tag e, em seguida, o nome da função. Agora eles não incluem informações do caminho da URL e da operação HTTP.
### Pré-processar a Especificação OpenAPI para o Gerador de Clientes
O código gerado ainda tem algumas **informações duplicadas**.
Nós já sabemos que esse método está relacionado aos **items** porque essa palavra está no `ItemsService` (retirada da tag), mas ainda temos o nome da tag prefixado no nome do método também. 😕
Provavelmente ainda queremos mantê-lo para o OpenAPI em geral, pois isso garantirá que os IDs de operação sejam **únicos**.
Mas para o cliente gerado, poderíamos **modificar** os IDs de operação do OpenAPI logo antes de gerar os clientes, apenas para tornar esses nomes de método mais **simples**.
Poderíamos baixar o JSON do OpenAPI para um arquivo `openapi.json` e então poderíamos **remover essa tag prefixada** com um script como este:
{* ../../docs_src/generate_clients/tutorial004.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
Com isso, os IDs de operação seriam renomeados de coisas como `items-get_items` para apenas `get_items`, dessa forma o gerador de clientes pode gerar nomes de métodos mais simples.
### Gerar um Cliente TypeScript com o OpenAPI Pré-processado
Agora, como o resultado final está em um arquivo `openapi.json`, você modificaria o `package.json` para usar esse arquivo local, por exemplo:
```JSON hl_lines="7"
{
"name": "frontend-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
},
"author": "",
"license": "",
"devDependencies": {
"@hey-api/openapi-ts": "^0.27.38",
"typescript": "^4.6.2"
}
}
```
Depois de gerar o novo cliente, você teria agora **nomes de métodos "limpos"**, com todo o **preenchimento automático**, **erros em linha**, etc:
<img src="/img/tutorial/generate-clients/image08.png">
## Benefícios
Ao usar os clientes gerados automaticamente, você teria **preenchimento automático** para:
* Métodos.
* Corpo de requisições, parâmetros da query, etc.
* Corpo de respostas.
Você também teria **erros em linha** para tudo.
E sempre que você atualizar o código do backend, e **regenerar** o frontend, ele teria quaisquer novas *operações de rota* disponíveis como métodos, as antigas removidas, e qualquer outra alteração seria refletida no código gerado. 🤓
Isso também significa que se algo mudar, será **refletido** no código do cliente automaticamente. E se você **construir** o cliente, ele dará erro se houver alguma **incompatibilidade** nos dados usados.
Então, você **detectaria vários erros** muito cedo no ciclo de desenvolvimento, em vez de ter que esperar que os erros apareçam para seus usuários finais em produção e então tentar depurar onde está o problema. ✨

6
docs/ru/docs/deployment/docker.md

@ -58,7 +58,7 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
## Образы контейнеров ## Образы контейнеров
Docker является одним оз основных инструментов для создания **образов** и **контейнеров** и управления ими. Docker является одним из основных инструментов для создания **образов** и **контейнеров** и управления ими.
Существует общедоступный <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> с подготовленными **официальными образами** многих инструментов, окружений, баз данных и приложений. Существует общедоступный <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> с подготовленными **официальными образами** многих инструментов, окружений, баз данных и приложений.
@ -87,7 +87,7 @@ Docker является одним оз основных инструменто
Когда **контейнер** запущен, он будет выполнять прописанные в нём команды и программы. Но вы можете изменить его так, чтоб он выполнял другие команды и программы. Когда **контейнер** запущен, он будет выполнять прописанные в нём команды и программы. Но вы можете изменить его так, чтоб он выполнял другие команды и программы.
Контейнер буде работать до тех пор, пока выполняется его **главный процесс** (команда или программа). Контейнер будет работать до тех пор, пока выполняется его **главный процесс** (команда или программа).
В контейнере обычно выполняется **только один процесс**, но от его имени можно запустить другие процессы, тогда в этом же в контейнере будет выполняться **множество процессов**. В контейнере обычно выполняется **только один процесс**, но от его имени можно запустить другие процессы, тогда в этом же в контейнере будет выполняться **множество процессов**.
@ -215,7 +215,7 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
Опция `--upgrade` указывает `pip` обновить библиотеки, емли они уже установлены. Опция `--upgrade` указывает `pip` обновить библиотеки, емли они уже установлены.
Ка и в предыдущем шаге с копированием файла, этот шаг также будет использовать **кэш Docker** в случае отсутствия изменений. Как и в предыдущем шаге с копированием файла, этот шаг также будет использовать **кэш Docker** в случае отсутствия изменений.
Использование кэша, особенно на этом шаге, позволит вам **сэкономить** кучу времени при повторной сборке образа, так как зависимости будут сохранены в кеше, а не **загружаться и устанавливаться каждый раз**. Использование кэша, особенно на этом шаге, позволит вам **сэкономить** кучу времени при повторной сборке образа, так как зависимости будут сохранены в кеше, а не **загружаться и устанавливаться каждый раз**.

75
docs/ru/docs/fastapi-cli.md

@ -0,0 +1,75 @@
# FastAPI CLI
**FastAPI CLI** это программа командной строки, которую вы можете использовать для запуска вашего FastAPI приложения, для управления FastAPI-проектом, а также для многих других вещей.
`fastapi-cli` устанавливается вместе со стандартным пакетом FastAPI (при запуске команды `pip install "fastapi[standard]"`). Данный пакет предоставляет доступ к программе `fastapi` через терминал.
Чтобы запустить приложение FastAPI в режиме разработки, вы можете использовать команду `fastapi dev`:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
<b>fastapi run</b>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
Приложение командной строки `fastapi` это и есть **FastAPI CLI**.
FastAPI CLI берет путь к вашей Python-программе (напр. `main.py`) и автоматически находит объект `FastAPI` (обычно это `app`), затем определяет правильный процесс импорта и запускает сервер приложения.
Для работы в production окружении вместо `fastapi dev` нужно использовать `fastapi run`. 🚀
Внутри **FastAPI CLI** используется <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a>, высокопроизводительный, готовый к работе в production сервер ASGI. 😎
## `fastapi dev`
Вызов `fastapi dev` запускает режим разработки.
По умолчанию включена автоматическая перезагрузка (**auto-reload**), благодаря этому при изменении кода происходит перезагрузка сервера приложения. Эта установка требует значительных ресурсов и делает систему менее стабильной. Используйте её только при разработке. Приложение слушает входящие подключения на IP `127.0.0.1`. Это IP адрес вашей машины, предназначенный для внутренних коммуникаций (`localhost`).
## `fastapi run`
Вызов `fastapi run` по умолчанию запускает FastAPI в режиме production.
По умолчанию функция перезагрузки **auto-reload** отключена. Приложение слушает входящие подключения на IP `0.0.0.0`, т.е. на всех доступных адресах компьютера. Таким образом, приложение будет находиться в публичном доступе для любого, кто может подсоединиться к вашей машине. Продуктовые приложения запускаются именно так, например, с помощью контейнеров.
В большинстве случаев вы будете (и должны) использовать прокси-сервер ("termination proxy"), который будет поддерживать HTTPS поверх вашего приложения. Всё будет зависеть от того, как вы развертываете приложение: за вас это либо сделает ваш провайдер, либо вам придется сделать настройки самостоятельно.
/// tip | Подсказка
Вы можете больше узнать об этом в документации по развертыванию приложений [deployment documentation](deployment/index.md){.internal-link target=_blank}.
///

132
docs/ru/docs/tutorial/query-param-models.md

@ -14,71 +14,7 @@
Объявите нужные **query-параметры** в **Pydantic-модели**, а после аннотируйте параметр как `Query`: Объявите нужные **query-параметры** в **Pydantic-модели**, а после аннотируйте параметр как `Query`:
//// tab | Python 3.10+ {* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
```Python hl_lines="9-13 17"
{!> ../../docs_src/query_param_models/tutorial001_an_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="8-12 16"
{!> ../../docs_src/query_param_models/tutorial001_an_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="10-14 18"
{!> ../../docs_src/query_param_models/tutorial001_an.py!}
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Совет
При возможности используйте версию с `Annotated`.
///
```Python hl_lines="9-13 17"
{!> ../../docs_src/query_param_models/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+ без Annotated
/// tip | Совет
При возможности используйте версию с `Annotated`.
///
```Python hl_lines="8-12 16"
{!> ../../docs_src/query_param_models/tutorial001_py39.py!}
```
////
//// tab | Python 3.8+ без Annotated
/// tip | Совет
При возможности используйте версию с `Annotated`.
///
```Python hl_lines="9-13 17"
{!> ../../docs_src/query_param_models/tutorial001_py310.py!}
```
////
**FastAPI извлечёт** данные соответствующие **каждому полю модели** из **query-параметров** запроса и выдаст вам объявленную Pydantic-модель заполненную ими. **FastAPI извлечёт** данные соответствующие **каждому полю модели** из **query-параметров** запроса и выдаст вам объявленную Pydantic-модель заполненную ими.
@ -96,71 +32,7 @@
Вы можете сконфигурировать Pydantic-модель так, чтобы запретить (`forbid`) все дополнительные (`extra`) поля. Вы можете сконфигурировать Pydantic-модель так, чтобы запретить (`forbid`) все дополнительные (`extra`) поля.
//// tab | Python 3.10+ {* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
```Python hl_lines="10"
{!> ../../docs_src/query_param_models/tutorial002_an_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="9"
{!> ../../docs_src/query_param_models/tutorial002_an_py39.py!}
```
////
//// tab | Python 3.8+
```Python hl_lines="11"
{!> ../../docs_src/query_param_models/tutorial002_an.py!}
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Совет
При возможности используйте версию с `Annotated`.
///
```Python hl_lines="10"
{!> ../../docs_src/query_param_models/tutorial002_py310.py!}
```
////
//// tab | Python 3.9+ без Annotated
/// tip | Совет
При возможности используйте версию с `Annotated`.
///
```Python hl_lines="9"
{!> ../../docs_src/query_param_models/tutorial002_py39.py!}
```
////
//// tab | Python 3.8+ без Annotated
/// tip | Совет
При возможности используйте версию с `Annotated`.
///
```Python hl_lines="11"
{!> ../../docs_src/query_param_models/tutorial002.py!}
```
////
Если клиент попробует отправить **дополнительные** данные в **query-параметрах**, то в ответ он получит **ошибку**. Если клиент попробует отправить **дополнительные** данные в **query-параметрах**, то в ответ он получит **ошибку**.

99
docs/ru/docs/tutorial/security/get-current-user.md

@ -0,0 +1,99 @@
# Данные текущего пользователя
В предыдущей главе система безопасности (основанная на системе внедрения зависимостей) передавала *функции, обрабатывающей эндпоинт,* `токен` в виде `строки`:
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
Это пока что не слишком нам полезно. Давайте изменим код так, чтобы он возвращал нам данные пользователя, отправившего запрос.
## Создание модели пользователя
Сначала создадим Pydantic-модель пользователя.
Точно так же, как мы использовали Pydantic для объявления тел запросов, мы можем использовать его где угодно:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## Создание зависимости `get_current_user`
Давайте создадим зависимость `get_current_user`.
Помните, что у зависимостей могут быть подзависимости?
`get_current_user` как раз будет иметь подзависимость `oauth2_scheme`, которую мы создали ранее.
Аналогично тому, как мы делали это ранее в *обработчике эндпоинта* наша новая зависимость `get_current_user` будет получать `token` в виде `строки` от подзависимости `oauth2_scheme`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Получение данных пользователя
`get_current_user` будет использовать созданную нами (ненастоящую) служебную функцию, которая принимает токен в виде `строки` и возвращает нашу Pydantic-модель `User`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Внедрение зависимости текущего пользователя
Теперь мы можем использовать тот же `Depends` с нашей зависимостью `get_current_user` в *функции обрабатывающей эндпоинт*:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Обратите внимание, что мы объявляем тип переменной `current_user` как Pydantic-модель `User`.
Это поможет выполнить внутри функции все проверки автозаполнения и типа.
/// tip | Подсказка
Возможно, вы помните, что тело запроса также объявляется с помощью Pydantic-моделей.
В этом месте у **FastAPI** не возникнет проблем, потому что вы используете `Depends`.
///
/// check | Заметка
То, как устроена эта система зависимостей, позволяет нам иметь различные зависимости, которые возвращают модель `User`.
Мы не ограничены наличием только одной зависимости, которая может возвращать данные такого типа.
///
## Другие модели
Теперь вы можете получать информацию о текущем пользователе непосредственно в *функции обрабатывающей эндпоинт* и работать с механизмами безопасности на уровне **Внедрения Зависимостей**, используя `Depends`.
Причем для обеспечения требований безопасности можно использовать любую модель или данные (в данном случае - Pydantic-модель `User`).
Но вы не ограничены использованием какой-то конкретной моделью данных, классом или типом.
Вы хотите использовать в модели `id` и `email`, а `username` вам не нужен? Ну разумеется. Воспользуйтесь тем же инструментарием.
Вам нужны только `строки`? Или только `словари`? Или непосредственно экземпляр модели класса базы данных? Все это работает точно также.
У вас нет пользователей, которые входят в ваше приложение, а только роботы, боты или другие системы, у которых есть только токен доступа? Опять же, все работает одинаково.
Просто используйте любую модель, любой класс, любую базу данных, которые нужны для вашего приложения. Система внедрения зависимостей **FastAPI** поможет вам в этом.
## Размер кода
Этот пример может показаться многословным. Следует иметь в виду, что в одном файле мы смешиваем безопасность, модели данных, служебные функции и *эндпоинты*.
Но вот ключевой момент:
Все, что касается безопасности и внедрения зависимостей, пишется один раз.
И вы можете сделать его настолько сложным, насколько захотите. И все это будет написано только один раз, в одном месте, со всей своей гибкостью.
И у вас могут быть тысячи конечных точек (*эндпоинтов*), использующих одну и ту же систему безопасности.
И все они (или любая их часть по вашему желанию) могут воспользоваться преимуществами повторного использования этих зависимостей или любых других зависимостей, которые вы создадите.
И все эти тысячи *эндпоинтов* могут составлять всего 3 строки:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Резюме
Теперь вы можете получать данные о текущем пользователе непосредственно в своей *функции обработчике эндпоинта*.
Мы уже на полпути к этому.
Осталось лишь добавить *эндпоинт* для отправки пользователем/клиентом своих `имени пользователя` и `пароля`.
Это будет рассмотрено в следующем разделе.

261
docs/ru/docs/tutorial/security/oauth2-jwt.md

@ -0,0 +1,261 @@
# OAuth2 с паролем (и хешированием), Bearer с JWT-токенами
Теперь, когда у нас определен процесс обеспечения безопасности, давайте сделаем приложение действительно безопасным, используя токены <abbr title="JSON Web Tokens">JWT</abbr> и безопасное хеширование паролей.
Этот код можно реально использовать в своем приложении, сохранять хэши паролей в базе данных и т.д.
Мы продолжим разбираться, начиная с того места, на котором остановились в предыдущей главе.
## Про JWT
JWT означает "JSON Web Tokens".
Это стандарт для кодирования JSON-объекта в виде длинной строки без пробелов. Выглядит это следующим образом:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
Он не зашифрован, поэтому любой человек может восстановить информацию из его содержимого.
Но он подписан. Следовательно, когда вы получаете токен, который вы эмитировали (выдавали), вы можете убедиться, что это именно вы его эмитировали.
Таким образом, можно создать токен со сроком действия, скажем, 1 неделя. А когда пользователь вернется на следующий день с тем же токеном, вы будете знать, что он все еще авторизирован в вашей системе.
Через неделю срок действия токена истечет, пользователь не будет авторизован и ему придется заново входить в систему, чтобы получить новый токен. А если пользователь (или третье лицо) попытается модифицировать токен, чтобы изменить срок действия, вы сможете это обнаружить, поскольку подписи не будут совпадать.
Если вы хотите поиграть с JWT-токенами и посмотреть, как они работают, посмотрите <a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a>.
## Установка `PyJWT`
Нам необходимо установить `pyjwt` для генерации и проверки JWT-токенов на языке Python.
Убедитесь, что вы создали [виртуальное окружение](../../virtual-environments.md){.internal-link target=_blank}, активируйте его, а затем установите `pyjwt`:
<div class="termy">
```console
$ pip install pyjwt
---> 100%
```
</div>
/// info | Дополнительная информация
Если вы планируете использовать алгоритмы цифровой подписи, такие как RSA или ECDSA, вам следует установить зависимость библиотеки криптографии `pyjwt[crypto]`.
Подробнее об этом можно прочитать в <a href=«https://pyjwt.readthedocs.io/en/latest/installation.html» class=«external-link» target=«_blank»>документации по установке PyJWT</a>.
///
## Хеширование паролей
"Хеширование" означает преобразование некоторого содержимого (в данном случае пароля) в последовательность байтов (просто строку), которая выглядит как тарабарщина.
Каждый раз, когда вы передаете точно такое же содержимое (точно такой же пароль), вы получаете точно такую же тарабарщину.
Но преобразовать тарабарщину обратно в пароль невозможно.
### Для чего нужно хеширование паролей
Если ваша база данных будет украдена, то вор не получит пароли пользователей в открытом виде, а только их хэши.
Таким образом, вор не сможет использовать этот пароль в другой системе (поскольку многие пользователи везде используют один и тот же пароль, это было бы опасно).
## Установка `passlib`
PassLib - это отличный пакет Python для работы с хэшами паролей.
Он поддерживает множество безопасных алгоритмов хеширования и утилит для работы с ними.
Рекомендуемый алгоритм - "Bcrypt".
Убедитесь, что вы создали и активировали виртуальное окружение, и затем установите PassLib вместе с Bcrypt:
<div class="termy">
```console
$ pip install "passlib[bcrypt]"
---> 100%
```
</div>
/// tip | Подсказка
С помощью `passlib` можно даже настроить его на чтение паролей, созданных **Django**, плагином безопасности **Flask** или многими другими библиотеками.
Таким образом, вы сможете, например, совместно использовать одни и те же данные из приложения Django в базе данных с приложением FastAPI. Или постепенно мигрировать Django-приложение, используя ту же базу данных.
При этом пользователи смогут одновременно входить в систему как из приложения Django, так и из приложения **FastAPI**.
///
## Хеширование и проверка паролей
Импортируйте необходимые инструменты из `passlib`.
Создайте "контекст" PassLib. Именно он будет использоваться для хэширования и проверки паролей.
/// tip | Подсказка
Контекст PassLib также имеет функциональность для использования различных алгоритмов хеширования, в том числе и устаревших, только для возможности их проверки и т.д.
Например, вы можете использовать его для чтения и проверки паролей, сгенерированных другой системой (например, Django), но хэшировать все новые пароли другим алгоритмом, например Bcrypt.
И при этом быть совместимым со всеми этими системами.
///
Создайте служебную функцию для хэширования пароля, поступающего от пользователя.
А затем создайте другую - для проверки соответствия полученного пароля и хранимого хэша.
И еще одну - для аутентификации и возврата пользователя.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *}
/// note | Технические детали
Если проверить новую (фальшивую) базу данных `fake_users_db`, то можно увидеть, как теперь выглядит хэшированный пароль: `"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`.
///
## Работа с JWT токенами
Импортируйте установленные модули.
Создайте случайный секретный ключ, который будет использоваться для подписи JWT-токенов.
Для генерации безопасного случайного секретного ключа используйте команду:
<div class="termy">
```console
$ openssl rand -hex 32
09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7
```
</div>
И скопируйте полученный результат в переменную `SECRET_KEY` (не используйте тот, что в примере).
Создайте переменную `ALGORITHM` с алгоритмом, используемым для подписи JWT-токена, и установите для нее значение `"HS256"`.
Создайте переменную для срока действия токена.
Определите Pydantic Model, которая будет использоваться для формирования ответа на запрос на получение токена.
Создайте служебную функцию для генерации нового токена доступа.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
## Обновление зависимостей
Обновите `get_current_user` для получения того же токена, что и раньше, но на этот раз с использованием JWT-токенов.
Декодируйте полученный токен, проверьте его и верните текущего пользователя.
Если токен недействителен, то сразу же верните HTTP-ошибку.
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *}
## Обновление *операции пути* `/token`
Создайте `timedelta` со временем истечения срока действия токена.
Создайте реальный токен доступа JWT и верните его
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
### Технические подробности о JWT ключе `sub`
В спецификации JWT говорится, что существует ключ `sub`, содержащий субъект токена.
Его использование необязательно, но это именно то место, куда вы должны поместить идентификатор пользователя, и поэтому мы здесь его и используем.
JWT может использоваться и для других целей, помимо идентификации пользователя и предоставления ему возможности выполнять операции непосредственно в вашем API.
Например, вы могли бы определить "автомобиль" или "запись в блоге".
Затем вы могли бы добавить права доступа к этой сущности, например "управлять" (для автомобиля) или "редактировать" (для блога).
Затем вы могли бы передать этот JWT-токен пользователю (или боту), и они использовали бы его для выполнения определенных действий (управление автомобилем или редактирование запись в блоге), даже не имея учетной записи, просто используя JWT-токен, сгенерированный вашим API.
Используя эти идеи, JWT можно применять для гораздо более сложных сценариев.
В отдельных случаях несколько сущностей могут иметь один и тот же идентификатор, скажем, `foo` (пользователь `foo`, автомобиль `foo` и запись в блоге `foo`).
Поэтому, чтобы избежать коллизий идентификаторов, при создании JWT-токена для пользователя можно добавить префикс `username` к значению ключа `sub`. Таким образом, в данном примере значение `sub` было бы `username:johndoe`.
Важно помнить, что ключ `sub` должен иметь уникальный идентификатор для всего приложения и представлять собой строку.
## Проверка в действии
Запустите сервер и перейдите к документации: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Вы увидите пользовательский интерфейс вида:
<img src="/img/tutorial/security/image07.png">
Пройдите авторизацию так же, как делали раньше.
Используя учетные данные пользователя:
Username: `johndoe`
Password: `secret`
/// check | Заметка
Обратите внимание, что нигде в коде не используется открытый текст пароля "`secret`", мы используем только его хэшированную версию.
///
<img src="/img/tutorial/security/image08.png">
Вызвав эндпоинт `/users/me/`, вы получите ответ в виде:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
<img src="/img/tutorial/security/image09.png">
Если открыть инструменты разработчика, то можно увидеть, что передаваемые данные включают только токен, пароль передается только в первом запросе для аутентификации пользователя и получения токена доступа, но не в последующих:
<img src="/img/tutorial/security/image10.png">
/// note | Техническая информация
Обратите внимание на заголовок `Authorization`, значение которого начинается с `Bearer`.
///
## Продвинутое использование `scopes`
В OAuth2 существует понятие "диапазоны" ("`scopes`").
С их помощью можно добавить определенный набор разрешений к JWT-токену.
Затем вы можете передать этот токен непосредственно пользователю или третьей стороне для взаимодействия с вашим API с определенным набором ограничений.
О том, как их использовать и как они интегрированы в **FastAPI**, читайте далее в **Руководстве пользователя**.
## Резюме
С учетом того, что вы видели до сих пор, вы можете создать безопасное приложение **FastAPI**, используя такие стандарты, как OAuth2 и JWT.
Практически в любом фреймворке работа с безопасностью довольно быстро превращается в сложную тему.
Многие пакеты, сильно упрощающие эту задачу, вынуждены идти на многочисленные компромиссы с моделью данных, с базой данных и с доступным функционалом. Некоторые из этих пакетов, которые пытаются уж слишком все упростить, имеют даже "дыры" в системе безопасности.
---
**FastAPI** не делает уступок ни одной базе данных, модели данных или инструментарию.
Он предоставляет вам полную свободу действий, позволяя выбирать то, что лучше всего подходит для вашего проекта.
Вы можете напрямую использовать многие хорошо поддерживаемые и широко распространенные пакеты, такие как `passlib` и `PyJWT`, поскольку **FastAPI** не требует сложных механизмов для интеграции внешних пакетов.
Напротив, он предоставляет инструменты, позволяющие максимально упростить этот процесс без ущерба для гибкости, надежности и безопасности.
При этом вы можете использовать и реализовывать безопасные стандартные протоколы, такие как OAuth2, относительно простым способом.
В **Руководстве пользователя** вы можете узнать больше о том, как использовать "диапазоны" ("`scopes`") OAuth2 для создания более точно настроенной системы разрешений в соответствии с теми же стандартами. OAuth2 с диапазонами - это механизм, используемый многими крупными провайдерами сервиса аутентификации, такими как Facebook, Google, GitHub, Microsoft, Twitter и др., для авторизации сторонних приложений на взаимодействие с их API от имени их пользователей.

272
docs/ru/docs/tutorial/security/simple-oauth2.md

@ -0,0 +1,272 @@
# Простая авторизация по протоколу OAuth2 с токеном типа Bearer
Теперь, отталкиваясь от предыдущей главы, добавим недостающие части, чтобы получить безопасную систему.
## Получение `имени пользователя` и `пароля`
Для получения `имени пользователя` и `пароля` мы будем использовать утилиты безопасности **FastAPI**.
Протокол OAuth2 определяет, что при использовании "аутентификации по паролю" (которую мы и используем) клиент/пользователь должен передавать поля `username` и `password` в полях формы.
В спецификации сказано, что поля должны быть названы именно так. Поэтому `user-name` или `email` работать не будут.
Но не волнуйтесь, вы можете показать его конечным пользователям во фронтенде в том виде, в котором хотите.
А ваши модели баз данных могут использовать любые другие имена.
Но при авторизации согласно спецификации, требуется использовать именно эти имена, что даст нам возможность воспользоваться встроенной системой документации API.
В спецификации также указано, что `username` и `password` должны передаваться в виде данных формы (так что никакого JSON здесь нет).
### Oбласть видимости (scope)
В спецификации также говорится, что клиент может передать еще одно поле формы "`scope`".
Имя поля формы - `scope` (в единственном числе), но на самом деле это длинная строка, состоящая из отдельных областей видимости (scopes), разделенных пробелами.
Каждая "область видимости" (scope) - это просто строка (без пробелов).
Обычно они используются для указания уровней доступа, например:
* `users:read` или `users:write` являются распространенными примерами.
* `instagram_basic` используется Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` используется компанией Google.
/// info | Дополнительнаяя информация
В OAuth2 "scope" - это просто строка, которая уточняет уровень доступа.
Не имеет значения, содержит ли он другие символы, например `:`, или является ли он URL.
Эти детали зависят от конкретной реализации.
Для OAuth2 это просто строки.
///
## Код получения `имени пользователя` и `пароля`
Для решения задачи давайте воспользуемся утилитами, предоставляемыми **FastAPI**.
### `OAuth2PasswordRequestForm`
Сначала импортируйте `OAuth2PasswordRequestForm` и затем используйте ее как зависимость с `Depends` в *эндпоинте* `/token`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` - это класс для использования в качестве зависимости для *функции обрабатывающей эндпоинт*, который определяет тело формы со следующими полями:
* `username`.
* `password`.
* Необязательное поле `scope` в виде большой строки, состоящей из строк, разделенных пробелами.
* Необязательное поле `grant_type`.
/// tip | Подсказка
По спецификации OAuth2 поле `grant_type` является обязательным и содержит фиксированное значение `password`, но `OAuth2PasswordRequestForm` не обеспечивает этого.
Если вам необходимо использовать `grant_type`, воспользуйтесь `OAuth2PasswordRequestFormStrict` вместо `OAuth2PasswordRequestForm`.
///
* Необязательное поле `client_id` (в нашем примере он не нужен).
* Необязательное поле `client_secret` (в нашем примере он не нужен).
/// info | Дополнительная информация
Форма `OAuth2PasswordRequestForm` не является специальным классом для **FastAPI**, как `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` указывает **FastAPI**, что это схема безопасности. Следовательно, она будет добавлена в OpenAPI.
Но `OAuth2PasswordRequestForm` - это всего лишь класс зависимости, который вы могли бы написать самостоятельно или вы могли бы объявить параметры `Form` напрямую.
Но, поскольку это распространённый вариант использования, он предоставляется **FastAPI** напрямую, просто чтобы облегчить задачу.
///
### Использование данных формы
/// tip | Подсказка
В экземпляре зависимого класса `OAuth2PasswordRequestForm` атрибут `scope`, состоящий из одной длинной строки, разделенной пробелами, заменен на атрибут `scopes`, состоящий из списка отдельных строк, каждая из которых соответствует определенному уровню доступа.
В данном примере мы не используем `scopes`, но если вам это необходимо, то такая функциональность имеется.
///
Теперь получим данные о пользователе из (ненастоящей) базы данных, используя `username` из поля формы.
Если такого пользователя нет, то мы возвращаем ошибку "неверное имя пользователя или пароль".
Для ошибки мы используем исключение `HTTPException`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Проверка пароля
На данный момент у нас есть данные о пользователе из нашей базы данных, но мы еще не проверили пароль.
Давайте сначала поместим эти данные в модель Pydantic `UserInDB`.
Ни в коем случае нельзя сохранять пароли в открытом виде, поэтому мы будем использовать (пока что ненастоящую) систему хеширования паролей.
Если пароли не совпадают, мы возвращаем ту же ошибку.
#### Хеширование паролей
"Хеширование" означает: преобразование некоторого содержимого (в данном случае пароля) в последовательность байтов (просто строку), которая выглядит как тарабарщина.
Каждый раз, когда вы передаете точно такое же содержимое (точно такой же пароль), вы получаете точно такую же тарабарщину.
Но преобразовать тарабарщину обратно в пароль невозможно.
##### Зачем использовать хеширование паролей
Если ваша база данных будет украдена, то у вора не будет паролей пользователей в открытом виде, только хэши.
Таким образом, вор не сможет использовать эти же пароли в другой системе (поскольку многие пользователи используют одни и те же пароли повсеместно, это было бы опасно).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### Про `**user_dict`
`UserInDB(**user_dict)` означает:
*Передавать ключи и значения `user_dict` непосредственно в качестве аргументов ключ-значение, что эквивалентно:*
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info | Дополнительная информация
Более полное объяснение `**user_dict` можно найти в [документации к **Дополнительным моделям**](../extra-models.md#about-user_indict){.internal-link target=_blank}.
///
## Возврат токена
Ответ эндпоинта `token` должен представлять собой объект в формате JSON.
Он должен иметь `token_type`. В нашем случае, поскольку мы используем токены типа "Bearer", тип токена должен быть "`bearer`".
И в нем должна быть строка `access_token`, содержащая наш токен доступа.
В этом простом примере мы нарушим все правила безопасности, и будем считать, что имя пользователя (username) полностью соответствует токену (token)
/// tip | Подсказка
В следующей главе мы рассмотрим реальную защищенную реализацию с хешированием паролей и токенами <abbr title="JSON Web Tokens">JWT</abbr>.
Но пока давайте остановимся на необходимых нам деталях.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | Подсказка
Согласно спецификации, вы должны возвращать JSON с `access_token` и `token_type`, как в данном примере.
Это то, что вы должны сделать сами в своем коде и убедиться, что вы используете эти JSON-ключи.
Это практически единственное, что нужно не забывать делать самостоятельно, чтобы следовать требованиям спецификации.
Все остальное за вас сделает **FastAPI**.
///
## Обновление зависимостей
Теперь мы обновим наши зависимости.
Мы хотим получить значение `current_user` *только* если этот пользователь активен.
Поэтому мы создаем дополнительную зависимость `get_current_active_user`, которая, в свою очередь, использует в качестве зависимости `get_current_user`.
Обе эти зависимости просто вернут HTTP-ошибку, если пользователь не существует или неактивен.
Таким образом, в нашем эндпоинте мы получим пользователя только в том случае, если он существует, правильно аутентифицирован и активен:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | Дополнительная информация
Дополнительный заголовок `WWW-Authenticate` со значением `Bearer`, который мы здесь возвращаем, также является частью спецификации.
Ответ сервера с HTTP-кодом 401 "UNAUTHORIZED" должен также возвращать заголовок `WWW-Authenticate`.
В случае с bearer-токенами (наш случай) значение этого заголовка должно быть `Bearer`.
На самом деле этот дополнительный заголовок можно пропустить и все будет работать.
Но он приведён здесь для соответствия спецификации.
Кроме того, могут существовать инструменты, которые ожидают его и могут использовать, и это может быть полезно для вас или ваших пользователей сейчас или в будущем.
В этом и заключается преимущество стандартов...
///
## Посмотим как это работает
Откроем интерактивную документацию: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
### Аутентификация
Нажмите кнопку "Авторизация".
Используйте учётные данные:
Пользователь: `johndoe`
Пароль: `secret`
<img src="/img/tutorial/security/image04.png">
После авторизации в системе вы увидите следующее:
<img src="/img/tutorial/security/image05.png">
### Получение собственных пользовательских данных
Теперь, используя операцию `GET` с путем `/users/me`, вы получите данные пользователя, например:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
<img src="/img/tutorial/security/image06.png">
Если щелкнуть на значке замка и выйти из системы, а затем попытаться выполнить ту же операцию ещё раз, то будет выдана ошибка HTTP 401:
```JSON
{
"detail": "Not authenticated"
}
```
### Неактивный пользователь
Теперь попробуйте пройти аутентификацию с неактивным пользователем:
Пользователь: `alice`
Пароль: `secret2`
И попробуйте использовать операцию `GET` с путем `/users/me`.
Вы получите ошибку "Inactive user", как тут:
```JSON
{
"detail": "Inactive user"
}
```
## Резюме
Теперь у вас есть инструменты для реализации полноценной системы безопасности на основе `имени пользователя` и `пароля` для вашего API.
Используя эти средства, можно сделать систему безопасности совместимой с любой базой данных, с любым пользователем или моделью данных.
Единственным недостатком нашей системы является то, что она всё ещё не защищена.
В следующей главе вы увидите, как использовать библиотеку безопасного хеширования паролей и токены <abbr title="JSON Web Tokens">JWT</abbr>.

358
docs/ru/docs/tutorial/sql-databases.md

@ -0,0 +1,358 @@
# SQL (реляционные) базы данных
**FastAPI** не требует использования реляционной базы данных. Вы можете воспользоваться любой базой данных, которой хотите.
В этом разделе мы продемонстрируем, как работать с <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel</a>.
Библиотека **SQLModel** построена на основе <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> и Pydantic. Она была разработана автором **FastAPI** специально для приложений на основе FastAPI, которые используют **реляционные базы данных**.
/// tip | Подсказка
Вы можете воспользоваться любой библиотекой для работы с реляционными (SQL) или нереляционными (NoSQL) базами данных. (Их ещё называют <abbr title="ORM = Object Relational Mapper, этот термин для библиотеки, в которой классы представляют SQL-таблицы, а экземпляры классов представляют строки в этих таблицах.">**ORM**</abbr> библиотеками). FastAPI не принуждает вас к использованию чего-либо конкретного. 😎
///
В основе SQLModel лежит SQLAlchemy, поэтому вы спокойно можете использовать любую базу данных, поддерживаемую SQLAlchemy (и, соответственно, поддерживаемую SQLModel), например:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, и т.д.
В данном примере мы будем использовать базу данных **SQLite**, т.к. она состоит из единственного файла и поддерживается встроенными библиотеками Python. Таким образом, вы сможете скопировать данный пример и запустить его как он есть.
В дальнейшем, для продакшн-версии вашего приложения, возможно, вам стоит использовать серверную базу данных, например, **PostgreSQL**.
/// tip | Подсказка
Существует официальный генератор проектов на **FastAPI** и **PostgreSQL**, который также включает frontend и дополнительные инструменты <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
///
Это очень простое и короткое руководство, поэтому, если вы хотите узнать о базах данных в целом, об SQL, разобраться с более продвинутым функционалом, то воспользуйтесь <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">документацией SQLModel</a>.
## Установка `SQLModel`
Создайте виртуальное окружение [virtual environment](../virtual-environments.md){.internal-link target=_blank}, активируйте его и установите `sqlmodel`:
<div class="termy">
```console
$ pip install sqlmodel
---> 100%
```
</div>
## Создание приложения с единственной моделью
Мы начнем с создания наиболее простой первой версии нашего приложения с одной единственной моделью **SQLModel**.
В дальнейшем с помощью **дополнительных моделей** мы его улучшим и сделаем более безопасным и универсальным. 🤓
### Создание моделей
Импортируйте `SQLModel` и создайте модель базы данных:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
Класс `Hero` очень напоминает модель Pydantic (фактически, под капотом, *это и есть модель Pydantic*).
Но есть и некоторые различия
* `table=True` для SQLModel означает, что это *модель-таблица*, которая должна представлять **таблицу** в реляционной базе данных. Это не просто *модель данных* (в отличие от обычного класса в Pydantic).
* `Field(primary_key=True)` для SQLModel означает, что поле `id` является первичным ключом в таблице базы данных (вы можете подробнее узнать о первичных ключах баз данных в документации по SQLModel).
Тип `int | None` сигнализирует для SQLModel, что столбец таблицы базы данных должен иметь тип `INTEGER`, или иметь пустое значение `NULL`.
* `Field(index=True)` для SQLModel означает, что нужно создать **SQL индекс** для данного столбца. Это обеспечит более быстрый поиск при чтении данных, отфильтрованных по данному столбцу.
SQLModel будет знать, что данные типа `str`, будут представлены в базе данных как `TEXT` (или `VARCHAR`, в зависимости от типа базы данных).
### Создание соединения с базой данных (Engine)
В SQLModel объект соединения `engine` (по сути это `Engine` из SQLAlchemy) **содержит пул соединений** к базе данных.
Для обеспечения всех подключений приложения к одной базе данных нужен только один объект соединения `engine`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
Использование настройки `check_same_thread=False` позволяет FastAPI использовать одну и ту же SQLite базу данных в различных потоках (threads). Это необходимо, когда **один запрос** использует **более одного потока** (например, в зависимостях).
Не беспокойтесь, учитывая структуру кода, мы позже позаботимся о том, чтобы использовать **отдельную SQLModel-сессию на каждый отдельный запрос**, это как раз то, что пытается обеспечить `check_same_thread`.
### Создание таблиц
Далее мы добавляем функцию, использующую `SQLModel.metadata.create_all(engine)`, для того, чтобы создать **таблицы** для каждой из **моделей таблицы**.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Создание зависимости Session
Сессия базы данных (**`Session`**) хранит **объекты в памяти** и отслеживает любые необходимые изменения в данных, а затем **использует `engine`** для коммуникации с базой данных.
Мы создадим FastAPI-**зависимость** с помощью `yield`, которая будет создавать новую сессию (Session) для каждого запроса. Это как раз и обеспечит использование отдельной сессии на каждый отдельный запрос. 🤓
Затем мы создадим объявленную (`Annotated`) зависимость `SessionDep`. Мы сделаем это для того, чтобы упростить остальной код, который будет использовать эту зависимость.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Создание таблиц базы данных при запуске приложения
Мы будем создавать таблицы базы данных при запуске приложения.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
В данном примере мы создаем таблицы при наступлении события запуска приложения.
В продуктовом приложении вы, скорее всего, будете использовать скрипт для миграции базы данных, который выполняется перед запуском приложения. 🤓
/// tip | Подсказка
В SQLModel будут включены утилиты миграции, входящие в состав Alembic, но на данный момент вы просто можете использовать
<a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a> напрямую.
///
### Создание героя (Hero)
Каждая модель в SQLModel является также моделью Pydantic, поэтому вы можете использовать её при **объявлении типов**, точно также, как и модели Pydantic.
Например, при объявлении параметра типа `Hero`, она будет считана из **тела JSON**.
Точно также, вы можете использовать её при объявлении типа значения, возвращаемого функцией, и тогда структурированные данные будут отображены через пользовательский интерфейс автоматически сгенерированной документации FastAPI.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Мы используем зависимость `SessionDep` (сессию базы данных) для того, чтобы добавить нового героя `Hero` в объект сессии (`Session`), сохранить изменения в базе данных, обновить данные героя и затем вернуть их.
### Чтение данных о героях
Мы можем **читать** данные героев из базы данных с помощью `select()`. Мы можем включить `limit` и `offset` для постраничного считывания результатов.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Чтение данных отдельного героя
Мы можем прочитать данные отдельного героя (`Hero`).
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Удаление данных героя
Мы также можем удалить героя `Hero` из базы данных.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Запуск приложения
Вы можете запустить приложение следующим образом:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Далее перейдите в пользовательский интерфейс API `/docs`. Вы увидите, что **FastAPI** использует модели для создания документации API. Эти же модели используются для сериализации и проверки данных.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image01.png">
</div>
## Добавление в приложение дополнительных (вспомогательных) моделей
Теперь давайте проведём **рефакторинг** нашего приложения, чтобы сделать его более безопасным и более универсальным.
Обратите внимание, что на данном этапе наше приложение позволяет на уровне клиента определять `id` создаваемого героя (`Hero`). 😱
Мы не можем этого допустить, т.к. существует риск переписать уже присвоенные `id` в базе данных. Присвоение `id` должно происходить **на уровне бэкэнда (backend)** или **на уровне базы данных**, но никак **не на уровне клиента**.
Кроме того, мы создаем секретное имя `secret_name` для героя, но пока что, мы возвращаем его повсеместно, и это слабо напоминает **секретность**... 😅
Мы поправим это с помощью нескольких дополнительных (вспомогательных) моделей. Вот где SQLModel по-настоящему покажет себя. ✨
### Создание дополнительных моделей
В **SQLModel**, любая модель с параметром `table=True` является **моделью таблицы**.
Любая модель, не содержащая `table=True` является **моделью данных**, это по сути обычные модели Pydantic (с несколько расширенным функционалом). 🤓
С помощью SQLModel мы можем использовать **наследование**, что поможет нам **избежать дублирования** всех полей.
#### Базовый класс `HeroBase`
Давайте начнём с модели `HeroBase`, которая содержит поля, общие для всех моделей:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### Модель таблицы `Hero`
Далее давайте создадим **модель таблицы** `Hero` с дополнительными полями, которых может не быть в других моделях:
* `id`
* `secret_name`
Модель `Hero` наследует от `HeroBase`, и поэтому включает также поля из `HeroBase`. Таким образом, все поля, содержащиеся в `Hero`, будут следующими:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### Публичная модель данных `HeroPublic`
Далее мы создадим модель `HeroPublic`. Мы будем возвращать её клиентам API.
Она включает в себя те же поля, что и `HeroBase`, и, соответственно, поле `secret_name` в ней отсутствует.
Наконец-то личность наших героев защищена! 🥷
В модели `HeroPublic` также объявляется поле `id: int`. Мы как бы заключаем договоренность с API клиентом, на то, что передаваемые данные всегда должны содержать поле `id`, и это поле должно содержать целое число (и никогда не содержать `None`).
/// tip | Подсказка
Модель ответа, гарантирующая наличие поля со значением типа `int` (не `None`), очень полезна при разработке API клиентов. Определенность в передаваемых данных может обеспечить написание более простого кода.
Также **автоматически генерируемые клиенты** будут иметь более простой интерфейс. И в результате жизнь разработчиков, использующих ваш API, станет значительно легче. 😎
///
`HeroPublic` содержит все поля `HeroBase`, а также поле `id`, объявленное как `int` (не `None`):
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### Модель для создания героя `HeroCreate`
Сейчас мы создадим модель `HeroCreate`. Эта модель будет использоваться для проверки данных, переданных клиентом.
Она содержит те же поля, что и `HeroBase`, а также поле `secret_name`.
Теперь, при создании нового героя, клиенты будут передавать секретное имя `secret_name`, которое будет сохранено в базе данных, но не будет возвращено в ответе API клиентам.
/// tip | Подсказка
Вот как нужно работать с **паролями**: получайте их, но не возвращайте их через API.
Также хэшируйте значения паролей перед тем, как их сохранить. Ни в коем случае не храните пароли в открытом виде, как обычный текст.
///
Поля модели `HeroCreate`:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### Модель для обновления данных героя `HeroUpdate`
В предыдущих версиях нашей программы мы не могли обновить данные героя, теперь, воспользовавшись дополнительными моделями, мы сможем это сделать. 🎉
Модель данных `HeroUpdate` в некотором смысле особенная. Она содержит все те же поля, что и модель создания героя, но все поля модели являются **необязательными**. (Все они имеют значение по умолчанию.) Таким образом, при обновлении данных героя, вам достаточно передать только те поля, которые требуют изменения.
Поскольку **все поля по сути меняются** (теперь тип каждого поля допускает значение `None` и значение по умолчанию `None`), мы должны их **объявить заново**.
Фактически, нам не нужно наследоваться от `HeroBase`, потому что мы будем заново объявлять все поля. Я оставлю наследование просто для поддержания общего стиля, но оно (наследование) здесь необязательно. 🤷
Поля `HeroUpdate`:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Создание героя с помощью `HeroCreate` и возвращение результатов с помощью `HeroPublic`
Теперь, когда у нас есть дополнительные модели, мы можем обновить те части приложения, которые их используют.
Вместе c запросом на создание героя мы получаем объект данных `HeroCreate`, и создаем на его основе объект модели таблицы `Hero`.
Созданный объект *модели таблицы* `Hero` будет иметь все поля, переданные клиентом, а также поле `id`, сгенерированное базой данных.
Далее функция вернёт объект *модели таблицы* `Hero`. Но поскольку, мы объявили `HeroPublic` как модель ответа, то **FastAPI** будет использовать именно её для проверки и сериализации данных.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | Подсказка
Теперь мы используем модель ответа `response_model=HeroPublic`, вместо того, чтобы объявить тип возвращаемого значения как `-> HeroPublic`. Мы это делаем потому, что тип возвращаемого значения не относится к `HeroPublic`.
Если бы мы объявили тип возвращаемого значения как `-> HeroPublic`, то редактор и линтер начали бы ругаться (и вполне справедливо), что возвращаемое значение принадлежит типу `Hero`, а совсем не `HeroPublic`.
Объявляя модель ответа в `response_model`, мы как бы говорим **FastAPI**: делай свое дело, не вмешиваясь в аннотацию типов и не полагаясь на помощь редактора или других инструментов.
///
### Чтение данных героев с помощью `HeroPublic`
Мы можем проделать то же самое **для чтения данных** героев. Мы применим модель ответа `response_model=list[HeroPublic]`, и тем самым обеспечим правильную проверку и сериализацию данных.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Чтение данных отдельного героя с помощью `HeroPublic`
Мы можем **прочитать** данные отдельного героя:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Обновление данных героя с помощью `HeroUpdate`
Мы можем **обновить данные героя**. Для этого мы воспользуемся HTTP методом `PATCH`.
В коде мы получаем объект словаря `dict` с данными, переданными клиентом (т.е. **только c данными, переданными клиентом**, исключая любые значения, которые могли бы быть там только потому, что они являются значениями по умолчанию). Для того чтобы сделать это, мы воспользуемся опцией `exclude_unset=True`. В этом главная хитрость. 🪄
Затем мы применим `hero_db.sqlmodel_update(hero_data)`, и обновим `hero_db`, использовав данные `hero_data`.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Удалим героя ещё раз
Операция **удаления** героя практически не меняется.
В данном случае желание *`отрефакторить всё`* остаётся неудовлетворенным. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Снова запустим приложение
Вы можете снова запустить приложение:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Если вы перейдете в пользовательский интерфейс API `/docs`, то вы увидите, что он был обновлен, и больше не принимает параметра `id` от клиента при создании нового героя, и т.д.
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image02.png">
</div>
## Резюме
Вы можете использовать <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a> для взаимодействия с реляционными базами данных, а также для упрощения работы с **моделями данных** и **моделями таблиц**.
Вы можете узнать гораздо больше информации в документации по **SQLModel**. Там вы найдете более подробное <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">мини-руководство по использованию SQLModel с **FastAPI**</a>. 🚀

839
docs/ru/docs/virtual-environments.md

@ -0,0 +1,839 @@
# Виртуальная среда
При работе с проектами в Python рекомендуется использовать **виртуальную среду разработки** (или какой-нибудь другой подобный механизм). Это нужно для того, чтобы изолировать устанавливаемые пакеты для каждого отдельного проекта.
/// info | Дополнительная информация
Если вы уже знакомы с виртуальными средами разработки, знаете как их создавать и использовать, то вы можете свободно пропустить данный раздел. 🤓
///
/// tip | Подсказка
**Виртуальная среда** и **переменная окружения** это две разные вещи.
**Переменная окружения** это системная переменная, которую могут использовать программы.
**Виртуальная среда** это папка, содержащая файлы.
///
/// info | Дополнительная информация
В этом разделе мы научим вас пользоваться виртуальными средами разработки и расскажем, как они работают.
Если же вы готовы воспользоваться инструментом, **который умеет управлять всем, что касается Python-проектов**,
(включая установку Python), то попробуйте <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
///
## Создание проекта
В первую очередь, создайте директорию для вашего проекта.
Я обычно создаю папку под названием `code` внутри моего домашнего каталога `/home/user`.
Затем внутри данной папки я создаю отдельную директорию под каждый свой проект.
<div class="termy">
```console
// Перейдите в домашний каталог
$ cd
// Создайте отдельную папку под все будущие программные проекты (code)
$ mkdir code
// Войдите в директорию code
$ cd code
// Создайте директрорию под данный проект (awesome-project)
$ mkdir awesome-project
// Перейдите в созданную директорию проекта
$ cd awesome-project
```
</div>
## Создание виртуальной среды разработки
Начиная работу с Python-проектом, сразу же создавайте виртуальную среду разработки
**<abbr title="есть и другие опции, но мы рассмотрим наиболее простой вариант">внутри вашего проекта</abbr>**.
/// tip | Подсказка
Виртуальная среда разработки создается один раз, и в дальнейшем, работая с проектом, этого больше делать не придется.
///
//// tab | `venv`
Для создания виртуальной среды вы можете воспользоваться модулем `venv`, который является частью встроенной библиотеки Python.
<div class="termy">
```console
$ python -m venv .venv
```
</div>
/// details | Что делает эта команда?
* `python`: использовать программу под именем `python`
* `-m`: вызывать модуль как скрипт, в следующей инструкции мы скажем какой именно модуль вызвать
* `venv`: использовать модуль под названием `venv`, который обычно устанавливается вместе с Python
* `.venv`: создать виртуальную среду разработки в новой директории `.venv`
///
////
//// tab | `uv`
Если вы установили <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>, то вы можете им воспользоваться для создания виртуальной среды разработки.
<div class="termy">
```console
$ uv venv
```
</div>
/// tip | Подсказка
По умолчанию `uv` создаст виртуальную среду разработки в папке под названием `.venv`.
Но вы можете это изменить, передав дополнительный аргумент с именем директории.
///
////
Данная команда создаст новую виртуальную среду разработки в папке `.venv`.
/// details | `.venv` или другое имя?
Вы можете поместить виртуальную среду разработки в папку с другим именем, но традиционным (конвенциональным) названием является `.venv` .
///
## Активация виртуальной среды разработки
Активируйте виртуальную среду разработки, и тогда любая запускаемая Python-команда или устанавливаемый пакет будут ее использовать.
/// tip | Подсказка
При работе над проектом делайте это **каждый раз** при запуске **новой сессии в терминале**.
///
//// tab | Linux, macOS
<div class="termy">
```console
$ source .venv/bin/activate
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ .venv\Scripts\Activate.ps1
```
</div>
////
//// tab | Windows Bash
Или при использовании Bash для Windows (напр. <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
<div class="termy">
```console
$ source .venv/Scripts/activate
```
</div>
////
## Проверка активации виртуальной среды
Проверьте, активна ли виртуальная среда (удостоверимся, что предыдущая команда сработала).
/// tip | Подсказка
Убедитесь в том, что все работает так, как нужно и вы используете именно ту виртуальную среду разработки, которую нужно. Делать это необязательно, но желательно.
///
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
</div>
Если данная команда показывает, что исполняемый файл `python` (`.venv\bin\python`), находится внутри виртуальной среды вашего проекта (у нас это `awesome-project`), значит все отработало как нужно. 🎉
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ Get-Command python
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
</div>
Если данная команда показывает, что исполняемый файл `python` (`.venv\Scripts\python`), находится внутри виртуальной среды вашего проекта (у нас это `awesome-project`), значит все отработало как нужно. 🎉
////
## Обновление `pip`
/// tip | Подсказка
Если вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>, то вы должны будете его использовать для установки пакетов вместо `pip`, поэтому обновлять `pip` вам ненужно. 😎
///
Если для установки пакетов вы используете `pip` (он устанавливается по умолчанию вместе с Python), то обновите `pip` до последней версии.
Большинство экзотических ошибок, возникающих при установке пакетов, устраняется предварительным обновлением `pip`.
/// tip | Подсказка
Обычно это делается только один раз, сразу после создания виртуальной среды разработки.
///
Убедитесь в том, что виртуальная среда активирована (с помощью вышеуказанной команды) и запустите следующую команду:
<div class="termy">
```console
$ python -m pip install --upgrade pip
---> 100%
```
</div>
## Добавление `.gitignore`
Если вы используете **Git** (а вы должны его использовать), то добавьте файл `.gitignore` и исключите из Git всё, что находится в папке `.venv`.
/// tip | Подсказка
Если для создания виртуальной среды вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>, то для вас все уже сделано и вы можете пропустить этот шаг. 😎
///
/// tip | Подсказка
Это делается один раз, сразу после создания виртуальной среды разработки.
///
<div class="termy">
```console
$ echo "*" > .venv/.gitignore
```
</div>
/// details | Что делает эта команда?
* `echo "*"`: напечатать `*` в консоли (следующий шаг это слегка изменит)
* `>`: все что находится слева от `>` не печатать в консоль, но записать в файл находящийся справа от `>`
* `.gitignore`: имя файла, в который нужно записать текст.
`*` в Git означает "всё". Т.е. будет проигнорировано всё, что содержится в папке `.venv`.
Данная команда создаст файл `.gitignore` следующего содержания:
```gitignore
*
```
///
## Установка пакетов
После установки виртуальной среды, вы можете устанавливать в нее пакеты.
/// tip | Подсказка
Сделайте это **один раз**, при установке или обновлении пакетов, нужных вашему проекту.
Если вам понадобится обновить версию пакета или добавить новый пакет, то вы должны будете **сделать это снова**.
///
### Установка пакетов напрямую
Если вы торопитесь и не хотите объявлять зависимости проекта в отдельном файле, то вы можете установить их напрямую.
/// tip | Подсказка
Объявление пакетов, которые использует ваш проект, и их версий в отдельном файле (например, в `requirements.txt` или в `pyproject.toml`) - это отличная идея.
///
//// tab | `pip`
<div class="termy">
```console
$ pip install "fastapi[standard]"
---> 100%
```
</div>
////
//// tab | `uv`
Если вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
<div class="termy">
```console
$ uv pip install "fastapi[standard]"
---> 100%
```
</div>
////
### Установка из `requirements.txt`
Если у вас есть `requirements.txt`, то вы можете использовать его для установки пакетов.
//// tab | `pip`
<div class="termy">
```console
$ pip install -r requirements.txt
---> 100%
```
</div>
////
//// tab | `uv`
Если вы используете <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
<div class="termy">
```console
$ uv pip install -r requirements.txt
---> 100%
```
</div>
////
/// details | `requirements.txt`
`requirements.txt` с парочкой пакетов внутри выглядит приблизительно так:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## Запуск программы
После активации виртуальной среды разработки вы можете запустить свою программу и она будет использовать версию Python и пакеты, установленные в виртуальной среде.
<div class="termy">
```console
$ python main.py
Hello World
```
</div>
## Настройка редактора
Вероятно, вы захотите воспользоваться редактором. Убедитесь, что вы настроили его на использование той самой виртуальной среды, которую вы создали. (Скорее всего, она автоматически будет обнаружена). Это позволит вам использовать авто-завершение и выделение ошибок в редакторе.
Например:
* <a href="https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment" class="external-link" target="_blank">VS Code</a>
* <a href="https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html" class="external-link" target="_blank">PyCharm</a>
/// tip | Подсказка
Обычно это делается один раз, при создании виртуальной среды разработки.
///
## Деактивация виртуальной среды разработки
По окончании работы над проектом вы можете деактивировать виртуальную среду.
<div class="termy">
```console
$ deactivate
```
</div>
Таким образом, при запуске `python`, будет использована версия `python` установленная глобально, а не из этой виртуальной среды вместе с установленными в ней пакетами.
## Все готово к работе
Теперь вы готовы к тому, чтобы начать работу над своим проектом.
/// tip | Подсказка
Хотите разобраться со всем, что написано выше?
Продолжайте читать. 👇🤓
///
## Зачем использовать виртуальную среду?
Для работы с FastAPI вам потребуется установить <a href="https://www.python.org/" class="external-link" target="_blank">Python</a>.
После этого вам нужно будет **установить** FastAPI и другие **пакеты**, которые вы собираетесь использовать.
Для установки пакетов обычно используют `pip`, который устанавливается вместе с Python (или же используют альтернативные решения).
Тем не менее, если вы просто используете `pip` напрямую, то пакеты будут установлены в **глобальное Python-окружение** (глобально установленный Python).
### Проблема
Так в чем же проблема с установкой пакетов в глобальную среду Python?
В какой-то момент вам, вероятно, придется писать множество разных программ, которые используют различные пакеты. 😱
Например, вы создаете проект `philosophers-stone`, который зависит от пакета под названием **`harry`, версии `1`**. Таким образом, вам нужно установить `harry`.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
Затем, в какой-то момент, вы создаете другой проект под названием `prisoner-of-azkaban`, и этот проект тоже зависит от `harry`, но он уже использует **`harry` версии `3`**.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
Проблема заключается в том, что при установке в глобальное окружение, а не в локальную виртуальную среду разработки, вам нужно будет выбирать, какую версию пакета `harry` устанавливать.
Если вам нужен `philosophers-stone`, то вам нужно сначала установить `harry` версии `1`:
<div class="termy">
```console
$ pip install "harry==1"
```
</div>
И тогда в вашем глобальном окружении Python будет установлен `harry` версии `1`:
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
Но если позднее вы захотите запустить `prisoner-of-azkaban`, то вам нужно будет удалить `harry` версии 1, и установить `harry` версии `3` (при установке пакета версии `3` поверх пакета версии `1`, пакет версии `1` удаляется автоматически).
<div class="termy">
```console
$ pip install "harry==3"
```
</div>
И тогда, в вашей глобальной среде окружения Python, будет установлен пакет `harry` версии `3`.
И когда вы снова попытаетесь запустить `philosophers-stone`, то существует вероятность того, что он не будет работать, так как он использует `harry` версии `1`.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[<strike>harry v1</strike>]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip | Подсказка
В пакетах Python очень часто стараются изо всех сил избегать внесения критических изменений в новые версии, но лучше перестраховаться и планово устанавливать новые версии, а затем запускать тесты, чтобы проверить, все ли работает правильно.
///
Теперь представьте, что это происходит со многими другими пакетами, которые используются в ваших проектах. С этим очень сложно справиться. И скорее всего, в конечном итоге вы будете запускать некоторые проекты с некоторыми несовместимыми зависимостями и не будете знать, почему что-то не работает.
Кроме того, в зависимости от вашей операционной системы (напр. Linux, Windows, macOS), она может поставляться с уже установленным Python. Вероятно, что в этом случае в ней уже установлены системные пакеты определенных версий. Если вы устанавливаете пакеты глобально, то вы можете **поломать** программы, являющиеся частью ОС.
## Куда устанавливаются пакеты?
Когда вы устанавливаете Python, то на вашей машине создается некоторое количество директорий, содержащих некоторое количество файлов.
Среди них есть каталоги, отвечающие за хранение всех устанавливаемых вами пакетов.
Когда вы запустите команду:
<div class="termy">
```console
// Не запускайте эту команду, это просто пример 🤓
$ pip install "fastapi[standard]"
---> 100%
```
</div>
То будет скачан сжатый файл, содержащий код FastAPI, обычно скачивание происходит с <a href="https://pypi.org/project/fastapi/" class="external-link" target="_blank">PyPI</a>.
Также будут скачаны файлы, содержащие пакеты, которые использует FastAPI.
Затем все файлы будут извлечены и помещены в директорию на вашем компьютере.
По умолчанию эти файлы будут загружены и извлечены в один из каталогов установки Python, т.е. в глобальную среду.
## Что такое виртуальная среда разработки?
Решением проблемы размещения всех пакетов в глобальной среде будет использование отдельной виртуальной среды под каждый проект, над которым вы работаете.
Виртуальная среда это обычная папка, очень похожая на глобальную, куда вы можете устанавливать пакеты для вашего проекта.
Таким образом, каждый проект будет иметь свою отдельную виртуальную среду разработки (в директории `.venv`) вместе со своими пакетами.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## Что означает активация виртуальной среды?
Когда вы активируете виртуальную среду разработки, например, так:
//// tab | Linux, macOS
<div class="termy">
```console
$ source .venv/bin/activate
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ .venv\Scripts\Activate.ps1
```
</div>
////
//// tab | Windows Bash
Или если вы воспользуетесь Bash под Windows (напр. <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
<div class="termy">
```console
$ source .venv/Scripts/activate
```
</div>
////
Эта команда создаст или изменит некоторые [переменные окружения](environment-variables.md){.internal-link target=_blank}, которые будут доступны для последующих команд.
Одной из таких переменных является `PATH`.
/// tip | Подсказка
Вы можете узнать больше о переменной окружения `PATH` в разделе [Переменные окружения](environment-variables.md#path-environment-variable){.internal-link target=_blank}.
///
При активации виртуальной среды путь `.venv/bin` (для Linux и macOS) или `.venv\Scripts` (для Windows) добавляется в переменную окружения `PATH`.
Предположим, что до активации виртуальной среды переменная `PATH` выглядела так:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
Это означает, что система ищет программы в следующих каталогах:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
Это означает, что система ищет программы в:
* `C:\Windows\System32`
////
После активации виртуальной среды переменная окружение `PATH` будет выглядеть примерно так:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Это означает, что система теперь будет искать программы в:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
прежде чем начать искать в других каталогах.
Таким образом, когда вы введете в консоль `python`, система будет искать Python в
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
и будет использовать именно его.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
Это означает, что система в первую очередь начнет искать программы в:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
прежде чем начать искать в других директориях.
Таким образом, если вы введете в консоль команду `python`, то система найдет Python в:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
и использует его.
////
Очень важной деталью является то, что путь к виртуальной среде будет помещен в самое начало переменной `PATH`. Система обнаружит данный путь к Python раньше, чем какой-либо другой. Таким образом, при запуске команды `python`, будет использован именно Python из виртуальной среды разработки, а не какой-нибудь другой (например, Python из глобальной среды)
Активация виртуальной среды разработки также меняет и несколько других вещей, но данная функция является основной.
## Проверка виртуальной среды
Когда вы проверяете активна ли виртуальная среда разработки, например, так:
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ Get-Command python
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
</div>
////
Это означает, что будет использоваться `python` **из виртуальной среды разработки**.
Вы используете `which` для Linux и macOS и `Get-Command` для Windows PowerShell.
Эта команда работает следующим образом: она проверяет переменную окружения `PATH`, проходя по очереди каждый указанный путь в поисках программы под названием `python`. И когда она её находит, то возвращает путь к данной программе.
Основной момент при вызове команды `python` состоит в том, какой именно "`python`" будет запущен.
Таким образом, вы можете убедиться, что используете правильную виртуальную среду разработки.
/// tip | Подсказка
Легко активировать одну виртуальную среду, вызвать один Python и **перейти к следующему проекту**.
И следующий проект не будет работать потому, что вы используете **неправильный Python** из виртуальной среды другого проекта.
Так что, будет нелишним проверить, какой `python` вы используете. 🤓
///
## Зачем деактивируют виртуальную среду?
Предположим, что вы работаете над проектом `philosophers-stone`, **активируете виртуальную среду разработки**, устанавливаете пакеты и работаете с данной средой.
И позже вам понадобилось поработать с **другим проектом** `prisoner-of-azkaban`.
Вы переходите к этому проекту:
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
```
</div>
Если вы не деактивировали виртуальное окружение проекта `philosophers-stone`, то при запуске `python` через консоль будет вызван Python из `philosophers-stone`
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Error importing sirius, it's not installed 😱
Traceback (most recent call last):
File "main.py", line 1, in <module>
import sirius
```
</div>
Но если вы деактивируете виртуальную среду разработки и активируете новую среду для `prisoner-of-askaban`, то вы тогда запустите Python из виртуального окружения `prisoner-of-azkaban`.
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
// Вам не требуется находится в старой директории для деактивации среды разработки, вы можете это сделать откуда угодно, даже из каталога другого проекта, в который вы перешли. 😎
$ deactivate
// Активируйте виртуальную среду разработки в prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Тепреь, когда вы запустите python, он найдет пакет sirius, установленный в виртуальной среде ✨
$ python main.py
Я торжественно клянусь в этом! 🐺
```
</div>
## Альтернативы
Это простое руководство поможет вам начать работу и научит тому, как всё работает **изнутри**.
Существует много альтернативных решений для работы с виртуальными средами разработки, с программными зависимостями, а также с проектами.
Когда вы будете готовы использовать единый инструмент для управления проектом, программными зависимостями, виртуальными средами разработки и т.д., то я рекомендую вам попробовать <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
`uv` может очень многое. Он умеет:
* **Устанавливать Python**, включая установку различных версий
* Управлять средой виртуального окружения вашего проекта
* Устанавливать **пакеты**
* Управлять пакетами и их версиями внутри вашего проекта
* Удостовериться, что вы используете **точный** набор пакетов и версий при установке, включая зависимости. Таким образом, вы можете быть уверенны, что проект, запускается в production, точно также, как и при разработке, этот механизм называется *locking*
* Многие другие вещи
## Заключение
Если вы прочитали и поняли всё это, то теперь вы знаете **гораздо больше** о виртуальных средах разработки, чем многие другие разработчики. 🤓
Скорее всего, знание этих деталей будет полезно вам в будущем. Когда вы будете отлаживать что-то, кажущееся сложным, вы будете знать, **как это работает под капотом**. 😎

83
docs/uk/docs/fastapi-cli.md

@ -0,0 +1,83 @@
# FastAPI CLI
**FastAPI CLI** це програма командного рядка, яку Ви можете використовувати, щоб обслуговувати Ваш додаток FastAPI, керувати Вашими FastApi проектами, тощо.
Коли Ви встановлюєте FastApi (тобто виконуєте `pip install "fastapi[standard]"`), Ви також встановлюєте пакунок `fastapi-cli`, цей пакунок надає команду `fastapi` в терміналі.
Для запуску Вашого FastAPI проекту для розробки, Ви можете скористатись командою `fastapi dev`:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:single">main.py</u>
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
│ │
│ 🐍 main.py │
│ │
╰──────────────────────╯
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
<font color="#3465A4">INFO </font> Found importable FastAPI app
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
│ │
<span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span>
│ │
╰──────────────────────────╯
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
<span style="background-color:#C4A000"><font color="#2E3436">╭────────── FastAPI CLI - Development mode ───────────╮</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ Serving at: http://127.0.0.1:8000 │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ API docs: http://127.0.0.1:8000/docs │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ Running in development mode, for production use: │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436"></font></span><span style="background-color:#C4A000"><font color="#555753"><b>fastapi run</b></font></span><span style="background-color:#C4A000"><font color="#2E3436"></font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">╰─────────────────────────────────────────────────────╯</font></span>
<font color="#4E9A06">INFO</font>: Will watch for changes in these directories: [&apos;/home/user/code/awesomeapp&apos;]
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://127.0.0.1:8000</b> (Press CTRL+C to quit)
<font color="#4E9A06">INFO</font>: Started reloader process [<font color="#34E2E2"><b>2265862</b></font>] using <font color="#34E2E2"><b>WatchFiles</b></font>
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">2265873</font>]
<font color="#4E9A06">INFO</font>: Waiting for application startup.
<font color="#4E9A06">INFO</font>: Application startup complete.
```
</div>
Програма командного рядка `fastapi` це **FastAPI CLI**.
FastAPI CLI приймає шлях до Вашої Python програми (напр. `main.py`) і автоматично виявляє екземпляр `FastAPI` (зазвичай названий `app`), обирає коректний процес імпорту, а потім обслуговує його.
Натомість, для запуску у продакшн використовуйте `fastapi run`. 🚀
Всередині **FastAPI CLI** використовує <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a>, високопродуктивний, production-ready, ASGI cервер. 😎
## `fastapi dev`
Використання `fastapi dev` ініціює режим розробки.
За замовчуванням, **автоматичне перезавантаження** увімкнене, автоматично перезавантажуючи сервер кожного разу, коли Ви змінюєте Ваш код. Це ресурсо-затратно, та може бути менш стабільним, ніж коли воно вимкнене. Ви повинні використовувати його тільки під час розробки. Воно також слухає IP-адресу `127.0.0.1`, що є IP Вашого девайсу для самостійної комунікації з самим собою (`localhost`).
## `fastapi run`
Виконання `fastapi run` запустить FastAPI у продакшн-режимі за замовчуванням.
За замовчуванням, **автоматичне перезавантаження** вимкнене. Воно також прослуховує IP-адресу `0.0.0.0`, що означає всі доступні IP адреси, тим самим даючи змогу будь-кому комунікувати з девайсом. Так Ви зазвичай будете запускати його у продакшн, наприклад у контейнері.
В більшості випадків Ви можете (і маєте) мати "termination proxy", який обробляє HTTPS для Вас, це залежить від способу розгортання вашого додатку, Ваш провайдер може зробити це для Вас, або Вам потрібно налаштувати його самостійно.
/// tip
Ви можете дізнатись більше про це у [документації про розгортування](deployment/index.md){.internal-link target=_blank}.
///

207
docs/zh-hant/docs/features.md

@ -0,0 +1,207 @@
# 特性
## FastAPI 特性
**FastAPI** 提供了以下内容:
### 建立在開放標準的基礎上
* 使用 <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> 來建立 API,包含<abbr title="path,也被叫做: endpoints, routes">路徑</abbr><abbr title="也叫做 HTTP 方法,例如 POST, GET, PUT, DELETE">操作</abbr>、參數、請求內文、安全性等聲明。
* 使用 <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a>(因為 OpenAPI 本身就是基於 JSON Schema)自動生成資料模型文件。
* 經過縝密的研究後圍繞這些標準進行設計,而不是事後在已有系統上附加的一層功能。
* 這也讓我們在多種語言中可以使用自動**用戶端程式碼生成**。
### 能夠自動生成文件
FastAPI 能生成互動式 API 文件和探索性的 Web 使用者介面。由於該框架基於 OpenAPI,因此有多種選擇,預設提供了兩種。
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a> 提供互動式探索,讓你可以直接從瀏覽器呼叫並測試你的 API 。
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a> 提供結構性的文件,讓你可以在瀏覽器中查看。
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### 現代 Python
這一切都基於標準的 **Python 型別**宣告(感謝 Pydantic)。無需學習新的語法,只需使用標準的現代 Python。
如果你需要 2 分鐘來學習如何使用 Python 型別(即使你不使用 FastAPI),可以看看這個簡短的教學:[Python 型別](python-types.md){.internal-link target=_blank}。
如果你寫帶有 Python 型別的程式碼:
```python
from datetime import date
from pydantic import BaseModel
# 宣告一個變數為 string
# 並在函式中獲得 editor support
def main(user_id: str):
return user_id
# 宣告一個 Pydantic model
class User(BaseModel):
id: int
name: str
joined: date
```
可以像這樣來使用:
```python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info
`**second_user_data` 意思是:
`second_user_data` 字典直接作為 key-value 引數傳遞,等同於:`User(id=4, name="Mary", joined="2018-11-30")`
///
### 多種編輯器支援
整個框架的設計是為了讓使用變得簡單且直觀,在開始開發之前,所有決策都在多個編輯器上進行了測試,以確保提供最佳的開發體驗。
在最近的 Python 開發者調查中,我們能看到<a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank"> 被使用最多的功能是 autocompletion</a>,此功能可以預測將要輸入文字,並自動補齊。
整個 **FastAPI** 框架就是基於這一點,任何地方都可以進行自動補齊。
你幾乎不需要經常來回看文件。
在這裡,你的編輯器可能會這樣幫助你:
* <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a> 中:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
* <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> 中:
![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png)
你將能進行程式碼補齊,這是在之前你可能曾認為不可能的事。例如,請求 JSON body(可能是巢狀的)中的鍵 `price`
這樣比較不會輸錯鍵名,不用來回翻看文件,也不用來回滾動尋找你最後使用的 `username` 或者 `user_name`
### 簡潔
FastAPI 為你提供了**預設值**,讓你不必在初期進行繁瑣的配置,一切都可以自動運作。如果你有更具體的需求,則可以進行調整和自定義,
但在大多數情況下,你只需要直接使用預設值,就能順利完成 API 開發。
### 驗證
所有的驗證都由完善且強大的 **Pydantic** 處理。
* 驗證大部分(甚至所有?)的 Python **資料型別**,包括:
* JSON 物件 (`dict`)。
* JSON 陣列 (`list`) 定義項目型別。
* 字串 (`str`) 欄位,定義最小或最大長度。
* 數字 (`int`, `float`) 與其最大值和最小值等。
* 驗證外來的型別,比如:
* URL
* Email
* UUID
### 安全性及身份驗證
FastAPI 已經整合了安全性和身份驗證的功能,但不會強制與特定的資料庫或資料模型進行綁定。
OpenAPI 中定義的安全模式,包括:
* HTTP 基本認證。
* **OAuth2**(也使用 **JWT tokens**)。在 [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank} 查看教學。
* API 密鑰,在:
* 標頭(Header)
* 查詢參數
* Cookies,等等。
加上来自 Starlette(包括 **session cookie**)的所有安全特性。
所有的這些都是可重複使用的工具和套件,可以輕鬆與你的系統、資料儲存(Data Stores)、關聯式資料庫(RDBMS)以及非關聯式資料庫(NoSQL)等等整合。
### 依賴注入(Dependency Injection)
FastAPI 有一個使用簡單,但是非常強大的<abbr title='也叫做 "components", "resources", "services", "providers"'><strong>依賴注入</strong></abbr>系統。
* 依賴項甚至可以有自己的依賴,從而形成一個層級或**依賴圖**的結構。
* 所有**自動化處理**都由框架完成。
* 依賴項不僅能從請求中提取資料,還能**對 API 的路徑操作進行強化**,並自動生成文檔。
* 即使是依賴項中定義的*路徑操作參數*,也會**自動進行驗證**。
* 支持複雜的用戶身份驗證系統、**資料庫連接**等。
* 不與資料庫、前端等進行強制綁定,但能輕鬆整合它們。
### 無限制「擴充功能」
或者說,無需其他額外配置,直接導入並使用你所需要的程式碼。
任何整合都被設計得非常簡單易用(通過依賴注入),你只需用與*路徑操作*相同的結構和語法,用兩行程式碼就能為你的應用程式建立一個「擴充功能」。
### 測試
* 100% 的<abbr title="有自動測試的程式碼">測試覆蓋率</abbr>
* 100% 的程式碼有<abbr title="Python 型別註釋,有了這個你的編輯器和外部工具可以給你更好的支援">型別註釋</abbr>
* 已能夠在生產環境應用程式中使用。
## Starlette 特性
**FastAPI** 完全相容且基於 <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a>。所以,你有其他的 Starlette 程式碼也能正常運作。FastAPI 繼承了 Starlette 的所有功能,如果你已經知道或者使用過 Starlette,大部分的功能會以相同的方式運作。
通過 **FastAPI** 你可以獲得所有 **Starlette** 的特性(FastAPI 就像加強版的 Starlette):
* 性能極其出色。它是 <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">Python 可用的最快框架之一,和 **NodeJS****Go** 相當</a>
* **支援 WebSocket**
* 能在行程內處理背景任務。
* 支援啟動和關閉事件。
* 有基於 HTTPX 的測試用戶端。
* 支援 **CORS**、GZip、靜態檔案、串流回應。
* 支援 **Session 和 Cookie**
* 100% 測試覆蓋率。
* 100% 型別註釋的程式碼庫。
## Pydantic 特性
**FastAPI** 完全相容且基於 <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>。所以,你有其他 Pydantic 程式碼也能正常工作。
相容包括基於 Pydantic 的外部函式庫, 例如用於資料庫的 <abbr title="Object-Relational Mapper">ORM</abbr>s, <abbr title="Object-Document Mapper">ODM</abbr>s。
這也意味著在很多情況下,你可以把從請求中獲得的物件**直接傳到資料庫**,因為所有資料都會自動進行驗證。
反之亦然,在很多情況下,你也可以把從資料庫中獲取的物件**直接傳給客戶端**。
通過 **FastAPI** 你可以獲得所有 **Pydantic** 的特性(FastAPI 基於 Pydantic 做了所有的資料處理):
* **更簡單**
* 不需要學習新的 micro-language 來定義結構。
* 如果你知道 Python 型別,你就知道如何使用 Pydantic。
* 和你的 **<abbr title="Integrated Development Environment,和程式碼編輯器類似">IDE</abbr>/<abbr title="一個檢查程式碼錯誤的工具">linter</abbr>/brain** 都能好好配合:
* 因為 Pydantic 的資料結構其實就是你自己定義的類別實例,所以自動補齊、linting、mypy 以及你的直覺都能很好地在經過驗證的資料上發揮作用。
* 驗證**複雜結構**:
* 使用 Pydantic 模型時,你可以把資料結構分層設計,並且用 Python 的 `List``Dict` 等型別來定義。
* 驗證器讓我們可以輕鬆地定義和檢查複雜的資料結構,並把它們轉換成 JSON Schema 進行記錄。
* 你可以擁有深層**巢狀的 JSON** 物件,並對它們進行驗證和註釋。
* **可擴展**
* Pydantic 讓我們可以定義客製化的資料型別,或者你可以使用帶有 validator 裝飾器的方法來擴展模型中的驗證功能。
* 100% 測試覆蓋率。

331
docs/zh-hant/docs/tutorial/first-steps.md

@ -0,0 +1,331 @@
# 第一步
最簡單的 FastAPI 檔案可能看起來像這樣:
{* ../../docs_src/first_steps/tutorial001.py *}
將其複製到一個名為 `main.py` 的文件中。
執行即時重新載入伺服器(live server):
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:single">main.py</u>
<font color="#3465A4">INFO </font> Using path <font color="#3465A4">main.py</font>
<font color="#3465A4">INFO </font> Resolved absolute path <font color="#75507B">/home/user/code/awesomeapp/</font><font color="#AD7FA8">main.py</font>
<font color="#3465A4">INFO </font> Searching for package file structure from directories with <font color="#3465A4">__init__.py</font> files
<font color="#3465A4">INFO </font> Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
╭─ <font color="#8AE234"><b>Python module file</b></font> ─╮
│ │
│ 🐍 main.py │
│ │
╰──────────────────────╯
<font color="#3465A4">INFO </font> Importing module <font color="#4E9A06">main</font>
<font color="#3465A4">INFO </font> Found importable FastAPI app
╭─ <font color="#8AE234"><b>Importable FastAPI app</b></font> ─╮
│ │
<span style="background-color:#272822"><font color="#FF4689">from</font></span><span style="background-color:#272822"><font color="#F8F8F2"> main </font></span><span style="background-color:#272822"><font color="#FF4689">import</font></span><span style="background-color:#272822"><font color="#F8F8F2"> app</font></span><span style="background-color:#272822"> </span>
│ │
╰──────────────────────────╯
<font color="#3465A4">INFO </font> Using import string <font color="#8AE234"><b>main:app</b></font>
<span style="background-color:#C4A000"><font color="#2E3436">╭────────── FastAPI CLI - Development mode ───────────╮</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ Serving at: http://127.0.0.1:8000 │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ API docs: http://127.0.0.1:8000/docs │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ Running in development mode, for production use: │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436"></font></span><span style="background-color:#C4A000"><font color="#555753"><b>fastapi run</b></font></span><span style="background-color:#C4A000"><font color="#2E3436"></font></span>
<span style="background-color:#C4A000"><font color="#2E3436">│ │</font></span>
<span style="background-color:#C4A000"><font color="#2E3436">╰─────────────────────────────────────────────────────╯</font></span>
<font color="#4E9A06">INFO</font>: Will watch for changes in these directories: [&apos;/home/user/code/awesomeapp&apos;]
<font color="#4E9A06">INFO</font>: Uvicorn running on <b>http://127.0.0.1:8000</b> (Press CTRL+C to quit)
<font color="#4E9A06">INFO</font>: Started reloader process [<font color="#34E2E2"><b>2265862</b></font>] using <font color="#34E2E2"><b>WatchFiles</b></font>
<font color="#4E9A06">INFO</font>: Started server process [<font color="#06989A">2265873</font>]
<font color="#4E9A06">INFO</font>: Waiting for application startup.
<font color="#4E9A06">INFO</font>: Application startup complete.
```
</div>
在輸出中,有一列類似於:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
那列顯示了你的應用程式正在本地端機器上運行的 URL。
### 查看它
在瀏覽器中打開 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
你將看到如下的 JSON 回應:
```JSON
{"message": "Hello World"}
```
### 互動式 API 文件
現在,前往 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
你將看到自動的互動式 API 文件(由 <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> 提供):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### 替代 API 文件
現在,前往 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
你將看到另一種自動文件(由 <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> 提供):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
### OpenAPI
**FastAPI** 使用定義 API 的 **OpenAPI** 標準來生成一個 「schema」 與你的所有 API。
#### 「Schema」
「schema」是對某個事物的定義或描述。它並不是實作它的程式碼,而僅僅是一個抽象的描述。
#### API 「schema」
在這種情況下,<a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> 是一個規範,它規定了如何定義 API 的 schema。
這個 schema 定義包含了你的 API 路徑、可能接收的參數等內容。
#### 資料 「schema」
「schema」這個術語也可能指某些資料的結構,比如 JSON 內容的結構。
在這種情況下,它指的是 JSON 的屬性、資料型別等。
#### OpenAPI 和 JSON Schema
OpenAPI 定義了 API 的 schema。這個 schema 包含了使用 **JSON Schema** 定義的資料,這是 JSON 資料 schema 的標準。
#### 檢查 `openapi.json`
如果你好奇原始的 OpenAPI schema 長什麼樣子,FastAPI 會自動生成一個包含所有 API 描述的 JSON (schema)。
你可以直接在 <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a> 查看它。
它會顯示一個 JSON,類似於:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### OpenAPI 的用途
OpenAPI schema 驅動了兩個互動式文件系統。
而且有許多替代方案,所有這些都是基於 OpenAPI。你可以輕鬆地將任何這些替代方案添加到使用 **FastAPI** 建置的應用程式中。
你也可以用它自動生成程式碼,讓前端、手機應用程式或物聯網設備等與你的 API 進行通訊。
## 逐步回顧
### 第一步:引入 `FastAPI`
{* ../../docs_src/first_steps/tutorial001.py h1[1] *}
`FastAPI` 是一個 Python 類別,提供所有 API 的全部功能。
/// note | Technical Details
`FastAPI` 是一個直接繼承自 `Starlette` 的類別。
你同樣可以透過 `FastAPI` 來使用 <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> 所有的功能。
///
### 第二步:建立一個 `FastAPI` 「實例」
{* ../../docs_src/first_steps/tutorial001.py h1[3] *}
這裡的 `app` 變數將會是 `FastAPI` 類別的「實例」。
這將是你建立所有 API 的主要互動點。
### 第三步:建立一個 *路徑操作*
#### 路徑
這裡的「路徑」指的是 URL 中自第一個 `/` 以後的部分。
例如,在 URL 中:
```
https://example.com/items/foo
```
……的路徑將會是:
```
/items/foo
```
/// info
「路徑」也常被稱為「端點 endpoint」或「路由 route」。
///
在建置 API 時,「路徑」是分離「關注點」和「資源」的主要方式。
#### 操作
這裡的「操作」指的是 HTTP 的「方法」之一。
其中包括:
* `POST`
* `GET`
* `PUT`
* `DELETE`
……以及更少見的:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
在 HTTP 協定中,你可以使用這些「方法」之一(或更多)與每個路徑進行通信。
---
在建置 API 時,你通常使用這些特定的 HTTP 方法來執行特定的動作。
通常你使用:
* `POST`:用來建立資料。
* `GET`:用來讀取資料。
* `PUT`:用來更新資料。
* `DELETE`:用來刪除資料。
所以,在 OpenAPI 中,每個 HTTP 方法都被稱為「操作」。
我們將會稱它們為「**操作**」。
#### 定義一個 *路徑操作裝飾器*
{* ../../docs_src/first_steps/tutorial001.py h1[6] *}
`@app.get("/")` 告訴 **FastAPI** 那個函式負責處理請求:
* 路徑 `/`
* 使用 <abbr title="HTTP GET 方法"><code>get</code>操作</abbr>
/// info | `@decorator` Info
Python 中的 `@something` 語法被稱為「裝飾器」。
你把它放在一個函式上面。像一個漂亮的裝飾帽子(我猜這是術語的來源)。
一個「裝飾器」會對下面的函式做一些事情。
在這種情況下,這個裝飾器告訴 **FastAPI** 那個函式對應於 **路徑** `/`**操作** `get`.
這就是「**路徑操作裝飾器**」。
///
你也可以使用其他的操作:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
以及更少見的:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip
你可以自由地使用每個操作(HTTP 方法)。
**FastAPI** 不強制任何特定的意義。
這裡的資訊作為一個指南,而不是要求。
例如,當使用 GraphQL 時,你通常只使用 `POST` 操作。
///
### 第四步:定義 **路徑操作函式**
這是我們的「**路徑操作函式**」:
* **path**: 是 `/`.
* **operation**: 是 `get`.
* **function**: 是裝飾器下面的函式(在 `@app.get("/")` 下面)。
{* ../../docs_src/first_steps/tutorial001.py h1[7] *}
這就是一個 Python 函式。
它將會在 **FastAPI** 收到一個請求時被呼叫,使用 `GET` 操作。
在這種情況下,它是一個 `async` 函式。
---
你可以將它定義為一個正常的函式,而不是 `async def`:
{* ../../docs_src/first_steps/tutorial003.py h1[7] *}
/// note
如果你不知道差別,請查看 [Async: *"In a hurry?"*](../async.md#in-a-hurry){.internal-link target=_blank}.
///
### 第五步:回傳內容
{* ../../docs_src/first_steps/tutorial001.py h1[8] *}
你可以返回一個 `dict`、`list`、單個值作為 `str`、`int` 等。
你也可以返回 Pydantic 模型(稍後你會看到更多關於這方面的內容)。
有很多其他物件和模型會自動轉換為 JSON(包括 ORMs,等等)。試用你最喜歡的,很有可能它們已經有支援。
## 回顧
* 引入 `FastAPI`.
* 建立一個 `app` 實例。
* 寫一個 **路徑操作裝飾器** 使用裝飾器像 `@app.get("/")`
* 定義一個 **路徑操作函式**;例如,`def root(): ...`。
* 使用命令 `fastapi dev` 執行開發伺服器。

4
docs/zh-hant/docs/tutorial/index.md

@ -85,9 +85,9 @@ $ pip install "fastapi[standard]"
/// note /// note
當你使用 `pip install "fastapi[standard]"` 安裝時,會包含一些預設的可選標準依項。 當你使用 `pip install "fastapi[standard]"` 安裝時,會包含一些預設的可選標準依項。
如果你不想包含那些可選的依項,你可以使用 `pip install fastapi` 來安裝。 如果你不想包含那些可選的依項,你可以使用 `pip install fastapi` 來安裝。
/// ///

844
docs/zh-hant/docs/virtual-environments.md

@ -0,0 +1,844 @@
# 虛擬環境
當你在 Python 專案中工作時,你可能會需要使用一個**虛擬環境**(或類似的機制)來隔離你為每個專案安裝的套件。
/// info
如果你已經了解虛擬環境,知道如何建立和使用它們,你可以考慮跳過這一部分。🤓
///
/// tip
**虛擬環境**和**環境變數**是不同的。
**環境變數**是系統中的一個變數,可以被程式使用。
**虛擬環境**是一個包含一些檔案的目錄。
///
/// info
這個頁面將教你如何使用**虛擬環境**以及了解它們的工作原理。
如果你計畫使用一個**可以為你管理一切的工具**(包括安裝 Python),試試 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>
///
## 建立一個專案
首先,為你的專案建立一個目錄。
我(指原作者 —— 譯者注)通常會在我的主目錄下建立一個名為 `code` 的目錄。
在這個目錄下,我再為每個專案建立一個目錄。
<div class="termy">
```console
// 進入主目錄
$ cd
// 建立一個用於存放所有程式碼專案的目錄
$ mkdir code
// 進入 code 目錄
$ cd code
// 建立一個用於存放這個專案的目錄
$ mkdir awesome-project
// 進入這個專案的目錄
$ cd awesome-project
```
</div>
## 建立一個虛擬環境
在開始一個 Python 專案的**第一時間**,**<abbr title="還有其他做法,此處僅作為一個簡單的指引">在你的專案內部</abbr>**建立一個虛擬環境。
/// tip
你只需要**在每個專案中操作一次**,而不是每次工作時都操作。
///
//// tab | `venv`
你可以使用 Python 自帶的 `venv` 模組來建立一個虛擬環境。
<div class="termy">
```console
$ python -m venv .venv
```
</div>
/// details | 上述命令的含義
* `python`: 使用名為 `python` 的程式
* `-m`: 以腳本的方式呼叫一個模組,我們將告訴它接下來使用哪個模組
* `venv`: 使用名為 `venv` 的模組,這個模組通常隨 Python 一起安裝
* `.venv`: 在新目錄 `.venv` 中建立虛擬環境
///
////
//// tab | `uv`
如果你安裝了 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>,你也可以使用它來建立一個虛擬環境。
<div class="termy">
```console
$ uv venv
```
</div>
/// tip
預設情況下,`uv` 會在一個名為 `.venv` 的目錄中建立一個虛擬環境。
但你可以透過傳遞一個額外的引數來自訂它,指定目錄的名稱。
///
////
這個命令會在一個名為 `.venv` 的目錄中建立一個新的虛擬環境。
/// details | `.venv`,或是其他名稱
你可以在不同的目錄下建立虛擬環境,但通常我們會把它命名為 `.venv`
///
## 啟動虛擬環境
啟動新的虛擬環境來確保你運行的任何 Python 指令或安裝的套件都能使用到它。
/// tip
**每次**開始一個**新的終端會話**來在這個專案工作時,你都需要執行這個操作。
///
//// tab | Linux, macOS
<div class="termy">
```console
$ source .venv/bin/activate
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ .venv\Scripts\Activate.ps1
```
</div>
////
//// tab | Windows Bash
或者,如果你在 Windows 上使用 Bash(例如 <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
<div class="termy">
```console
$ source .venv/Scripts/activate
```
</div>
////
/// tip
每次你在這個環境中安裝一個**新的套件**時,都需要**重新啟動**這個環境。
這麼做確保了當你使用一個由這個套件安裝的**終端(<abbr title="命令列介面">CLI</abbr>)程式**時,你使用的是你的虛擬環境中的程式,而不是全域安裝、可能版本不同的程式。
///
## 檢查虛擬環境是否啟動
檢查虛擬環境是否啟動(前面的指令是否生效)。
/// tip
這是**非必需的**,但這是一個很好的方法,可以**檢查**一切是否按預期工作,以及你是否使用了你打算使用的虛擬環境。
///
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
</div>
如果它顯示了在你專案(在這個例子中是 `awesome-project`)的 `.venv/bin/python` 中的 `python` 二進位檔案,那麼它就生效了。🎉
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ Get-Command python
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
</div>
如果它顯示了在你專案(在這個例子中是 `awesome-project`)的 `.venv\Scripts\python` 中的 `python` 二進位檔案,那麼它就生效了。🎉
////
## 升級 `pip`
/// tip
如果你使用 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> 來安裝內容,而不是 `pip`,那麼你就不需要升級 `pip`。😎
///
如果你使用 `pip` 來安裝套件(它是 Python 的預設元件),你應該將它**升級**到最新版本。
在安裝套件時出現的許多奇怪的錯誤都可以透過先升級 `pip` 來解決。
/// tip
通常你只需要在建立虛擬環境後**執行一次**這個操作。
///
確保虛擬環境是啟動的(使用上面的指令),然後運行:
<div class="termy">
```console
$ python -m pip install --upgrade pip
---> 100%
```
</div>
## 加入 `.gitignore`
如果你使用 **Git**(這是你應該使用的),加入一個 `.gitignore` 檔案來排除你的 `.venv` 中的所有內容。
/// tip
如果你使用 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> 來建立虛擬環境,它會自動為你完成這個操作,你可以跳過這一步。😎
///
/// tip
通常你只需要在建立虛擬環境後**執行一次**這個操作。
///
<div class="termy">
```console
$ echo "*" > .venv/.gitignore
```
</div>
/// details | 上述指令的含義
- `echo "*"`: 將在終端中「顯示」文本 `*`(接下來的部分會對這個操作進行一些修改)
- `>`: 使左邊的指令顯示到終端的任何內容實際上都不會被顯示,而是會被寫入到右邊的檔案中
- `.gitignore`: 被寫入文本的檔案的名稱
`*` 對於 Git 來說意味著「所有內容」。所以,它會忽略 `.venv` 目錄中的所有內容。
該指令會建立一個名為 .gitignore 的檔案,內容如下:
```gitignore
*
```
///
## 安裝套件
在啟用虛擬環境後,你可以在其中安裝套件。
/// tip
當你需要安裝或升級套件時,執行本操作**一次**;
如果你需要再升級版本或新增套件,你可以**再次執行此操作**。
///
### 直接安裝套件
如果你急於安裝,不想使用檔案來聲明專案的套件依賴,你可以直接安裝它們。
/// tip
將程式所需的套件及其版本放在檔案中(例如 `requirements.txt``pyproject.toml`)是個好(而且非常好)的主意。
///
//// tab | `pip`
<div class="termy">
```console
$ pip install "fastapi[standard]"
---> 100%
```
</div>
////
//// tab | `uv`
如果你有 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
<div class="termy">
```console
$ uv pip install "fastapi[standard]"
---> 100%
```
</div>
////
### 從 `requirements.txt` 安裝
如果你有一個 `requirements.txt` 檔案,你可以使用它來安裝其中的套件。
//// tab | `pip`
<div class="termy">
```console
$ pip install -r requirements.txt
---> 100%
```
</div>
////
//// tab | `uv`
如果你有 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
<div class="termy">
```console
$ uv pip install -r requirements.txt
---> 100%
```
</div>
////
/// details | 關於 `requirements.txt`
一個包含一些套件的 `requirements.txt` 檔案看起來應該是這樣的:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## 執行程式
在啟用虛擬環境後,你可以執行你的程式,它將使用虛擬環境中的 Python 和你在其中安裝的套件。
<div class="termy">
```console
$ python main.py
Hello World
```
</div>
## 設定編輯器
你可能會用到編輯器,請確保設定它使用你建立的相同虛擬環境(它可能會自動偵測到),以便你可以獲得自動完成和内嵌錯誤提示。
例如:
* <a href="https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment" class="external-link" target="_blank">VS Code</a>
* <a href="https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html" class="external-link" target="_blank">PyCharm</a>
/// tip
通常你只需要在建立虛擬環境時執行此操作**一次**。
///
## 退出虛擬環境
當你完成工作後,你可以**退出**虛擬環境。
<div class="termy">
```console
$ deactivate
```
</div>
這樣,當你執行 `python` 時它不會嘗試從已安裝套件的虛擬環境中執行。
## 開始工作
現在你已經準備好開始你的工作了。
/// tip
你想要理解上面的所有內容嗎?
繼續閱讀。👇🤓
///
## 為什麼要使用虛擬環境
你需要安裝 <a href="https://www.python.org/" class="external-link" target="_blank">Python</a> 才能使用 FastAPI。
接下來,你需要**安裝** FastAPI 以及你想使用的其他**套件**。
要安裝套件,你通常會使用隨 Python 一起提供的 `pip` 指令(或類似的替代工具)。
然而,如果你直接使用 `pip`,套件將會安裝在你的**全域 Python 環境**中(即 Python 的全域安裝)。
### 存在的問題
那麼,在全域 Python 環境中安裝套件有什麼問題呢?
有時候,你可能會開發許多不同的程式,而這些程式各自依賴於**不同的套件**;有些專案甚至需要依賴於**相同套件的不同版本**。😱
例如,你可能會建立一個名為 `philosophers-stone` 的專案,這個程式依賴於另一個名為 **`harry` 的套件,並使用版本 `1`**。因此,你需要安裝 `harry`
```mermaid
flowchart LR
stone(philosophers-stone) -->|需要| harry-1[harry v1]
```
然而,在此之後,你又建立了另一個名為 `prisoner-of-azkaban` 的專案,而這個專案也依賴於 `harry`,但需要的是 **`harry` 版本 `3`**。
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |需要| harry-3[harry v3]
```
現在的問題是,如果你在全域環境中安裝套件而不是在本地**虛擬環境**中,你將面臨選擇安裝哪個版本的 `harry` 的困境。
如果你想運行 `philosophers-stone`,你需要先安裝 `harry` 版本 `1`,例如:
<div class="termy">
```console
$ pip install "harry==1"
```
</div>
然後你會在全域 Python 環境中安裝 `harry` 版本 `1`
```mermaid
flowchart LR
subgraph global[全域環境]
harry-1[harry v1]
end
subgraph stone-project[專案 philosophers-stone]
stone(philosophers-stone) -->|需要| harry-1
end
```
但如果你想運行 `prisoner-of-azkaban`,你需要解除安裝 `harry` 版本 `1` 並安裝 `harry` 版本 `3`(或者只要你安裝版本 `3`,版本 `1` 就會自動移除)。
<div class="termy">
```console
$ pip install "harry==3"
```
</div>
於是,你在全域 Python 環境中安裝了 `harry` 版本 `3`
如果你再次嘗試運行 `philosophers-stone`,很可能會**無法正常運作**,因為它需要的是 `harry` 版本 `1`
```mermaid
flowchart LR
subgraph global[全域環境]
harry-1[<strike>harry v1</strike>]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[專案 philosophers-stone]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[專案 prisoner-of-azkaban]
azkaban(prisoner-of-azkaban) --> |需要| harry-3
end
```
/// tip
Python 套件在推出**新版本**時通常會儘量**避免破壞性更改**,但最好還是要謹慎,在安裝新版本前進行測試,以確保一切能正常運行。
///
現在,想像一下如果有**許多**其他**套件**,它們都是你的**專案所依賴的**。這樣是非常難以管理的。你可能會發現有些專案使用了一些**不相容的套件版本**,而無法得知為什麼某些程式無法正常運作。
此外,取決於你的操作系統(例如 Linux、Windows、macOS),它可能已經預先安裝了 Python。在這種情況下,它可能已經有一些系統所需的套件和特定版本。如果你在全域 Python 環境中安裝套件,可能會**破壞**某些隨作業系統一起安裝的程式。
## 套件安裝在哪裡
當你安裝 Python 時,它會在你的電腦中建立一些目錄並放置一些檔案。
其中一些目錄專門用來存放你所安裝的所有套件。
當你運行:
<div class="termy">
```console
// 先別去運行這個指令,這只是個示例 🤓
$ pip install "fastapi[standard]"
---> 100%
```
</div>
這會從 <a href="https://pypi.org/project/fastapi/" class="external-link" target="_blank">PyPI</a> 下載一個壓縮檔案,其中包含 FastAPI 的程式碼。
它還會**下載** FastAPI 所依賴的其他套件的檔案。
接著,它會**解壓**所有這些檔案,並將它們放在你的電腦中的某個目錄中。
預設情況下,這些下載和解壓的檔案會放置於隨 Python 安裝的目錄中,即**全域環境**。
## 什麼是虛擬環境
解決套件都安裝在全域環境中的問題方法是為你所做的每個專案使用一個**虛擬環境**。
虛擬環境是一個**目錄**,與全域環境非常相似,你可以在其中針對某個專案安裝套件。
這樣,每個專案都會有自己的虛擬環境(`.venv` 目錄),其中包含自己的套件。
```mermaid
flowchart TB
subgraph stone-project[專案 philosophers-stone]
stone(philosophers-stone) --->|需要| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[專案 prisoner-of-azkaban]
azkaban(prisoner-of-azkaban) --->|需要| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## 啟用虛擬環境意味著什麼
當你啟用了虛擬環境,例如:
//// tab | Linux, macOS
<div class="termy">
```console
$ source .venv/bin/activate
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ .venv\Scripts\Activate.ps1
```
</div>
////
//// tab | Windows Bash
或者如果你在 Windows 上使用 Bash(例如 <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
<div class="termy">
```console
$ source .venv/Scripts/activate
```
</div>
////
這個命令會建立或修改一些[環境變數](environment-variables.md){.internal-link target=_blank},這些環境變數將在接下來的指令中可用。
其中之一是 `PATH` 變數。
/// tip
你可以在 [環境變數](environment-variables.md#path-environment-variable){.internal-link target=_blank} 部分了解更多關於 `PATH` 環境變數的內容。
///
啟用虛擬環境會將其路徑 `.venv/bin`(在 Linux 和 macOS 上)或 `.venv\Scripts`(在 Windows 上)加入到 `PATH` 環境變數中。
假設在啟用環境之前,`PATH` 變數看起來像這樣:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
這意味著系統會在以下目錄中查找程式:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
這意味著系統會在以下目錄中查找程式:
* `C:\Windows\System32`
////
啟用虛擬環境後,`PATH` 變數會變成這樣:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
這意味著系統現在會首先在以下目錄中查找程式:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
然後再在其他目錄中查找。
因此,當你在終端機中輸入 `python` 時,系統會在以下目錄中找到 Python 程式:
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
並使用這個。
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
這意味著系統現在會首先在以下目錄中查找程式:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
然後再在其他目錄中查找。
因此,當你在終端機中輸入 `python` 時,系統會在以下目錄中找到 Python 程式:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
並使用這個。
////
一個重要的細節是,虛擬環境路徑會被放在 `PATH` 變數的**開頭**。系統會在找到任何其他可用的 Python **之前**找到它。這樣,當你運行 `python` 時,它會使用**虛擬環境中的** Python,而不是任何其他 `python`(例如,全域環境中的 `python`)。
啟用虛擬環境還會改變其他一些內容,但這是它所做的最重要的事情之一。
## 檢查虛擬環境
當你檢查虛擬環境是否啟動時,例如:
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ Get-Command python
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
</div>
////
這表示將使用的 `python` 程式是**在虛擬環境中**的那一個。
在 Linux 和 macOS 中使用 `which`,在 Windows PowerShell 中使用 `Get-Command`
這個指令的運作方式是,它會在 `PATH` 環境變數中搜尋,依序**逐個路徑**查找名為 `python` 的程式。一旦找到,它會**顯示該程式的路徑**。
最重要的是,當你呼叫 `python` 時,將執行的就是這個確切的 "`python`"。
因此,你可以確認是否在正確的虛擬環境中。
/// tip
啟動一個虛擬環境,取得一個 Python,然後**切換到另一個專案**是件很容易的事;
但如果第二個專案**無法正常運作**,那可能是因為你使用了來自其他專案的虛擬環境的、**不正確的 Python**。
因此,檢查正在使用的 `python` 是非常實用的。🤓
///
## 為什麼要停用虛擬環境
例如,你可能正在一個專案 `philosophers-stone` 上工作,**啟動了該虛擬環境**,安裝了套件並使用了該環境,
然後你想要在**另一個專案** `prisoner-of-azkaban` 上工作,
你進入那個專案:
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
```
</div>
如果你不去停用 `philosophers-stone` 的虛擬環境,當你在終端中執行 `python` 時,它會嘗試使用 `philosophers-stone` 中的 Python。
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// 匯入 sirius 錯誤,未安裝 😱
Traceback (most recent call last):
File "main.py", line 1, in <module>
import sirius
```
</div>
但如果你停用虛擬環境並啟用 `prisoner-of-askaban` 的新虛擬環境,那麼當你執行 `python` 時,它會使用 `prisoner-of-askaban` 中虛擬環境的 Python。
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
// 你不需要在舊目錄中操作停用,你可以在任何地方操作停用,甚至在切換到另一個專案之後 😎
$ deactivate
// 啟用 prisoner-of-azkaban/.venv 中的虛擬環境 🚀
$ source .venv/bin/activate
// 現在當你執行 python 時,它會在這個虛擬環境中找到已安裝的 sirius 套件 ✨
$ python main.py
I solemnly swear 🐺
```
</div>
## 替代方案
這是一個簡單的指南,幫助你入門並教會你如何理解一切**底層**的原理。
有許多**替代方案**來管理虛擬環境、套件依賴(requirements)、專案。
當你準備好並想要使用一個工具來**管理整個專案**、套件依賴、虛擬環境等,建議你嘗試 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>
`uv` 可以執行許多操作,它可以:
* 為你**安裝 Python**,包括不同的版本
* 為你的專案管理**虛擬環境**
* 安裝**套件**
* 為你的專案管理套件的**依賴和版本**
* 確保你有一個**精確**的套件和版本集合來安裝,包括它們的依賴項,這樣你可以確保專案在生產環境中運行的狀態與開發時在你的電腦上運行的狀態完全相同,這被稱為**鎖定**
* 還有很多其他功能
## 結論
如果你讀過並理解了所有這些,現在**你對虛擬環境的了解已超過許多開發者**。🤓
未來當你為看起來複雜的問題除錯時,了解這些細節很可能會有所幫助,你會知道**它是如何在底層運作的**。😎

99
docs/zh/docs/advanced/async-tests.md

@ -0,0 +1,99 @@
# 异步测试
您已经了解了如何使用 `TestClient` 测试 **FastAPI** 应用程序。但是到目前为止,您只了解了如何编写同步测试,而没有使用 `async` 异步函数。
在测试中能够使用异步函数可能会很有用,比如当您需要异步查询数据库的时候。想象一下,您想要测试向 FastAPI 应用程序发送请求,然后验证您的后端是否成功在数据库中写入了正确的数据,与此同时您使用了异步的数据库的库。
让我们看看如何才能实现这一点。
## pytest.mark.anyio
如果我们想在测试中调用异步函数,那么我们的测试函数必须是异步的。 AnyIO 为此提供了一个简洁的插件,它允许我们指定一些测试函数要异步调用。
## HTTPX
即使您的 **FastAPI** 应用程序使用普通的 `def` 函数而不是 `async def` ,它本质上仍是一个 `async` 异步应用程序。
`TestClient` 在内部通过一些“魔法”操作,使得您可以在普通的 `def` 测试函数中调用异步的 FastAPI 应用程序,并使用标准的 pytest。但当我们在异步函数中使用它时,这种“魔法”就不再生效了。由于测试以异步方式运行,我们无法在测试函数中继续使用 `TestClient`
`TestClient` 是基于 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> 的。幸运的是,我们可以直接使用它来测试API。
## 示例
举个简单的例子,让我们来看一个[更大的应用](../tutorial/bigger-applications.md){.internal-link target=_blank}和[测试](../tutorial/testing.md){.internal-link target=_blank}中描述的类似文件结构:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py
```
文件 `main.py` 将包含:
{* ../../docs_src/async_tests/main.py *}
文件 `test_main.py` 将包含针对 `main.py` 的测试,现在它可能看起来如下:
{* ../../docs_src/async_tests/test_main.py *}
## 运行测试
您可以通过以下方式照常运行测试:
<div class="termy">
```console
$ pytest
---> 100%
```
</div>
## 详细说明
这个标记 `@pytest.mark.anyio` 会告诉 pytest 该测试函数应该被异步调用:
{* ../../docs_src/async_tests/test_main.py hl[7] *}
/// tip
请注意,测试函数现在用的是 `async def`,而不是像以前使用 `TestClient` 时那样只是 `def`
///
我们现在可以使用应用程序创建一个 `AsyncClient` ,并使用 `await` 向其发送异步请求。
{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
这相当于:
```Python
response = client.get('/')
```
我们曾经通过它向 `TestClient` 发出请求。
/// tip
请注意,我们正在将 async/await 与新的 `AsyncClient` 一起使用——请求是异步的。
///
/// warning
如果您的应用程序依赖于生命周期事件, `AsyncClient` 将不会触发这些事件。为了确保它们被触发,请使用 <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a> 中的 `LifespanManager`
///
## 其他异步函数调用
由于测试函数现在是异步的,因此除了在测试中向 FastAPI 应用程序发送请求之外,您现在还可以调用(和使用 `await` 等待)其他 `async` 异步函数,就和您在代码中的其他任何地方调用它们的方法一样。
/// tip
如果您在测试程序中集成异步函数调用的时候遇到一个 `RuntimeError: Task attached to a different loop` 的报错(例如,使用 <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB 的 MotorClient</a> 时),请记住,只能在异步函数中实例化需要事件循环的对象,例如通过 `'@app.on_event("startup")` 回调函数进行初始化。
///

132
docs/zh/docs/advanced/events.md

@ -1,16 +1,114 @@
# 事件:启动 - 关闭 # 生命周期事件
**FastAPI** 支持定义在应用启动前,或应用关闭后执行的事件处理器(函数) 你可以定义在应用**启动**前执行的逻辑(代码)。这意味着在应用**开始接收请求**之前,这些代码只会被执行**一次**
事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。 同样地,你可以定义在应用**关闭**时应执行的逻辑。在这种情况下,这段代码将在**处理可能的多次请求后**执行**一次**。
因为这段代码在应用开始接收请求**之前**执行,也会在处理可能的若干请求**之后**执行,它覆盖了整个应用程序的**生命周期**("生命周期"这个词很重要😉)。
这对于设置你需要在整个应用中使用的**资源**非常有用,这些资源在请求之间**共享**,你可能需要在之后进行**释放**。例如,数据库连接池,或加载一个共享的机器学习模型。
## 用例
让我们从一个示例用例开始,看看如何解决它。
假设你有几个**机器学习的模型**,你想要用它们来处理请求。
相同的模型在请求之间是共享的,因此并非每个请求或每个用户各自拥有一个模型。
假设加载模型可能**需要相当长的时间**,因为它必须从**磁盘**读取大量数据。因此你不希望每个请求都加载它。
你可以在模块/文件的顶部加载它,但这也意味着即使你只是在运行一个简单的自动化测试,它也会**加载模型**,这样测试将**变慢**,因为它必须在能够独立运行代码的其他部分之前等待模型加载完成。
这就是我们要解决的问题——在处理请求前加载模型,但只是在应用开始接收请求前,而不是代码执行时。
## 生命周期 lifespan
你可以使用`FastAPI()`应用的`lifespan`参数和一个上下文管理器(稍后我将为你展示)来定义**启动**和**关闭**的逻辑。
让我们从一个例子开始,然后详细介绍。
我们使用`yield`创建了一个异步函数`lifespan()`像这样:
```Python hl_lines="16 19"
{!../../docs_src/events/tutorial003.py!}
```
在这里,我们在 `yield` 之前将(虚拟的)模型函数放入机器学习模型的字典中,以此模拟加载模型的耗时**启动**操作。这段代码将在应用程序**开始处理请求之前**执行,即**启动**期间。
然后,在 `yield` 之后,我们卸载模型。这段代码将会在应用程序**完成处理请求后**执行,即在**关闭**之前。这可以释放诸如内存或 GPU 之类的资源。
/// tip | 提示
**关闭**事件只会在你停止应用时触发。
可能你需要启动一个新版本,或者你只是你厌倦了运行它。 🤷
///
## 生命周期函数
首先要注意的是,我们定义了一个带有 `yield` 的异步函数。这与带有 `yield` 的依赖项非常相似。
```Python hl_lines="14-19"
{!../../docs_src/events/tutorial003.py!}
```
这个函数在 `yield`之前的部分,会在应用启动前执行。
剩下的部分在 `yield` 之后,会在应用完成后执行。
## 异步上下文管理器
如你所见,这个函数有一个装饰器 `@asynccontextmanager`
它将函数转化为所谓的“**异步上下文管理器**”。
```Python hl_lines="1 13"
{!../../docs_src/events/tutorial003.py!}
```
在 Python 中, **上下文管理器**是一个你可以在 `with` 语句中使用的东西,例如,`open()` 可以作为上下文管理器使用。
```Python
with open("file.txt") as file:
file.read()
```
Python 的最近几个版本也有了一个**异步上下文管理器**,你可以通过 `async with` 来使用:
```Python
async with lifespan(app):
await do_stuff()
```
你可以像上面一样创建了一个上下文管理器或者异步上下文管理器,它的作用是在进入 `with` 块时,执行 `yield` 之前的代码,并且在离开 `with` 块时,执行 `yield` 后面的代码。
但在我们上面的例子里,我们并不是直接使用,而是传递给 FastAPI 来供其使用。
`FastAPI()``lifespan` 参数接受一个**异步上下文管理器**,所以我们可以把我们新定义的上下文管理器 `lifespan` 传给它。
```Python hl_lines="22"
{!../../docs_src/events/tutorial003.py!}
```
## 替代事件(弃用)
/// warning | 警告 /// warning | 警告
**FastAPI** 只执行主应用中的事件处理器,不执行[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}中的事件处理器。 配置**启动**和**关闭**事件的推荐方法是使用 `FastAPI()` 应用的 `lifespan` 参数,如前所示。如果你提供了一个 `lifespan` 参数,启动(`startup`)和关闭(`shutdown`)事件处理器将不再生效。要么使用 `lifespan`,要么配置所有事件,两者不能共用。
你可以跳过这一部分。
/// ///
## `startup` 事件 有一种替代方法可以定义在**启动**和**关闭**期间执行的逻辑。
**FastAPI** 支持定义在应用启动前,或应用关闭时执行的事件处理器(函数)。
事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。
### `startup` 事件
使用 `startup` 事件声明 `app` 启动前运行的函数: 使用 `startup` 事件声明 `app` 启动前运行的函数:
@ -22,7 +120,7 @@
只有所有 `startup` 事件处理器运行完毕,**FastAPI** 应用才开始接收请求。 只有所有 `startup` 事件处理器运行完毕,**FastAPI** 应用才开始接收请求。
## `shutdown` 事件 ### `shutdown` 事件
使用 `shutdown` 事件声明 `app` 关闭时运行的函数: 使用 `shutdown` 事件声明 `app` 关闭时运行的函数:
@ -48,8 +146,28 @@
/// ///
### `startup``shutdown` 一起使用
启动和关闭的逻辑很可能是连接在一起的,你可能希望启动某个东西然后结束它,获取一个资源然后释放它等等。
在不共享逻辑或变量的不同函数中处理这些逻辑比较困难,因为你需要在全局变量中存储值或使用类似的方式。
因此,推荐使用 `lifespan`
## 技术细节
只是为好奇者提供的技术细节。🤓
在底层,这部分是<a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">生命周期协议</a>的一部分,参见 ASGI 技术规范,定义了称为启动(`startup`)和关闭(`shutdown`)的事件。
/// info | 说明 /// info | 说明
有关事件处理器的详情,请参阅 <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette 官档 - 事件</a> 有关事件处理器的详情,请参阅 <a href="https://www.starlette.io/lifespan/" class="external-link" target="_blank">Starlette 官档 - 事件</a>
包括如何处理生命周期状态,这可以用于程序的其他部分。
/// ///
## 子应用
🚨 **FastAPI** 只会触发主应用中的生命周期事件,不包括[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}中的。

55
docs/zh/docs/advanced/openapi-webhooks.md

@ -0,0 +1,55 @@
# OpenAPI 网络钩子
有些情况下,您可能想告诉您的 API **用户**,您的应用程序可以携带一些数据调用*他们的*应用程序(给它们发送请求),通常是为了**通知**某种**事件**。
这意味着,除了您的用户向您的 API 发送请求的一般情况,**您的 API**(或您的应用)也可以向**他们的系统**(他们的 API、他们的应用)**发送请求**。
这通常被称为**网络钩子**(Webhook)。
## 使用网络钩子的步骤
通常的过程是**您**在代码中**定义**要发送的消息,即**请求的主体**。
您还需要以某种方式定义您的应用程序将在**何时**发送这些请求或事件。
**用户**会以某种方式(例如在某个网页仪表板上)定义您的应用程序发送这些请求应该使用的 **URL**
所有关于注册网络钩子的 URL 的**逻辑**以及发送这些请求的实际代码都由您决定。您可以在**自己的代码**中以任何想要的方式来编写它。
## 使用 `FastAPI` 和 OpenAPI 文档化网络钩子
使用 **FastAPI**,您可以利用 OpenAPI 来自定义这些网络钩子的名称、您的应用可以发送的 HTTP 操作类型(例如 `POST`、`PUT` 等)以及您的应用将发送的**请求体**。
这能让您的用户更轻松地**实现他们的 API** 来接收您的**网络钩子**请求,他们甚至可能能够自动生成一些自己的 API 代码。
/// info
网络钩子在 OpenAPI 3.1.0 及以上版本中可用,FastAPI `0.99.0` 及以上版本支持。
///
## 带有网络钩子的应用程序
当您创建一个 **FastAPI** 应用程序时,有一个 `webhooks` 属性可以用来定义网络钩子,方式与您定义*路径操作*的时候相同,例如使用 `@app.webhooks.post()`
{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
您定义的网络钩子将被包含在 `OpenAPI` 的架构中,并出现在自动生成的**文档 UI** 中。
/// info
`app.webhooks` 对象实际上只是一个 `APIRouter` ,与您在使用多个文件来构建应用程序时所使用的类型相同。
///
请注意,使用网络钩子时,您实际上并没有声明一个*路径*(比如 `/items/` ),您传递的文本只是这个网络钩子的**标识符**(事件的名称)。例如在 `@app.webhooks.post("new-subscription")` 中,网络钩子的名称是 `new-subscription`
这是因为我们预计**您的用户**会以其他方式(例如通过网页仪表板)来定义他们希望接收网络钩子的请求的实际 **URL 路径**
### 查看文档
现在您可以启动您的应用程序并访问 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
您会看到您的文档不仅有正常的*路径操作*显示,现在还多了一些**网络钩子**:
<img src="/img/tutorial/openapi-webhooks/image01.png">

2
docs/zh/docs/advanced/testing-dependencies.md

@ -28,7 +28,7 @@
这样一来,**FastAPI** 就会调用覆盖依赖项,不再调用原依赖项。 这样一来,**FastAPI** 就会调用覆盖依赖项,不再调用原依赖项。
{* ../../docs_src/dependency_testing/tutorial001.py hl[26:27,30] *} {* ../../docs_src/dependency_testing/tutorial001_an_py310.py hl[26:27,30] *}
/// tip | 提示 /// tip | 提示

34
docs/zh/docs/async.md

@ -251,7 +251,7 @@ Python 的现代版本支持通过一种叫**"协程"**——使用 `async` 和
这与 **FastAPI** 的性能水平相同。 这与 **FastAPI** 的性能水平相同。
您可以同时拥有并行性和异步性,您可以获得比大多数经过测试的 NodeJS 框架更高的性能,并且与 Go 不相上下, Go 是一种更接近于 C 的编译语言(<a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">全部归功于 Starlette</a>)。 你可以同时拥有并行性和异步性,你可以获得比大多数经过测试的 NodeJS 框架更高的性能,并且与 Go 不相上下, Go 是一种更接近于 C 的编译语言(<a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">全部归功于 Starlette</a>)。
### 并发比并行好吗? ### 并发比并行好吗?
@ -275,7 +275,7 @@ Python 的现代版本支持通过一种叫**"协程"**——使用 `async` 和
但在这种情况下,如果你能带上 8 名前收银员/厨师,现在是清洁工一起清扫,他们中的每一个人(加上你)都能占据房子的一个区域来清扫,你就可以在额外的帮助下并行的更快地完成所有工作。 但在这种情况下,如果你能带上 8 名前收银员/厨师,现在是清洁工一起清扫,他们中的每一个人(加上你)都能占据房子的一个区域来清扫,你就可以在额外的帮助下并行的更快地完成所有工作。
在这个场景中,每个清洁工(包括)都将是一个处理器,完成这个工作的一部分。 在这个场景中,每个清洁工(包括)都将是一个处理器,完成这个工作的一部分。
由于大多数执行时间是由实际工作(而不是等待)占用的,并且计算机中的工作是由 <abbr title="Central Processing Unit">CPU</abbr> 完成的,所以他们称这些问题为"CPU 密集型"。 由于大多数执行时间是由实际工作(而不是等待)占用的,并且计算机中的工作是由 <abbr title="Central Processing Unit">CPU</abbr> 完成的,所以他们称这些问题为"CPU 密集型"。
@ -292,9 +292,9 @@ CPU 密集型操作的常见示例是需要复杂的数学处理。
### 并发 + 并行: Web + 机器学习 ### 并发 + 并行: Web + 机器学习
使用 **FastAPI**可以利用 Web 开发中常见的并发机制的优势(NodeJS 的主要吸引力)。 使用 **FastAPI**可以利用 Web 开发中常见的并发机制的优势(NodeJS 的主要吸引力)。
并且,也可以利用并行和多进程(让多个进程并行运行)的优点来处理与机器学习系统中类似的 **CPU 密集型** 工作。 并且,也可以利用并行和多进程(让多个进程并行运行)的优点来处理与机器学习系统中类似的 **CPU 密集型** 工作。
这一点,再加上 Python 是**数据科学**、机器学习(尤其是深度学习)的主要语言这一简单事实,使得 **FastAPI** 与数据科学/机器学习 Web API 和应用程序(以及其他许多应用程序)非常匹配。 这一点,再加上 Python 是**数据科学**、机器学习(尤其是深度学习)的主要语言这一简单事实,使得 **FastAPI** 与数据科学/机器学习 Web API 和应用程序(以及其他许多应用程序)非常匹配。
@ -304,7 +304,7 @@ CPU 密集型操作的常见示例是需要复杂的数学处理。
现代版本的 Python 有一种非常直观的方式来定义异步代码。这使它看起来就像正常的"顺序"代码,并在适当的时候"等待"。 现代版本的 Python 有一种非常直观的方式来定义异步代码。这使它看起来就像正常的"顺序"代码,并在适当的时候"等待"。
当有一个操作需要等待才能给出结果,且支持这个新的 Python 特性时,可以编写如下代码: 当有一个操作需要等待才能给出结果,且支持这个新的 Python 特性时,可以编写如下代码:
```Python ```Python
burgers = await get_burgers(2) burgers = await get_burgers(2)
@ -340,7 +340,7 @@ burgers = get_burgers(2)
--- ---
因此,如果您使用的库告诉您可以使用 `await` 调用它,则需要使用 `async def` 创建路径操作函数 ,如: 因此,如果你使用的库告诉你可以使用 `await` 调用它,则需要使用 `async def` 创建路径操作函数 ,如:
```Python hl_lines="2-3" ```Python hl_lines="2-3"
@app.get('/burgers') @app.get('/burgers')
@ -351,15 +351,15 @@ async def read_burgers():
### 更多技术细节 ### 更多技术细节
可能已经注意到,`await` 只能在 `async def` 定义的函数内部使用。 可能已经注意到,`await` 只能在 `async def` 定义的函数内部使用。
但与此同时,必须"等待"通过 `async def` 定义的函数。因此,带 `async def` 的函数也只能在 `async def` 定义的函数内部调用。 但与此同时,必须"等待"通过 `async def` 定义的函数。因此,带 `async def` 的函数也只能在 `async def` 定义的函数内部调用。
那么,这关于先有鸡还是先有蛋的问题,如何调用第一个 `async` 函数? 那么,这关于先有鸡还是先有蛋的问题,如何调用第一个 `async` 函数?
如果使用 **FastAPI**,你不必担心这一点,因为"第一个"函数将是你的路径操作函数,FastAPI 将知道如何做正确的事情。 如果使用 **FastAPI**,你不必担心这一点,因为"第一个"函数将是你的路径操作函数,FastAPI 将知道如何做正确的事情。
但如果想在没有 FastAPI 的情况下使用 `async` / `await`,则可以这样做。 但如果想在没有 FastAPI 的情况下使用 `async` / `await`,则可以这样做。
### 编写自己的异步代码 ### 编写自己的异步代码
@ -367,7 +367,9 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
特别是,你可以直接使用 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> 来处理高级的并发用例,这些用例需要在自己的代码中使用更高级的模式。 特别是,你可以直接使用 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> 来处理高级的并发用例,这些用例需要在自己的代码中使用更高级的模式。
即使您没有使用 **FastAPI**,您也可以使用 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。 即使你没有使用 **FastAPI**,你也可以使用 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。
我(指原作者 —— 译者注)基于 AnyIO 新建了一个库,作为一个轻量级的封装层,用来优化类型注解,同时提供了更好的**自动补全**、**内联错误提示**等功能。这个库还附带了一个友好的入门指南和教程,能帮助你**理解**并编写**自己的异步代码**:<a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>。如果你有**结合使用异步代码和常规**(阻塞/同步)代码的需求,这个库会特别有用。
### 其他形式的异步代码 ### 其他形式的异步代码
@ -407,7 +409,7 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
这些都是 FastAPI 如何在内部工作的技术细节。 这些都是 FastAPI 如何在内部工作的技术细节。
如果有相当多的技术知识(协程、线程、阻塞等),并且对 FastAPI 如何处理 `async def` 与常规 `def` 感到好奇,请继续。 如果有相当多的技术知识(协程、线程、阻塞等),并且对 FastAPI 如何处理 `async def` 与常规 `def` 感到好奇,请继续。
/// ///
@ -415,9 +417,9 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
当你使用 `def` 而不是 `async def` 来声明一个*路径操作函数*时,它运行在外部的线程池中并等待其结果,而不是直接调用(因为它会阻塞服务器)。 当你使用 `def` 而不是 `async def` 来声明一个*路径操作函数*时,它运行在外部的线程池中并等待其结果,而不是直接调用(因为它会阻塞服务器)。
如果您使用过另一个不以上述方式工作的异步框架,并且您习惯于用普通的 `def` 定义普通的仅计算路径操作函数,以获得微小的性能增益(大约100纳秒),请注意,在 FastAPI 中,效果将完全相反。在这些情况下,最好使用 `async def`,除非路径操作函数内使用执行阻塞 <abbr title="输入/输出:磁盘读写,网络通讯.">I/O</abbr> 的代码。 如果你使用过另一个不以上述方式工作的异步框架,并且你习惯于用普通的 `def` 定义普通的仅计算路径操作函数,以获得微小的性能增益(大约100纳秒),请注意,在 FastAPI 中,效果将完全相反。在这些情况下,最好使用 `async def`,除非路径操作函数内使用执行阻塞 <abbr title="输入/输出:磁盘读写,网络通讯.">I/O</abbr> 的代码。
在这两种情况下,与之前的框架相比,**FastAPI** 可能[仍然很快](index.md#_11){.internal-link target=_blank}。 在这两种情况下,与之前的框架相比,**FastAPI** 可能[仍然很快](index.md#_11){.internal-link target=_blank}。
### 依赖 ### 依赖
@ -429,9 +431,9 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
### 其他函数 ### 其他函数
可直接调用通过 `def``async def` 创建的任何其他函数,FastAPI 不会影响调用它们的方式。 可直接调用通过 `def``async def` 创建的任何其他函数,FastAPI 不会影响调用它们的方式。
这与 FastAPI 为调用*路径操作函数*和依赖项的逻辑相反。 这与 FastAPI 为调用*路径操作函数*和依赖项的逻辑相反。
如果你的函数是通过 `def` 声明的,它将被直接调用(在代码中编写的地方),而不会在线程池中,如果这个函数通过 `async def` 声明,当在代码中调用时,你就应该使用 `await` 等待函数的结果。 如果你的函数是通过 `def` 声明的,它将被直接调用(在代码中编写的地方),而不会在线程池中,如果这个函数通过 `async def` 声明,当在代码中调用时,你就应该使用 `await` 等待函数的结果。
@ -439,4 +441,4 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
再次提醒,这些是非常技术性的细节,如果你来搜索它可能对你有用。 再次提醒,这些是非常技术性的细节,如果你来搜索它可能对你有用。
否则,最好应该遵守的指导原则<a href="#_1">赶时间吗?</a>. 否则,最好应该遵守的指导原则<a href="#_1">赶时间吗?</a>.

76
docs/zh/docs/tutorial/cookie-param-models.md

@ -0,0 +1,76 @@
# Cookie 参数模型
如果您有一组相关的 **cookie**,您可以创建一个 **Pydantic 模型**来声明它们。🍪
这将允许您在**多个地方**能够**重用模型**,并且可以一次性声明所有参数的验证方式和元数据。😎
/// note
自 FastAPI 版本 `0.115.0` 起支持此功能。🤓
///
/// tip
此技术同样适用于 `Query``Cookie``Header` 。😎
///
## 带有 Pydantic 模型的 Cookie
**Pydantic** 模型中声明所需的 **cookie** 参数,然后将参数声明为 `Cookie`
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI** 将从请求中接收到的 **cookie** 中**提取**出**每个字段**的数据,并提供您定义的 Pydantic 模型。
## 查看文档
您可以在文档 UI 的 `/docs` 中查看定义的 cookie:
<div class="screenshot">
<img src="/img/tutorial/cookie-param-models/image01.png">
</div>
/// info
请记住,由于**浏览器**以特殊方式**处理 cookie**,并在后台进行操作,因此它们**不会**轻易允许 **JavaScript** 访问这些 cookie。
如果您访问 `/docs`**API 文档 UI**,您将能够查看您*路径操作*的 cookie **文档**
但是即使您**填写数据**并点击“执行”,由于文档界面使用 **JavaScript**,cookie 将不会被发送。而您会看到一条**错误**消息,就好像您没有输入任何值一样。
///
## 禁止额外的 Cookie
在某些特殊使用情况下(可能并不常见),您可能希望**限制**您想要接收的 cookie。
您的 API 现在可以控制自己的 <abbr title="顺带一提,这是一个笑话。它与 cookie 同意无关,但现在连API都能拒绝那些可怜的 cookie,真是太有意思了。来,吃块小饼干(cookie)吧。🍪">cookie 同意</abbr>。🤪🍪
您可以使用 Pydantic 的模型配置来禁止( `forbid` )任何额外( `extra` )字段:
{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *}
如果客户尝试发送一些**额外的 cookie**,他们将收到**错误**响应。
可怜的 cookie 通知条,费尽心思为了获得您的同意,却被<abbr title="这又是一个笑话,别管我了,给您的小饼干(cookie)配上点咖啡吧。☕">API 拒绝了</abbr>。🍪
例如,如果客户端尝试发送一个值为 `good-list-please``santa_tracker` cookie,客户端将收到一个**错误**响应,告知他们 `santa_tracker` <abbr title="圣诞老人(Santa)不赞成没有小饼干(cookie)。🎅 好吧,不会再开 cookie 的玩笑了。">cookie 是不允许的</abbr>
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## 总结
您可以使用 **Pydantic 模型**在 **FastAPI** 中声明 <abbr title="走之前再来块小饼干吧。 🍪">**cookie**</abbr>。😎

2
docs/zh/docs/tutorial/first-steps.md

@ -313,7 +313,7 @@ https://example.com/items/foo
/// note /// note
如果你不知道两者的区别,请查阅 [Async: *"In a hurry?"*](https://fastapi.tiangolo.com/async/#in-a-hurry){.internal-link target=_blank}。 如果你不知道两者的区别,请查阅 [并发: *赶时间吗?*](../async.md#_1){.internal-link target=_blank}。
/// ///

56
docs/zh/docs/tutorial/header-param-models.md

@ -0,0 +1,56 @@
# Header 参数模型
如果您有一组相关的 **header 参数**,您可以创建一个 **Pydantic 模型**来声明它们。
这将允许您在**多个地方**能够**重用模型**,并且可以一次性声明所有参数的验证和元数据。😎
/// note
自 FastAPI 版本 `0.115.0` 起支持此功能。🤓
///
## 使用 Pydantic 模型的 Header 参数
**Pydantic 模型**中声明所需的 **header 参数**,然后将参数声明为 `Header` :
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI** 将从请求中接收到的 **headers** 中**提取**出**每个字段**的数据,并提供您定义的 Pydantic 模型。
## 查看文档
您可以在文档 UI 的 `/docs` 中查看所需的 headers:
<div class="screenshot">
<img src="/img/tutorial/header-param-models/image01.png">
</div>
## 禁止额外的 Headers
在某些特殊使用情况下(可能并不常见),您可能希望**限制**您想要接收的 headers。
您可以使用 Pydantic 的模型配置来禁止( `forbid` )任何额外( `extra` )字段:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
如果客户尝试发送一些**额外的 headers**,他们将收到**错误**响应。
例如,如果客户端尝试发送一个值为 `plumbus``tool` header,客户端将收到一个**错误**响应,告知他们 header 参数 `tool` 是不允许的:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## 总结
您可以使用 **Pydantic 模型**在 **FastAPI** 中声明 **headers**。😎

2
docs/zh/docs/tutorial/path-params.md

@ -137,7 +137,7 @@ FastAPI 充分地利用了 <a href="https://docs.pydantic.dev/" class="external-
/// info | 说明 /// info | 说明
Python 3.4 及之后版本支持<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">枚举(即 enums)</a> Python 3.4 及之后版本支持<a href="https://docs.python.org/zh-cn/3/library/enum.html" class="external-link" target="_blank">枚举(即 enums)</a>
/// ///

2
docs/zh/docs/tutorial/query-params-str-validations.md

@ -116,7 +116,7 @@ q: Union[str, None] = Query(default=None, min_length=3)
/// info /// info
如果你之前没见过 `...` 这种用法:它是一个特殊的单独值,它是 <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">Python 的一部分并且被称为「省略号」</a> 如果你之前没见过 `...` 这种用法:它是一个特殊的单独值,它是 <a href="https://docs.python.org/zh-cn/3/library/constants.html#Ellipsis" class="external-link" target="_blank">Python 的一部分并且被称为“Ellipsis”(意为省略号 —— 译者注)</a>
Pydantic 和 FastAPI 使用它来显式的声明需要一个值。 Pydantic 和 FastAPI 使用它来显式的声明需要一个值。
/// ///

78
docs/zh/docs/tutorial/request-form-models.md

@ -0,0 +1,78 @@
# 表单模型
您可以使用 **Pydantic 模型**在 FastAPI 中声明**表单字段**
/// info
要使用表单,需预先安装 <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>
确保您创建、激活一个[虚拟环境](../virtual-environments.md){.internal-link target=_blank}后再安装。
```console
$ pip install python-multipart
```
///
/// note
自 FastAPI 版本 `0.113.0` 起支持此功能。🤓
///
## 表单的 Pydantic 模型
您只需声明一个 **Pydantic 模型**,其中包含您希望接收的**表单字段**,然后将参数声明为 `Form` :
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
**FastAPI** 将从请求中的**表单数据**中**提取**出**每个字段**的数据,并提供您定义的 Pydantic 模型。
## 检查文档
您可以在文档 UI 中验证它,地址为 `/docs`
<div class="screenshot">
<img src="/img/tutorial/request-form-models/image01.png">
</div>
## 禁止额外的表单字段
在某些特殊使用情况下(可能并不常见),您可能希望将表单字段**限制**为仅在 Pydantic 模型中声明过的字段,并**禁止**任何**额外**的字段。
/// note
自 FastAPI 版本 `0.114.0` 起支持此功能。🤓
///
您可以使用 Pydantic 的模型配置来禁止( `forbid` )任何额外( `extra` )字段:
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
如果客户端尝试发送一些额外的数据,他们将收到**错误**响应。
例如,如果客户端尝试发送这样的表单字段:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
他们将收到一条错误响应,表明字段 `extra` 是不被允许的:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## 总结
您可以使用 Pydantic 模型在 FastAPI 中声明表单字段。😎

360
docs/zh/docs/tutorial/sql-databases.md

@ -0,0 +1,360 @@
# SQL(关系型)数据库
**FastAPI** 并不要求您使用 SQL(关系型)数据库。您可以使用**任何**想用的数据库。
这里,我们来看一个使用 <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel</a> 的示例。
**SQLModel** 是基于 <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> 和 Pydantic 构建的。它由 **FastAPI** 的同一作者制作,旨在完美匹配需要使用 **SQL 数据库**的 FastAPI 应用程序。
/// tip
您可以使用任何其他您想要的 SQL 或 NoSQL 数据库(在某些情况下称为 <abbr title="对象关系映射器(Object Relational Mapper,ORM),一个术语,用来指代一种库,其中某些类对应于 SQL 数据表,这些类的实例则对应于表中的行。">“ORM”</abbr>),FastAPI 不会强迫您使用任何东西。😎
///
由于 SQLModel 基于 SQLAlchemy,因此您可以轻松使用任何由 SQLAlchemy **支持的数据库**(这也让它们被 SQLModel 支持),例如:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server 等.
在这个例子中,我们将使用 **SQLite**,因为它使用单个文件,并且 Python 对其有集成支持。因此,您可以直接复制这个例子并运行。
之后,对于您的生产应用程序,您可能会想要使用像 PostgreSQL 这样的数据库服务器。
/// tip
有一个使用 **FastAPI****PostgreSQL** 的官方的项目生成器,其中包括了前端和更多工具: <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a>
///
这是一个非常简单和简短的教程。如果您想了解一般的数据库、SQL 或更高级的功能,请查看 <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel 文档</a>
## 安装 `SQLModel`
首先,确保您创建并激活了[虚拟环境](../virtual-environments.md){.internal-link target=_blank},然后安装了 `sqlmodel` :
<div class="termy">
```console
$ pip install sqlmodel
---> 100%
```
</div>
## 创建含有单一模型的应用程序
我们首先创建应用程序的最简单的第一个版本,只有一个 **SQLModel** 模型。
稍后我们将通过下面的**多个模型**提高其安全性和多功能性。🤓
### 创建模型
导入 `SQLModel` 并创建一个数据库模型:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
`Hero` 类与 Pydantic 模型非常相似(实际上,从底层来看,它确实*就是一个 Pydantic 模型*)。
有一些区别:
* `table=True` 会告诉 SQLModel 这是一个*表模型*,它应该表示 SQL 数据库中的一个*表*,而不仅仅是一个*数据模型*(就像其他常规的 Pydantic 类一样)。
* `Field(primary_key=True)` 会告诉 SQLModel `id` 是 SQL 数据库中的**主键**(您可以在 SQLModel 文档中了解更多关于 SQL 主键的信息)。
把类型设置为 `int | None` ,SQLModel 就能知道该列在 SQL 数据库中应该是 `INTEGER` 类型,并且应该是 `NULLABLE`
* `Field(index=True)` 会告诉 SQLModel 应该为此列创建一个 **SQL 索引**,这样在读取按此列过滤的数据时,程序能在数据库中进行更快的查找。
SQLModel 会知道声明为 `str` 的内容将是类型为 `TEXT` (或 `VARCHAR` ,具体取决于数据库)的 SQL 列。
### 创建引擎(Engine)
SQLModel 的引擎 `engine`(实际上它是一个 SQLAlchemy `engine` )是用来与数据库**保持连接**的。
您只需构建**一个 `engine`**,来让您的所有代码连接到同一个数据库。
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
使用 `check_same_thread=False` 可以让 FastAPI 在不同线程中使用同一个 SQLite 数据库。这很有必要,因为**单个请求**可能会使用**多个线程**(例如在依赖项中)。
不用担心,我们会按照代码结构确保**每个请求使用一个单独的 SQLModel *会话***,这实际上就是 `check_same_thread` 想要实现的。
### 创建表
然后,我们来添加一个函数,使用 `SQLModel.metadata.create_all(engine)` 为所有*表模型***创建表**。
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### 创建会话(Session)依赖项
**`Session`** 会存储**内存中的对象**并跟踪数据中所需更改的内容,然后它**使用 `engine`** 与数据库进行通信。
我们会使用 `yield` 创建一个 FastAPI **依赖项**,为每个请求提供一个新的 `Session` 。这确保我们每个请求使用一个单独的会话。🤓
然后我们创建一个 `Annotated` 的依赖项 `SessionDep` 来简化其他也会用到此依赖的代码。
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### 在启动时创建数据库表
我们会在应用程序启动时创建数据库表。
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
此处,在应用程序启动事件中,我们创建了表。
而对于生产环境,您可能会用一个能够在启动应用程序之前运行的迁移脚本。🤓
/// tip
SQLModel 将会拥有封装 Alembic 的迁移工具,但目前您可以直接使用 <a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a>
///
### 创建 Hero 类
因为每个 SQLModel 模型同时也是一个 Pydantic 模型,所以您可以在与 Pydantic 模型相同的**类型注释**中使用它。
例如,如果您声明一个类型为 `Hero` 的参数,它将从 **JSON 主体**中读取数据。
同样,您可以将其声明为函数的**返回类型**,然后数据的结构就会显示在自动生成的 API 文档界面中。
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
</details>
这里,我们使用 `SessionDep` 依赖项(一个 `Session` )将新的 `Hero` 添加到 `Session` 实例中,提交更改到数据库,刷新 hero 中的数据,并返回它。
### 读取 Hero 类
我们可以使用 `select()` 从数据库中**读取** `Hero` 类,并利用 `limit``offset` 来对结果进行分页。
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### 读取单个 Hero
我们可以**读取**单个 `Hero`
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### 删除单个 Hero
我们也可以**删除**单个 `Hero`
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### 运行应用程序
您可以运行这个应用程序:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
然后在 `/docs` UI 中,您能够看到 **FastAPI** 会用这些**模型**来**记录** API,并且还会用它们来**序列化**和**验证**数据。
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image01.png">
</div>
## 更新应用程序以支持多个模型
现在让我们稍微**重构**一下这个应用,以提高**安全性**和**多功能性**。
如果您查看之前的应用程序,您可以在 UI 界面中看到,到目前为止,由客户端决定要创建的 `Hero``id` 值。😱
我们不应该允许这样做,因为他们可能会覆盖我们在数据库中已经分配的 `id` 。决定 `id` 的行为应该由**后端**或**数据库**来完成,**而非客户端**。
此外,我们为 hero 创建了一个 `secret_name` ,但到目前为止,我们在各处都返回了它,这就不太**秘密**了……😅
我们将通过添加一些**额外的模型**来解决这些问题,而 SQLModel 将在这里大放异彩。✨
### 创建多个模型
**SQLModel** 中,任何含有 `table=True` 属性的模型类都是一个**表模型**。
任何不含有 `table=True` 属性的模型类都是**数据模型**,这些实际上只是 Pydantic 模型(附带一些小的额外功能)。🤓
有了 SQLModel,我们就可以利用**继承**来在所有情况下**避免重复**所有字段。
#### `HeroBase` - 基类
我们从一个 `HeroBase` 模型开始,该模型具有所有模型**共享的字段**:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - *表模型*
接下来,我们创建 `Hero` ,实际的*表模型*,并添加那些不总是在其他模型中的**额外字段**:
* `id`
* `secret_name`
因为 `Hero` 继承自 HeroBase ,所以它**也**包含了在 `HeroBase` 中声明过的**字段**。因此 `Hero` 的所有字段为:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - 公共*数据模型*
接下来,我们创建一个 `HeroPublic` 模型,这是将**返回**给 API 客户端的模型。
它包含与 `HeroBase` 相同的字段,因此不会包括 `secret_name`
终于,我们英雄(hero)的身份得到了保护! 🥷
它还重新声明了 `id: int` 。这样我们便与 API 客户端建立了一种**约定**,使他们始终可以期待 `id` 存在并且是一个整数 `int`(永远不会是 `None` )。
/// tip
确保返回模型始终提供一个值并且始终是 `int` (而不是 `None` )对 API 客户端非常有用,他们可以在这种确定性下编写更简单的代码。
此外,**自动生成的客户端**将拥有更简洁的接口,这样与您的 API 交互的开发者就能更轻松地使用您的 API。😎
///
`HeroPublic` 中的所有字段都与 `HeroBase` 中的相同,其中 `id` 声明为 `int` (不是 `None` ):
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - 用于创建 hero 的*数据模型*
现在我们创建一个 `HeroCreate` 模型,这是用于**验证**客户数据的模型。
它不仅拥有与 `HeroBase` 相同的字段,还有 `secret_name`
现在,当客户端**创建一个新的 hero** 时,他们会发送 `secret_name` ,它会被存储到数据库中,但这些 `secret_name` 不会通过 API 返回给客户端。
/// tip
这应当是**密码**被处理的方式:接收密码,但不要通过 API 返回它们。
在存储密码之前,您还应该对密码的值进行**哈希**处理,**绝不要以明文形式存储它们**。
///
`HeroCreate` 的字段包括:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - 用于更新 hero 的*数据模型*
在之前的应用程序中,我们没有办法**更新 hero**,但现在有了**多个模型**,我们便能做到这一点了。🎉
`HeroUpdate` *数据模型*有些特殊,它包含创建新 hero 所需的**所有相同字段**,但所有字段都是**可选的**(它们都有默认值)。这样,当您更新一个 hero 时,您可以只发送您想要更新的字段。
因为所有**字段实际上**都发生了**变化**(类型现在包括 `None` ,并且它们现在有一个默认值 `None` ),我们需要**重新声明**它们。
我们会重新声明所有字段,因此我们并不是真的需要从 `HeroBase` 继承。我会让它继承只是为了保持一致,但这并不必要。这更多是个人喜好的问题。🤷
`HeroUpdate` 的字段包括:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### 使用 `HeroCreate` 创建并返回 `HeroPublic`
既然我们有了**多个模型**,我们就可以对使用它们的应用程序部分进行更新。
我们在请求中接收到一个 `HeroCreate` *数据模型*,然后从中创建一个 `Hero` *表模型*
这个新的*表模型* `Hero` 会包含客户端发送的字段,以及一个由数据库生成的 `id`
然后我们将与函数中相同的*表模型* `Hero` 原样返回。但是由于我们使用 `HeroPublic` *数据模型*声明了 `response_model` ,**FastAPI** 会使用 `HeroPublic` 来验证和序列化数据。
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip
现在我们使用 `response_model=HeroPublic` 来代替**返回类型注释** `-> HeroPublic` ,因为我们返回的值实际上**并不是** `HeroPublic` 类型。
如果我们声明了 `-> HeroPublic` ,您的编辑器和代码检查工具会抱怨(但也确实理所应当)您返回了一个 `Hero` 而不是一个 `HeroPublic`
通过 `response_model` 的声明,我们让 **FastAPI** 按照它自己的方式处理,而不会干扰类型注解以及编辑器和其他工具提供的帮助。
///
### 用 `HeroPublic` 读取 Hero
我们可以像之前一样**读取** `Hero` 。同样,使用 `response_model=list[HeroPublic]` 确保正确地验证和序列化数据。
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### 用 `HeroPublic` 读取单个 Hero
我们可以**读取**单个 `hero`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### 用 `HeroUpdate` 更新单个 Hero
我们可以**更新**单个 `hero` 。为此,我们会使用 HTTP 的 `PATCH` 操作。
在代码中,我们会得到一个 `dict` ,其中包含客户端发送的所有数据,**只有客户端发送的数据**,并排除了任何一个仅仅作为默认值存在的值。为此,我们使用 `exclude_unset=True` 。这是最主要的技巧。🪄
然后我们会使用 `hero_db.sqlmodel_update(hero_data)` ,来利用 `hero_data` 的数据更新 `hero_db`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### (又一次)删除单个 Hero
**删除**一个 hero 基本保持不变。
我们不会满足在这一部分中重构一切的愿望。😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### (又一次)运行应用程序
您可以再运行一次应用程序:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
您会在 `/docs` API UI 看到它现在已经更新,并且在进行创建 hero 等操作时,它不会再期望从客户端接收 `id` 数据。
<div class="screenshot">
<img src="/img/tutorial/sql-databases/image02.png">
</div>
## 总结
您可以使用 <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a> 与 SQL 数据库进行交互,并通过*数据模型*和*表模型*简化代码。
您可以在 SQLModel 的文档中学习到更多内容,其中有一个更详细的关于<a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">如何将 SQLModel 与 FastAPI 一起使用的教程</a>。🚀

2
requirements-docs.txt

@ -14,6 +14,6 @@ cairosvg==2.7.1
mkdocstrings[python]==0.26.1 mkdocstrings[python]==0.26.1
griffe-typingdoc==0.2.7 griffe-typingdoc==0.2.7
# For griffe, it formats with black # For griffe, it formats with black
black==24.3.0 black==24.10.0
mkdocs-macros-plugin==1.0.5 mkdocs-macros-plugin==1.0.5
markdown-include-variants==0.0.3 markdown-include-variants==0.0.3

6
requirements-tests.txt

@ -3,14 +3,14 @@
pytest >=7.1.3,<9.0.0 pytest >=7.1.3,<9.0.0
coverage[toml] >= 6.5.0,< 8.0 coverage[toml] >= 6.5.0,< 8.0
mypy ==1.8.0 mypy ==1.8.0
dirty-equals ==0.6.0 dirty-equals ==0.8.0
sqlmodel==0.0.22 sqlmodel==0.0.22
flask >=1.1.2,<4.0.0 flask >=1.1.2,<4.0.0
anyio[trio] >=3.2.1,<4.0.0 anyio[trio] >=3.2.1,<4.0.0
PyJWT==2.8.0 PyJWT==2.8.0
pyyaml >=5.3.1,<7.0.0 pyyaml >=5.3.1,<7.0.0
passlib[bcrypt] >=1.7.2,<2.0.0 passlib[bcrypt] >=1.7.2,<2.0.0
inline-snapshot==0.13.0 inline-snapshot==0.14.0
# types # types
types-ujson ==5.7.0.1 types-ujson ==5.10.0.20240515
types-orjson ==3.6.2 types-orjson ==3.6.2

Loading…
Cancel
Save