Compare commits

..

36 Commits

Author SHA1 Message Date
github-actions[bot]
a456e92a21 📝 Update release notes
[skip ci]
2026-01-11 22:23:21 +00:00
Sebastián Ramírez
1be80f4885 📝 Add contribution instructions about LLM generated code and comments and automated tools for PRs (#14706) 2026-01-11 23:22:58 +01:00
github-actions[bot]
e63f382b0f 📝 Update release notes
[skip ci]
2026-01-11 21:19:50 +00:00
Sebastián Ramírez
7b864acf37 📝 Update docs for management tasks (#14705) 2026-01-11 21:19:26 +00:00
github-actions[bot]
e9e0419ed0 📝 Update release notes
[skip ci]
2026-01-11 19:19:29 +00:00
Sebastián Ramírez
249a776b70 📝 Update docs about managing translations (#14704) 2026-01-11 19:19:05 +00:00
github-actions[bot]
97aa825422 📝 Update release notes
[skip ci]
2026-01-11 18:18:58 +00:00
Sebastián Ramírez
1054fbd256 📝 Update docs for contributing with translations (#14701) 2026-01-11 18:18:38 +00:00
github-actions[bot]
effe493ae0 📝 Update release notes
[skip ci]
2026-01-11 16:43:35 +00:00
github-actions[bot]
612a2d20bc 📝 Update release notes
[skip ci]
2026-01-11 16:43:25 +00:00
dependabot[bot]
14f3068762 ⬆ Bump actions/cache from 4 to 5 (#14511)
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-11 17:42:51 +01:00
dependabot[bot]
d05b18ec40 ⬆ Bump actions/upload-artifact from 5 to 6 (#14525)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-11 17:42:37 +01:00
github-actions[bot]
9a76f2fec9 📝 Update release notes
[skip ci]
2026-01-11 16:42:08 +00:00
dependabot[bot]
0383fb3ab9 ⬆ Bump actions/download-artifact from 6 to 7 (#14526)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-11 17:41:45 +01:00
github-actions[bot]
50fa3f7c88 📝 Update release notes
[skip ci]
2026-01-11 00:21:31 +00:00
Sebastián Ramírez
5ec2615b1a 👷 Tweak CI input names (#14688) 2026-01-11 01:21:07 +01:00
github-actions[bot]
16e583413c 📝 Update release notes
[skip ci]
2026-01-11 00:16:33 +00:00
github-actions[bot]
1fedd1c73b 📝 Update release notes
[skip ci]
2026-01-11 00:15:31 +00:00
Sebastián Ramírez
cf8dc98aad 🌐 Update translations for ko (update-outdated) (#14589)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Yurii Motov <yurii.motov.monte@gmail.com>
2026-01-11 00:15:26 +00:00
Sebastián Ramírez
6f977366a4 🌐 Update translations for uk (update-outdated) (#14587)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Yurii Motov <yurii.motov.monte@gmail.com>
2026-01-11 01:15:06 +01:00
github-actions[bot]
154ce03ff0 📝 Update release notes
[skip ci]
2026-01-11 00:04:10 +00:00
Sebastián Ramírez
49653aa295 🔨 Refactor translation script to allow committing in place (#14687) 2026-01-11 00:03:50 +00:00
github-actions[bot]
f03a1502a0 📝 Update release notes
[skip ci]
2026-01-10 23:41:44 +00:00
Sebastián Ramírez
a2912ffa26 🌐 Update translations for es (update-outdated) (#14686)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-11 00:41:20 +01:00
github-actions[bot]
8183e748ee 📝 Update release notes
[skip ci]
2026-01-10 23:06:59 +00:00
Sebastián Ramírez
cefd50702a 🐛 Fix translation script path (#14685) 2026-01-10 23:06:37 +00:00
github-actions[bot]
3d1f9268fc 📝 Update release notes
[skip ci]
2026-01-10 22:44:05 +00:00
Sebastián Ramírez
7eac6e3169 Enable tests in CI for scripts (#14684) 2026-01-10 23:43:44 +01:00
github-actions[bot]
21d2c5cea0 📝 Update release notes
[skip ci]
2026-01-10 22:18:13 +00:00
Sebastián Ramírez
c75ae058e4 🔧 Add pre-commit local script to fix language translations (#14683) 2026-01-10 22:17:46 +00:00
github-actions[bot]
961b2e844a 📝 Update release notes
[skip ci]
2026-01-10 22:03:19 +00:00
Jonathan Ehwald
b4ba7f4652 ⬆️ Migrate to uv (#14676) 2026-01-10 23:02:57 +01:00
github-actions[bot]
c35e1fd4b4 📝 Update release notes
[skip ci]
2026-01-10 21:48:32 +00:00
Motov Yurii
d1c67c0055 🔨 Add LLM translations tool fixer (#14652)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2026-01-10 21:48:08 +00:00
github-actions[bot]
18762e38a9 📝 Update release notes
[skip ci]
2026-01-10 21:35:32 +00:00
Motov Yurii
b1db1395b6 📝 Specify language code for code block (#14656) 2026-01-10 22:35:09 +01:00
227 changed files with 15523 additions and 8052 deletions

View File

@@ -8,7 +8,7 @@ updates:
commit-message:
prefix:
# Python
- package-ecosystem: "pip"
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "monthly"

View File

@@ -8,9 +8,6 @@ on:
- opened
- synchronize
env:
UV_SYSTEM_PYTHON: 1
jobs:
changes:
runs-on: ubuntu-latest
@@ -31,8 +28,8 @@ jobs:
- README.md
- docs/**
- docs_src/**
- requirements-docs.txt
- pyproject.toml
- uv.lock
- mkdocs.yml
- mkdocs.env.yml
- .github/workflows/build-docs.yml
@@ -49,21 +46,20 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install docs extras
run: uv pip install -r requirements-docs.txt
run: uv sync --locked --no-dev --group docs
- name: Export Language Codes
id: show-langs
run: |
echo "langs=$(python ./scripts/docs.py langs-json)" >> $GITHUB_OUTPUT
echo "langs=$(uv run ./scripts/docs.py langs-json)" >> $GITHUB_OUTPUT
build-docs:
needs:
@@ -83,26 +79,25 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install docs extras
run: uv pip install -r requirements-docs.txt
run: uv sync --locked --no-dev --group docs
- name: Update Languages
run: python ./scripts/docs.py update-languages
- uses: actions/cache@v4
run: uv run ./scripts/docs.py update-languages
- uses: actions/cache@v5
with:
key: mkdocs-cards-${{ matrix.lang }}-${{ github.ref }}
path: docs/${{ matrix.lang }}/.cache
- name: Build Docs
run: python ./scripts/docs.py build-lang ${{ matrix.lang }}
- uses: actions/upload-artifact@v5
run: uv run ./scripts/docs.py build-lang ${{ matrix.lang }}
- uses: actions/upload-artifact@v6
with:
name: docs-site-${{ matrix.lang }}
path: ./site/**

View File

@@ -10,9 +10,6 @@ on:
required: false
default: "false"
env:
UV_SYSTEM_PYTHON: 1
jobs:
job:
if: github.repository_owner == 'fastapi'
@@ -28,17 +25,16 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: uv pip install -r requirements-github-actions.txt
run: uv sync --locked --no-dev --group github-actions
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
@@ -48,6 +44,6 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }}
- name: FastAPI People Contributors
run: python ./scripts/contributors.py
run: uv run ./scripts/contributors.py
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }}

View File

@@ -12,9 +12,6 @@ permissions:
pull-requests: write
statuses: write
env:
UV_SYSTEM_PYTHON: 1
jobs:
deploy-docs:
runs-on: ubuntu-latest
@@ -27,19 +24,18 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install GitHub Actions dependencies
run: uv pip install -r requirements-github-actions.txt
run: uv sync --locked --no-dev --group github-actions
- name: Deploy Docs Status Pending
run: python ./scripts/deploy_docs_status.py
run: uv run ./scripts/deploy_docs_status.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COMMIT_SHA: ${{ github.event.workflow_run.head_sha }}
@@ -49,7 +45,7 @@ jobs:
run: |
rm -rf ./site
mkdir ./site
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v7
with:
path: ./site/
pattern: docs-site-*
@@ -70,14 +66,14 @@ jobs:
command: pages deploy ./site --project-name=${{ env.PROJECT_NAME }} --branch=${{ env.BRANCH }}
- name: Deploy Docs Status Error
if: failure()
run: python ./scripts/deploy_docs_status.py
run: uv run ./scripts/deploy_docs_status.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COMMIT_SHA: ${{ github.event.workflow_run.head_sha }}
RUN_ID: ${{ github.run_id }}
STATE: "error"
- name: Comment Deploy
run: python ./scripts/deploy_docs_status.py
run: uv run ./scripts/deploy_docs_status.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEPLOY_URL: ${{ steps.deploy.outputs.deployment-url }}

View File

@@ -41,11 +41,15 @@ jobs:
"message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR.",
"reminder": {
"before": "P3D",
"message": "Heads-up: this will be closed in 3 days unless theres new activity."
"message": "Heads-up: this will be closed in 3 days unless there's new activity."
}
},
"invalid": {
"delay": 0,
"message": "This was marked as invalid and will be closed now. If this is an error, please provide additional details."
},
"maybe-ai": {
"delay": 0,
"message": "This was marked as potentially AI generated and will be closed now. If this is an error, please provide additional details, make sure to read the docs about contributing and AI."
}
}

View File

@@ -8,9 +8,6 @@ on:
permissions:
pull-requests: write
env:
UV_SYSTEM_PYTHON: 1
jobs:
label-approved:
if: github.repository_owner == 'fastapi'
@@ -24,19 +21,18 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install GitHub Actions dependencies
run: uv pip install -r requirements-github-actions.txt
run: uv sync --locked --no-dev --group github-actions
- name: Label Approved
run: python ./scripts/label_approved.py
run: uv run ./scripts/label_approved.py
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
CONFIG: >

View File

@@ -15,9 +15,6 @@ on:
required: false
default: 'false'
env:
UV_SYSTEM_PYTHON: 1
jobs:
job:
runs-on: ubuntu-latest
@@ -32,17 +29,16 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: uv pip install -r requirements-github-actions.txt
run: uv sync --locked --no-dev --group github-actions
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
@@ -52,7 +48,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify Translations
run: python ./scripts/notify_translations.py
run: uv run ./scripts/notify_translations.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUMBER: ${{ github.event.inputs.number || null }}

View File

@@ -10,9 +10,6 @@ on:
required: false
default: "false"
env:
UV_SYSTEM_PYTHON: 1
jobs:
job:
if: github.repository_owner == 'fastapi'
@@ -28,17 +25,16 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: uv pip install -r requirements-github-actions.txt
run: uv sync --locked --no-dev --group github-actions
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
@@ -48,7 +44,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PEOPLE }}
- name: FastAPI People Experts
run: python ./scripts/people.py
run: uv run ./scripts/people.py
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PEOPLE }}
SLEEP_INTERVAL: ${{ vars.PEOPLE_SLEEP_INTERVAL }}

View File

@@ -40,18 +40,15 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.14"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: |
uv venv
uv pip install -r requirements.txt
run: uv sync --locked --extra all
- name: Run prek - pre-commit
id: precommit
run: uvx prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure

View File

@@ -15,6 +15,7 @@ jobs:
- fastapi-slim
permissions:
id-token: write
contents: read
steps:
- name: Dump GitHub context
env:
@@ -24,19 +25,15 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.10"
python-version-file: ".python-version"
# Issue ref: https://github.com/actions/setup-python/issues/436
# cache: "pip"
# cache-dependency-path: pyproject.toml
- name: Install build dependencies
run: pip install build
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Build distribution
run: uv build
env:
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
run: python -m build
- name: Publish
uses: pypa/gh-action-pypi-publish@v1.13.0
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
run: uv publish

View File

@@ -8,9 +8,6 @@ on:
permissions:
statuses: write
env:
UV_SYSTEM_PYTHON: 1
jobs:
smokeshow:
runs-on: ubuntu-latest
@@ -23,15 +20,15 @@ jobs:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
cache-dependency-glob: |
requirements**.txt
pyproject.toml
- run: uv pip install -r requirements-github-actions.txt
- uses: actions/download-artifact@v6
uv.lock
- run: uv sync --locked --no-dev --group github-actions
- uses: actions/download-artifact@v7
with:
name: coverage-html
path: htmlcov
@@ -41,7 +38,7 @@ jobs:
- name: Upload coverage to Smokeshow
run: |
for i in 1 2 3 4 5; do
if smokeshow upload htmlcov; then
if uv run smokeshow upload htmlcov; then
echo "Smokeshow upload success!"
break
fi

View File

@@ -10,9 +10,6 @@ on:
required: false
default: "false"
env:
UV_SYSTEM_PYTHON: 1
jobs:
job:
if: github.repository_owner == 'fastapi'
@@ -28,17 +25,16 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: uv pip install -r requirements-github-actions.txt
run: uv sync --locked --no-dev --group github-actions
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
@@ -46,7 +42,7 @@ jobs:
with:
limit-access-to-actor: true
- name: FastAPI People Sponsors
run: python ./scripts/sponsors.py
run: uv run ./scripts/sponsors.py
env:
SPONSORS_TOKEN: ${{ secrets.SPONSORS_TOKEN }}
PR_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }}

View File

@@ -26,7 +26,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.10"
python-version-file: ".python-version"
- name: Install build dependencies
run: pip install build
- name: Build source distribution
@@ -40,7 +40,7 @@ jobs:
- name: Install test dependencies
run: |
cd dist/fastapi*/
pip install -r requirements-tests.txt
pip install --group tests --editable .[all]
env:
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
- name: Run source distribution tests

View File

@@ -13,7 +13,7 @@ on:
- cron: "0 0 * * 1"
env:
UV_SYSTEM_PYTHON: 1
UV_NO_SYNC: true
jobs:
test:
@@ -44,6 +44,8 @@ jobs:
coverage: coverage
fail-fast: false
runs-on: ${{ matrix.os }}
env:
UV_PYTHON: ${{ matrix.python-version }}
steps:
- name: Dump GitHub context
env:
@@ -57,17 +59,16 @@ jobs:
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: uv pip install -r requirements-tests.txt
run: uv sync --locked --no-dev --group tests --extra all
- run: mkdir coverage
- name: Test
if: matrix.codspeed != 'codspeed'
run: bash scripts/test.sh
run: uv run bash scripts/test.sh
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
@@ -79,11 +80,11 @@ jobs:
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
with:
mode: simulation
run: coverage run -m pytest tests/ --codspeed
run: uv run coverage run -m pytest tests/ --codspeed
# Do not store coverage for all possible combinations to avoid file size max errors in Smokeshow
- name: Store coverage files
if: matrix.coverage == 'coverage'
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: coverage-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/coverage/.coverage.*') }}
path: coverage
@@ -100,33 +101,32 @@ jobs:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.11'
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: uv pip install -r requirements-tests.txt
run: uv sync --locked --no-dev --group tests --extra all
- name: Get coverage files
uses: actions/download-artifact@v6
uses: actions/download-artifact@v7
with:
pattern: coverage-*
path: coverage
merge-multiple: true
- run: ls -la coverage
- run: coverage combine coverage
- run: coverage html --title "Coverage for ${{ github.sha }}"
- run: uv run coverage combine coverage
- run: uv run coverage html --title "Coverage for ${{ github.sha }}"
- name: Store coverage HTML
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: coverage-html
path: htmlcov
include-hidden-files: true
- run: coverage report --fail-under=100
- run: uv run coverage report --fail-under=100
# https://github.com/marketplace/actions/alls-green#why
check: # This job does nothing and is only used for the branch protection

View File

@@ -5,9 +5,6 @@ on:
- cron: "0 12 1 * *"
workflow_dispatch:
env:
UV_SYSTEM_PYTHON: 1
jobs:
topic-repos:
if: github.repository_owner == 'fastapi'
@@ -23,18 +20,17 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
version: "0.4.15"
enable-cache: true
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install GitHub Actions dependencies
run: uv pip install -r requirements-github-actions.txt
run: uv sync --locked --no-dev --group github-actions
- name: Update Topic Repos
run: python ./scripts/topic_repos.py
run: uv run ./scripts/topic_repos.py
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }}

View File

@@ -30,9 +30,11 @@ on:
type: string
required: false
default: ""
env:
UV_SYSTEM_PYTHON: 1
commit_in_place:
description: Commit changes directly instead of making a PR
type: boolean
required: false
default: false
jobs:
langs:
@@ -45,20 +47,20 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: uv pip install -r requirements-github-actions.txt -r requirements-translations.txt
run: uv sync --locked --no-dev --group github-actions --group translations
- name: Export Language Codes
id: show-langs
run: |
echo "langs=$(python ./scripts/translate.py llm-translatable-json)" >> $GITHUB_OUTPUT
echo "commands=$(python ./scripts/translate.py commands-json)" >> $GITHUB_OUTPUT
echo "langs=$(uv run ./scripts/translate.py llm-translatable-json)" >> $GITHUB_OUTPUT
echo "commands=$(uv run ./scripts/translate.py commands-json)" >> $GITHUB_OUTPUT
env:
LANGUAGE: ${{ github.event.inputs.language }}
COMMAND: ${{ github.event.inputs.command }}
@@ -84,15 +86,15 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: uv pip install -r requirements-github-actions.txt -r requirements-translations.txt
run: uv sync --locked --no-dev --group github-actions --group translations
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
@@ -104,11 +106,12 @@ jobs:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: FastAPI Translate
run: |
python ./scripts/translate.py ${{ matrix.command }}
python ./scripts/translate.py make-pr
uv run ./scripts/translate.py ${{ matrix.command }}
uv run ./scripts/translate.py make-pr
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_TRANSLATIONS }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
LANGUAGE: ${{ matrix.lang }}
EN_PATH: ${{ github.event.inputs.en_path }}
COMMAND: ${{ matrix.command }}
COMMIT_IN_PLACE: ${{ github.event.inputs.commit_in_place }}

3
.gitignore vendored
View File

@@ -29,7 +29,4 @@ archive.zip
# macOS
.DS_Store
# Ignore while the setup still depends on requirements.txt files
uv.lock
.codspeed

View File

@@ -6,6 +6,7 @@ repos:
hooks:
- id: check-added-large-files
args: ['--maxkb=750']
exclude: ^uv.lock$
- id: check-toml
- id: check-yaml
args:
@@ -57,3 +58,9 @@ repos:
entry: uv run ./scripts/docs.py ensure-non-translated
files: ^docs/(?!en/).*|^scripts/docs\.py$
pass_filenames: false
- id: fix-translations
language: unsupported
name: fix translations
entry: uv run ./scripts/translation_fixer.py fix-pages
files: ^docs/(?!en/).*/docs/.*\.md$

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.11

View File

@@ -6,44 +6,20 @@ First, you might want to see the basic ways to [help FastAPI and get help](help-
If you already cloned the <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">fastapi repository</a> and you want to deep dive in the code, here are some guidelines to set up your environment.
### Virtual environment
Follow the instructions to create and activate a [virtual environment](virtual-environments.md){.internal-link target=_blank} for the internal code of `fastapi`.
### Install requirements
After activating the environment, install the required packages:
//// tab | `pip`
Create a virtual environment and install the required packages with <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
<div class="termy">
```console
$ pip install -r requirements.txt
$ uv sync
---> 100%
```
</div>
////
//// tab | `uv`
If you have <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>
////
It will install all the dependencies and your local FastAPI in your local environment.
### Using your local FastAPI
@@ -56,9 +32,9 @@ That way, you don't have to "install" your local version to be able to test ever
/// note | Technical Details
This only happens when you install using this included `requirements.txt` instead of running `pip install fastapi` directly.
This only happens when you install using `uv sync` instead of running `pip install fastapi` directly.
That is because inside the `requirements.txt` file, the local version of FastAPI is marked to be installed in "editable" mode, with the `-e` option.
That is because `uv sync` will install the local version of FastAPI in "editable" mode by default.
///
@@ -201,252 +177,81 @@ as Uvicorn by default will use the port `8000`, the documentation on port `8008`
### Translations
/// warning | Attention
**Update on Translations**
We're updating the way we handle documentation translations.
Until now, we invited community members to translate pages via pull requests, which were then reviewed by at least two native speakers. While this has helped bring FastAPI to many more users, weve also run into several challenges - some languages have only a few translated pages, others are outdated and hard to maintain over time.
To improve this, were working on automation tools 🤖 to manage translations more efficiently. Once ready, documentation will be machine-translated and still reviewed by at least two native speakers ✅ before publishing. This will allow us to keep translations up-to-date while reducing the review burden on maintainers.
Whats changing now:
* 🚫 Were no longer accepting new community-submitted translation PRs.
* ⏳ Existing open PRs will be reviewed and can still be merged if completed within the next 3 weeks (since July 11 2025).
* 🌐 In the future, we will only support languages where at least three active native speakers are available to review and maintain translations.
This transition will help us keep translations more consistent and timely while better supporting our contributors 🙌. Thank you to everyone who has contributed so far — your help has been invaluable! 💖
///
Help with translations is VERY MUCH appreciated! And it can't be done without the help from the community. 🌎 🚀
Here are the steps to help with translations.
#### Tips and guidelines
#### Review Translation PRs
Translation pull requests are made by LLMs guided with prompts designed by the FastAPI team together with the community of native speakers for each supported language.
These translations are normally still reviewed by native speakers, and here's where you can help!
* Check the currently <a href="https://github.com/fastapi/fastapi/pulls" class="external-link" target="_blank">existing pull requests</a> for your language. You can filter the pull requests by the ones with the label for your language. For example, for Spanish, the label is <a href="https://github.com/fastapi/fastapi/pulls?q=is%3Aopen+sort%3Aupdated-desc+label%3Alang-es+label%3Aawaiting-review" class="external-link" target="_blank">`lang-es`</a>.
* Review those pull requests, requesting changes or approving them. For the languages I don't speak, I'll wait for several others to review the translation before merging.
* When reviewing a pull request, it's better not to suggest changes in the same pull request, because it is LLM generated, and it won't be possible to make sure that small individual changes are replicated in other similar sections, or that they are preserved when translating the same content again.
* Instead of adding suggestions to the translation PR, make the suggestions to the LLM prompt file for that language, in a new PR. For example, for Spanish, the LLM prompt file is at: <a href="https://github.com/fastapi/fastapi/blob/master/docs/es/llm-prompt.md" class="external-link" target="_blank">`docs/es/llm-prompt.md`</a>.
/// tip
You can <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request" class="external-link" target="_blank">add comments with change suggestions</a> to existing pull requests.
Check the docs about <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-reviews" class="external-link" target="_blank">adding a pull request review</a> to approve it or request changes.
///
#### Subscribe to Notifications for Your Language
* Check if there's a <a href="https://github.com/fastapi/fastapi/discussions/categories/translations" class="external-link" target="_blank">GitHub Discussion</a> to coordinate translations for your language. You can subscribe to it, and when there's a new pull request to review, an automatic comment will be added to the discussion.
* If you translate pages, add a single pull request per page translated. That will make it much easier for others to review it.
* To check the 2-letter code for the language you want to translate, you can use the table <a href="https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" class="external-link" target="_blank">List of ISO 639-1 codes</a>.
#### Existing language
Let's say you want to translate a page for a language that already has translations for some pages, like Spanish.
In the case of Spanish, the 2-letter code is `es`. So, the directory for Spanish translations is located at `docs/es/`.
/// tip
The main ("official") language is English, located at `docs/en/`.
///
Now run the live server for the docs in Spanish:
<div class="termy">
```console
// Use the command "live" and pass the language code as a CLI argument
$ python ./scripts/docs.py live es
<span style="color: green;">[INFO]</span> Serving on http://127.0.0.1:8008
<span style="color: green;">[INFO]</span> Start watching changes
<span style="color: green;">[INFO]</span> Start detecting changes
```
</div>
/// tip
Alternatively, you can perform the same steps that scripts does manually.
Go into the language directory, for the Spanish translations it's at `docs/es/`:
```console
$ cd docs/es/
```
Then run `mkdocs` in that directory:
```console
$ mkdocs serve --dev-addr 127.0.0.1:8008
```
///
Now you can go to <a href="http://127.0.0.1:8008" class="external-link" target="_blank">http://127.0.0.1:8008</a> and see your changes live.
You will see that every language has all the pages. But some pages are not translated and have an info box at the top, about the missing translation.
Now let's say that you want to add a translation for the section [Features](features.md){.internal-link target=_blank}.
* Copy the file at:
```
docs/en/docs/features.md
```
* Paste it in exactly the same location but for the language you want to translate, e.g.:
```
docs/es/docs/features.md
```
/// tip
Notice that the only change in the path and file name is the language code, from `en` to `es`.
///
If you go to your browser you will see that now the docs show your new section (the info box at the top is gone). 🎉
Now you can translate it all and see how it looks as you save the file.
#### Don't Translate these Pages
🚨 Don't translate:
* Files under `reference/`
* `release-notes.md`
* `fastapi-people.md`
* `external-links.md`
* `newsletter.md`
* `management-tasks.md`
* `management.md`
* `contributing.md`
Some of these files are updated very frequently and a translation would always be behind, or they include the main content from English source files, etc.
#### Request a New Language
Let's say that you want to request translations for a language that is not yet translated, not even some pages. For example, Latin.
If there is no discussion for that language, you can start by requesting the new language. For that, you can follow these steps:
* The first step would be for you to find other 2 people that would be willing to be reviewing translation PRs for that language with you.
* Once there are at least 3 people that would be willing to commit to help maintain that language, you can continue the next steps.
* Create a new discussion following the template.
* Get a few native speakers to comment on the discussion and commit to help review translations for that language.
* Tag the other 2 people that will help with the language, and ask them to confirm there they will help.
Once there are several people in the discussion, the FastAPI team can evaluate it and can make it an official translation.
Then the docs will be automatically translated using AI, and the team of native speakers can review the translation, and help tweak the AI prompts.
Then the docs will be automatically translated using LLMs, and the team of native speakers can review the translation, and help tweak the LLM prompts.
Once there's a new translation, for example if docs are updated or there's a new section, there will be a comment in the same discussion with the link to the new translation to review.
#### New Language
## Automated Code and AI
/// note
You are encouraged to use all the tools you want to do your work and contribute as efficiently as possible, this includes AI (LLM) tools, etc. Nevertheless, contributions should have meaningful human intervention, judgement, context, etc.
These steps will be performed by the FastAPI team.
If the **human effort** put in a PR, e.g. writing LLM prompts, is **less** than the **effort we would need to put** to **review it**, please **don't** submit the PR.
///
Think of it this way: we can already write LLM prompts or run automated tools ourselves, and that would be faster than reviewing external PRs.
Checking the link from above (List of ISO 639-1 codes), you can see that the 2-letter code for Latin is `la`.
### Closing Automated and AI PRs
Now you can create a new directory for the new language, running the following script:
If we see PRs that seem AI generated or automated in similar ways, we'll flag them and close them.
<div class="termy">
The same applies to comments and descriptions, please don't copy paste the content generated by an LLM.
```console
// Use the command new-lang, pass the language code as a CLI argument
$ python ./scripts/docs.py new-lang la
### Human Effort Denial of Service
Successfully initialized: docs/la
```
Using automated tools and AI to submit PRs or comments that we have to carefully review and handle would be the equivalent of a <a href="https://en.wikipedia.org/wiki/Denial-of-service_attack" class="external-link" target="_blank">Denial-of-service attack</a> on our human effort.
</div>
It would be very little effort from the person submitting the PR (an LLM prompt) that generates a large amount of effort on our side (carefully reviewing code).
Now you can check in your code editor the newly created directory `docs/la/`.
Please don't do that.
That command created a file `docs/la/mkdocs.yml` with a simple config that inherits everything from the `en` version:
We'll need to block accounts that spam us with repeated automated PRs or comments.
```yaml
INHERIT: ../en/mkdocs.yml
```
### Use Tools Wisely
/// tip
As Uncle Ben said:
You could also simply create that file with those contents manually.
<blockquote>
With great <strike>power</strike> <strong>tools</strong> comes great responsibility.
</blockquote>
///
Avoid inadvertently doing harm.
That command also created a dummy file `docs/la/index.md` for the main page, you can start by translating that one.
You can continue with the previous instructions for an "Existing Language" for that process.
You can make the first pull request with those two files, `docs/la/mkdocs.yml` and `docs/la/index.md`. 🎉
#### Preview the result
As already mentioned above, you can use the `./scripts/docs.py` with the `live` command to preview the results (or `mkdocs serve`).
Once you are done, you can also test it all as it would look online, including all the other languages.
To do that, first build all the docs:
<div class="termy">
```console
// Use the command "build-all", this will take a bit
$ python ./scripts/docs.py build-all
Building docs for: en
Building docs for: es
Successfully built docs for: es
```
</div>
This builds all those independent MkDocs sites for each language, combines them, and generates the final output at `./site/`.
Then you can serve that with the command `serve`:
<div class="termy">
```console
// Use the command "serve" after running "build-all"
$ python ./scripts/docs.py serve
Warning: this is a very simple server. For development, use mkdocs serve instead.
This is here only to preview a site with translations already built.
Make sure you run the build-all command first.
Serving at: http://127.0.0.1:8008
```
</div>
#### Translation specific tips and guidelines
* Translate only the Markdown documents (`.md`). Do not translate the code examples at `./docs_src`.
* In code blocks within the Markdown document, translate comments (`# a comment`), but leave the rest unchanged.
* Do not change anything enclosed in "``" (inline code).
* In lines starting with `///` translate only the text part after `|`. Leave the rest unchanged.
* You can translate info boxes like `/// warning` with for example `/// warning | Achtung`. But do not change the word immediately after the `///`, it determines the color of the info box.
* Do not change the paths in links to images, code files, Markdown documents.
* However, when a Markdown document is translated, the `#hash-parts` in links to its headings may change. Update these links if possible.
* Search for such links in the translated document using the regex `#[^# ]`.
* Search in all documents already translated into your language for `your-translated-document.md`. For example VS Code has an option "Edit" -> "Find in Files".
* When translating a document, do not "pre-translate" `#hash-parts` that link to headings in untranslated documents.
You have amazing tools at hand, use them wisely to help effectively.

View File

@@ -196,31 +196,11 @@ They have contributed source code, documentation, etc. 📦
There are hundreds of other contributors, you can see them all in the <a href="https://github.com/fastapi/fastapi/graphs/contributors" class="external-link" target="_blank">FastAPI GitHub Contributors page</a>. 👷
## Top Translators
These are the **Top Translators**. 🌐
These users have created the most Pull Requests with [translations to other languages](contributing.md#translations){.internal-link target=_blank} that have been *merged*.
<div class="user-list user-list-center">
{% for user in (translators.values() | list)[:50] %}
{% if user.login not in skip_users %}
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Translations: {{ user.count }}</div></div>
{% endif %}
{% endfor %}
</div>
## Top Translation Reviewers
These users are the **Top Translation Reviewers**. 🕵️
I only speak a few languages (and not very well 😅). So, the reviewers are the ones that have the [**power to approve translations**](contributing.md#translations){.internal-link target=_blank} of the documentation. Without them, there wouldn't be documentation in several other languages.
Translation reviewers have the [**power to approve translations**](contributing.md#translations){.internal-link target=_blank} of the documentation. Without them, there wouldn't be documentation in several other languages.
<div class="user-list user-list-center">
{% for user in (translation_reviewers.values() | list)[:50] %}

View File

@@ -74,7 +74,7 @@ Make sure you use a supported label from the <a href="https://github.com/tiangol
* `refactor`: Refactors
* This is normally for changes to the internal code that don't change the behavior. Normally it improves maintainability, or enables future features, etc.
* `upgrade`: Upgrades
* This is for upgrades to direct dependencies from the project, or extra optional dependencies, normally in `pyproject.toml`. So, things that would affect final users, they would end up receiving the upgrade in their code base once they update. But this is not for upgrades to internal dependencies used for development, testing, docs, etc. Those internal dependencies, normally in `requirements.txt` files or GitHub Action versions should be marked as `internal`, not `upgrade`.
* This is for upgrades to direct dependencies from the project, or extra optional dependencies, normally in `pyproject.toml`. So, things that would affect final users, they would end up receiving the upgrade in their code base once they update. But this is not for upgrades to internal dependencies used for development, testing, docs, etc. Those internal dependencies or GitHub Action versions should be marked as `internal`, not `upgrade`.
* `docs`: Docs
* Changes in docs. This includes updating the docs, fixing typos. But it doesn't include changes to translations.
* You can normally quickly detect it by going to the "Files changed" tab in the PR and checking if the updated file(s) starts with `docs/en/docs`. The original version of the docs is always in English, so in `docs/en/docs`.
@@ -106,135 +106,25 @@ This way, we can notice when there are new translations ready, because they have
## Merge Translation PRs
For Spanish, as I'm a native speaker and it's a language close to me, I will give it a final review myself and in most cases tweak the PR a bit before merging it.
Translations are generated automatically with LLMs and scripts.
For the other languages, confirm that:
There's one GitHub Action that can be manually run to add or update translations for a language: <a href="https://github.com/fastapi/fastapi/actions/workflows/translate.yml" class="external-link" target="_blank">`translate.yml`</a>.
* The title is correct following the instructions above.
For these language translation PRs, confirm that:
* The PR was automated (authored by @tiangolo), not made by another user.
* It has the labels `lang-all` and `lang-{lang code}`.
* The PR changes only one Markdown file adding a translation.
* Or in some cases, at most two files, if they are small, for the same language, and people reviewed them.
* If it's the first translation for that language, it will have additional `mkdocs.yml` files, for those cases follow the instructions below.
* The PR doesn't add any additional or extraneous files.
* The translation seems to have a similar structure as the original English file.
* The translation doesn't seem to change the original content, for example with obvious additional documentation sections.
* The translation doesn't use different Markdown structures, for example adding HTML tags when the original didn't have them.
* The "admonition" sections, like `tip`, `info`, etc. are not changed or translated. For example:
```
/// tip
This is a tip.
///
```
looks like this:
/// tip
This is a tip.
///
...it could be translated as:
```
/// tip
Esto es un consejo.
///
```
...but needs to keep the exact `tip` keyword. If it was translated to `consejo`, like:
```
/// consejo
Esto es un consejo.
///
```
it would change the style to the default one, it would look like:
/// consejo
Esto es un consejo.
///
Those don't have to be translated, but if they are, they need to be written as:
```
/// tip | consejo
Esto es un consejo.
///
```
Which looks like:
/// tip | consejo
Esto es un consejo.
///
## First Translation PR
When there's a first translation for a language, it will have a `docs/{lang code}/docs/index.md` translated file and a `docs/{lang code}/mkdocs.yml`.
For example, for Bosnian, it would be:
* `docs/bs/docs/index.md`
* `docs/bs/mkdocs.yml`
The `mkdocs.yml` file will have only the following content:
```YAML
INHERIT: ../en/mkdocs.yml
```
The language code would normally be in the <a href="https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes" class="external-link" target="_blank">ISO 639-1 list of language codes</a>.
In any case, the language code should be in the file <a href="https://github.com/fastapi/fastapi/blob/master/docs/language_names.yml" class="external-link" target="_blank">docs/language_names.yml</a>.
There won't be yet a label for the language code, for example, if it was Bosnian, there wouldn't be a `lang-bs`. Before creating the label and adding it to the PR, create the GitHub Discussion:
* Go to the <a href="https://github.com/fastapi/fastapi/discussions/categories/translations" class="external-link" target="_blank">Translations GitHub Discussions</a>
* Create a new discussion with the title `Bosnian Translations` (or the language name in English)
* A description of:
```Markdown
## Bosnian translations
This is the issue to track translations of the docs to Bosnian. 🚀
Here are the [PRs to review with the label `lang-bs`](https://github.com/fastapi/fastapi/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc+label%3Alang-bs+label%3A%22awaiting-review%22). 🤓
```
Update "Bosnian" with the new language.
And update the search link to point to the new language label that will be created, like `lang-bs`.
Create and add the label to that new Discussion just created, like `lang-bs`.
Then go back to the PR, and add the label, like `lang-bs`, and `lang-all` and `awaiting-review`.
Now the GitHub action will automatically detect the label `lang-bs` and will post in that Discussion that this PR is waiting to be reviewed.
* If the PR is approved by at least one native speaker, you can merge it.
## Review PRs
If a PR doesn't explain what it does or why, ask for more information.
* If a PR doesn't explain what it does or why, if it seems like it could be useful, ask for more information. Otherwise, feel free to close it.
A PR should have a specific use case that it is solving.
* If a PR seems to be spam, meaningless, only to change statistics (to appear as "contributor") or similar, you can simply mark it as `invalid`, and it will be automatically closed.
* If a PR seems to be AI generated, and seems like reviewing it would take more time from you than the time it took to write the prompt, mark it as `maybe-ai`, and it will be automatically closed.
* A PR should have a specific use case that it is solving.
* If the PR is for a feature, it should have docs.
* Unless it's a feature we want to discourage, like support for a corner case that we don't want users to use.
@@ -254,27 +144,12 @@ Every month, a GitHub Action updates the FastAPI People data. Those PRs look lik
If the tests are passing, you can merge it right away.
## External Links PRs
When people add external links they edit this file <a href="https://github.com/fastapi/fastapi/blob/master/docs/en/data/external_links.yml" class="external-link" target="_blank">external_links.yml</a>.
* Make sure the new link is in the correct category (e.g. "Podcasts") and language (e.g. "Japanese").
* A new link should be at the top of its list.
* The link URL should work (it should not return a 404).
* The content of the link should be about FastAPI.
* The new addition should have these fields:
* `author`: The name of the author.
* `link`: The URL with the content.
* `title`: The title of the link (the title of the article, podcast, etc).
After checking all these things and ensuring the PR has the right labels, you can merge it.
## Dependabot PRs
Dependabot will create PRs to update dependencies for several things, and those PRs all look similar, but some are way more delicate than others.
* If the PR is for a direct dependency, so, Dependabot is modifying `pyproject.toml`, **don't merge it**. 😱 Let me check it first. There's a good chance that some additional tweaks or updates are needed.
* If the PR updates one of the internal dependencies, for example it's modifying `requirements.txt` files, or GitHub Action versions, if the tests are passing, the release notes (shown in a summary in the PR) don't show any obvious potential breaking change, you can merge it. 😎
* If the PR is for a direct dependency, so, Dependabot is modifying `pyproject.toml` in the main dependencies, **don't merge it**. 😱 Let me check it first. There's a good chance that some additional tweaks or updates are needed.
* If the PR updates one of the internal dependencies, for example the group `dev` in `pyproject.toml`, or GitHub Action versions, if the tests are passing, the release notes (shown in a summary in the PR) don't show any obvious potential breaking change, you can merge it. 😎
## Mark GitHub Discussions Answers

View File

@@ -7,14 +7,35 @@ hide:
## Latest Changes
### Docs
* 📝 Add contribution instructions about LLM generated code and comments and automated tools for PRs. PR [#14706](https://github.com/fastapi/fastapi/pull/14706) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update docs for management tasks. PR [#14705](https://github.com/fastapi/fastapi/pull/14705) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update docs about managing translations. PR [#14704](https://github.com/fastapi/fastapi/pull/14704) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update docs for contributing with translations. PR [#14701](https://github.com/fastapi/fastapi/pull/14701) by [@tiangolo](https://github.com/tiangolo).
* 📝 Specify language code for code block. PR [#14656](https://github.com/fastapi/fastapi/pull/14656) by [@YuriiMotov](https://github.com/YuriiMotov).
### Translations
* 🌐 Update translations for ko (update-outdated). PR [#14589](https://github.com/fastapi/fastapi/pull/14589) by [@tiangolo](https://github.com/tiangolo).
* 🌐 Update translations for uk (update-outdated). PR [#14587](https://github.com/fastapi/fastapi/pull/14587) by [@tiangolo](https://github.com/tiangolo).
* 🌐 Update translations for es (update-outdated). PR [#14686](https://github.com/fastapi/fastapi/pull/14686) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Add LLM prompt file for Turkish, generated from the existing translations. PR [#14547](https://github.com/fastapi/fastapi/pull/14547) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Add LLM prompt file for Traditional Chinese, generated from the existing translations. PR [#14550](https://github.com/fastapi/fastapi/pull/14550) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Add LLM prompt file for Simplified Chinese, generated from the existing translations. PR [#14549](https://github.com/fastapi/fastapi/pull/14549) by [@tiangolo](https://github.com/tiangolo).
### Internal
* ⬆ Bump actions/cache from 4 to 5. PR [#14511](https://github.com/fastapi/fastapi/pull/14511) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump actions/upload-artifact from 5 to 6. PR [#14525](https://github.com/fastapi/fastapi/pull/14525) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump actions/download-artifact from 6 to 7. PR [#14526](https://github.com/fastapi/fastapi/pull/14526) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 👷 Tweak CI input names. PR [#14688](https://github.com/fastapi/fastapi/pull/14688) by [@tiangolo](https://github.com/tiangolo).
* 🔨 Refactor translation script to allow committing in place. PR [#14687](https://github.com/fastapi/fastapi/pull/14687) by [@tiangolo](https://github.com/tiangolo).
* 🐛 Fix translation script path. PR [#14685](https://github.com/fastapi/fastapi/pull/14685) by [@tiangolo](https://github.com/tiangolo).
* ✅ Enable tests in CI for scripts. PR [#14684](https://github.com/fastapi/fastapi/pull/14684) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Add pre-commit local script to fix language translations. PR [#14683](https://github.com/fastapi/fastapi/pull/14683) by [@tiangolo](https://github.com/tiangolo).
* ⬆️ Migrate to uv. PR [#14676](https://github.com/fastapi/fastapi/pull/14676) by [@DoctorJohn](https://github.com/DoctorJohn).
* 🔨 Add LLM translations tool fixer. PR [#14652](https://github.com/fastapi/fastapi/pull/14652) by [@YuriiMotov](https://github.com/YuriiMotov).
* 👥 Update FastAPI People - Sponsors. PR [#14626](https://github.com/fastapi/fastapi/pull/14626) by [@tiangolo](https://github.com/tiangolo).
* 👥 Update FastAPI GitHub topic repositories. PR [#14630](https://github.com/fastapi/fastapi/pull/14630) by [@tiangolo](https://github.com/tiangolo).
* 👥 Update FastAPI People - Contributors and Translators. PR [#14625](https://github.com/fastapi/fastapi/pull/14625) by [@tiangolo](https://github.com/tiangolo).

View File

@@ -56,7 +56,7 @@ from app.routers import items
The same file structure with comments:
```
```bash
.
├── app # "app" is a Python package
│   ├── __init__.py # this file makes "app" a "Python package"

View File

@@ -317,12 +317,14 @@ extra:
name: de - Deutsch
- link: /es/
name: es - español
- link: /fr/
name: fr - français
- link: /ko/
name: ko - 한국어
- link: /pt/
name: pt - português
- link: /ru/
name: ru - русский язык
- link: /uk/
name: uk - українська мова
extra_css:
- css/termynal.css
- css/custom.css

View File

@@ -1,17 +1,17 @@
# Archivo de prueba de LLM { #llm-test-file }
Este documento prueba si el <abbr title="Large Language Model Modelo de lenguaje grande">LLM</abbr>, que traduce la documentación, entiende el `general_prompt` en `scripts/translate.py` y el prompt específico del idioma en `docs/{language code}/llm-prompt.md`. El prompt específico del idioma se agrega al final de `general_prompt`.
Este documento prueba si el <abbr title="Large Language Model - Modelo de lenguaje grande">LLM</abbr>, que traduce la documentación, entiende el `general_prompt` en `scripts/translate.py` y el prompt específico del idioma en `docs/{language code}/llm-prompt.md`. El prompt específico del idioma se agrega al final de `general_prompt`.
Las pruebas añadidas aquí serán vistas por todas las personas que diseñan prompts específicos del idioma.
Úsalo de la siguiente manera:
* Ten un prompt específico del idioma `docs/{language code}/llm-prompt.md`.
* Ten un prompt específico del idioma - `docs/{language code}/llm-prompt.md`.
* Haz una traducción fresca de este documento a tu idioma destino (mira, por ejemplo, el comando `translate-page` de `translate.py`). Esto creará la traducción en `docs/{language code}/docs/_llm-test.md`.
* Comprueba si todo está bien en la traducción.
* Revisa si las cosas están bien en la traducción.
* Si es necesario, mejora tu prompt específico del idioma, el prompt general, o el documento en inglés.
* Luego corrige manualmente los problemas restantes en la traducción para que sea una buena traducción.
* Vuelve a traducir, teniendo la buena traducción en su lugar. El resultado ideal sería que el LLM ya no hiciera cambios a la traducción. Eso significa que el prompt general y tu prompt específico del idioma están tan bien como pueden estar (a veces hará algunos cambios aparentemente aleatorios; la razón es que <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">los LLMs no son algoritmos deterministas</a>).
* Vuelve a traducir, teniendo la buena traducción en su lugar. El resultado ideal sería que el LLM ya no hiciera cambios a la traducción. Eso significa que el prompt general y tu prompt específico del idioma están tan bien como pueden estar (A veces hará algunos cambios aparentemente aleatorios; la razón es que <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">los LLMs no son algoritmos deterministas</a>).
Las pruebas:
@@ -23,7 +23,7 @@ Este es un fragmento de código: `foo`. Y este es otro fragmento de código: `ba
////
//// tab | Información
//// tab | Info
El contenido de los fragmentos de código debe dejarse tal cual.
@@ -45,7 +45,7 @@ El LLM probablemente traducirá esto mal. Lo interesante es si mantiene la tradu
////
//// tab | Información
//// tab | Info
La persona que diseña el prompt puede elegir si quiere convertir comillas neutras a comillas tipográficas. También está bien dejarlas como están.
@@ -67,7 +67,7 @@ Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you
////
//// tab | Información
//// tab | Info
... Sin embargo, las comillas dentro de fragmentos de código deben quedarse tal cual.
@@ -112,7 +112,7 @@ works(foo="bar") # Esto funciona 🎉
////
//// tab | Información
//// tab | Info
El código en bloques de código no debe modificarse, con la excepción de los comentarios.
@@ -154,7 +154,7 @@ Algo de texto
////
//// tab | Información
//// tab | Info
Las pestañas y los bloques `Info`/`Note`/`Warning`/etc. deben tener la traducción de su título añadida después de una barra vertical (`|`).
@@ -181,7 +181,7 @@ El texto del enlace debe traducirse, la dirección del enlace debe apuntar a la
////
//// tab | Información
//// tab | Info
Los enlaces deben traducirse, pero su dirección debe permanecer sin cambios. Una excepción son los enlaces absolutos a páginas de la documentación de FastAPI. En ese caso deben enlazar a la traducción.
@@ -197,10 +197,10 @@ Aquí algunas cosas envueltas en elementos HTML "abbr" (algunas son inventadas):
### El abbr da una frase completa { #the-abbr-gives-a-full-phrase }
* <abbr title="Getting Things Done Hacer las cosas">GTD</abbr>
* <abbr title="less than menor que"><code>lt</code></abbr>
* <abbr title="XML Web Token Token web XML">XWT</abbr>
* <abbr title="Parallel Server Gateway Interface Interfaz de pasarela de servidor paralela">PSGI</abbr>
* <abbr title="Getting Things Done - Hacer las cosas">GTD</abbr>
* <abbr title="less than - menor que"><code>lt</code></abbr>
* <abbr title="XML Web Token - Token web XML">XWT</abbr>
* <abbr title="Parallel Server Gateway Interface - Interfaz de pasarela de servidor paralela">PSGI</abbr>
### El abbr da una explicación { #the-abbr-gives-an-explanation }
@@ -209,12 +209,12 @@ Aquí algunas cosas envueltas en elementos HTML "abbr" (algunas son inventadas):
### El abbr da una frase completa y una explicación { #the-abbr-gives-a-full-phrase-and-an-explanation }
* <abbr title="Mozilla Developer Network Red de Desarrolladores de Mozilla: documentación para desarrolladores, escrita por la gente de Firefox">MDN</abbr>
* <abbr title="Input/Output Entrada/Salida: lectura o escritura de disco, comunicaciones de red.">I/O</abbr>.
* <abbr title="Mozilla Developer Network - Red de Desarrolladores de Mozilla: documentación para desarrolladores, escrita por la gente de Firefox">MDN</abbr>
* <abbr title="Input/Output: lectura o escritura de disco, comunicaciones de red.">I/O</abbr>.
////
//// tab | Información
//// tab | Info
Los atributos "title" de los elementos "abbr" se traducen siguiendo instrucciones específicas.
@@ -242,7 +242,7 @@ Hola de nuevo.
////
//// tab | Información
//// tab | Info
La única regla estricta para los encabezados es que el LLM deje la parte del hash dentro de llaves sin cambios, lo que asegura que los enlaces no se rompan.
@@ -355,7 +355,7 @@ Para instrucciones específicas del idioma, mira p. ej. la sección `### Heading
* los headers
* el header de autorización
* el header `Authorization`
* el header Forwarded
* el header forwarded
* el sistema de inyección de dependencias
* la dependencia
@@ -368,7 +368,7 @@ Para instrucciones específicas del idioma, mira p. ej. la sección `### Heading
* paralelismo
* multiprocesamiento
* la variable de entorno
* la env var
* la variable de entorno
* el `PATH`
* la variable `PATH`
@@ -433,7 +433,7 @@ Para instrucciones específicas del idioma, mira p. ej. la sección `### Heading
* el motor de plantillas
* la anotación de tipos
* la anotación de tipos
* las anotaciones de tipos
* el worker del servidor
* el worker de Uvicorn
@@ -468,7 +468,7 @@ Para instrucciones específicas del idioma, mira p. ej. la sección `### Heading
* el ítem
* el paquete
* el lifespan
* el bloqueo
* el lock
* el middleware
* la aplicación móvil
* el módulo
@@ -494,7 +494,7 @@ Para instrucciones específicas del idioma, mira p. ej. la sección `### Heading
////
//// tab | Información
//// tab | Info
Esta es una lista no completa y no normativa de términos (mayormente) técnicos vistos en la documentación. Puede ayudar a la persona que diseña el prompt a identificar para qué términos el LLM necesita una mano. Por ejemplo cuando sigue revirtiendo una buena traducción a una traducción subóptima. O cuando tiene problemas conjugando/declinando un término en tu idioma.

View File

@@ -10,7 +10,7 @@ Si no eres un "experto" en OpenAPI, probablemente no necesites esto.
Puedes establecer el `operationId` de OpenAPI para ser usado en tu *path operation* con el parámetro `operation_id`.
Tienes que asegurarte de que sea único para cada operación.
Tendrías que asegurarte de que sea único para cada operación.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
@@ -46,7 +46,7 @@ Para excluir una *path operation* del esquema OpenAPI generado (y por lo tanto,
Puedes limitar las líneas usadas del docstring de una *path operation function* para OpenAPI.
Añadir un `\f` (un carácter de separación de página escapado) hace que **FastAPI** trunque la salida usada para OpenAPI en este punto.
Añadir un `\f` (un carácter "form feed" escapado) hace que **FastAPI** trunque la salida usada para OpenAPI en este punto.
No aparecerá en la documentación, pero otras herramientas (como Sphinx) podrán usar el resto.
@@ -141,9 +141,9 @@ Podrías hacer eso con `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el cuerpo del request ni siquiera se <abbr title="convertido de algún formato plano, como bytes, a objetos de Python">parse</abbr> como JSON, se lee directamente como `bytes`, y la función `magic_data_reader()` sería la encargada de parsearlo de alguna manera.
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el request body ni siquiera se <abbr title="converted from some plain format, like bytes, into Python objects - convertido de algún formato plano, como bytes, a objetos de Python">parse</abbr> como JSON, se lee directamente como `bytes`, y la función `magic_data_reader()` sería la encargada de parsearlo de alguna manera.
Sin embargo, podemos declarar el esquema esperado para el cuerpo del request.
Sin embargo, podemos declarar el esquema esperado para el request body.
### Tipo de contenido personalizado de OpenAPI { #custom-openapi-content-type }
@@ -153,48 +153,16 @@ Y podrías hacer esto incluso si el tipo de datos en el request no es JSON.
Por ejemplo, en esta aplicación no usamos la funcionalidad integrada de FastAPI para extraer el JSON Schema de los modelos Pydantic ni la validación automática para JSON. De hecho, estamos declarando el tipo de contenido del request como YAML, no JSON:
//// tab | Pydantic v2
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
////
//// tab | Pydantic v1
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1_py39.py hl[15:20, 22] *}
////
/// info | Información
En la versión 1 de Pydantic el método para obtener el JSON Schema para un modelo se llamaba `Item.schema()`, en la versión 2 de Pydantic, el método se llama `Item.model_json_schema()`.
///
Sin embargo, aunque no estamos usando la funcionalidad integrada por defecto, aún estamos usando un modelo Pydantic para generar manualmente el JSON Schema para los datos que queremos recibir en YAML.
Luego usamos el request directamente, y extraemos el cuerpo como `bytes`. Esto significa que FastAPI ni siquiera intentará parsear la carga útil del request como JSON.
Y luego en nuestro código, parseamos ese contenido YAML directamente, y nuevamente estamos usando el mismo modelo Pydantic para validar el contenido YAML:
//// tab | Pydantic v2
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
////
//// tab | Pydantic v1
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1_py39.py hl[24:31] *}
////
/// info | Información
En la versión 1 de Pydantic el método para parsear y validar un objeto era `Item.parse_obj()`, en la versión 2 de Pydantic, el método se llama `Item.model_validate()`.
///
/// tip | Consejo
Aquí reutilizamos el mismo modelo Pydantic.

View File

@@ -46,12 +46,6 @@ $ pip install "fastapi[all]"
</div>
/// info | Información
En Pydantic v1 venía incluido con el paquete principal. Ahora se distribuye como este paquete independiente para que puedas elegir si instalarlo o no si no necesitas esa funcionalidad.
///
### Crear el objeto `Settings` { #create-the-settings-object }
Importa `BaseSettings` de Pydantic y crea una sub-clase, muy similar a un modelo de Pydantic.
@@ -60,31 +54,15 @@ De la misma forma que con los modelos de Pydantic, declaras atributos de clase c
Puedes usar todas las mismas funcionalidades de validación y herramientas que usas para los modelos de Pydantic, como diferentes tipos de datos y validaciones adicionales con `Field()`.
//// tab | Pydantic v2
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
////
//// tab | Pydantic v1
/// info | Información
En Pydantic v1 importarías `BaseSettings` directamente desde `pydantic` en lugar de desde `pydantic_settings`.
///
{* ../../docs_src/settings/tutorial001_pv1_py39.py hl[2,5:8,11] *}
////
/// tip | Consejo
Si quieres algo rápido para copiar y pegar, no uses este ejemplo, usa el último más abajo.
///
Luego, cuando creas una instance de esa clase `Settings` (en este caso, en el objeto `settings`), Pydantic leerá las variables de entorno de una manera indiferente a mayúsculas y minúsculas, por lo que una variable en mayúsculas `APP_NAME` aún será leída para el atributo `app_name`.
Luego, cuando creas un instance de esa clase `Settings` (en este caso, en el objeto `settings`), Pydantic leerá las variables de entorno de una manera indiferente a mayúsculas y minúsculas, por lo que una variable en mayúsculas `APP_NAME` aún será leída para el atributo `app_name`.
Luego convertirá y validará los datos. Así que, cuando uses ese objeto `settings`, tendrás datos de los tipos que declaraste (por ejemplo, `items_per_user` será un `int`).
@@ -110,7 +88,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
/// tip | Consejo
Para establecer múltiples variables de entorno para un solo comando, simplemente sepáralas con un espacio y ponlas todas antes del comando.
Para establecer múltiples env vars para un solo comando, simplemente sepáralas con un espacio y ponlas todas antes del comando.
///
@@ -150,7 +128,7 @@ Proveniente del ejemplo anterior, tu archivo `config.py` podría verse como:
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
Nota que ahora no creamos una instance por defecto `settings = Settings()`.
Nota que ahora no creamos un instance por defecto `settings = Settings()`.
### El archivo principal de la app { #the-main-app-file }
@@ -172,11 +150,11 @@ Y luego podemos requerirlo desde la *path operation function* como una dependenc
### Configuraciones y pruebas { #settings-and-testing }
Luego sería muy fácil proporcionar un objeto de configuraciones diferente durante las pruebas al sobrescribir una dependencia para `get_settings`:
Luego sería muy fácil proporcionar un objeto de configuraciones diferente durante las pruebas al crear una sobrescritura de dependencia para `get_settings`:
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
En la dependencia sobreescrita establecemos un nuevo valor para el `admin_email` al crear el nuevo objeto `Settings`, y luego devolvemos ese nuevo objeto.
En la sobrescritura de dependencia establecemos un nuevo valor para el `admin_email` al crear el nuevo objeto `Settings`, y luego devolvemos ese nuevo objeto.
Luego podemos probar que se está usando.
@@ -215,8 +193,6 @@ APP_NAME="ChimichangApp"
Y luego actualizar tu `config.py` con:
//// tab | Pydantic v2
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
/// tip | Consejo
@@ -225,26 +201,6 @@ El atributo `model_config` se usa solo para configuración de Pydantic. Puedes l
///
////
//// tab | Pydantic v1
{* ../../docs_src/settings/app03_an_py39/config_pv1.py hl[9:10] *}
/// tip | Consejo
La clase `Config` se usa solo para configuración de Pydantic. Puedes leer más en <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>.
///
////
/// info | Información
En la versión 1 de Pydantic la configuración se hacía en una clase interna `Config`, en la versión 2 de Pydantic se hace en un atributo `model_config`. Este atributo toma un `dict`, y para obtener autocompletado y errores en línea, puedes importar y usar `SettingsConfigDict` para definir ese `dict`.
///
Aquí definimos la configuración `env_file` dentro de tu clase Pydantic `Settings`, y establecemos el valor en el nombre del archivo con el archivo dotenv que queremos usar.
### Creando el `Settings` solo una vez con `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
@@ -331,7 +287,7 @@ participant execute as Ejecutar función
end
```
En el caso de nuestra dependencia `get_settings()`, la función ni siquiera toma argumentos, por lo que siempre devolve el mismo valor.
En el caso de nuestra dependencia `get_settings()`, la función ni siquiera toma argumentos, por lo que siempre devuelve el mismo valor.
De esa manera, se comporta casi como si fuera solo una variable global. Pero como usa una función de dependencia, entonces podemos sobrescribirla fácilmente para las pruebas.

View File

@@ -2,21 +2,23 @@
Si tienes una app de FastAPI antigua, podrías estar usando Pydantic versión 1.
FastAPI ha tenido compatibilidad con Pydantic v1 o v2 desde la versión 0.100.0.
FastAPI versión 0.100.0 tenía compatibilidad con Pydantic v1 o v2. Usaba la que tuvieras instalada.
Si tenías instalado Pydantic v2, lo usaba. Si en cambio tenías Pydantic v1, usaba ese.
FastAPI versión 0.119.0 introdujo compatibilidad parcial con Pydantic v1 desde dentro de Pydantic v2 (como `pydantic.v1`), para facilitar la migración a v2.
Pydantic v1 está deprecado y su soporte se eliminará en las próximas versiones de FastAPI, deberías migrar a Pydantic v2. Así obtendrás las funcionalidades, mejoras y correcciones más recientes.
FastAPI 0.126.0 eliminó la compatibilidad con Pydantic v1, aunque siguió soportando `pydantic.v1` por un poquito más de tiempo.
/// warning | Advertencia
Además, el equipo de Pydantic dejó de dar soporte a Pydantic v1 para las versiones más recientes de Python, comenzando con Python 3.14.
El equipo de Pydantic dejó de dar soporte a Pydantic v1 para las versiones más recientes de Python, comenzando con **Python 3.14**.
Esto incluye `pydantic.v1`, que ya no está soportado en Python 3.14 y superiores.
Si quieres usar las funcionalidades más recientes de Python, tendrás que asegurarte de usar Pydantic v2.
///
Si tienes una app de FastAPI antigua con Pydantic v1, aquí te muestro cómo migrarla a Pydantic v2 y las nuevas funcionalidades en FastAPI 0.119.0 para ayudarte con una migración gradual.
Si tienes una app de FastAPI antigua con Pydantic v1, aquí te muestro cómo migrarla a Pydantic v2, y las **funcionalidades en FastAPI 0.119.0** para ayudarte con una migración gradual.
## Guía oficial { #official-guide }
@@ -44,9 +46,9 @@ Después de esto, puedes ejecutar los tests y revisa si todo funciona. Si es as
## Pydantic v1 en v2 { #pydantic-v1-in-v2 }
Pydantic v2 incluye todo lo de Pydantic v1 como un submódulo `pydantic.v1`.
Pydantic v2 incluye todo lo de Pydantic v1 como un submódulo `pydantic.v1`. Pero esto ya no está soportado en versiones por encima de Python 3.13.
Esto significa que puedes instalar la versión más reciente de Pydantic v2 e importar y usar los componentes viejos de Pydantic v1 desde ese submódulo, como si tuvieras instalado el Pydantic v1 antiguo.
Esto significa que puedes instalar la versión más reciente de Pydantic v2 e importar y usar los componentes viejos de Pydantic v1 desde este submódulo, como si tuvieras instalado el Pydantic v1 antiguo.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
@@ -66,7 +68,7 @@ Ten en cuenta que, como el equipo de Pydantic ya no da soporte a Pydantic v1 en
### Pydantic v1 y v2 en la misma app { #pydantic-v1-and-v2-on-the-same-app }
No está soportado por Pydantic tener un modelo de Pydantic v2 con sus propios campos definidos como modelos de Pydantic v1 o viceversa.
**No está soportado** por Pydantic tener un modelo de Pydantic v2 con sus propios campos definidos como modelos de Pydantic v1 o viceversa.
```mermaid
graph TB
@@ -106,7 +108,7 @@ graph TB
style V2Field fill:#f9fff3
```
En algunos casos, incluso es posible tener modelos de Pydantic v1 y v2 en la misma path operation de tu app de FastAPI:
En algunos casos, incluso es posible tener modelos de Pydantic v1 y v2 en la misma **path operation** de tu app de FastAPI:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
@@ -122,12 +124,12 @@ Si necesitas usar algunas de las herramientas específicas de FastAPI para pará
/// tip | Consejo
Primero prueba con `bump-pydantic`; si tus tests pasan y eso funciona, entonces terminaste con un solo comando. ✨
Primero prueba con `bump-pydantic`, si tus tests pasan y eso funciona, entonces terminaste con un solo comando. ✨
///
Si `bump-pydantic` no funciona para tu caso, puedes usar la compatibilidad de modelos Pydantic v1 y v2 en la misma app para hacer la migración a Pydantic v2 de forma gradual.
Si `bump-pydantic` no funciona para tu caso de uso, puedes usar la compatibilidad de modelos Pydantic v1 y v2 en la misma app para hacer la migración a Pydantic v2 de forma gradual.
Podrías primero actualizar Pydantic para usar la última versión 2 y cambiar los imports para usar `pydantic.v1` para todos tus modelos.
Podrías primero actualizar Pydantic para usar la última versión 2, y cambiar los imports para usar `pydantic.v1` para todos tus modelos.
Luego puedes empezar a migrar tus modelos de Pydantic v1 a v2 por grupos, en pasos graduales. 🚶

View File

@@ -1,6 +1,6 @@
# Separación de Esquemas OpenAPI para Entrada y Salida o No { #separate-openapi-schemas-for-input-and-output-or-not }
Al usar **Pydantic v2**, el OpenAPI generado es un poco más exacto y **correcto** que antes. 😎
Desde que se lanzó **Pydantic v2**, el OpenAPI generado es un poco más exacto y **correcto** que antes. 😎
De hecho, en algunos casos, incluso tendrá **dos JSON Schemas** en OpenAPI para el mismo modelo Pydantic, para entrada y salida, dependiendo de si tienen **valores por defecto**.
@@ -85,7 +85,7 @@ Probablemente el caso principal para esto es si ya tienes algún código cliente
En ese caso, puedes desactivar esta funcionalidad en **FastAPI**, con el parámetro `separate_input_output_schemas=False`.
/// info | Información
/// info
El soporte para `separate_input_output_schemas` fue agregado en FastAPI `0.102.0`. 🤓
@@ -100,5 +100,3 @@ Y ahora habrá un único esquema para entrada y salida para el modelo, solo `Ite
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image05.png">
</div>
Este es el mismo comportamiento que en Pydantic v1. 🤓

View File

@@ -93,13 +93,13 @@ Las funcionalidades clave son:
"_Estoy súper emocionado con **FastAPI**. ¡Es tan divertido!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">host del podcast Python Bytes</a></strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> host del podcast</strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"_Honestamente, lo que has construido parece súper sólido y pulido. En muchos aspectos, es lo que quería que **Hug** fuera; es realmente inspirador ver a alguien construir eso._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://github.com/hugapi/hug" target="_blank">creador de Hug</a></strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://github.com/hugapi/hug" target="_blank">Hug</a> creador</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
@@ -117,6 +117,12 @@ Las funcionalidades clave son:
---
## Mini documental de FastAPI { #fastapi-mini-documentary }
Hay un <a href="https://www.youtube.com/watch?v=mpR8ngthqiE" class="external-link" target="_blank">mini documental de FastAPI</a> lanzado a finales de 2025, puedes verlo online:
<a href="https://www.youtube.com/watch?v=mpR8ngthqiE" target="_blank"><img src="https://fastapi.tiangolo.com/img/fastapi-documentary.jpg" alt="FastAPI Mini Documentary"></a>
## **Typer**, el FastAPI de las CLIs { #typer-the-fastapi-of-clis }
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
@@ -268,7 +274,7 @@ Verás la documentación interactiva automática de la API (proporcionada por <a
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Documentación de API Alternativa { #alternative-api-docs }
### Documentación alternativa de la API { #alternative-api-docs }
Y ahora, ve a <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
@@ -276,7 +282,7 @@ Verás la documentación alternativa automática (proporcionada por <a href="htt
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Actualización del Ejemplo { #example-upgrade }
## Actualización del ejemplo { #example-upgrade }
Ahora modifica el archivo `main.py` para recibir un body desde un request `PUT`.
@@ -314,7 +320,7 @@ def update_item(item_id: int, item: Item):
El servidor `fastapi dev` debería recargarse automáticamente.
### Actualización de la Documentación Interactiva de la API { #interactive-api-docs-upgrade }
### Actualización de la documentación interactiva de la API { #interactive-api-docs-upgrade }
Ahora ve a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
@@ -330,7 +336,7 @@ Ahora ve a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_b
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Actualización de la Documentación Alternativa de la API { #alternative-api-docs-upgrade }
### Actualización de la documentación alternativa de la API { #alternative-api-docs-upgrade }
Y ahora, ve a <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
@@ -393,13 +399,13 @@ Volviendo al ejemplo de código anterior, **FastAPI**:
* Validará que haya un `item_id` en el path para requests `GET` y `PUT`.
* Validará que el `item_id` sea del tipo `int` para requests `GET` y `PUT`.
* Si no lo es, el cliente verá un error útil y claro.
* Comprobará si hay un parámetro de query opcional llamado `q` (como en `http://127.0.0.1:8000/items/foo?q=somequery`) para requests `GET`.
* Revisa si hay un parámetro de query opcional llamado `q` (como en `http://127.0.0.1:8000/items/foo?q=somequery`) para requests `GET`.
* Como el parámetro `q` está declarado con `= None`, es opcional.
* Sin el `None` sería requerido (como lo es el body en el caso con `PUT`).
* Para requests `PUT` a `/items/{item_id}`, leerá el body como JSON:
* Comprobará que tiene un atributo requerido `name` que debe ser un `str`.
* Comprobará que tiene un atributo requerido `price` que debe ser un `float`.
* Comprobará que tiene un atributo opcional `is_offer`, que debe ser un `bool`, si está presente.
* Revisa que tiene un atributo requerido `name` que debe ser un `str`.
* Revisa que tiene un atributo requerido `price` que debe ser un `float`.
* Revisa que tiene un atributo opcional `is_offer`, que debe ser un `bool`, si está presente.
* Todo esto también funcionaría para objetos JSON profundamente anidados.
* Convertirá de y a JSON automáticamente.
* Documentará todo con OpenAPI, que puede ser usado por:

View File

@@ -56,7 +56,7 @@ from app.routers import items
La misma estructura de archivos con comentarios:
```
```bash
.
├── app # "app" es un paquete de Python
│   ├── __init__.py # este archivo hace que "app" sea un "paquete de Python"
@@ -185,7 +185,7 @@ El resultado final es que los paths de item son ahora:
* Todos incluirán las `responses` predefinidas.
* Todas estas *path operations* tendrán la lista de `dependencies` evaluadas/ejecutadas antes de ellas.
* Si también declaras dependencias en una *path operation* específica, **también se ejecutarán**.
* Las dependencias del router se ejecutan primero, luego las [dependencias en el decorador](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, y luego las dependencias de parámetros normales.
* Las dependencias del router se ejecutan primero, luego las [`dependencies` en el decorador](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, y luego las dependencias de parámetros normales.
* También puedes agregar [dependencias de `Security` con `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}.
/// tip | Consejo
@@ -214,7 +214,7 @@ Así que usamos un import relativo con `..` para las dependencias:
/// tip | Consejo
Si sabes perfectamente cómo funcionan los imports, continúa a la siguiente sección.
Si sabes perfectamente cómo funcionan los imports, continúa a la siguiente sección abajo.
///
@@ -271,7 +271,7 @@ eso significaría:
Eso se referiría a algún paquete arriba de `app/`, con su propio archivo `__init__.py`, etc. Pero no tenemos eso. Así que, eso lanzaría un error en nuestro ejemplo. 🚨
Pero ahora sabes cómo funciona, para que puedas usar imports relativos en tus propias aplicaciones sin importar cuán complejas sean. 🤓
Pero ahora sabes cómo funciona, para que puedas usar imports relativos en tus propias apps sin importar cuán complejas sean. 🤓
### Agregar algunos `tags`, `responses`, y `dependencies` personalizados { #add-some-custom-tags-responses-and-dependencies }
@@ -283,7 +283,7 @@ Pero aún podemos agregar _más_ `tags` que se aplicarán a una *path operation*
/// tip | Consejo
Esta última *path operation* tendrá la combinación de tags: `["items", "custom"]`.
Esta última path operation tendrá la combinación de tags: `["items", "custom"]`.
Y también tendrá ambas responses en la documentación, una para `404` y otra para `403`.
@@ -301,7 +301,7 @@ Y como la mayor parte de tu lógica ahora vivirá en su propio módulo específi
### Importar `FastAPI` { #import-fastapi }
Importas y creas una clase `FastAPI` como de costumbre.
Importas y creas una clase `FastAPI` como normalmente.
Y podemos incluso declarar [dependencias globales](dependencies/global-dependencies.md){.internal-link target=_blank} que se combinarán con las dependencias para cada `APIRouter`:
@@ -398,7 +398,7 @@ Incluirá todas las rutas de ese router como parte de ella.
En realidad creará internamente una *path operation* para cada *path operation* que fue declarada en el `APIRouter`.
Así, detrás de escena, funcionará como si todo fuera la misma única aplicación.
Así, detrás de escena, funcionará como si todo fuera la misma única app.
///
@@ -430,20 +430,20 @@ Podemos declarar todo eso sin tener que modificar el `APIRouter` original pasand
De esa manera, el `APIRouter` original permanecerá sin modificar, por lo que aún podemos compartir ese mismo archivo `app/internal/admin.py` con otros proyectos en la organización.
El resultado es que, en nuestra aplicación, cada una de las *path operations* del módulo `admin` tendrá:
El resultado es que, en nuestra app, cada una de las *path operations* del módulo `admin` tendrá:
* El prefix `/admin`.
* El tag `admin`.
* La dependencia `get_token_header`.
* La response `418`. 🍵
Pero eso solo afectará a ese `APIRouter` en nuestra aplicación, no en ningún otro código que lo utilice.
Pero eso solo afectará a ese `APIRouter` en nuestra app, no en ningún otro código que lo utilice.
Así, por ejemplo, otros proyectos podrían usar el mismo `APIRouter` con un método de autenticación diferente.
### Incluir una *path operation* { #include-a-path-operation }
También podemos agregar *path operations* directamente a la aplicación de `FastAPI`.
También podemos agregar *path operations* directamente a la app de `FastAPI`.
Aquí lo hacemos... solo para mostrar que podemos 🤷:
@@ -461,13 +461,13 @@ Los `APIRouter`s no están "montados", no están aislados del resto de la aplica
Esto se debe a que queremos incluir sus *path operations* en el esquema de OpenAPI y las interfaces de usuario.
Como no podemos simplemente aislarlos y "montarlos" independientemente del resto, se "clonan" las *path operations* (se vuelven a crear), no se incluyen directamente.
Como no podemos simplemente aislarlos y "montarlos" independientemente del resto, las *path operations* se "clonan" (se vuelven a crear), no se incluyen directamente.
///
## Revisa la documentación automática de la API { #check-the-automatic-api-docs }
Ahora, ejecuta tu aplicación:
Ahora, ejecuta tu app:
<div class="termy">
@@ -481,7 +481,7 @@ $ fastapi dev app/main.py
Y abre la documentación en <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Verás la documentación automática de la API, incluyendo los paths de todos los submódulos, usando los paths correctos (y prefijos) y las tags correctas:
Verás la documentación automática de la API, incluyendo los paths de todos los submódulos, usando los paths correctos (y prefijos) y los tags correctos:
<img src="/img/tutorial/bigger-applications/image01.png">
@@ -501,4 +501,4 @@ De la misma manera que puedes incluir un `APIRouter` en una aplicación `FastAPI
router.include_router(other_router)
```
Asegúrate de hacerlo antes de incluir `router` en la aplicación de `FastAPI`, para que las *path operations* de `other_router` también se incluyan.
Asegúrate de hacerlo antes de incluir `router` en la app de `FastAPI`, para que las *path operations* de `other_router` también se incluyan.

View File

@@ -1,4 +1,4 @@
# Cuerpo - Actualizaciones { #body-updates }
# Body - Actualizaciones { #body-updates }
## Actualización reemplazando con `PUT` { #update-replacing-with-put }
@@ -50,14 +50,6 @@ Si quieres recibir actualizaciones parciales, es muy útil usar el parámetro `e
Como `item.model_dump(exclude_unset=True)`.
/// info | Información
En Pydantic v1 el método se llamaba `.dict()`, fue deprecado (pero aún soportado) en Pydantic v2, y renombrado a `.model_dump()`.
Los ejemplos aquí usan `.dict()` para compatibilidad con Pydantic v1, pero deberías usar `.model_dump()` si puedes usar Pydantic v2.
///
Eso generaría un `dict` solo con los datos que se establecieron al crear el modelo `item`, excluyendo los valores por defecto.
Luego puedes usar esto para generar un `dict` solo con los datos que se establecieron (enviados en el request), omitiendo los valores por defecto:
@@ -68,14 +60,6 @@ Luego puedes usar esto para generar un `dict` solo con los datos que se establec
Ahora, puedes crear una copia del modelo existente usando `.model_copy()`, y pasar el parámetro `update` con un `dict` que contenga los datos a actualizar.
/// info | Información
En Pydantic v1 el método se llamaba `.copy()`, fue deprecado (pero aún soportado) en Pydantic v2, y renombrado a `.model_copy()`.
Los ejemplos aquí usan `.copy()` para compatibilidad con Pydantic v1, pero deberías usar `.model_copy()` si puedes usar Pydantic v2.
///
Como `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
@@ -90,9 +74,9 @@ En resumen, para aplicar actualizaciones parciales deberías:
* Generar un `dict` sin valores por defecto del modelo de entrada (usando `exclude_unset`).
* De esta manera puedes actualizar solo los valores realmente establecidos por el usuario, en lugar de sobrescribir valores ya almacenados con valores por defecto en tu modelo.
* Crear una copia del modelo almacenado, actualizando sus atributos con las actualizaciones parciales recibidas (usando el parámetro `update`).
* Convertir el modelo copiado en algo que pueda almacenarse en tu base de datos (por ejemplo, usando el `jsonable_encoder`).
* Convertir el modelo copiado en algo que pueda almacenarse en tu DB (por ejemplo, usando el `jsonable_encoder`).
* Esto es comparable a usar el método `.model_dump()` del modelo de nuevo, pero asegura (y convierte) los valores a tipos de datos que pueden convertirse a JSON, por ejemplo, `datetime` a `str`.
* Guardar los datos en tu base de datos.
* Guardar los datos en tu DB.
* Devolver el modelo actualizado.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}

View File

@@ -32,7 +32,8 @@ Usa tipos estándar de Python para todos los atributos:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Al igual que al declarar parámetros de query, cuando un atributo del modelo tiene un valor por defecto, no es obligatorio. De lo contrario, es obligatorio. Usa `None` para hacerlo opcional.
Al igual que al declarar parámetros de query, cuando un atributo del modelo tiene un valor por defecto, no es obligatorio. De lo contrario, es obligatorio. Usa `None` para hacerlo solo opcional.
Por ejemplo, el modelo anterior declara un “`object`” JSON (o `dict` en Python) como:
@@ -127,14 +128,6 @@ Dentro de la función, puedes acceder a todos los atributos del objeto modelo di
{* ../../docs_src/body/tutorial002_py310.py *}
/// info | Información
En Pydantic v1 el método se llamaba `.dict()`, se marcó como obsoleto (pero sigue soportado) en Pydantic v2, y se renombró a `.model_dump()`.
Los ejemplos aquí usan `.dict()` por compatibilidad con Pydantic v1, pero deberías usar `.model_dump()` si puedes usar Pydantic v2.
///
## Request body + parámetros de path { #request-body-path-parameters }
Puedes declarar parámetros de path y request body al mismo tiempo.
@@ -143,6 +136,7 @@ Puedes declarar parámetros de path y request body al mismo tiempo.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Request body + path + parámetros de query { #request-body-path-query-parameters }
También puedes declarar parámetros de **body**, **path** y **query**, todos al mismo tiempo.
@@ -155,7 +149,7 @@ Los parámetros de la función se reconocerán de la siguiente manera:
* Si el parámetro también se declara en el **path**, se utilizará como un parámetro de path.
* Si el parámetro es de un **tipo singular** (como `int`, `float`, `str`, `bool`, etc.), se interpretará como un parámetro de **query**.
* Si el parámetro se declara como del tipo de un **modelo de Pydantic**, se interpretará como un **request body**.
* Si el parámetro se declara como del tipo de un **modelo de Pydantic**, se interpretará como un **body** de request.
/// note | Nota

View File

@@ -22,21 +22,13 @@ Aquí tienes una idea general de cómo podrían ser los modelos con sus campos d
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
/// info | Información
### Acerca de `**user_in.model_dump()` { #about-user-in-model-dump }
En Pydantic v1 el método se llamaba `.dict()`, fue deprecado (pero aún soportado) en Pydantic v2, y renombrado a `.model_dump()`.
Los ejemplos aquí usan `.dict()` para compatibilidad con Pydantic v1, pero deberías usar `.model_dump()` en su lugar si puedes usar Pydantic v2.
///
### Acerca de `**user_in.dict()` { #about-user-in-dict }
#### `.dict()` de Pydantic { #pydantics-dict }
#### `.model_dump()` de Pydantic { #pydantics-model-dump }
`user_in` es un modelo Pydantic de la clase `UserIn`.
Los modelos Pydantic tienen un método `.dict()` que devuelve un `dict` con los datos del modelo.
Los modelos Pydantic tienen un método `.model_dump()` que devuelve un `dict` con los datos del modelo.
Así que, si creamos un objeto Pydantic `user_in` como:
@@ -47,7 +39,7 @@ user_in = UserIn(username="john", password="secret", email="john.doe@example.com
y luego llamamos a:
```Python
user_dict = user_in.dict()
user_dict = user_in.model_dump()
```
ahora tenemos un `dict` con los datos en la variable `user_dict` (es un `dict` en lugar de un objeto modelo Pydantic).
@@ -58,7 +50,7 @@ Y si llamamos a:
print(user_dict)
```
obtendremos un `dict` de Python con:
obtendríamos un `dict` de Python con:
```Python
{
@@ -103,20 +95,20 @@ UserInDB(
#### Un modelo Pydantic a partir del contenido de otro { #a-pydantic-model-from-the-contents-of-another }
Como en el ejemplo anterior obtuvimos `user_dict` de `user_in.dict()`, este código:
Como en el ejemplo anterior obtuvimos `user_dict` de `user_in.model_dump()`, este código:
```Python
user_dict = user_in.dict()
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
sería equivalente a:
```Python
UserInDB(**user_in.dict())
UserInDB(**user_in.model_dump())
```
...porque `user_in.dict()` es un `dict`, y luego hacemos que Python lo "desempaquete" al pasarlo a `UserInDB` con el prefijo `**`.
...porque `user_in.model_dump()` es un `dict`, y luego hacemos que Python lo "desempaquete" al pasarlo a `UserInDB` con el prefijo `**`.
Así, obtenemos un modelo Pydantic a partir de los datos en otro modelo Pydantic.
@@ -125,7 +117,7 @@ Así, obtenemos un modelo Pydantic a partir de los datos en otro modelo Pydantic
Y luego agregando el argumento de palabra clave adicional `hashed_password=hashed_password`, como en:
```Python
UserInDB(**user_in.dict(), hashed_password=hashed_password)
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
...termina siendo como:
@@ -156,7 +148,7 @@ Y estos modelos están compartiendo muchos de los datos y duplicando nombres y t
Podríamos hacerlo mejor.
Podemos declarar un modelo `UserBase` que sirva como base para nuestros otros modelos. Y luego podemos hacer subclases de ese modelo que heredan sus atributos (anotaciones de tipos, validación, etc).
Podemos declarar un modelo `UserBase` que sirva como base para nuestros otros modelos. Y luego podemos hacer subclases de ese modelo que heredan sus atributos (declaraciones de tipos, validación, etc).
Toda la conversión de datos, validación, documentación, etc. seguirá funcionando normalmente.
@@ -180,20 +172,19 @@ Al definir una <a href="https://docs.pydantic.dev/latest/concepts/types/#unions"
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` en Python 3.10 { #union-in-python-3-10 }
En este ejemplo pasamos `Union[PlaneItem, CarItem]` como el valor del argumento `response_model`.
Porque lo estamos pasando como un **valor a un argumento** en lugar de ponerlo en **anotaciones de tipos**, tenemos que usar `Union` incluso en Python 3.10.
Porque lo estamos pasando como un **valor a un argumento** en lugar de ponerlo en una **anotación de tipos**, tenemos que usar `Union` incluso en Python 3.10.
Si estuviera en anotaciones de tipos podríamos haber usado la barra vertical, como:
Si estuviera en una anotación de tipos podríamos haber usado la barra vertical, como:
```Python
some_variable: PlaneItem | CarItem
```
Pero si ponemos eso en la asignación `response_model=PlaneItem | CarItem` obtendríamos un error, porque Python intentaría realizar una **operación inválida** entre `PlaneItem` y `CarItem` en lugar de interpretar eso como anotaciones de tipos.
Pero si ponemos eso en la asignación `response_model=PlaneItem | CarItem` obtendríamos un error, porque Python intentaría realizar una **operación inválida** entre `PlaneItem` y `CarItem` en lugar de interpretar eso como una anotación de tipos.
## Lista de modelos { #list-of-models }
@@ -203,7 +194,6 @@ Para eso, usa el `typing.List` estándar de Python (o simplemente `list` en Pyth
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
## Response con `dict` arbitrario { #response-with-arbitrary-dict }
También puedes declarar un response usando un `dict` arbitrario plano, declarando solo el tipo de las claves y valores, sin usar un modelo Pydantic.
@@ -214,7 +204,6 @@ En este caso, puedes usar `typing.Dict` (o solo `dict` en Python 3.9 y posterior
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
## Recapitulación { #recap }
Usa múltiples modelos Pydantic y hereda libremente para cada caso.

View File

@@ -109,7 +109,7 @@ FastAPI ahora:
## Alternativa (antigua): `Query` como valor por defecto { #alternative-old-query-as-the-default-value }
Versiones anteriores de FastAPI (antes de <abbr title="antes de 2023-03">0.95.0</abbr>) requerían que usaras `Query` como el valor por defecto de tu parámetro, en lugar de ponerlo en `Annotated`, hay una alta probabilidad de que veas código usándolo alrededor, así que te lo explicaré.
Versiones anteriores de FastAPI (antes de <abbr title="before 2023-03 - antes de 2023-03">0.95.0</abbr>) requerían que usaras `Query` como el valor por defecto de tu parámetro, en lugar de ponerlo en `Annotated`, hay una alta probabilidad de que veas código usándolo alrededor, así que te lo explicaré.
/// tip | Consejo
@@ -192,7 +192,7 @@ También puedes agregar un parámetro `min_length`:
## Agregar expresiones regulares { #add-regular-expressions }
Puedes definir un <abbr title="Una expresión regular, regex o regexp es una secuencia de caracteres que define un patrón de búsqueda para strings.">expresión regular</abbr> `pattern` que el parámetro debe coincidir:
Puedes definir una <abbr title="Una expresión regular, regex o regexp es una secuencia de caracteres que define un patrón de búsqueda para strings.">expresión regular</abbr> `pattern` que el parámetro debe coincidir:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
@@ -206,20 +206,6 @@ Si te sientes perdido con todas estas ideas de **"expresión regular"**, no te p
Ahora sabes que cuando las necesites puedes usarlas en **FastAPI**.
### Pydantic v1 `regex` en lugar de `pattern` { #pydantic-v1-regex-instead-of-pattern }
Antes de la versión 2 de Pydantic y antes de FastAPI 0.100.0, el parámetro se llamaba `regex` en lugar de `pattern`, pero ahora está en desuso.
Todavía podrías ver algo de código que lo usa:
//// tab | Pydantic v1
{* ../../docs_src/query_params_str_validations/tutorial004_regex_an_py310.py hl[11] *}
////
Pero que sepas que esto está deprecado y debería actualizarse para usar el nuevo parámetro `pattern`. 🤓
## Valores por defecto { #default-values }
Puedes, por supuesto, usar valores por defecto diferentes de `None`.
@@ -280,7 +266,7 @@ Entonces, con una URL como:
http://localhost:8000/items/?q=foo&q=bar
```
recibirías los múltiples valores del *query parameter* `q` (`foo` y `bar`) en una `list` de Python dentro de tu *path operation function*, en el *parámetro de función* `q`.
recibirías los múltiples valores de los *query parameters* `q` (`foo` y `bar`) en una `list` de Python dentro de tu *path operation function*, en el *parámetro de función* `q`.
Entonces, el response a esa URL sería:
@@ -386,7 +372,7 @@ Entonces puedes declarar un `alias`, y ese alias será usado para encontrar el v
Ahora digamos que ya no te gusta este parámetro.
Tienes que dejarlo allí por un tiempo porque hay clientes usándolo, pero quieres que la documentación lo muestre claramente como <abbr title="obsoleto, se recomienda no usarlo">deprecated</abbr>.
Tienes que dejarlo allí por un tiempo porque hay clientes usándolo, pero quieres que la documentación lo muestre claramente como <abbr title="obsolete, recommended not to use it - obsoleto, se recomienda no usarlo">deprecated</abbr>.
Luego pasa el parámetro `deprecated=True` a `Query`:
@@ -416,7 +402,7 @@ Pydantic también tiene <a href="https://docs.pydantic.dev/latest/concepts/valid
///
Por ejemplo, este validador personalizado comprueba que el ID del ítem empiece con `isbn-` para un número de libro <abbr title="International Standard Book Number Número Estándar Internacional de Libro">ISBN</abbr> o con `imdb-` para un ID de URL de película de <abbr title="IMDB (Internet Movie Database) es un sitio web con información sobre películas">IMDB</abbr>:
Por ejemplo, este validador personalizado comprueba que el ID del ítem empiece con `isbn-` para un número de libro <abbr title="ISBN means International Standard Book Number - ISBN significa International Standard Book Number">ISBN</abbr> o con `imdb-` para un ID de URL de película de <abbr title="IMDB (Internet Movie Database) is a website with information about movies - IMDB (Internet Movie Database) es un sitio web con información sobre películas">IMDB</abbr>:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
@@ -450,7 +436,7 @@ Pero si te da curiosidad este ejemplo de código específico y sigues entretenid
#### Un ítem aleatorio { #a-random-item }
Con `data.items()` obtenemos un <abbr title="Algo que podemos iterar con un for, como una list, set, etc.">objeto iterable</abbr> con tuplas que contienen la clave y el valor para cada elemento del diccionario.
Con `data.items()` obtenemos un <abbr title="Algo que podemos iterar con un for loop, como una list, set, etc.">objeto iterable</abbr> con tuplas que contienen la clave y el valor para cada elemento del diccionario.
Convertimos este objeto iterable en una `list` propiamente dicha con `list(data.items())`.

View File

@@ -2,7 +2,7 @@
Puedes declarar el tipo utilizado para el response anotando el **tipo de retorno** de la *path operation function*.
Puedes utilizar **anotaciones de tipos** de la misma manera que lo harías para datos de entrada en **parámetros** de función, puedes utilizar modelos de Pydantic, list, diccionarios, valores escalares como enteros, booleanos, etc.
Puedes utilizar **anotaciones de tipos** de la misma manera que lo harías para datos de entrada en **parámetros** de función, puedes utilizar modelos de Pydantic, lists, diccionarios, valores escalares como enteros, booleanos, etc.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
@@ -27,7 +27,7 @@ Por ejemplo, podrías querer **devolver un diccionario** u objeto de base de dat
Si añadiste la anotación del tipo de retorno, las herramientas y editores se quejarían con un error (correcto) diciéndote que tu función está devolviendo un tipo (por ejemplo, un dict) que es diferente de lo que declaraste (por ejemplo, un modelo de Pydantic).
En esos casos, puedes usar el parámetro del decorador de path operation `response_model` en lugar del tipo de retorno.
En esos casos, puedes usar el parámetro del *decorador de path operation* `response_model` en lugar del tipo de retorno.
Puedes usar el parámetro `response_model` en cualquiera de las *path operations*:
@@ -153,7 +153,7 @@ Primero vamos a ver cómo los editores, mypy y otras herramientas verían esto.
`BaseUser` tiene los campos base. Luego `UserIn` hereda de `BaseUser` y añade el campo `password`, por lo que incluirá todos los campos de ambos modelos.
Anotamos el tipo de retorno de la función como `BaseUser`, pero en realidad estamos devolviendo un instance de `UserIn`.
Anotamos el tipo de retorno de la función como `BaseUser`, pero en realidad estamos devolviendo un `UserIn` instance.
El editor, mypy y otras herramientas no se quejarán de esto porque, en términos de tipificación, `UserIn` es una subclase de `BaseUser`, lo que significa que es un tipo *válido* cuando se espera algo que es un `BaseUser`.
@@ -252,20 +252,6 @@ Entonces, si envías un request a esa *path operation* para el ítem con ID `foo
/// info | Información
En Pydantic v1 el método se llamaba `.dict()`, fue deprecado (pero aún soportado) en Pydantic v2, y renombrado a `.model_dump()`.
Los ejemplos aquí usan `.dict()` para compatibilidad con Pydantic v1, pero deberías usar `.model_dump()` en su lugar si puedes usar Pydantic v2.
///
/// info | Información
FastAPI usa el método `.dict()` del modelo de Pydantic con <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">su parámetro `exclude_unset`</a> para lograr esto.
///
/// info | Información
También puedes usar:
* `response_model_exclude_defaults=True`

View File

@@ -8,35 +8,13 @@ Aquí tienes varias formas de hacerlo.
Puedes declarar `examples` para un modelo de Pydantic que se añadirá al JSON Schema generado.
//// tab | Pydantic v2
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
////
Esa información extra se añadirá tal cual al **JSON Schema** resultante para ese modelo, y se usará en la documentación de la API.
//// tab | Pydantic v1
Puedes usar el atributo `model_config` que toma un `dict` como se describe en <a href="https://docs.pydantic.dev/latest/api/config/" class="external-link" target="_blank">la documentación de Pydantic: Configuración</a>.
{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *}
////
Esa información extra se añadirá tal cual al **JSON Schema** generado para ese modelo, y se usará en la documentación de la API.
//// tab | Pydantic v2
En Pydantic versión 2, usarías el atributo `model_config`, que toma un `dict` como se describe en <a href="https://docs.pydantic.dev/latest/api/config/" class="external-link" target="_blank">la documentación de Pydantic: Configuración</a>.
Puedes establecer `"json_schema_extra"` con un `dict` que contenga cualquier dato adicional que desees que aparezca en el JSON Schema generado, incluyendo `examples`.
////
//// tab | Pydantic v1
En Pydantic versión 1, usarías una clase interna `Config` y `schema_extra`, como se describe en <a href="https://docs.pydantic.dev/1.10/usage/schema/#schema-customization" class="external-link" target="_blank">la documentación de Pydantic: Personalización de Esquema</a>.
Puedes establecer `schema_extra` con un `dict` que contenga cualquier dato adicional que desees que aparezca en el JSON Schema generado, incluyendo `examples`.
////
Puedes establecer `"json_schema_extra"` con un `dict` que contenga cualquier dato adicional que te gustaría que aparezca en el JSON Schema generado, incluyendo `examples`.
/// tip | Consejo
@@ -50,7 +28,7 @@ Por ejemplo, podrías usarlo para añadir metadatos para una interfaz de usuario
OpenAPI 3.1.0 (usado desde FastAPI 0.99.0) añadió soporte para `examples`, que es parte del estándar de **JSON Schema**.
Antes de eso, solo soportaba la palabra clave `example` con un solo ejemplo. Eso aún es soportado por OpenAPI 3.1.0, pero está obsoleto y no es parte del estándar de JSON Schema. Así que se recomienda migrar de `example` a `examples`. 🤓
Antes de eso, solo soportaba la palabra clave `example` con un solo ejemplo. Eso aún es soportado por OpenAPI 3.1.0, pero está obsoleto y no es parte del estándar de JSON Schema. Así que se te anima a migrar `example` a `examples`. 🤓
Puedes leer más al final de esta página.
@@ -94,7 +72,7 @@ Por supuesto, también puedes pasar múltiples `examples`:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Cuando haces esto, los ejemplos serán parte del **JSON Schema** interno para esos datos de body.
Cuando haces esto, los ejemplos serán parte del **JSON Schema** interno para esos datos del body.
Sin embargo, al <abbr title="2023-08-26">momento de escribir esto</abbr>, Swagger UI, la herramienta encargada de mostrar la interfaz de documentación, no soporta mostrar múltiples ejemplos para los datos en **JSON Schema**. Pero lee más abajo para una solución alternativa.
@@ -203,17 +181,17 @@ Debido a eso, las versiones de FastAPI anteriores a 0.99.0 todavía usaban versi
### `examples` de Pydantic y FastAPI { #pydantic-and-fastapi-examples }
Cuando añades `examples` dentro de un modelo de Pydantic, usando `schema_extra` o `Field(examples=["algo"])`, ese ejemplo se añade al **JSON Schema** para ese modelo de Pydantic.
Cuando añades `examples` dentro de un modelo de Pydantic, usando `schema_extra` o `Field(examples=["something"])`, ese ejemplo se añade al **JSON Schema** para ese modelo de Pydantic.
Y ese **JSON Schema** del modelo de Pydantic se incluye en el **OpenAPI** de tu API, y luego se usa en la interfaz de documentación.
En las versiones de FastAPI antes de 0.99.0 (0.99.0 y superior usan el nuevo OpenAPI 3.1.0) cuando usabas `example` o `examples` con cualquiera de las otras utilidades (`Query()`, `Body()`, etc.) esos ejemplos no se añadían al JSON Schema que describe esos datos (ni siquiera a la propia versión de JSON Schema de OpenAPI), se añadían directamente a la declaración de la *path operation* en OpenAPI (fuera de las partes de OpenAPI que usan JSON Schema).
En las versiones de FastAPI antes de 0.99.0 (0.99.0 y superiores usan el nuevo OpenAPI 3.1.0) cuando usabas `example` o `examples` con cualquiera de las otras utilidades (`Query()`, `Body()`, etc.) esos ejemplos no se añadían al JSON Schema que describe esos datos (ni siquiera a la propia versión de JSON Schema de OpenAPI), se añadían directamente a la declaración de la *path operation* en OpenAPI (fuera de las partes de OpenAPI que usan JSON Schema).
Pero ahora que FastAPI 0.99.0 y superiores usa OpenAPI 3.1.0, que usa JSON Schema 2020-12, y Swagger UI 5.0.0 y superiores, todo es más consistente y los ejemplos se incluyen en JSON Schema.
### Swagger UI y `examples` específicos de OpenAPI { #swagger-ui-and-openapi-specific-examples }
Ahora, como Swagger UI no soportaba múltiples ejemplos de JSON Schema (a fecha de 2023-08-26), los usuarios no tenían una forma de mostrar múltiples ejemplos en los documentos.
Ahora, como Swagger UI no soportaba múltiples ejemplos de JSON Schema (a fecha de 2023-08-26), los usuarios no tenían una forma de mostrar múltiples ejemplos en la documentación.
Para resolver eso, FastAPI `0.103.0` **añadió soporte** para declarar el mismo viejo campo **específico de OpenAPI** `examples` con el nuevo parámetro `openapi_examples`. 🤓

View File

@@ -1,4 +1,4 @@
# Réponses supplémentaires dans OpenAPI { #additional-responses-in-openapi }
# Réponses supplémentaires dans OpenAPI
/// warning | Attention
@@ -14,9 +14,9 @@ Ces réponses supplémentaires seront incluses dans le schéma OpenAPI, elles ap
Mais pour ces réponses supplémentaires, vous devez vous assurer de renvoyer directement une `Response` comme `JSONResponse`, avec votre code HTTP et votre contenu.
## Réponse supplémentaire avec `model` { #additional-response-with-model }
## Réponse supplémentaire avec `model`
Vous pouvez passer à vos *décorateurs d'opération de chemin* un paramètre `responses`.
Vous pouvez ajouter à votre décorateur de *paramètre de chemin* un paramètre `responses`.
Il prend comme valeur un `dict` dont les clés sont des codes HTTP pour chaque réponse, comme `200`, et la valeur de ces clés sont d'autres `dict` avec des informations pour chacun d'eux.
@@ -26,7 +26,7 @@ Chacun de ces `dict` de réponse peut avoir une clé `model`, contenant un modè
Par exemple, pour déclarer une autre réponse avec un code HTTP `404` et un modèle Pydantic `Message`, vous pouvez écrire :
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
/// note | Remarque
@@ -169,13 +169,13 @@ Les schémas sont référencés à un autre endroit du modèle OpenAPI :
}
```
## Types de médias supplémentaires pour la réponse principale { #additional-media-types-for-the-main-response }
## Types de médias supplémentaires pour la réponse principale
Vous pouvez utiliser ce même paramètre `responses` pour ajouter différents types de médias pour la même réponse principale.
Par exemple, vous pouvez ajouter un type de média supplémentaire `image/png`, en déclarant que votre *opération de chemin* peut renvoyer un objet JSON (avec le type de média `application/json`) ou une image PNG :
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
/// note | Remarque
@@ -191,7 +191,7 @@ Mais si vous avez spécifié une classe de réponse personnalisée avec `None` c
///
## Combinaison d'informations { #combining-information }
## Combinaison d'informations
Vous pouvez également combiner des informations de réponse provenant de plusieurs endroits, y compris les paramètres `response_model`, `status_code` et `responses`.
@@ -203,17 +203,17 @@ Par exemple, vous pouvez déclarer une réponse avec un code HTTP `404` qui util
Et une réponse avec un code HTTP `200` qui utilise votre `response_model`, mais inclut un `example` personnalisé :
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
Tout sera combiné et inclus dans votre OpenAPI, et affiché dans la documentation de l'API :
<img src="/img/tutorial/additional-responses/image01.png">
## Combinez les réponses prédéfinies et les réponses personnalisées { #combine-predefined-responses-and-custom-ones }
## Combinez les réponses prédéfinies et les réponses personnalisées
Vous voulez peut-être avoir des réponses prédéfinies qui s'appliquent à de nombreuses *opérations de chemin*, mais vous souhaitez les combiner avec des réponses personnalisées nécessaires à chaque *opération de chemin*.
Vous voulez peut-être avoir des réponses prédéfinies qui s'appliquent à de nombreux *paramètre de chemin*, mais vous souhaitez les combiner avec des réponses personnalisées nécessaires à chaque *opération de chemin*.
Dans ces cas, vous pouvez utiliser la technique Python «d'affection par décomposition» (appelé _unpacking_ en anglais) d'un `dict` avec `**dict_to_unpack` :
Dans ces cas, vous pouvez utiliser la technique Python "d'affection par décomposition" (appelé _unpacking_ en anglais) d'un `dict` avec `**dict_to_unpack` :
```Python
old_dict = {
@@ -233,15 +233,15 @@ Ici, `new_dict` contiendra toutes les paires clé-valeur de `old_dict` plus la n
}
```
Vous pouvez utiliser cette technique pour réutiliser certaines réponses prédéfinies dans vos *opérations de chemin* et les combiner avec des réponses personnalisées supplémentaires.
Vous pouvez utiliser cette technique pour réutiliser certaines réponses prédéfinies dans vos *paramètres de chemin* et les combiner avec des réponses personnalisées supplémentaires.
Par exemple:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
## Plus d'informations sur les réponses OpenAPI { #more-information-about-openapi-responses }
## Plus d'informations sur les réponses OpenAPI
Pour voir exactement ce que vous pouvez inclure dans les réponses, vous pouvez consulter ces sections dans la spécification OpenAPI :
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object" class="external-link" target="_blank">Objet Responses de OpenAPI</a>, il inclut le `Response Object`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object" class="external-link" target="_blank">Objet Response de OpenAPI</a>, vous pouvez inclure n'importe quoi directement dans chaque réponse à l'intérieur de votre paramètre `responses`. Y compris `description`, `headers`, `content` (à l'intérieur de cela, vous déclarez différents types de médias et schémas JSON) et `links`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject" class="external-link" target="_blank">Objet Responses de OpenAPI </a>, il inclut le `Response Object`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject" class="external-link" target="_blank">Objet Response de OpenAPI </a>, vous pouvez inclure n'importe quoi directement dans chaque réponse à l'intérieur de votre paramètre `responses`. Y compris `description`, `headers`, `content` (à l'intérieur de cela, vous déclarez différents types de médias et schémas JSON) et `links`.

View File

@@ -1,20 +1,20 @@
# Codes HTTP supplémentaires { #additional-status-codes }
# Codes HTTP supplémentaires
Par défaut, **FastAPI** renverra les réponses à l'aide d'une structure de données `JSONResponse`, en plaçant la réponse de votre *opération de chemin* à l'intérieur de cette `JSONResponse`.
Par défaut, **FastAPI** renverra les réponses à l'aide d'une structure de données `JSONResponse`, en plaçant la réponse de votre *chemin d'accès* à l'intérieur de cette `JSONResponse`.
Il utilisera le code HTTP par défaut ou celui que vous avez défini dans votre *opération de chemin*.
Il utilisera le code HTTP par défaut ou celui que vous avez défini dans votre *chemin d'accès*.
## Codes HTTP supplémentaires { #additional-status-codes_1 }
## Codes HTTP supplémentaires
Si vous souhaitez renvoyer des codes HTTP supplémentaires en plus du code principal, vous pouvez le faire en renvoyant directement une `Response`, comme une `JSONResponse`, et en définissant directement le code HTTP supplémentaire.
Par exemple, disons que vous voulez avoir une *opération de chemin* qui permet de mettre à jour les éléments et renvoie les codes HTTP 200 «OK» en cas de succès.
Par exemple, disons que vous voulez avoir un *chemin d'accès* qui permet de mettre à jour les éléments et renvoie les codes HTTP 200 "OK" en cas de succès.
Mais vous voulez aussi qu'il accepte de nouveaux éléments. Et lorsque les éléments n'existaient pas auparavant, il les crée et renvoie un code HTTP de 201 «Créé».
Mais vous voulez aussi qu'il accepte de nouveaux éléments. Et lorsque les éléments n'existaient pas auparavant, il les crée et renvoie un code HTTP de 201 "Créé".
Pour y parvenir, importez `JSONResponse` et renvoyez-y directement votre contenu, en définissant le `status_code` que vous souhaitez :
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
{* ../../docs_src/additional_status_codes/tutorial001.py hl[4,25] *}
/// warning | Attention
@@ -22,7 +22,7 @@ Lorsque vous renvoyez une `Response` directement, comme dans l'exemple ci-dessus
Elle ne sera pas sérialisée avec un modèle.
Assurez-vous qu'elle contient les données souhaitées et que les valeurs soient dans un format JSON valides (si vous utilisez une `JSONResponse`).
Assurez-vous qu'il contient les données souhaitées et que les valeurs soient dans un format JSON valides (si vous utilisez une `JSONResponse`).
///
@@ -30,12 +30,12 @@ Assurez-vous qu'elle contient les données souhaitées et que les valeurs soient
Vous pouvez également utiliser `from starlette.responses import JSONResponse`.
Pour plus de commodités, **FastAPI** fournit les objets `starlette.responses` sous forme d'un alias accessible par `fastapi.responses`. Mais la plupart des réponses disponibles proviennent directement de Starlette. Il en est de même avec `status`.
Pour plus de commodités, **FastAPI** fournit les objets `starlette.responses` sous forme d'un alias accessible par `fastapi.responses`. Mais la plupart des réponses disponibles proviennent directement de Starlette. Il en est de même avec l'objet `statut`.
///
## OpenAPI et documentation de l'API { #openapi-and-api-docs }
## Documents OpenAPI et API
Si vous renvoyez directement des codes HTTP et des réponses supplémentaires, ils ne seront pas inclus dans le schéma OpenAPI (la documentation de l'API), car FastAPI n'a aucun moyen de savoir à l'avance ce que vous allez renvoyer.
Mais vous pouvez documenter cela dans votre code, en utilisant : [Réponses supplémentaires](additional-responses.md){.internal-link target=_blank}.
Mais vous pouvez documenter cela dans votre code, en utilisant : [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.

View File

@@ -1,21 +1,27 @@
# Guide de l'utilisateur avancé { #advanced-user-guide }
# Guide de l'utilisateur avancé
## Caractéristiques supplémentaires { #additional-features }
## Caractéristiques supplémentaires
Le [Tutoriel - Guide de l'utilisateur](../tutorial/index.md){.internal-link target=_blank} devrait suffire à vous faire découvrir toutes les fonctionnalités principales de **FastAPI**.
Dans les sections suivantes, vous verrez des options, configurations et fonctionnalités supplémentaires.
/// tip | Astuce
/// note | Remarque
Les sections de ce chapitre ne sont **pas nécessairement «avancées»**.
Les sections de ce chapitre ne sont **pas nécessairement "avancées"**.
Et il est possible que pour votre cas d'utilisation, la solution se trouve dans l'un d'entre eux.
///
## Lisez d'abord le didacticiel { #read-the-tutorial-first }
## Lisez d'abord le didacticiel
Vous pouvez utiliser la plupart des fonctionnalités de **FastAPI** grâce aux connaissances du [Tutoriel - Guide de l'utilisateur](../tutorial/index.md){.internal-link target=_blank}.
Et les sections suivantes supposent que vous l'avez lu et que vous en connaissez les idées principales.
## Cours TestDriven.io
Si vous souhaitez suivre un cours pour débutants avancés pour compléter cette section de la documentation, vous pouvez consulter : <a href="https://testdrive.io/courses/tdd-fastapi/" class="external- link" target="_blank">Développement piloté par les tests avec FastAPI et Docker</a> par **TestDriven.io**.
10 % de tous les bénéfices de ce cours sont reversés au développement de **FastAPI**. 🎉 😄

View File

@@ -1,108 +1,106 @@
# Configuration avancée des opérations de chemin { #path-operation-advanced-configuration }
# Configuration avancée des paramètres de chemin
## ID d'opération OpenAPI { #openapi-operationid }
## ID d'opération OpenAPI
/// warning | Attention
Si vous n'êtes pas un «expert» d'OpenAPI, vous n'en avez probablement pas besoin.
Si vous n'êtes pas un "expert" en OpenAPI, vous n'en avez probablement pas besoin.
///
Vous pouvez définir l'OpenAPI `operationId` à utiliser dans votre *opération de chemin* avec le paramètre `operation_id`.
Dans OpenAPI, les chemins sont des ressources, tels que /users/ ou /items/, exposées par votre API, et les opérations sont les méthodes HTTP utilisées pour manipuler ces chemins, telles que GET, POST ou DELETE. Les operationId sont des chaînes uniques facultatives utilisées pour identifier une opération d'un chemin. Vous pouvez définir l'OpenAPI `operationId` à utiliser dans votre *opération de chemin* avec le paramètre `operation_id`.
Vous devrez vous assurer qu'il soit unique pour chaque opération.
Vous devez vous assurer qu'il est unique pour chaque opération.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
### Utiliser le nom de la *fonction d'opération de chemin* comme operationId { #using-the-path-operation-function-name-as-the-operationid }
### Utilisation du nom *path operation function* comme operationId
Si vous souhaitez utiliser les noms de fonction de vos API comme `operationId`, vous pouvez les parcourir tous et remplacer chaque `operation_id` de l*opération de chemin* en utilisant leur `APIRoute.name`.
Si vous souhaitez utiliser les noms de fonction de vos API comme `operationId`, vous pouvez les parcourir tous et remplacer chaque `operation_id` de l'*opération de chemin* en utilisant leur `APIRoute.name`.
Vous devriez le faire après avoir ajouté toutes vos *opérations de chemin*.
Vous devriez le faire après avoir ajouté toutes vos *paramètres de chemin*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2,12:21,24] *}
/// tip | Astuce
Si vous appelez manuellement `app.openapi()`, vous devez mettre à jour les `operationId` avant cela.
Si vous appelez manuellement `app.openapi()`, vous devez mettre à jour les `operationId` avant.
///
/// warning | Attention
Si vous faites cela, vous devez vous assurer que chacune de vos *fonctions d'opération de chemin* a un nom unique.
Pour faire cela, vous devez vous assurer que chacun de vos *chemin* ait un nom unique.
Même si elles se trouvent dans des modules différents (fichiers Python).
Même s'ils se trouvent dans des modules différents (fichiers Python).
///
## Exclure d'OpenAPI { #exclude-from-openapi }
## Exclusion d'OpenAPI
Pour exclure une *opération de chemin* du schéma OpenAPI généré (et donc des systèmes de documentation automatiques), utilisez le paramètre `include_in_schema` et assignez-lui la valeur `False` :
Pour exclure un *chemin* du schéma OpenAPI généré (et donc des systèmes de documentation automatiques), utilisez le paramètre `include_in_schema` et assignez-lui la valeur `False` :
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
## Description avancée depuis la docstring { #advanced-description-from-docstring }
## Description avancée de docstring
Vous pouvez limiter les lignes utilisées de la docstring d'une *fonction d'opération de chemin* pour OpenAPI.
Vous pouvez limiter le texte utilisé de la docstring d'une *fonction de chemin* qui sera affiché sur OpenAPI.
L'ajout d'un `\f` (un caractère d'échappement «form feed») fait que **FastAPI** tronque la sortie utilisée pour OpenAPI à cet endroit.
L'ajout d'un `\f` (un caractère d'échappement "form feed") va permettre à **FastAPI** de tronquer la sortie utilisée pour OpenAPI à ce stade.
Cela n'apparaîtra pas dans la documentation, mais d'autres outils (tels que Sphinx) pourront utiliser le reste.
Il n'apparaîtra pas dans la documentation, mais d'autres outils (tel que Sphinx) pourront utiliser le reste.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
## Réponses supplémentaires { #additional-responses }
## Réponses supplémentaires
Vous avez probablement vu comment déclarer le `response_model` et le `status_code` pour une *opération de chemin*.
Cela définit les métadonnées de la réponse principale d'une *opération de chemin*.
Cela définit les métadonnées sur la réponse principale d'une *opération de chemin*.
Vous pouvez également déclarer des réponses supplémentaires avec leurs modèles, codes de statut, etc.
Il y a un chapitre entier dans la documentation à ce sujet, vous pouvez le lire dans [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
Il y a un chapitre entier ici dans la documentation à ce sujet, vous pouvez le lire sur [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
## OpenAPI supplémentaire { #openapi-extra }
## OpenAPI supplémentaire
Lorsque vous déclarez une *opération de chemin* dans votre application, **FastAPI** génère automatiquement les métadonnées pertinentes concernant cette *opération de chemin* à inclure dans le schéma OpenAPI.
Lorsque vous déclarez un *chemin* dans votre application, **FastAPI** génère automatiquement les métadonnées concernant ce *chemin* à inclure dans le schéma OpenAPI.
/// note | Détails techniques
Dans la spécification OpenAPI, cela s'appelle l<a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Objet d'opération</a>.
La spécification OpenAPI appelle ces métadonnées des <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Objets d'opération</a>.
///
Il contient toutes les informations sur l*opération de chemin* et est utilisé pour générer automatiquement la documentation.
Il contient toutes les informations sur le *chemin* et est utilisé pour générer automatiquement la documentation.
Il inclut les `tags`, `parameters`, `requestBody`, `responses`, etc.
Ce schéma OpenAPI spécifique à l*opération de chemin* est normalement généré automatiquement par **FastAPI**, mais vous pouvez également l'étendre.
Ce schéma OpenAPI spécifique aux *operations* est normalement généré automatiquement par **FastAPI**, mais vous pouvez également l'étendre.
/// tip | Astuce
Ceci est un point d'extension de bas niveau.
Si vous avez seulement besoin de déclarer des réponses supplémentaires, un moyen plus pratique de le faire est d'utiliser [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
Si vous avez seulement besoin de déclarer des réponses supplémentaires, un moyen plus pratique de le faire est d'utiliser les [réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
///
Vous pouvez étendre le schéma OpenAPI pour une *opération de chemin* en utilisant le paramètre `openapi_extra`.
### Extensions OpenAPI { #openapi-extensions }
### Extensions OpenAPI
Cet `openapi_extra` peut être utile, par exemple, pour déclarer des [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) :
Cet `openapi_extra` peut être utile, par exemple, pour déclarer [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) :
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
Si vous ouvrez la documentation automatique de l'API, votre extension apparaîtra en bas de l*opération de chemin* spécifique.
Si vous ouvrez la documentation automatique de l'API, votre extension apparaîtra au bas du *chemin* spécifique.
<img src="/img/tutorial/path-operation-advanced-configuration/image01.png">
Et si vous regardez lOpenAPI résultant (à `/openapi.json` dans votre API), vous verrez également votre extension dans le cadre de l*opération de chemin* spécifique :
Et dans le fichier openapi généré (`/openapi.json`), vous verrez également votre extension dans le cadre du *chemin* spécifique :
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"openapi": "3.0.2",
"info": {
"title": "FastAPI",
"version": "0.1.0"
@@ -129,44 +127,44 @@ Et si vous regardez lOpenAPI résultant (à `/openapi.json` dans votre API),
}
```
### Schéma OpenAPI personnalisé pour une *opération de chemin* { #custom-openapi-path-operation-schema }
### Personnalisation du Schéma OpenAPI pour un chemin
Le dictionnaire dans `openapi_extra` sera fusionné en profondeur avec le schéma OpenAPI généré automatiquement pour l*opération de chemin*.
Le dictionnaire contenu dans la variable `openapi_extra` sera fusionné avec le schéma OpenAPI généré automatiquement pour l'*opération de chemin*.
Ainsi, vous pouvez ajouter des données supplémentaires au schéma généré automatiquement.
Par exemple, vous pourriez décider de lire et de valider la requête avec votre propre code, sans utiliser les fonctionnalités automatiques de FastAPI avec Pydantic, mais vous pourriez quand même vouloir définir la requête dans le schéma OpenAPI.
Par exemple, vous pouvez décider de lire et de valider la requête avec votre propre code, sans utiliser les fonctionnalités automatiques de validation proposée par Pydantic, mais vous pouvez toujours définir la requête dans le schéma OpenAPI.
Vous pouvez le faire avec `openapi_extra` :
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[20:37,39:40] *}
Dans cet exemple, nous n'avons déclaré aucun modèle Pydantic. En fait, le corps de la requête n'est même pas <abbr title="converti d'un format simple, comme des octets, en objets Python">parsé</abbr> en tant que JSON, il est lu directement en tant que `bytes`, et la fonction `magic_data_reader()` serait chargée de l'analyser d'une manière ou d'une autre.
Dans cet exemple, nous n'avons déclaré aucun modèle Pydantic. En fait, le corps de la requête n'est même pas <abbr title="converti d'un format simple, comme des octets, en objets Python">parsé</abbr> en tant que JSON, il est lu directement en tant que `bytes`, et la fonction `magic_data_reader()` serait chargé de l'analyser d'une manière ou d'une autre.
Néanmoins, nous pouvons déclarer le schéma attendu pour le corps de la requête.
### Type de contenu OpenAPI personnalisé { #custom-openapi-content-type }
### Type de contenu OpenAPI personnalisé
En utilisant cette même astuce, vous pourriez utiliser un modèle Pydantic pour définir le JSON Schema qui est ensuite inclus dans la section de schéma OpenAPI personnalisée pour l*opération de chemin*.
En utilisant cette même astuce, vous pouvez utiliser un modèle Pydantic pour définir le schéma JSON qui est ensuite inclus dans la section de schéma OpenAPI personnalisée pour le *chemin* concerné.
Et vous pourriez le faire même si le type de données dans la requête n'est pas du JSON.
Et vous pouvez le faire même si le type de données dans la requête n'est pas au format JSON.
Par exemple, dans cette application nous n'utilisons pas la fonctionnalité intégrée de FastAPI pour extraire le JSON Schema des modèles Pydantic ni la validation automatique pour JSON. En fait, nous déclarons le type de contenu de la requête comme YAML, et non JSON :
Dans cet exemple, nous n'utilisons pas les fonctionnalités de FastAPI pour extraire le schéma JSON des modèles Pydantic ni la validation automatique pour JSON. En fait, nous déclarons le type de contenu de la requête en tant que YAML, et non JSON :
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[17:22,24] *}
Néanmoins, bien que nous n'utilisions pas la fonctionnalité intégrée par défaut, nous utilisons toujours un modèle Pydantic pour générer manuellement le JSON Schema pour les données que nous souhaitons recevoir en YAML.
Néanmoins, bien que nous n'utilisions pas la fonctionnalité par défaut, nous utilisons toujours un modèle Pydantic pour générer manuellement le schéma JSON pour les données que nous souhaitons recevoir en YAML.
Ensuite, nous utilisons directement la requête et extrayons son corps en tant que `bytes`. Cela signifie que FastAPI n'essaiera même pas d'analyser le payload de la requête en tant que JSON.
Ensuite, nous utilisons directement la requête et extrayons son contenu en tant qu'octets. Cela signifie que FastAPI n'essaiera même pas d'analyser le payload de la requête en tant que JSON.
Et puis, dans notre code, nous analysons directement ce contenu YAML, puis nous utilisons à nouveau le même modèle Pydantic pour valider le contenu YAML :
Et nous analysons directement ce contenu YAML, puis nous utilisons à nouveau le même modèle Pydantic pour valider le contenu YAML :
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[26:33] *}
/// tip | Astuce
Ici, nous réutilisons le même modèle Pydantic.
Mais de la même manière, nous aurions tout aussi bien pu le valider autrement.
Mais nous aurions pu tout aussi bien pu le valider d'une autre manière.
///

View File

@@ -1,6 +1,6 @@
# Renvoyer directement une réponse { #return-a-response-directly }
# Renvoyer directement une réponse
Lorsque vous créez une *opération de chemin* **FastAPI**, vous pouvez normalement retourner n'importe quelle donnée : un `dict`, une `list`, un modèle Pydantic, un modèle de base de données, etc.
Lorsque vous créez une *opération de chemins* **FastAPI**, vous pouvez normalement retourner n'importe quelle donnée : un `dict`, une `list`, un modèle Pydantic, un modèle de base de données, etc.
Par défaut, **FastAPI** convertirait automatiquement cette valeur de retour en JSON en utilisant le `jsonable_encoder` expliqué dans [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
@@ -10,11 +10,11 @@ Mais vous pouvez retourner une `JSONResponse` directement à partir de vos *opé
Cela peut être utile, par exemple, pour retourner des en-têtes personnalisés ou des cookies.
## Renvoyer une `Response` { #return-a-response }
## Renvoyer une `Response`
En fait, vous pouvez retourner n'importe quelle `Response` ou n'importe quelle sous-classe de celle-ci.
/// tip | Astuce
/// note | Remarque
`JSONResponse` est elle-même une sous-classe de `Response`.
@@ -26,15 +26,15 @@ Elle ne fera aucune conversion de données avec les modèles Pydantic, elle ne c
Cela vous donne beaucoup de flexibilité. Vous pouvez retourner n'importe quel type de données, surcharger n'importe quelle déclaration ou validation de données.
## Utiliser le `jsonable_encoder` dans une `Response` { #using-the-jsonable-encoder-in-a-response }
## Utiliser le `jsonable_encoder` dans une `Response`
Parce que **FastAPI** n'apporte aucune modification à une `Response` que vous retournez, vous devez vous assurer que son contenu est prêt à être utilisé.
Parce que **FastAPI** n'apporte aucune modification à une `Response` que vous retournez, vous devez vous assurer que son contenu est prêt à être utilisé (sérialisable).
Par exemple, vous ne pouvez pas mettre un modèle Pydantic dans une `JSONResponse` sans d'abord le convertir en un `dict` avec tous les types de données (comme `datetime`, `UUID`, etc.) convertis en types compatibles avec JSON.
Pour ces cas, vous pouvez spécifier un appel à `jsonable_encoder` pour convertir vos données avant de les passer à une réponse :
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
/// note | Détails techniques
@@ -44,7 +44,7 @@ Vous pouvez aussi utiliser `from starlette.responses import JSONResponse`.
///
## Renvoyer une `Response` personnalisée { #returning-a-custom-response }
## Renvoyer une `Response` personnalisée
L'exemple ci-dessus montre toutes les parties dont vous avez besoin, mais il n'est pas encore très utile, car vous auriez pu retourner l'`item` directement, et **FastAPI** l'aurait mis dans une `JSONResponse` pour vous, en le convertissant en `dict`, etc. Tout cela par défaut.
@@ -54,9 +54,9 @@ Disons que vous voulez retourner une réponse <a href="https://en.wikipedia.org/
Vous pouvez mettre votre contenu XML dans une chaîne de caractères, la placer dans une `Response`, et la retourner :
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
## Notes { #notes }
## Notes
Lorsque vous renvoyez une `Response` directement, ses données ne sont pas validées, converties (sérialisées), ni documentées automatiquement.

View File

@@ -1,34 +1,34 @@
# Tests de performance { #benchmarks }
# Test de performance
Des tests de performance indépendants de TechEmpower montrent que les applications **FastAPI** exécutées sous Uvicorn sont <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">parmi les frameworks Python les plus rapides disponibles</a>, seulement derrière Starlette et Uvicorn euxmêmes (tous deux utilisés en interne par FastAPI).
Les tests de performance de TechEmpower montrent que les applications **FastAPI** tournant sous Uvicorn comme <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">étant l'un des frameworks Python les plus rapides disponibles</a>, seulement inférieur à Starlette et Uvicorn (tous deux utilisés au cœur de FastAPI). (*)
Mais lorsque vous examinez les tests de performance et les comparaisons, vous devez garder les points suivants à l'esprit.
Mais en prêtant attention aux tests de performance et aux comparaisons, il faut tenir compte de ce qu'il suit.
## Tests de performance et rapidité { #benchmarks-and-speed }
## Tests de performance et rapidité
Lorsque vous vérifiez les tests de performance, il est courant de voir plusieurs outils de types différents comparés comme équivalents.
Lorsque vous vérifiez les tests de performance, il est commun de voir plusieurs outils de différents types comparés comme équivalents.
En particulier, on voit Uvicorn, Starlette et FastAPI comparés ensemble (parmi de nombreux autres outils).
En particulier, on voit Uvicorn, Starlette et FastAPI comparés (parmi de nombreux autres outils).
Plus le problème résolu par loutil est simple, meilleures seront ses performances. Et la plupart des tests de performance ne testent pas les fonctionnalités additionnelles fournies par loutil.
Plus le problème résolu par un outil est simple, mieux seront les performances obtenues. Et la plupart des tests de performance ne prennent pas en compte les fonctionnalités additionnelles fournies par les outils.
La hiérarchie est la suivante :
* **Uvicorn** : un serveur ASGI
* **Starlette** : (utilise Uvicorn) un microframework web
* **FastAPI** : (utilise Starlette) un microframework dAPI avec plusieurs fonctionnalités supplémentaires pour créer des API, notamment la validation des données, etc.
* **Starlette** : (utilise Uvicorn) un micro-framework web
* **FastAPI**: (utilise Starlette) un micro-framework pour API disposant de fonctionnalités additionnelles pour la création d'API, avec la validation des données, etc.
* **Uvicorn** :
* Aura la meilleure performance, car il na pas beaucoup de code supplémentaire en dehors du serveur luimême.
* Vous nécririez pas une application directement avec Uvicorn. Cela signifierait que votre code devrait inclure, plus ou moins, au minimum, tout le code fourni par Starlette (ou **FastAPI**). Et si vous faisiez cela, votre application finale aurait la même surcharge que si vous aviez utilisé un framework, tout en minimisant le code de votre application et les bugs.
* Si vous comparez Uvicorn, comparezle à Daphne, Hypercorn, uWSGI, etc. Des serveurs dapplications.
* A les meilleures performances, étant donné qu'il n'a pas beaucoup de code mis-à-part le serveur en lui-même.
* On n'écrit pas une application avec uniquement Uvicorn. Cela signifie que le code devrait inclure plus ou moins, au minimum, tout le code offert par Starlette (ou **FastAPI**). Et si on fait cela, l'application finale apportera les mêmes complications que si on avait utilisé un framework et que l'on avait minimisé la quantité de code et de bugs.
* Si on compare Uvicorn, il faut le comparer à d'autre applications de serveurs comme Daphne, Hypercorn, uWSGI, etc.
* **Starlette** :
* Aura la deuxième meilleure performance, après Uvicorn. En fait, Starlette utilise Uvicorn pour sexécuter. Donc, il ne peut probablement être que «plus lent» quUvicorn, puisquil doit exécuter plus de code.
* Mais il fournit les outils pour construire des applications web simples, avec un routage basé sur les chemins, etc.
* Si vous comparez Starlette, comparezle à Sanic, Flask, Django, etc. Des frameworks web (ou microframeworks).
* A les seconde meilleures performances après Uvicorn. Starlette utilise en réalité Uvicorn. De ce fait, il ne peut quêtre plus "lent" qu'Uvicorn car il requiert l'exécution de plus de code.
* Cependant il nous apporte les outils pour construire une application web simple, avec un routage basé sur des chemins, etc.
* Si on compare Starlette, il faut le comparer à d'autres frameworks web (ou micorframework) comme Sanic, Flask, Django, etc.
* **FastAPI** :
* De la même manière que Starlette utilise Uvicorn et ne peut pas être plus rapide que lui, **FastAPI** utilise Starlette, il ne peut donc pas être plus rapide que lui.
* FastAPI apporte davantage de fonctionnalités audessus de Starlette. Des fonctionnalités dont vous avez presque toujours besoin lors de la création dAPI, comme la validation des données et la sérialisation. Et en lutilisant, vous obtenez gratuitement une documentation automatique (la documentation automatique najoute même pas de surcharge aux applications en cours dexécution, elle est générée au démarrage).
* Si vous nutilisiez pas FastAPI et utilisiez directement Starlette (ou un autre outil, comme Sanic, Flask, Responder, etc.), vous devriez implémenter vousmême toute la validation et la sérialisation des données. Ainsi, votre application finale aurait tout de même la même surcharge que si elle avait été construite avec FastAPI. Et, dans de nombreux cas, cette validation et cette sérialisation des données représentent la plus grande quantité de code écrite dans les applications.
* Donc, en utilisant FastAPI, vous gagnez du temps de développement, réduisez les bugs, diminuez le nombre de lignes de code, et vous obtiendrez probablement les mêmes performances (ou de meilleures) que si vous ne lutilisiez pas (car vous auriez à tout implémenter dans votre code).
* Si vous comparez FastAPI, comparezle à un framework (ou un ensemble doutils) dapplications web qui fournit la validation des données, la sérialisation et la documentation, comme Flaskapispec, NestJS, Molten, etc. Des frameworks avec validation des données, sérialisation et documentation automatiques intégrées.
* Comme Starlette, FastAPI utilise Uvicorn et ne peut donc pas être plus rapide que ce dernier.
* FastAPI apporte des fonctionnalités supplémentaires à Starlette. Des fonctionnalités qui sont nécessaires presque systématiquement lors de la création d'une API, comme la validation des données, la sérialisation. En utilisant FastAPI, on obtient une documentation automatiquement (qui ne requiert aucune manipulation pour être mise en place).
* Si on n'utilisait pas FastAPI mais directement Starlette (ou un outil équivalent comme Sanic, Flask, Responder, etc) il faudrait implémenter la validation des données et la sérialisation par nous-même. Le résultat serait donc le même dans les deux cas mais du travail supplémentaire serait à réaliser avec Starlette, surtout en considérant que la validation des données et la sérialisation représentent la plus grande quantité de code à écrire dans une application.
* De ce fait, en utilisant FastAPI on minimise le temps de développement, les bugs, le nombre de lignes de code, et on obtient les mêmes performances (si ce n'est de meilleurs performances) que l'on aurait pu avoir sans ce framework (en ayant à implémenter de nombreuses fonctionnalités importantes par nous-mêmes).
* Si on compare FastAPI, il faut le comparer à d'autres frameworks web (ou ensemble d'outils) qui fournissent la validation des données, la sérialisation et la documentation, comme Flask-apispec, NestJS, Molten, etc.

View File

@@ -1,151 +1,74 @@
# FastAPI dans des conteneurs - Docker { #fastapi-in-containers-docker }
# Déployer avec Docker
Lors du déploiement d'applications FastAPI, une approche courante consiste à construire une **image de conteneur Linux**. C'est généralement fait avec <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>. Vous pouvez ensuite déployer cette image de conteneur de plusieurs manières possibles.
Dans cette section, vous verrez des instructions et des liens vers des guides pour savoir comment :
L'utilisation de conteneurs Linux présente plusieurs avantages, notamment la **sécurité**, la **réplicabilité**, la **simplicité**, et d'autres.
* Faire de votre application **FastAPI** une image/conteneur Docker avec une performance maximale. En environ **5 min**.
* (Optionnellement) comprendre ce que vous, en tant que développeur, devez savoir sur HTTPS.
* Configurer un cluster en mode Docker Swarm avec HTTPS automatique, même sur un simple serveur à 5 dollars US/mois. En environ **20 min**.
* Générer et déployer une application **FastAPI** complète, en utilisant votre cluster Docker Swarm, avec HTTPS, etc. En environ **10 min**.
Vous pouvez utiliser <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> pour le déploiement. Il présente plusieurs avantages comme la sécurité, la réplicabilité, la simplicité de développement, etc.
Si vous utilisez Docker, vous pouvez utiliser l'image Docker officielle :
## <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>
Cette image est dotée d'un mécanisme d'"auto-tuning", de sorte qu'il vous suffit d'ajouter votre code pour obtenir automatiquement des performances très élevées. Et sans faire de sacrifices.
Mais vous pouvez toujours changer et mettre à jour toutes les configurations avec des variables d'environnement ou des fichiers de configuration.
/// tip | Astuce
Pressé et vous connaissez déjà tout ça ? Passez au [`Dockerfile` ci-dessous 👇](#build-a-docker-image-for-fastapi).
Pour voir toutes les configurations et options, rendez-vous sur la page de l'image Docker : <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
///
<details>
<summary>Aperçu du Dockerfile 👀</summary>
## Créer un `Dockerfile`
* Allez dans le répertoire de votre projet.
* Créez un `Dockerfile` avec :
```Dockerfile
FROM python:3.9
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
# Si vous exécutez derrière un proxy comme Nginx ou Traefik, ajoutez --proxy-headers
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
COPY ./app /app
```
</details>
### Applications plus larges
## Qu'est-ce qu'un conteneur { #what-is-a-container }
Si vous avez suivi la section sur la création d' [Applications avec plusieurs fichiers](../tutorial/bigger-applications.md){.internal-link target=_blank}, votre `Dockerfile` pourrait ressembler à ceci :
Les conteneurs (principalement les conteneurs Linux) sont une manière très **légère** d'emballer des applications, y compris toutes leurs dépendances et fichiers nécessaires, tout en les gardant isolées des autres conteneurs (autres applications ou composants) dans le même système.
```Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
Les conteneurs Linux s'exécutent en utilisant le même noyau Linux que lhôte (machine, machine virtuelle, serveur cloud, etc.). Cela signifie simplement quils sont très légers (comparés aux machines virtuelles complètes qui émuleraient un système dexploitation entier).
De cette façon, les conteneurs consomment **peu de ressources**, une quantité comparable à lexécution des processus directement (une machine virtuelle consommerait bien plus).
Les conteneurs ont également leurs **processus isolés** (généralement un seul processus), leur système de fichiers et leur réseau, ce qui simplifie le déploiement, la sécurité, le développement, etc.
## Qu'est-ce qu'une image de conteneur { #what-is-a-container-image }
Un **conteneur** est exécuté à partir dune **image de conteneur**.
Une image de conteneur est une version **statique** de tous les fichiers, variables denvironnement et de la commande/programme par défaut qui doivent être présents dans un conteneur. **Statique** signifie ici que l**image** du conteneur nest pas en cours dexécution, elle nest pas exécutée, ce sont uniquement les fichiers et métadonnées empaquetés.
Par opposition à une «**image de conteneur**» qui est le contenu statique stocké, un «**conteneur**» fait normalement référence à linstance en cours dexécution, la chose qui est **exécutée**.
Lorsque le **conteneur** est démarré et en cours dexécution (démarré à partir dune **image de conteneur**), il peut créer ou modifier des fichiers, des variables denvironnement, etc. Ces changements nexisteront que dans ce conteneur, mais ne persisteront pas dans limage de conteneur sous-jacente (ils ne seront pas sauvegardés sur le disque).
Une image de conteneur est comparable au **programme** et à ses fichiers, par exemple `python` et un fichier `main.py`.
Et le **conteneur** lui-même (par opposition à l**image de conteneur**) est linstance réellement en cours dexécution de limage, comparable à un **processus**. En fait, un conteneur nest en cours dexécution que lorsquil a un **processus en cours** (et normalement un seul processus). Le conteneur sarrête lorsquil ny a plus de processus en cours à lintérieur.
## Images de conteneur { #container-images }
Docker a été lun des principaux outils pour créer et gérer des **images de conteneur** et des **conteneurs**.
Il existe un <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> public avec des **images de conteneur officielles** préconstruites pour de nombreux outils, environnements, bases de données et applications.
Par exemple, il existe une <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">image Python</a> officielle.
Et il existe de nombreuses autres images pour différentes choses comme des bases de données, par exemple pour :
* <a href="https://hub.docker.com/_/postgres" class="external-link" target="_blank">PostgreSQL</a>
* <a href="https://hub.docker.com/_/mysql" class="external-link" target="_blank">MySQL</a>
* <a href="https://hub.docker.com/_/mongo" class="external-link" target="_blank">MongoDB</a>
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a>, etc.
En utilisant une image de conteneur préconstruite, il est très facile de **combiner** et dutiliser différents outils. Par exemple, pour essayer une nouvelle base de données. Dans la plupart des cas, vous pouvez utiliser les **images officielles**, et simplement les configurer avec des variables denvironnement.
De cette manière, dans de nombreux cas, vous pouvez apprendre les conteneurs et Docker et réutiliser ces connaissances avec de nombreux outils et composants différents.
Ainsi, vous exécuteriez **plusieurs conteneurs** avec différentes choses, comme une base de données, une application Python, un serveur web avec une application frontend React, et vous les connecteriez ensemble via leur réseau interne.
Tous les systèmes de gestion de conteneurs (comme Docker ou Kubernetes) intègrent ces fonctionnalités réseau.
## Conteneurs et processus { #containers-and-processes }
Une **image de conteneur** inclut normalement dans ses métadonnées le programme ou la commande par défaut à exécuter lorsque le **conteneur** est démarré et les paramètres à passer à ce programme. Très similaire à ce que vous feriez en ligne de commande.
Lorsquun **conteneur** est démarré, il exécute cette commande/ce programme (bien que vous puissiez la/le remplacer et faire exécuter une commande/un programme différent).
Un conteneur est en cours dexécution tant que le **processus principal** (commande ou programme) est en cours dexécution.
Un conteneur a normalement un **seul processus**, mais il est également possible de démarrer des sous-processus à partir du processus principal, et ainsi vous aurez **plusieurs processus** dans le même conteneur.
Mais il nest pas possible davoir un conteneur en cours dexécution sans **au moins un processus en cours**. Si le processus principal sarrête, le conteneur sarrête.
## Construire une image Docker pour FastAPI { #build-a-docker-image-for-fastapi }
Daccord, construisons quelque chose maintenant ! 🚀
Je vais vous montrer comment construire une **image Docker** pour FastAPI **depuis zéro**, basée sur limage **Python officielle**.
Cest ce que vous voudrez faire dans **la plupart des cas**, par exemple :
* En utilisant **Kubernetes** ou des outils similaires
* Lors de lexécution sur un **Raspberry Pi**
* En utilisant un service cloud qui exécuterait une image de conteneur pour vous, etc.
### Dépendances des packages { #package-requirements }
Vous avez normalement les **dépendances** de votre application dans un fichier.
Cela dépend principalement de loutil que vous utilisez pour **installer** ces dépendances.
La manière la plus courante de le faire est davoir un fichier `requirements.txt` avec les noms des packages et leurs versions, un par ligne.
Vous utiliseriez bien sûr les mêmes idées lues dans [À propos des versions de FastAPI](versions.md){.internal-link target=_blank} pour définir les plages de versions.
Par exemple, votre `requirements.txt` pourrait ressembler à :
```
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
COPY ./app /app/app
```
Et vous installeriez normalement ces dépendances de packages avec `pip`, par exemple :
### Raspberry Pi et autres architectures
<div class="termy">
Si vous utilisez Docker sur un Raspberry Pi (qui a un processeur ARM) ou toute autre architecture, vous pouvez créer un `Dockerfile` à partir de zéro, basé sur une image de base Python (qui est multi-architecture) et utiliser Uvicorn seul.
```console
$ pip install -r requirements.txt
---> 100%
Successfully installed fastapi pydantic
Dans ce cas, votre `Dockerfile` pourrait ressembler à ceci :
```Dockerfile
FROM python:3.7
RUN pip install fastapi uvicorn
EXPOSE 80
COPY ./app /app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
```
</div>
## Créer le code **FastAPI**.
/// info
Il existe dautres formats et outils pour définir et installer des dépendances de packages.
///
### Créer le code **FastAPI** { #create-the-fastapi-code }
* Créez un répertoire `app` et entrez dedans.
* Créez un fichier vide `__init__.py`.
* Créer un répertoire `app` et y entrer.
* Créez un fichier `main.py` avec :
```Python
from typing import Union
from typing import Optional
from fastapi import FastAPI
@@ -158,169 +81,23 @@ def read_root():
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
def read_item(item_id: int, q: Optional[str] = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Maintenant, dans le même répertoire de projet, créez un fichier `Dockerfile` avec :
```{ .dockerfile .annotate }
# (1)!
FROM python:3.9
# (2)!
WORKDIR /code
# (3)!
COPY ./requirements.txt /code/requirements.txt
# (4)!
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (5)!
COPY ./app /code/app
# (6)!
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Démarrer à partir de limage de base Python officielle.
2. Définir le répertoire de travail courant sur `/code`.
Cest là que nous placerons le fichier `requirements.txt` et le répertoire `app`.
3. Copier le fichier des dépendances dans le répertoire `/code`.
Copier **uniquement** le fichier des dépendances en premier, pas le reste du code.
Comme ce fichier **ne change pas souvent**, Docker le détectera et utilisera le **cache** pour cette étape, activant aussi le cache pour létape suivante.
4. Installer les dépendances de packages du fichier des dépendances.
Loption `--no-cache-dir` indique à `pip` de ne pas enregistrer localement les packages téléchargés, ce qui nest utile que si `pip` devait être relancé pour installer les mêmes packages, ce qui nest pas le cas lorsquon travaille avec des conteneurs.
/// note | Remarque
Le `--no-cache-dir` concerne uniquement `pip`, cela na rien à voir avec Docker ou les conteneurs.
///
Loption `--upgrade` indique à `pip` de mettre à niveau les packages sils sont déjà installés.
Comme létape précédente de copie du fichier peut être détectée par le **cache Docker**, cette étape **utilisera également le cache Docker** lorsquil est disponible.
Utiliser le cache à cette étape vous fera **gagner** beaucoup de **temps** lors de la reconstruction de limage à maintes reprises pendant le développement, au lieu de **télécharger et installer** toutes les dépendances **à chaque fois**.
5. Copier le répertoire `./app` dans le répertoire `/code`.
Comme cela contient tout le code, qui est ce qui **change le plus fréquemment**, le **cache** Docker ne sera pas facilement utilisé pour cette étape ni pour les **étapes suivantes**.
Il est donc important de mettre ceci **vers la fin** du `Dockerfile`, pour optimiser les temps de construction de limage de conteneur.
6. Définir la **commande** pour utiliser `fastapi run`, qui utilise Uvicorn en dessous.
`CMD` prend une liste de chaînes, chacune de ces chaînes est ce que vous taperiez en ligne de commande séparé par des espaces.
Cette commande sera exécutée depuis le **répertoire de travail courant**, le même répertoire `/code` que vous avez défini ci-dessus avec `WORKDIR /code`.
/// tip | Astuce
Passez en revue ce que fait chaque ligne en cliquant sur chaque bulle numérotée dans le code. 👆
///
/// warning | Attention
Assurez-vous dutiliser **toujours** la **forme exec** de linstruction `CMD`, comme expliqué ci-dessous.
///
#### Utiliser `CMD` - forme Exec { #use-cmd-exec-form }
Linstruction Docker <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a> peut être écrite sous deux formes :
✅ Forme **exec** :
```Dockerfile
# ✅ À faire
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ Forme **shell** :
```Dockerfile
# ⛔️ À ne pas faire
CMD fastapi run app/main.py --port 80
```
Assurez-vous dutiliser toujours la forme **exec** afin que FastAPI puisse sarrêter proprement et que les [événements de durée de vie](../advanced/events.md){.internal-link target=_blank} soient déclenchés.
Vous pouvez en lire davantage dans la <a href="https://docs.docker.com/reference/dockerfile/#shell-and-exec-form" class="external-link" target="_blank">documentation Docker sur les formes shell et exec</a>.
Cela peut être assez perceptible avec `docker compose`. Voir cette section de la FAQ Docker Compose pour plus de détails techniques : <a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">Pourquoi mes services prennent-ils 10 secondes pour être recréés ou arrêtés ?</a>.
#### Structure des répertoires { #directory-structure }
Vous devriez maintenant avoir une structure de répertoires comme :
* Vous devriez maintenant avoir une structure de répertoire telle que :
```
.
├── app
│   ├── __init__.py
│ └── main.py
── Dockerfile
└── requirements.txt
── Dockerfile
```
#### Derrière un proxy de terminaison TLS { #behind-a-tls-termination-proxy }
## Construire l'image Docker
Si vous exécutez votre conteneur derrière un proxy de terminaison TLS (équilibreur de charge) comme Nginx ou Traefik, ajoutez loption `--proxy-headers`. Cela indiquera à Uvicorn (via la CLI FastAPI) de faire confiance aux en-têtes envoyés par ce proxy lui indiquant que lapplication sexécute derrière HTTPS, etc.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Cache Docker { #docker-cache }
Il y a une astuce importante dans ce `Dockerfile` : nous copions dabord **le fichier des dépendances seul**, pas le reste du code. Laissez-moi vous expliquer pourquoi.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker et dautres outils **construisent** ces images de conteneur **de manière incrémentale**, en ajoutant **une couche au-dessus de lautre**, en commençant par le haut du `Dockerfile` et en ajoutant les fichiers créés par chacune des instructions du `Dockerfile`.
Docker et des outils similaires utilisent également un **cache interne** lors de la construction de limage : si un fichier na pas changé depuis la dernière construction, il **réutilisera la même couche** créée précédemment, au lieu de recopier le fichier et de créer une nouvelle couche à partir de zéro.
Éviter simplement la copie de fichiers naméliore pas forcément énormément les choses, mais comme le cache a été utilisé pour cette étape, il peut **être utilisé pour létape suivante**. Par exemple, il pourra être utilisé pour linstruction qui installe les dépendances :
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
Le fichier avec les dépendances des packages **ne changera pas fréquemment**. Ainsi, en copiant uniquement ce fichier, Docker pourra **utiliser le cache** pour cette étape.
Et ensuite, Docker pourra **utiliser le cache pour létape suivante** qui télécharge et installe ces dépendances. Et cest là que nous **économisons beaucoup de temps**. ✨ ... et évitons lennui en attendant. 😪😆
Télécharger et installer les dépendances de packages **peut prendre des minutes**, alors quutiliser le **cache** ne **prendra que quelques secondes** au maximum.
Et comme vous reconstruirez limage de conteneur encore et encore pendant le développement pour vérifier que vos modifications de code fonctionnent, cela vous fera gagner beaucoup de temps cumulé.
Puis, vers la fin du `Dockerfile`, nous copions tout le code. Comme cest ce qui **change le plus fréquemment**, nous le mettons vers la fin, car presque toujours, tout ce qui suit cette étape ne pourra pas utiliser le cache.
```Dockerfile
COPY ./app /code/app
```
### Construire limage Docker { #build-the-docker-image }
Maintenant que tous les fichiers sont en place, construisons limage de conteneur.
* Allez dans le répertoire du projet (là où se trouve votre `Dockerfile`, contenant votre répertoire `app`).
* Construisez votre image FastAPI :
* Allez dans le répertoire du projet (dans lequel se trouve votre `Dockerfile`, contenant votre répertoire `app`).
* Construisez votre image FastAPI :
<div class="termy">
@@ -332,15 +109,7 @@ $ docker build -t myimage .
</div>
/// tip | Astuce
Remarquez le `.` à la fin, cest équivalent à `./`, il indique à Docker le répertoire à utiliser pour construire limage de conteneur.
Dans ce cas, cest le répertoire courant (`.`).
///
### Démarrer le conteneur Docker { #start-the-docker-container }
## Démarrer le conteneur Docker
* Exécutez un conteneur basé sur votre image :
@@ -352,9 +121,12 @@ $ docker run -d --name mycontainer -p 80:80 myimage
</div>
## Vérifier { #check-it }
Vous disposez maintenant d'un serveur FastAPI optimisé dans un conteneur Docker. Configuré automatiquement pour votre
serveur actuel (et le nombre de cœurs du CPU).
Vous devriez pouvoir le vérifier dans lURL de votre conteneur Docker, par exemple : <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> ou <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (ou équivalent, en utilisant votre hôte Docker).
## Vérifier
Vous devriez pouvoir accéder à votre application via l'URL de votre conteneur Docker, par exemple : <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> ou <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (ou équivalent, en utilisant votre hôte Docker).
Vous verrez quelque chose comme :
@@ -362,15 +134,15 @@ Vous verrez quelque chose comme :
{"item_id": 5, "q": "somequery"}
```
## Documentation interactive de lAPI { #interactive-api-docs }
## Documentation interactive de l'API
Vous pouvez maintenant aller sur <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> ou <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (ou équivalent, en utilisant votre hôte Docker).
Vous pouvez maintenant visiter <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> ou <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (ou équivalent, en utilisant votre hôte Docker).
Vous verrez la documentation API interactive automatique (fournie par <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>) :
Vous verrez la documentation interactive automatique de l'API (fournie par <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)
## Documentation API alternative { #alternative-api-docs }
## Documentation de l'API alternative
Et vous pouvez également aller sur <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> ou <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (ou équivalent, en utilisant votre hôte Docker).
@@ -378,243 +150,36 @@ Vous verrez la documentation automatique alternative (fournie par <a href="https
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Construire une image Docker avec un FastAPI monofichier { #build-a-docker-image-with-a-single-file-fastapi }
## Traefik
Si votre FastAPI est un seul fichier, par exemple `main.py` sans répertoire `./app`, votre structure de fichiers pourrait ressembler à ceci :
<a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a> est un reverse proxy/load balancer
haute performance. Il peut faire office de "Proxy de terminaison TLS" (entre autres fonctionnalités).
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Il est intégré à Let's Encrypt. Ainsi, il peut gérer toutes les parties HTTPS, y compris l'acquisition et le renouvellement des certificats.
Vous nauriez alors quà changer les chemins correspondants pour copier le fichier dans le `Dockerfile` :
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (1)!
COPY ./main.py /code/
# (2)!
CMD ["fastapi", "run", "main.py", "--port", "80"]
```
1. Copier le fichier `main.py` directement dans le répertoire `/code` (sans répertoire `./app`).
2. Utiliser `fastapi run` pour servir votre application dans le fichier unique `main.py`.
Quand vous passez le fichier à `fastapi run`, il détectera automatiquement quil sagit dun fichier unique et non dun package et saura comment limporter et servir votre application FastAPI. 😎
## Concepts de déploiement { #deployment-concepts }
Parlons à nouveau de certains des mêmes [Concepts de déploiement](concepts.md){.internal-link target=_blank} en termes de conteneurs.
Les conteneurs sont principalement un outil pour simplifier le processus de **construction et de déploiement** dune application, mais ils nimposent pas une approche particulière pour gérer ces **concepts de déploiement**, et il existe plusieurs stratégies possibles.
La **bonne nouvelle** est quavec chaque stratégie différente, il existe un moyen de couvrir tous les concepts de déploiement. 🎉
Réexaminons ces **concepts de déploiement** en termes de conteneurs :
* HTTPS
* Exécution au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours)
* Mémoire
* Étapes préalables avant de démarrer
## HTTPS { #https }
Si lon se concentre uniquement sur **limage de conteneur** pour une application FastAPI (et plus tard le **conteneur** en cours), HTTPS serait normalement géré **en externe** par un autre outil.
Cela pourrait être un autre conteneur, par exemple avec <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>, gérant **HTTPS** et lacquisition **automatique** des **certificats**.
/// tip | Astuce
Traefik a des intégrations avec Docker, Kubernetes, et dautres, il est donc très facile de configurer HTTPS pour vos conteneurs avec lui.
///
Alternativement, HTTPS pourrait être géré par un fournisseur cloud comme lun de leurs services (tout en exécutant lapplication dans un conteneur).
## Exécution au démarrage et redémarrages { #running-on-startup-and-restarts }
Il y a normalement un autre outil chargé de **démarrer et exécuter** votre conteneur.
Cela pourrait être **Docker** directement, **Docker Compose**, **Kubernetes**, un **service cloud**, etc.
Dans la plupart (ou toutes) les situations, il y a une option simple pour activer lexécution du conteneur au démarrage et les redémarrages en cas déchec. Par exemple, dans Docker, cest loption de ligne de commande `--restart`.
Sans utiliser les conteneurs, faire en sorte que les applications sexécutent au démarrage et avec redémarrages peut être fastidieux et difficile. Mais en **travaillant avec des conteneurs**, dans la plupart des cas, cette fonctionnalité est incluse par défaut. ✨
## Réplication - Nombre de processus { #replication-number-of-processes }
Si vous avez un <abbr title="Un groupe de machines configurées pour être connectées et travailler ensemble d'une certaine manière.">cluster</abbr> de machines avec **Kubernetes**, Docker Swarm Mode, Nomad, ou un autre système complexe similaire pour gérer des conteneurs distribués sur plusieurs machines, alors vous voudrez probablement **gérer la réplication** au **niveau du cluster** plutôt que dutiliser un **gestionnaire de processus** (comme Uvicorn avec des workers) dans chaque conteneur.
Lun de ces systèmes de gestion de conteneurs distribués, comme Kubernetes, dispose normalement dun moyen intégré de gérer la **réplication des conteneurs** tout en prenant en charge **léquilibrage de charge** pour les requêtes entrantes. Tout cela au **niveau du cluster**.
Dans ces cas, vous voudrez probablement construire une **image Docker depuis zéro** comme [expliqué ci-dessus](#dockerfile), en installant vos dépendances, et en exécutant **un seul processus Uvicorn** au lieu dutiliser plusieurs workers Uvicorn.
### Équilibreur de charge { #load-balancer }
Avec les conteneurs, vous avez normalement un composant **à lécoute sur le port principal**. Il pourrait sagir dun autre conteneur qui est également un **proxy de terminaison TLS** pour gérer **HTTPS**, ou dun outil similaire.
Comme ce composant prend la **charge** des requêtes et les distribue entre les workers de façon (espérons-le) **équilibrée**, on lappelle aussi couramment un **équilibreur de charge**.
/// tip | Astuce
Le même composant **proxy de terminaison TLS** utilisé pour HTTPS sera probablement aussi un **équilibreur de charge**.
///
Et en travaillant avec des conteneurs, le même système que vous utilisez pour les démarrer et les gérer disposera déjà doutils internes pour transmettre la **communication réseau** (par ex. les requêtes HTTP) depuis cet **équilibreur de charge** (qui peut également être un **proxy de terminaison TLS**) vers le ou les conteneurs avec votre application.
### Un équilibreur de charge - plusieurs conteneurs worker { #one-load-balancer-multiple-worker-containers }
Avec **Kubernetes** ou des systèmes de gestion de conteneurs distribués similaires, lutilisation de leurs mécanismes réseau internes permet au **seul** équilibreur de charge qui écoute sur le **port** principal de transmettre la communication (les requêtes) vers **plusieurs conteneurs** exécutant votre application.
Chacun de ces conteneurs exécutant votre application aura normalement **un seul processus** (par ex. un processus Uvicorn exécutant votre application FastAPI). Ils seront tous des **conteneurs identiques**, exécutant la même chose, mais chacun avec son propre processus, sa propre mémoire, etc. De cette manière, vous profitez de la **parallélisation** sur **différents cœurs** du CPU, voire sur **différentes machines**.
Et le système de conteneurs distribué avec l**équilibreur de charge** **distribuera les requêtes** à chacun des conteneurs exécutant votre application **à tour de rôle**. Ainsi, chaque requête pourra être traitée par lun des **conteneurs répliqués** exécutant votre application.
Et normalement, cet **équilibreur de charge** sera capable de gérer les requêtes qui vont vers *dautres* applications de votre cluster (par ex. vers un autre domaine, ou sous un autre préfixe de chemin dURL), et transmettra cette communication aux bons conteneurs pour *cette autre* application sexécutant dans votre cluster.
### Un processus par conteneur { #one-process-per-container }
Dans ce type de scénario, vous voudrez probablement avoir **un seul processus (Uvicorn) par conteneur**, car vous gérez déjà la réplication au niveau du cluster.
Dans ce cas, vous ne voudrez **pas** avoir plusieurs workers dans le conteneur, par exemple avec loption de ligne de commande `--workers`. Vous voudrez avoir simplement **un seul processus Uvicorn** par conteneur (mais probablement plusieurs conteneurs).
Avoir un autre gestionnaire de processus dans le conteneur (comme ce serait le cas avec plusieurs workers) ne ferait quajouter une **complexité inutile** que votre système de cluster gère très probablement déjà.
### Conteneurs avec plusieurs processus et cas particuliers { #containers-with-multiple-processes-and-special-cases }
Bien sûr, il existe des **cas particuliers** où vous pourriez vouloir avoir **un conteneur** avec plusieurs **processus worker Uvicorn** à lintérieur.
Dans ces cas, vous pouvez utiliser loption de ligne de commande `--workers` pour définir le nombre de workers que vous souhaitez exécuter :
```{ .dockerfile .annotate }
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Ici, nous utilisons loption de ligne de commande `--workers` pour définir le nombre de workers à 4.
Voici quelques exemples où cela peut avoir du sens :
#### Une application simple { #a-simple-app }
Vous pourriez vouloir un gestionnaire de processus dans le conteneur si votre application est **suffisamment simple** pour sexécuter sur un **seul serveur**, et non un cluster.
#### Docker Compose { #docker-compose }
Vous pourriez déployer sur un **seul serveur** (pas un cluster) avec **Docker Compose**, vous nauriez donc pas un moyen simple de gérer la réplication de conteneurs (avec Docker Compose) tout en conservant le réseau partagé et **léquilibrage de charge**.
Vous pourriez alors vouloir avoir **un seul conteneur** avec un **gestionnaire de processus** démarrant **plusieurs processus worker** à lintérieur.
Il est également intégré à Docker. Ainsi, vous pouvez déclarer vos domaines dans les configurations de chaque application et faire en sorte qu'elles lisent ces configurations, génèrent les certificats HTTPS et servent via HTTPS à votre application automatiquement, sans nécessiter aucune modification de leurs configurations.
---
Lidée principale est qu**aucune** de ces approches nest **gravée dans la pierre** que vous devez suivre aveuglément. Vous pouvez utiliser ces idées pour **évaluer votre propre cas dutilisation** et décider de la meilleure approche pour votre système, en vérifiant comment gérer les concepts :
Avec ces informations et ces outils, passez à la section suivante pour tout combiner.
* Sécurité - HTTPS
* Exécution au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours)
* Mémoire
* Étapes préalables avant de démarrer
## Cluster en mode Docker Swarm avec Traefik et HTTPS
## Mémoire { #memory }
Vous pouvez avoir un cluster en mode Docker Swarm configuré en quelques minutes (environ 20 min) avec un processus Traefik principal gérant HTTPS (y compris l'acquisition et le renouvellement des certificats).
Si vous exécutez **un seul processus par conteneur**, vous aurez une quantité de mémoire consommée plus ou moins bien définie, stable et limitée par chacun de ces conteneurs (plus dun sils sont répliqués).
En utilisant le mode Docker Swarm, vous pouvez commencer par un "cluster" d'une seule machine (il peut même s'agir
d'un serveur à 5 USD/mois) et ensuite vous pouvez vous développer autant que vous le souhaitez en ajoutant d'autres serveurs.
Vous pourrez ensuite définir ces mêmes limites et exigences de mémoire dans vos configurations pour votre système de gestion de conteneurs (par exemple dans **Kubernetes**). Ainsi, il pourra **répliquer les conteneurs** sur les **machines disponibles** en tenant compte de la quantité de mémoire nécessaire pour eux et de la quantité disponible sur les machines du cluster.
Pour configurer un cluster en mode Docker Swarm avec Traefik et la gestion de HTTPS, suivez ce guide :
Si votre application est **simple**, cela ne sera probablement **pas un problème**, et vous naurez peut-être pas besoin de spécifier des limites strictes de mémoire. Mais si vous **utilisez beaucoup de mémoire** (par exemple avec des modèles de **machine learning**), vous devriez vérifier la quantité de mémoire que vous consommez et ajuster le **nombre de conteneurs** exécutés sur **chaque machine** (et peut-être ajouter davantage de machines à votre cluster).
### <a href="https://medium.com/@tiangolo/docker-swarm-mode-and-traefik-for-a-https-cluster-20328dba6232" class="external-link" target="_blank">Docker Swarm Mode et Traefik pour un cluster HTTPS</a>
Si vous exécutez **plusieurs processus par conteneur**, vous devrez vous assurer que le nombre de processus lancés ne **consomme pas plus de mémoire** que celle disponible.
### Déployer une application FastAPI
## Étapes préalables avant de démarrer et conteneurs { #previous-steps-before-starting-and-containers }
La façon la plus simple de tout mettre en place, serait d'utiliser les [**Générateurs de projet FastAPI**](../project-generation.md){.internal-link target=_blank}.
Si vous utilisez des conteneurs (par ex. Docker, Kubernetes), alors il y a deux approches principales que vous pouvez utiliser.
Le génerateur de projet adéquat est conçu pour être intégré à ce cluster Docker Swarm avec Traefik et HTTPS décrit ci-dessus.
### Plusieurs conteneurs { #multiple-containers }
Vous pouvez générer un projet en 2 min environ.
Si vous avez **plusieurs conteneurs**, probablement chacun exécutant un **seul processus** (par exemple, dans un cluster **Kubernetes**), alors vous voudrez probablement avoir un **conteneur séparé** effectuant le travail des **étapes préalables** dans un seul conteneur, exécutant un seul processus, **avant** dexécuter les conteneurs worker répliqués.
/// info
Si vous utilisez Kubernetes, il sagira probablement dun <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>.
///
Si, dans votre cas dutilisation, il ny a pas de problème à exécuter ces étapes préalables **plusieurs fois en parallèle** (par exemple si vous nexécutez pas de migrations de base de données, mais vérifiez simplement si la base est prête), vous pouvez aussi simplement les placer dans chaque conteneur juste avant de démarrer le processus principal.
### Un seul conteneur { #single-container }
Si vous avez une configuration simple, avec un **seul conteneur** qui démarre ensuite plusieurs **processus worker** (ou aussi un seul processus), alors vous pouvez exécuter ces étapes préalables dans le même conteneur, juste avant de démarrer le processus avec lapplication.
### Image Docker de base { #base-docker-image }
Il existait une image Docker officielle FastAPI : <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Mais elle est maintenant dépréciée. ⛔️
Vous ne devriez probablement **pas** utiliser cette image Docker de base (ni aucune autre similaire).
Si vous utilisez **Kubernetes** (ou autres) et que vous définissez déjà la **réplication** au niveau du cluster, avec plusieurs **conteneurs**, dans ces cas, il vaut mieux **construire une image depuis zéro** comme décrit ci-dessus : [Construire une image Docker pour FastAPI](#build-a-docker-image-for-fastapi).
Et si vous avez besoin davoir plusieurs workers, vous pouvez simplement utiliser loption de ligne de commande `--workers`.
/// note | Détails techniques
Limage Docker a été créée à une époque où Uvicorn ne prenait pas en charge la gestion et le redémarrage des workers morts, il fallait donc utiliser Gunicorn avec Uvicorn, ce qui ajoutait pas mal de complexité, uniquement pour que Gunicorn gère et redémarre les processus worker Uvicorn.
Mais maintenant quUvicorn (et la commande `fastapi`) prennent en charge lutilisation de `--workers`, il ny a plus de raison dutiliser une image Docker de base au lieu de construire la vôtre (cela représente à peu près la même quantité de code 😅).
///
## Déployer limage de conteneur { #deploy-the-container-image }
Après avoir une image de conteneur (Docker), il existe plusieurs façons de la déployer.
Par exemple :
* Avec **Docker Compose** sur un seul serveur
* Avec un cluster **Kubernetes**
* Avec un cluster en mode Docker Swarm
* Avec un autre outil comme Nomad
* Avec un service cloud qui prend votre image de conteneur et la déploie
## Image Docker avec `uv` { #docker-image-with-uv }
Si vous utilisez <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> pour installer et gérer votre projet, vous pouvez suivre leur <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">guide Docker uv</a>.
## Récapitulatif { #recap }
Avec les systèmes de conteneurs (par ex. avec **Docker** et **Kubernetes**), il devient assez simple de gérer tous les **concepts de déploiement** :
* HTTPS
* Exécution au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours)
* Mémoire
* Étapes préalables avant de démarrer
Dans la plupart des cas, vous ne voudrez probablement pas utiliser dimage de base, et plutôt **construire une image de conteneur depuis zéro** basée sur limage Docker Python officielle.
En prenant soin de **lordre** des instructions dans le `Dockerfile` et du **cache Docker**, vous pouvez **minimiser les temps de construction** pour maximiser votre productivité (et éviter lennui). 😎
Le projet généré a des instructions pour le déployer et le faire prend 2 min de plus.

View File

@@ -1,231 +1,56 @@
# À propos de HTTPS { #about-https }
# À propos de HTTPS
Il est facile de penser que HTTPS est quelque chose qui est simplement «activé» ou non.
Il est facile de penser que HTTPS peut simplement être "activé" ou non.
Mais c'est bien plus complexe que cela.
Mais c'est beaucoup plus complexe que cela.
/// tip | Astuce
/// tip
Si vous êtes pressé ou si cela ne vous intéresse pas, passez aux sections suivantes pour obtenir des instructions pas à pas afin de tout configurer avec différentes techniques.
Si vous êtes pressé ou si cela ne vous intéresse pas, passez aux sections suivantes pour obtenir des instructions étape par étape afin de tout configurer avec différentes techniques.
///
Pour apprendre les bases de **HTTPS**, du point de vue d'un utilisateur, consultez <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
Pour apprendre les bases du HTTPS, du point de vue d'un utilisateur, consultez <a href="https://howhttps.works/"
class="external-link" target="_blank">https://howhttps.works/</a>.
Maintenant, du point de vue d'un **développeur**, voici plusieurs points à garder à l'esprit en pensant à HTTPS :
Maintenant, du point de vue d'un développeur, voici plusieurs choses à avoir en tête en pensant au HTTPS :
* Pour HTTPS, **le serveur** doit **avoir des «certificats»** générés par une **tierce partie**.
* Ces certificats sont en réalité **acquis** auprès de la tierce partie, pas «générés».
* Les certificats ont une **durée de vie**.
* Ils **expirent**.
* Ils doivent ensuite être **renouvelés**, **acquis à nouveau** auprès de la tierce partie.
* Le **cryptage** de la connexion se fait au **niveau TCP**.
* C'est une couche **en dessous de HTTP**.
* Donc, la **gestion du certificat et du cryptage** est effectuée **avant HTTP**.
* **TCP ne connaît pas les «domaines»**. Seulement les adresses IP.
* L'information sur le **domaine spécifique** demandé se trouve dans les **données HTTP**.
* Les **certificats HTTPS** «certifient» un **certain domaine**, mais le protocole et le cryptage ont lieu au niveau TCP, **avant de savoir** quel domaine est traité.
* **Par défaut**, cela signifie que vous ne pouvez avoir **qu'un seul certificat HTTPS par adresse IP**.
* Peu importe la taille de votre serveur ou la petitesse de chacune des applications qu'il héberge.
* Il existe toutefois une **solution** à cela.
* Il existe une **extension** du protocole **TLS** (celui qui gère le cryptage au niveau TCP, avant HTTP) appelée **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication Indication du nom du serveur">SNI</abbr></a>**.
* Cette extension SNI permet à un seul serveur (avec **une seule adresse IP**) d'avoir **plusieurs certificats HTTPS** et de servir **plusieurs domaines/applications HTTPS**.
* Pour que cela fonctionne, un **seul** composant (programme) s'exécutant sur le serveur, à l'écoute de l'**adresse IP publique**, doit avoir **tous les certificats HTTPS** du serveur.
* **Après** l'obtention d'une connexion sécurisée, le protocole de communication est **toujours HTTP**.
* Le contenu est **crypté**, même s'il est envoyé avec le **protocole HTTP**.
* Pour le HTTPS, le serveur a besoin de "certificats" générés par une tierce partie.
* Ces certificats sont en fait acquis auprès de la tierce partie, et non "générés".
* Les certificats ont une durée de vie.
* Ils expirent.
* Puis ils doivent être renouvelés et acquis à nouveau auprès de la tierce partie.
* Le cryptage de la connexion se fait au niveau du protocole TCP.
* C'est une couche en dessous de HTTP.
* Donc, le certificat et le traitement du cryptage sont faits avant HTTP.
* TCP ne connaît pas les "domaines", seulement les adresses IP.
* L'information sur le domaine spécifique demandé se trouve dans les données HTTP.
* Les certificats HTTPS "certifient" un certain domaine, mais le protocole et le cryptage se font au niveau TCP, avant de savoir quel domaine est traité.
* Par défaut, cela signifie que vous ne pouvez avoir qu'un seul certificat HTTPS par adresse IP.
* Quelle que soit la taille de votre serveur ou la taille de chacune des applications qu'il contient.
* Il existe cependant une solution à ce problème.
* Il existe une extension du protocole TLS (celui qui gère le cryptage au niveau TCP, avant HTTP) appelée <a
href="https://fr.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr
title="Server Name Indication (indication du nom du serveur)">SNI (indication du nom du serveur)</abbr></a>.
* Cette extension SNI permet à un seul serveur (avec une seule adresse IP) d'avoir plusieurs certificats HTTPS et de servir plusieurs domaines/applications HTTPS.
* Pour que cela fonctionne, un seul composant (programme) fonctionnant sur le serveur, écoutant sur l'adresse IP publique, doit avoir tous les certificats HTTPS du serveur.
* Après avoir obtenu une connexion sécurisée, le protocole de communication est toujours HTTP.
* Le contenu est crypté, même s'il est envoyé avec le protocole HTTP.
Il est courant d'avoir **un programme/serveur HTTP** fonctionnant sur le serveur (la machine, l'hôte, etc.) et **gérant toutes les parties HTTPS** : recevoir les **requêtes HTTPS chiffrées**, envoyer les **requêtes HTTP décryptées** à l'application HTTP réelle fonctionnant sur le même serveur (dans ce cas, l'application **FastAPI**), prendre la **réponse HTTP** de l'application, **la crypter** en utilisant le **certificat HTTPS** approprié et la renvoyer au client en utilisant **HTTPS**. Ce serveur est souvent appelé un **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">proxy de terminaison TLS</a>**.
Il est courant d'avoir un seul programme/serveur HTTP fonctionnant sur le serveur (la machine, l'hôte, etc.) et
gérant toutes les parties HTTPS : envoyer les requêtes HTTP décryptées à l'application HTTP réelle fonctionnant sur
le même serveur (dans ce cas, l'application **FastAPI**), prendre la réponse HTTP de l'application, la crypter en utilisant le certificat approprié et la renvoyer au client en utilisant HTTPS. Ce serveur est souvent appelé un <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Proxy de terminaison TLS</a>.
Parmi les options que vous pourriez utiliser comme proxy de terminaison TLS :
## Let's Encrypt
* Traefik (qui peut aussi gérer le renouvellement des certificats)
* Caddy (qui peut aussi gérer le renouvellement des certificats)
* Nginx
* HAProxy
Avant Let's Encrypt, ces certificats HTTPS étaient vendus par des tiers de confiance.
## Let's Encrypt { #lets-encrypt }
Le processus d'acquisition d'un de ces certificats était auparavant lourd, nécessitait pas mal de paperasses et les certificats étaient assez chers.
Avant Let's Encrypt, ces **certificats HTTPS** étaient vendus par des tiers de confiance.
Mais ensuite, <a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a> a été créé.
Le processus d'acquisition d'un de ces certificats était auparavant lourd, nécessitait pas mal de paperasse et les certificats étaient assez chers.
Mais ensuite, **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>** a été créé.
Il s'agit d'un projet de la Fondation Linux. Il fournit des **certificats HTTPS gratuitement**, de manière automatisée. Ces certificats utilisent toutes les sécurités cryptographiques standard et ont une durée de vie courte (environ 3 mois), de sorte que la **sécurité est en fait meilleure** en raison de leur durée de vie réduite.
Il s'agit d'un projet de la Fondation Linux. Il fournit des certificats HTTPS gratuitement. De manière automatisée. Ces certificats utilisent toutes les sécurités cryptographiques standard et ont une durée de vie courte (environ 3 mois), de sorte que la sécurité est en fait meilleure en raison de leur durée de vie réduite.
Les domaines sont vérifiés de manière sécurisée et les certificats sont générés automatiquement. Cela permet également d'automatiser le renouvellement de ces certificats.
L'idée est d'automatiser l'acquisition et le renouvellement de ces certificats, afin que vous puissiez disposer d'un **HTTPS sécurisé, gratuit et pour toujours**.
## HTTPS pour les développeurs { #https-for-developers }
Voici un exemple de ce à quoi une API HTTPS pourrait ressembler, étape par étape, en se concentrant principalement sur les idées importantes pour les développeurs.
### Nom de domaine { #domain-name }
Tout commencerait probablement par **acquérir** un **nom de domaine**. Ensuite, vous le configureriez dans un serveur DNS (éventuellement votre même fournisseur cloud).
Vous obtiendriez probablement un serveur cloud (une machine virtuelle) ou quelque chose de similaire, et il aurait une **adresse IP publique** <abbr title="qui ne change pas">fixe</abbr>.
Dans le ou les serveurs DNS, vous configureriez un enregistrement (un «`A record`») pour faire pointer **votre domaine** vers l'**adresse IP** publique de **votre serveur**.
Vous feriez probablement cela une seule fois, la première fois, lors de la mise en place de l'ensemble.
/// tip | Astuce
Cette partie sur le nom de domaine se situe bien avant HTTPS, mais comme tout dépend du domaine et de l'adresse IP, cela vaut la peine de la mentionner ici.
///
### DNS { #dns }
Concentrons-nous maintenant sur toutes les parties réellement liées à HTTPS.
D'abord, le navigateur vérifierait auprès des **serveurs DNS** quelle est l'**IP du domaine**, dans ce cas, `someapp.example.com`.
Les serveurs DNS indiqueraient au navigateur d'utiliser une **adresse IP** spécifique. Ce serait l'adresse IP publique utilisée par votre serveur, que vous avez configurée dans les serveurs DNS.
<img src="/img/deployment/https/https01.drawio.svg">
### Début de la poignée de main TLS { #tls-handshake-start }
Le navigateur communiquerait ensuite avec cette adresse IP sur le **port 443** (le port HTTPS).
La première partie de la communication consiste simplement à établir la connexion entre le client et le serveur et à décider des clés cryptographiques qu'ils utiliseront, etc.
<img src="/img/deployment/https/https02.drawio.svg">
Cette interaction entre le client et le serveur pour établir la connexion TLS est appelée la **poignée de main TLS**.
### TLS avec extension SNI { #tls-with-sni-extension }
**Un seul processus** sur le serveur peut être à l'écoute d'un **port** spécifique sur une **adresse IP** spécifique. Il pourrait y avoir d'autres processus à l'écoute d'autres ports sur la même adresse IP, mais un seul pour chaque combinaison adresse IP/port.
TLS (HTTPS) utilise par défaut le port spécifique `443`. C'est donc le port dont nous aurions besoin.
Comme un seul processus peut écouter sur ce port, le processus qui le ferait serait le **proxy de terminaison TLS**.
Le proxy de terminaison TLS aurait accès à un ou plusieurs **certificats TLS** (certificats HTTPS).
En utilisant l'**extension SNI** évoquée ci-dessus, le proxy de terminaison TLS vérifierait lequel des certificats TLS (HTTPS) disponibles il doit utiliser pour cette connexion, en utilisant celui qui correspond au domaine attendu par le client.
Dans ce cas, il utiliserait le certificat pour `someapp.example.com`.
<img src="/img/deployment/https/https03.drawio.svg">
Le client **fait déjà confiance** à l'entité qui a généré ce certificat TLS (dans ce cas Let's Encrypt, mais nous verrons cela plus tard), il peut donc **vérifier** que le certificat est valide.
Ensuite, en utilisant le certificat, le client et le proxy de terminaison TLS **décident comment chiffrer** le reste de la **communication TCP**. Cela complète la partie **poignée de main TLS**.
Après cela, le client et le serveur ont une **connexion TCP chiffrée**, c'est ce que fournit TLS. Ils peuvent ensuite utiliser cette connexion pour démarrer la **communication HTTP** réelle.
Et c'est ce qu'est **HTTPS** : c'est simplement du **HTTP** ordinaire à l'intérieur d'une **connexion TLS sécurisée** au lieu d'une connexion TCP pure (non chiffrée).
/// tip | Astuce
Remarquez que le chiffrement de la communication se fait au **niveau TCP**, pas au niveau HTTP.
///
### Requête HTTPS { #https-request }
Maintenant que le client et le serveur (spécifiquement le navigateur et le proxy de terminaison TLS) ont une **connexion TCP chiffrée**, ils peuvent démarrer la **communication HTTP**.
Ainsi, le client envoie une **requête HTTPS**. Il s'agit simplement d'une requête HTTP à travers une connexion TLS chiffrée.
<img src="/img/deployment/https/https04.drawio.svg">
### Déchiffrer la requête { #decrypt-the-request }
Le proxy de terminaison TLS utiliserait le chiffrement convenu pour **décrypter la requête**, et transmettrait la **requête HTTP en clair (décryptée)** au processus exécutant l'application (par exemple un processus avec Uvicorn exécutant l'application FastAPI).
<img src="/img/deployment/https/https05.drawio.svg">
### Réponse HTTP { #http-response }
L'application traiterait la requête et enverrait une **réponse HTTP en clair (non chiffrée)** au proxy de terminaison TLS.
<img src="/img/deployment/https/https06.drawio.svg">
### Réponse HTTPS { #https-response }
Le proxy de terminaison TLS **chiffrerait ensuite la réponse** en utilisant la cryptographie convenue précédemment (qui a commencé avec le certificat pour `someapp.example.com`), et la renverrait au navigateur.
Ensuite, le navigateur vérifierait que la réponse est valide et chiffrée avec la bonne clé cryptographique, etc. Il **décrypterait la réponse** et la traiterait.
<img src="/img/deployment/https/https07.drawio.svg">
Le client (navigateur) saura que la réponse provient du bon serveur parce qu'elle utilise la cryptographie convenue précédemment à l'aide du **certificat HTTPS**.
### Applications multiples { #multiple-applications }
Sur le même serveur (ou des serveurs), il peut y avoir **plusieurs applications**, par exemple d'autres programmes d'API ou une base de données.
Un seul processus peut gérer l'IP et le port spécifiques (le proxy de terminaison TLS dans notre exemple), mais les autres applications/processus peuvent également s'exécuter sur le ou les serveurs, tant qu'ils n'essaient pas d'utiliser la même **combinaison d'IP publique et de port**.
<img src="/img/deployment/https/https08.drawio.svg">
De cette façon, le proxy de terminaison TLS pourrait gérer HTTPS et les certificats pour **plusieurs domaines**, pour plusieurs applications, puis transmettre les requêtes à la bonne application dans chaque cas.
### Renouvellement des certificats { #certificate-renewal }
À un moment donné dans le futur, chaque certificat **expirera** (environ 3 mois après son acquisition).
Ensuite, un autre programme (dans certains cas c'est un autre programme, dans d'autres cas cela peut être le même proxy de terminaison TLS) communiquerait avec Let's Encrypt et renouvellerait le ou les certificats.
<img src="/img/deployment/https/https.drawio.svg">
Les **certificats TLS** sont **associés à un nom de domaine**, pas à une adresse IP.
Donc, pour renouveler les certificats, le programme de renouvellement doit **prouver** à l'autorité (Let's Encrypt) qu'il **«possède» et contrôle ce domaine**.
Pour ce faire, et pour s'adapter aux différents besoins des applications, il existe plusieurs manières de procéder. Parmi les méthodes populaires :
* **Modifier certains enregistrements DNS**.
* Pour cela, le programme de renouvellement doit prendre en charge les API du fournisseur DNS ; selon le fournisseur DNS que vous utilisez, cela peut être ou non une option.
* **S'exécuter comme un serveur** (au moins pendant le processus d'acquisition du certificat) sur l'adresse IP publique associée au domaine.
* Comme nous l'avons dit plus haut, un seul processus peut écouter sur une adresse IP et un port spécifiques.
* C'est l'une des raisons pour lesquelles il est très utile que le même proxy de terminaison TLS s'occupe également du processus de renouvellement des certificats.
* Sinon, vous pourriez devoir arrêter momentanément le proxy de terminaison TLS, démarrer le programme de renouvellement pour acquérir les certificats, puis les configurer avec le proxy de terminaison TLS, et redémarrer ensuite le proxy de terminaison TLS. Ce n'est pas idéal, car votre/vos application(s) ne seront pas disponibles pendant le temps où le proxy de terminaison TLS est arrêté.
Tout ce processus de renouvellement, tout en continuant à servir l'application, est l'une des principales raisons pour lesquelles vous voudriez avoir un **système séparé pour gérer HTTPS** avec un proxy de terminaison TLS au lieu d'utiliser directement les certificats TLS avec le serveur d'application (par ex. Uvicorn).
## En-têtes transférés par le proxy { #proxy-forwarded-headers }
Lorsque vous utilisez un proxy pour gérer HTTPS, votre **serveur d'application** (par exemple Uvicorn via FastAPI CLI) ne connaît rien du processus HTTPS, il communique en HTTP simple avec le **proxy de terminaison TLS**.
Ce **proxy** définirait normalement certains en-têtes HTTP à la volée avant de transmettre la requête au **serveur d'application**, pour informer le serveur d'application que la requête est **transférée** par le proxy.
/// note | Détails techniques
Les en-têtes du proxy sont :
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
///
Néanmoins, comme le **serveur d'application** ne sait pas qu'il est derrière un **proxy** de confiance, par défaut, il ne ferait pas confiance à ces en-têtes.
Mais vous pouvez configurer le **serveur d'application** pour faire confiance aux en-têtes transférés envoyés par le **proxy**. Si vous utilisez FastAPI CLI, vous pouvez utiliser l*option de CLI* `--forwarded-allow-ips` pour lui indiquer depuis quelles IP il doit faire confiance à ces en-têtes transférés.
Par exemple, si le **serveur d'application** ne reçoit des communications que du **proxy** de confiance, vous pouvez définir `--forwarded-allow-ips="*"` pour le faire faire confiance à toutes les IP entrantes, puisqu'il ne recevra des requêtes que depuis l'IP utilisée par le **proxy**.
Ainsi, l'application pourra savoir quelle est sa propre URL publique, si elle utilise HTTPS, le domaine, etc.
Cela serait utile par exemple pour gérer correctement les redirections.
/// tip | Astuce
Vous pouvez en apprendre davantage dans la documentation [Derrière un proxy - Activer les en-têtes transférés par le proxy](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank}
///
## Récapitulatif { #recap }
Avoir **HTTPS** est très important, et assez **critique** dans la plupart des cas. La majeure partie de l'effort que vous, en tant que développeur, avez à fournir autour de HTTPS consiste simplement à **comprendre ces concepts** et leur fonctionnement.
Mais une fois que vous connaissez les informations de base de **HTTPS pour les développeurs**, vous pouvez facilement combiner et configurer différents outils pour vous aider à tout gérer simplement.
Dans certains des prochains chapitres, je vous montrerai plusieurs exemples concrets de configuration de **HTTPS** pour des applications **FastAPI**. 🔒
L'idée est d'automatiser l'acquisition et le renouvellement de ces certificats, afin que vous puissiez disposer d'un HTTPS sécurisé, gratuitement et pour toujours.

View File

@@ -1,23 +1,28 @@
# Déploiement { #deployment }
# Déploiement
Le déploiement d'une application **FastAPI** est relativement simple.
## Que signifie le déploiement { #what-does-deployment-mean }
## Que signifie le déploiement
**Déployer** une application signifie effectuer les étapes nécessaires pour la rendre **disponible pour les utilisateurs**.
**Déployer** une application signifie effectuer les étapes nécessaires pour la rendre **disponible pour les
utilisateurs**.
Pour une **API Web**, cela implique normalement de la placer sur une **machine distante**, avec un **programme serveur** qui offre de bonnes performances, une bonne stabilité, _etc._, afin que vos **utilisateurs** puissent **accéder** à l'application efficacement et sans interruption ni problème.
Pour une **API Web**, cela implique normalement de la placer sur une **machine distante**, avec un **programme serveur**
qui offre de bonnes performances, une bonne stabilité, _etc._, afin que vos **utilisateurs** puissent **accéder** à
l'application efficacement et sans interruption ni problème.
Ceci contraste avec les étapes de **développement**, où vous êtes constamment en train de modifier le code, de le casser et de le réparer, d'arrêter et de redémarrer le serveur de développement, _etc._
Ceci contraste avec les étapes de **développement**, où vous êtes constamment en train de modifier le code, de le casser
et de le réparer, d'arrêter et de redémarrer le serveur de développement, _etc._
## Stratégies de déploiement { #deployment-strategies }
## Stratégies de déploiement
Il existe plusieurs façons de procéder, en fonction de votre cas d'utilisation spécifique et des outils que vous utilisez.
Il existe plusieurs façons de procéder, en fonction de votre cas d'utilisation spécifique et des outils que vous
utilisez.
Vous pouvez **déployer un serveur** vous-même en utilisant une combinaison d'outils, vous pouvez utiliser un **service cloud** qui fait une partie du travail pour vous, ou encore d'autres options possibles.
Vous pouvez **déployer un serveur** vous-même en utilisant une combinaison d'outils, vous pouvez utiliser un **service
cloud** qui fait une partie du travail pour vous, ou encore d'autres options possibles.
Par exemple, nous, l'équipe derrière FastAPI, avons créé <a href="https://fastapicloud.com" class="external-link" target="_blank">**FastAPI Cloud**</a>, pour rendre le déploiement d'applications FastAPI dans le cloud aussi simple que possible, avec la même expérience développeur que lorsque vous travaillez avec FastAPI.
Je vais vous montrer certains des principaux concepts que vous devriez probablement avoir à l'esprit lors du déploiement d'une application **FastAPI** (bien que la plupart de ces concepts s'appliquent à tout autre type d'application web).
Je vais vous montrer certains des principaux concepts que vous devriez probablement avoir à l'esprit lors du déploiement
d'une application **FastAPI** (bien que la plupart de ces concepts s'appliquent à tout autre type d'application web).
Vous verrez plus de détails à avoir en tête et certaines des techniques pour le faire dans les sections suivantes. ✨

View File

@@ -1,4 +1,4 @@
# À propos des versions de FastAPI { #about-fastapi-versions }
# À propos des versions de FastAPI
**FastAPI** est déjà utilisé en production dans de nombreuses applications et systèmes. Et la couverture de test est maintenue à 100 %. Mais son développement est toujours aussi rapide.
@@ -6,50 +6,51 @@ De nouvelles fonctionnalités sont ajoutées fréquemment, des bogues sont corri
amélioré continuellement.
C'est pourquoi les versions actuelles sont toujours `0.x.x`, cela reflète que chaque version peut potentiellement
recevoir des changements non rétrocompatibles. Cela suit les conventions de <a href="https://semver.org/" class="external-link" target="_blank">versionnage sémantique</a>.
recevoir des changements non rétrocompatibles. Cela suit les conventions de <a href="https://semver.org/" class="external-link"
target="_blank">versionnage sémantique</a>.
Vous pouvez créer des applications de production avec **FastAPI** dès maintenant (et vous le faites probablement depuis un certain temps), vous devez juste vous assurer que vous utilisez une version qui fonctionne correctement avec le reste de votre code.
## Épinglez votre version de `fastapi` { #pin-your-fastapi-version }
## Épinglez votre version de `fastapi`
Tout d'abord il faut «épingler» la version de **FastAPI** que vous utilisez à la dernière version dont vous savez
Tout d'abord il faut "épingler" la version de **FastAPI** que vous utilisez à la dernière version dont vous savez
qu'elle fonctionne correctement pour votre application.
Par exemple, disons que vous utilisez la version `0.112.0` dans votre application.
Par exemple, disons que vous utilisez la version `0.45.0` dans votre application.
Si vous utilisez un fichier `requirements.txt`, vous pouvez spécifier la version avec :
```txt
fastapi[standard]==0.112.0
fastapi==0.45.0
```
ce qui signifierait que vous utiliseriez exactement la version `0.112.0`.
ce qui signifierait que vous utiliseriez exactement la version `0.45.0`.
Ou vous pourriez aussi l'épingler avec :
```txt
fastapi[standard]>=0.112.0,<0.113.0
fastapi>=0.45.0,<0.46.0
```
cela signifierait que vous utiliseriez les versions `0.112.0` ou supérieures, mais inférieures à `0.113.0`, par exemple, une version `0.112.2` serait toujours acceptée.
cela signifierait que vous utiliseriez les versions `0.45.0` ou supérieures, mais inférieures à `0.46.0`, par exemple, une version `0.45.2` serait toujours acceptée.
Si vous utilisez un autre outil pour gérer vos installations, comme `uv`, Poetry, Pipenv, ou autres, ils ont tous un moyen que vous pouvez utiliser pour définir des versions spécifiques pour vos paquets.
Si vous utilisez un autre outil pour gérer vos installations, comme Poetry, Pipenv, ou autres, ils ont tous un moyen que vous pouvez utiliser pour définir des versions spécifiques pour vos paquets.
## Versions disponibles { #available-versions }
## Versions disponibles
Vous pouvez consulter les versions disponibles (par exemple, pour vérifier quelle est la dernière version en date) dans les [Notes de version](../release-notes.md){.internal-link target=_blank}.
## À propos des versions { #about-versions }
## À propos des versions
Suivant les conventions de versionnage sémantique, toute version inférieure à `1.0.0` peut potentiellement ajouter
des changements non rétrocompatibles.
FastAPI suit également la convention que tout changement de version «PATCH» est pour des corrections de bogues et
FastAPI suit également la convention que tout changement de version "PATCH" est pour des corrections de bogues et
des changements rétrocompatibles.
/// tip | Astuce
Le «PATCH» est le dernier chiffre, par exemple, dans `0.2.3`, la version PATCH est `3`.
Le "PATCH" est le dernier chiffre, par exemple, dans `0.2.3`, la version PATCH est `3`.
///
@@ -59,26 +60,26 @@ Donc, vous devriez être capable d'épingler une version comme suit :
fastapi>=0.45.0,<0.46.0
```
Les changements non rétrocompatibles et les nouvelles fonctionnalités sont ajoutés dans les versions «MINOR».
Les changements non rétrocompatibles et les nouvelles fonctionnalités sont ajoutés dans les versions "MINOR".
/// tip | Astuce
Le «MINOR» est le numéro au milieu, par exemple, dans `0.2.3`, la version MINOR est `2`.
Le "MINOR" est le numéro au milieu, par exemple, dans `0.2.3`, la version MINOR est `2`.
///
## Mettre à niveau les versions de FastAPI { #upgrading-the-fastapi-versions }
## Mise à jour des versions FastAPI
Vous devriez ajouter des tests pour votre application.
Vous devriez tester votre application.
Avec **FastAPI** c'est très facile (merci à Starlette), consultez la documentation : [Tests](../tutorial/testing.md){.internal-link target=_blank}
Avec **FastAPI** c'est très facile (merci à Starlette), consultez la documentation : [Testing](../tutorial/testing.md){.internal-link target=_blank}
Après avoir effectué des tests, vous pouvez mettre à niveau la version de **FastAPI** vers une version plus récente, et vous assurer que tout votre code fonctionne correctement en exécutant vos tests.
Après avoir effectué des tests, vous pouvez mettre à jour la version **FastAPI** vers une version plus récente, et vous assurer que tout votre code fonctionne correctement en exécutant vos tests.
Si tout fonctionne, ou après avoir fait les changements nécessaires, et que tous vos tests passent, vous pouvez
épingler votre version de `fastapi` à cette nouvelle version récente.
## À propos de Starlette { #about-starlette }
## À propos de Starlette
Vous ne devriez pas épingler la version de `starlette`.
@@ -86,15 +87,15 @@ Différentes versions de **FastAPI** utiliseront une version spécifique plus r
Ainsi, vous pouvez simplement laisser **FastAPI** utiliser la bonne version de Starlette.
## À propos de Pydantic { #about-pydantic }
## À propos de Pydantic
Pydantic inclut des tests pour **FastAPI** avec ses propres tests, ainsi les nouvelles versions de Pydantic (au-dessus
de `1.0.0`) sont toujours compatibles avec **FastAPI**.
Vous pouvez épingler Pydantic à toute version supérieure à `1.0.0` qui fonctionne pour vous.
Vous pouvez épingler Pydantic à toute version supérieure à `1.0.0` qui fonctionne pour vous et inférieure à `2.0.0`.
Par exemple :
```txt
pydantic>=2.7.0,<3.0.0
pydantic>=1.2.0,<2.0.0
```

View File

@@ -1,11 +1,11 @@
# FastAPI { #fastapi }
# FastAPI
<style>
.md-content .md-typeset h1 { display: none; }
</style>
<p align="center">
<a href="https://fastapi.tiangolo.com/fr"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>Framework FastAPI, haute performance, facile à apprendre, rapide à coder, prêt pour la production</em>
@@ -27,138 +27,134 @@
---
**Documentation** : <a href="https://fastapi.tiangolo.com/fr" target="_blank">https://fastapi.tiangolo.com/fr</a>
**Documentation** : <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**Code source** : <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
**Code Source** : <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
---
FastAPI est un framework web moderne et rapide (haute performance) pour la création dAPI avec Python, basé sur les annotations de type standard de Python.
FastAPI est un framework web moderne et rapide (haute performance) pour la création d'API avec Python, basé sur les annotations de type standard de Python.
Les principales fonctionnalités sont :
* **Rapide** : Très hautes performances, au niveau de **NodeJS** et **Go** (grâce à Starlette et Pydantic). [Lun des frameworks Python les plus rapides](#performance).
* **Rapide à coder** : Augmente la vitesse de développement des fonctionnalités denviron 200 % à 300 %. *
* **Moins de bugs** : Réduit denviron 40 % les erreurs induites par le développeur. *
* **Intuitif** : Excellente prise en charge par les éditeurs. <abbr title="également connu sous le nom dauto-complétion, autocomplétion, IntelliSense">Complétion</abbr> partout. Moins de temps passé à déboguer.
* **Rapidité** : De très hautes performances, au niveau de **NodeJS** et **Go** (grâce à Starlette et Pydantic). [L'un des frameworks Python les plus rapides](#performance).
* **Rapide à coder** : Augmente la vitesse de développement des fonctionnalités d'environ 200 % à 300 %. *
* **Moins de bugs** : Réduit d'environ 40 % les erreurs induites par le développeur. *
* **Intuitif** : Excellente compatibilité avec les IDE. <abbr title="également connu sous le nom d'auto-complétion, autocomplétion, IntelliSense">Complétion</abbr> complète. Moins de temps passé à déboguer.
* **Facile** : Conçu pour être facile à utiliser et à apprendre. Moins de temps passé à lire la documentation.
* **Concis** : Minimise la duplication de code. Plusieurs fonctionnalités à partir de chaque déclaration de paramètre. Moins de bugs.
* **Robuste** : Obtenez du code prêt pour la production. Avec une documentation interactive automatique.
* **Basé sur des standards** : Basé sur (et entièrement compatible avec) les standards ouverts pour les API : <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (anciennement connu sous le nom de Swagger) et <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
* **Concis** : Diminue la duplication de code. De nombreuses fonctionnalités liées à la déclaration de chaque paramètre. Moins de bugs.
* **Robuste** : Obtenez un code prêt pour la production. Avec une documentation interactive automatique.
* **Basé sur des normes** : Basé sur (et entièrement compatible avec) les standards ouverts pour les APIs : <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (précédemment connu sous le nom de Swagger) et <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* estimation basée sur des tests dune équipe de développement interne, construisant des applications de production.</small>
<small>* estimation basée sur des tests d'une équipe de développement interne, construisant des applications de production.</small>
## Sponsors { #sponsors }
## Sponsors
<!-- sponsors -->
### Sponsor clé de voûte { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
### Sponsors Or et Argent { #gold-and-silver-sponsors }
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
<!-- /sponsors -->
<a href="https://fastapi.tiangolo.com/fr/fastapi-people/#sponsors" class="external-link" target="_blank">Autres sponsors</a>
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
## Opinions { #opinions }
## Opinions
«_[...] Jutilise beaucoup **FastAPI** ces derniers temps. [...] Je prévois de lutiliser dans mon équipe pour tous les **services de ML chez Microsoft**. Certains dentre eux seront intégrés dans le cœur de **Windows** et dans certains produits **Office**._»
"_[...] J'utilise beaucoup **FastAPI** ces derniers temps. [...] Je prévois de l'utiliser dans mon équipe pour tous les **services de ML chez Microsoft**. Certains d'entre eux seront intégrés dans le coeur de **Windows** et dans certains produits **Office**._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/fastapi/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
«_Nous avons adopté la bibliothèque **FastAPI** pour créer un serveur **REST** qui peut être interrogé pour obtenir des **prédictions**. [pour Ludwig]_»
"_Nous avons adopté la bibliothèque **FastAPI** pour créer un serveur **REST** qui peut être interrogé pour obtenir des **prédictions**. [pour Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin et Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
«_**Netflix** a le plaisir dannoncer la sortie en open-source de notre framework dorchestration de **gestion de crise** : **Dispatch** ! [construit avec **FastAPI**]_»
"_**Netflix** a le plaisir d'annoncer la sortie en open-source de notre framework d'orchestration de **gestion de crise** : **Dispatch** ! [construit avec **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
«_Je suis très enthousiaste à propos de **FastAPI**. Cest un bonheur !_»
"_Je suis très enthousiaste à propos de **FastAPI**. C'est un bonheur !_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong>Animateur du podcast <a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a></strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong>Auteur du podcast <a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a></strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
«_Honnêtement, ce que vous avez construit a lair super solide et élégant. À bien des égards, cest comme ça que je voulais que **Hug** soit cest vraiment inspirant de voir quelquun construire ça._»
"_Honnêtement, ce que vous avez construit a l'air super solide et élégant. A bien des égards, c'est comme ça que je voulais que **Hug** soit - c'est vraiment inspirant de voir quelqu'un construire ça._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong>Créateur de <a href="https://github.com/hugapi/hug" target="_blank">Hug</a></strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong> Créateur de <a href="https://github.com/hugapi/hug" target="_blank">Hug</a></strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
«_Si vous cherchez à apprendre un **framework moderne** pour créer des API REST, regardez **FastAPI** [...] Cest rapide, facile à utiliser et à apprendre [...]_»
"_Si vous cherchez à apprendre un **framework moderne** pour créer des APIs REST, regardez **FastAPI** [...] C'est rapide, facile à utiliser et à apprendre [...]_"
«_Nous sommes passés à **FastAPI** pour nos **API** [...] Je pense que vous laimerez [...]_»
"_Nous sommes passés à **FastAPI** pour nos **APIs** [...] Je pense que vous l'aimerez [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong>Fondateurs de <a href="https://explosion.ai" target="_blank">Explosion AI</a> Créateurs de <a href="https://spacy.io" target="_blank">spaCy</a></strong> <a href="https://x.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://x.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong>Fondateurs de <a href="https://explosion.ai" target="_blank">Explosion AI</a> - Créateurs de <a href="https://spacy.io" target="_blank">spaCy</a></strong> <a href="https://x.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://x.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
«_Si quelquun cherche à construire une API Python de production, je recommande vivement **FastAPI**. Il est **bien conçu**, **simple à utiliser** et **très évolutif**, il est devenu un **composant clé** dans notre stratégie de développement API first et il est à lorigine de nombreux automatismes et services tels que notre ingénieur virtuel TAC._»
"_Si quelqu'un cherche à construire une API Python de production, je recommande vivement **FastAPI**. Il est **bien conçu**, **simple à utiliser** et **très évolutif**. Il est devenu un **composant clé** dans notre stratégie de développement API first et il est à l'origine de nombreux automatismes et services tels que notre ingénieur virtuel TAC._"
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
---
## Mini documentaire FastAPI { #fastapi-mini-documentary }
Un <a href="https://www.youtube.com/watch?v=mpR8ngthqiE" class="external-link" target="_blank">mini documentaire FastAPI</a> est sorti à la fin de 2025, vous pouvez le regarder en ligne :
<a href="https://www.youtube.com/watch?v=mpR8ngthqiE" target="_blank"><img src="https://fastapi.tiangolo.com/img/fastapi-documentary.jpg" alt="FastAPI Mini Documentary"></a>
## **Typer**, le FastAPI des <abbr title="Command Line Interface Interface en ligne de commande">CLI</abbr> { #typer-the-fastapi-of-clis }
## **Typer**, le FastAPI des <abbr title="Command Line Interface">CLI</abbr>
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
Si vous souhaitez construire une application <abbr title="Command Line Interface Interface en ligne de commande">CLI</abbr> utilisable dans un terminal au lieu dune API web, regardez <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
Si vous souhaitez construire une application <abbr title="Command Line Interface">CLI</abbr> utilisable dans un terminal au lieu d'une API web, regardez <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
**Typer** est le petit frère de FastAPI. Et il est destiné à être le **FastAPI des CLI**. ⌨️ 🚀
**Typer** est le petit frère de FastAPI. Et il est destiné à être le **FastAPI des <abbr title="Command Line Interface">CLI</abbr>**. ⌨️ 🚀
## Prérequis { #requirements }
## Prérequis
FastAPI repose sur les épaules de géants :
* <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> pour les parties web.
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> pour les parties données.
## Installation { #installation }
Créez et activez un <a href="https://fastapi.tiangolo.com/fr/virtual-environments/" class="external-link" target="_blank">environnement virtuel</a> puis installez FastAPI :
## Installation
<div class="termy">
```console
$ pip install "fastapi[standard]"
$ pip install fastapi
---> 100%
```
</div>
**Remarque** : Assurez-vous de mettre `"fastapi[standard]"` entre guillemets pour garantir que cela fonctionne dans tous les terminaux.
Vous aurez également besoin d'un serveur ASGI pour la production tel que <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a> ou <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
## Exemple { #example }
<div class="termy">
### Créer { #create-it }
```console
$ pip install "uvicorn[standard]"
Créez un fichier `main.py` avec :
---> 100%
```
</div>
## Exemple
### Créez
* Créez un fichier `main.py` avec :
```Python
from typing import Union
@@ -179,7 +175,7 @@ def read_item(item_id: int, q: Union[str, None] = None):
```
<details markdown="1">
<summary>Ou utilisez <code>async def</code>...</summary>
<summary>Ou utilisez <code>async def</code> ...</summary>
Si votre code utilise `async` / `await`, utilisez `async def` :
@@ -201,37 +197,24 @@ async def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
**Remarque** :
**Note**
Si vous ne savez pas, consultez la section _«In a hurry?»_ à propos de <a href="https://fastapi.tiangolo.com/fr/async/#in-a-hurry" target="_blank">`async` et `await` dans la documentation</a>.
Si vous n'êtes pas familier avec cette notion, consultez la section _"Vous êtes pressés ?"_ à propos de <a href="https://fastapi.tiangolo.com/fr/async/#vous-etes-presses" target="_blank">`async` et `await` dans la documentation</a>.
</details>
### Lancer { #run-it }
### Lancez
Lancez le serveur avec :
<div class="termy">
```console
$ fastapi dev main.py
$ uvicorn main:app --reload
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
@@ -239,54 +222,54 @@ INFO: Application startup complete.
</div>
<details markdown="1">
<summary>À propos de la commande <code>fastapi dev main.py</code>...</summary>
<summary>À propos de la commande <code>uvicorn main:app --reload</code> ...</summary>
La commande `fastapi dev` lit votre fichier `main.py`, détecte lapplication **FastAPI** quil contient et démarre un serveur avec <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>.
La commande `uvicorn main:app` fait référence à :
Par défaut, `fastapi dev` démarre avec le rechargement automatique activé pour le développement local.
Vous pouvez en lire davantage dans la <a href="https://fastapi.tiangolo.com/fr/fastapi-cli/" target="_blank">documentation de la CLI FastAPI</a>.
* `main` : le fichier `main.py` (le "module" Python).
* `app` : l'objet créé à l'intérieur de `main.py` avec la ligne `app = FastAPI()`.
* `--reload` : fait redémarrer le serveur après des changements de code. À n'utiliser que pour le développement.
</details>
### Vérifier { #check-it }
### Vérifiez
Ouvrez votre navigateur à ladresse <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
Ouvrez votre navigateur à l'adresse <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
Vous verrez la réponse JSON :
Vous obtenez alors cette réponse <abbr title="JavaScript Object Notation">JSON</abbr> :
```JSON
{"item_id": 5, "q": "somequery"}
```
Vous avez déjà créé une API qui :
Vous venez de créer une API qui :
* Reçoit des requêtes HTTP pour les _chemins_ `/` et `/items/{item_id}`.
* Les deux _chemins_ acceptent des <em>opérations</em> `GET` (également connues comme _méthodes_ HTTP).
* Le _chemin_ `/items/{item_id}` a un _paramètre de chemin_ `item_id` qui doit être un `int`.
* Le _chemin_ `/items/{item_id}` a un _paramètre de requête_ optionnel `q` de type `str`.
* Reçoit les requêtes HTTP pour les _chemins_ `/` et `/items/{item_id}`.
* Les deux _chemins_ acceptent des <em>opérations</em> `GET` (également connu sous le nom de _méthodes_ HTTP).
* Le _chemin_ `/items/{item_id}` a un _<abbr title="en anglais : path parameter">paramètre</abbr>_ `item_id` qui doit être un `int`.
* Le _chemin_ `/items/{item_id}` a un _<abbr title="en anglais : query param">paramètre de requête</abbr>_ optionnel `q` de type `str`.
### Documentation API interactive { #interactive-api-docs }
### Documentation API interactive
Maintenant, allez sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Maintenant, rendez-vous sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Vous verrez la documentation API interactive automatique (fournie par <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>) :
Vous verrez la documentation interactive automatique de l'API (fournie par <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)
### Documentation API alternative { #alternative-api-docs }
### Documentation API alternative
Et maintenant, allez sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Et maintenant, rendez-vous sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Vous verrez la documentation automatique alternative (fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>) :
Vous verrez la documentation interactive automatique de l'API (fournie par <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)
## Exemple de mise à niveau { #example-upgrade }
## Exemple plus poussé
Modifiez maintenant le fichier `main.py` pour recevoir le corps dune requête `PUT`.
Maintenant, modifiez le fichier `main.py` pour recevoir <abbr title="en anglais : body">le corps</abbr> d'une requête `PUT`.
Déclarez le corps en utilisant les types Python standards, grâce à Pydantic.
Déclarez ce corps en utilisant les types Python standards, grâce à Pydantic.
```Python hl_lines="4 9-12 25-27"
from typing import Union
@@ -318,43 +301,43 @@ def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
Le serveur `fastapi dev` devrait se recharger automatiquement.
Le serveur se recharge normalement automatiquement (car vous avez pensé à `--reload` dans la commande `uvicorn` ci-dessus).
### Mise à niveau de la documentation API interactive { #interactive-api-docs-upgrade }
### Plus loin avec la documentation API interactive
Maintenant, allez sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Maintenant, rendez-vous sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
* La documentation API interactive sera automatiquement mise à jour, y compris le nouveau corps :
* La documentation interactive de l'API sera automatiquement mise à jour, y compris le nouveau corps de la requête :
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Cliquez sur le bouton «Try it out», il vous permet de renseigner les paramètres et dinteragir directement avec lAPI :
* Cliquez sur le bouton "Try it out", il vous permet de renseigner les paramètres et d'interagir directement avec l'API :
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Cliquez ensuite sur le bouton «Execute», linterface utilisateur communiquera avec votre API, enverra les paramètres, obtiendra les résultats et les affichera à lécran :
* Cliquez ensuite sur le bouton "Execute", l'interface utilisateur communiquera avec votre API, enverra les paramètres, obtiendra les résultats et les affichera à l'écran :
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Mise à niveau de la documentation API alternative { #alternative-api-docs-upgrade }
### Plus loin avec la documentation API alternative
Et maintenant, allez sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Et maintenant, rendez-vous sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
* La documentation alternative reflètera également le nouveau paramètre de requête et le nouveau corps :
* La documentation alternative reflétera également le nouveau paramètre de requête et le nouveau corps :
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Récapitulatif { #recap }
### En résumé
En résumé, vous déclarez **une fois** les types de paramètres, le corps, etc. comme paramètres de fonction.
En résumé, vous déclarez **une fois** les types de paramètres, <abbr title="en anglais : body">le corps</abbr> de la requête, etc. en tant que paramètres de fonction.
Vous faites cela avec les types Python modernes standards.
Vous faites cela avec les types Python standard modernes.
Vous navez pas à apprendre une nouvelle syntaxe, les méthodes ou classes dune bibliothèque spécifique, etc.
Vous n'avez pas à apprendre une nouvelle syntaxe, les méthodes ou les classes d'une bibliothèque spécifique, etc.
Juste du **Python** standard.
Par exemple, pour un `int` :
Par exemple, pour un `int`:
```Python
item_id: int
@@ -366,58 +349,58 @@ ou pour un modèle `Item` plus complexe :
item: Item
```
... et avec cette seule déclaration vous obtenez :
... et avec cette déclaration unique, vous obtenez :
* Assistance de léditeur, notamment :
* Complétion.
* Vérifications de type.
* Validation des données :
* Des erreurs automatiques et claires lorsque les données ne sont pas valides.
* Une validation même pour des objets JSON profondément imbriqués.
* <abbr title="également connu sous le nom de : sérialisation, analyse, marshalling">Conversion</abbr> des données dentrée : depuis le réseau vers les données et types Python. Lecture depuis :
* JSON.
* Paramètres de chemin.
* Paramètres de requête.
* Cookies.
* En-têtes.
* Formulaires.
* Fichiers.
* <abbr title="également connu sous le nom de : sérialisation, analyse, marshalling">Conversion</abbr> des données de sortie : conversion des données et types Python en données réseau (au format JSON) :
* Conversion des types Python (`str`, `int`, `float`, `bool`, `list`, etc).
* Objets `datetime`.
* Objets `UUID`.
* Modèles de base de données.
* ... et bien plus.
* Documentation API interactive automatique, avec 2 interfaces utilisateur alternatives :
* Une assistance dans votre IDE, notamment :
* la complétion.
* la vérification des types.
* La validation des données :
* des erreurs automatiques et claires lorsque les données ne sont pas valides.
* une validation même pour les objets <abbr title="JavaScript Object Notation">JSON</abbr> profondément imbriqués.
* <abbr title="aussi connu sous le nom de : serialization, parsing, marshalling">Une conversion</abbr> des données d'entrée : venant du réseau et allant vers les données et types de Python, permettant de lire :
* le <abbr title="JavaScript Object Notation">JSON</abbr>.
* <abbr title="en anglais : path parameters">les paramètres du chemin</abbr>.
* <abbr title="en anglais : query parameters">les paramètres de la requête</abbr>.
* les cookies.
* <abbr title="en anglais : headers">les en-têtes</abbr>.
* <abbr title="en anglais : forms">les formulaires</abbr>.
* <abbr title="en anglais : files">les fichiers</abbr>.
* <abbr title="aussi connu sous le nom de : serialization, parsing, marshalling">La conversion</abbr> des données de sortie : conversion des données et types Python en données réseau (au format <abbr title="JavaScript Object Notation">JSON</abbr>), permettant de convertir :
* les types Python (`str`, `int`, `float`, `bool`, `list`, etc).
* les objets `datetime`.
* les objets `UUID`.
* les modèles de base de données.
* ... et beaucoup plus.
* La documentation API interactive automatique, avec 2 interfaces utilisateur au choix :
* Swagger UI.
* ReDoc.
---
Pour revenir à lexemple de code précédent, **FastAPI** va :
Pour revenir à l'exemple de code précédent, **FastAPI** permet de :
* Valider la présence dun `item_id` dans le chemin pour les requêtes `GET` et `PUT`.
* Valider que `item_id` existe dans le chemin des requêtes `GET` et `PUT`.
* Valider que `item_id` est de type `int` pour les requêtes `GET` et `PUT`.
* Si ce nest pas le cas, le client verra une erreur utile et claire.
* Vérifier sil existe un paramètre de requête optionnel nommé `q` (comme dans `http://127.0.0.1:8000/items/foo?q=somequery`) pour les requêtes `GET`.
* Comme le paramètre `q` est déclaré avec `= None`, il est optionnel.
* Sans le `None`, il serait requis (comme lest le corps dans le cas du `PUT`).
* Pour les requêtes `PUT` vers `/items/{item_id}`, lire le corps en JSON :
* Vérifier quil possède un attribut obligatoire `name` qui doit être un `str`.
* Vérifier quil possède un attribut obligatoire `price` qui doit être un `float`.
* Vérifier quil possède un attribut optionnel `is_offer`, qui doit être un `bool`, sil est présent.
* Tout cela fonctionnerait également pour des objets JSON profondément imbriqués.
* Convertir depuis et vers JSON automatiquement.
* Tout documenter avec OpenAPI, qui peut être utilisé par :
* Des systèmes de documentation interactive.
* Des systèmes de génération automatique de code client, pour de nombreuses langues.
* Si ce n'est pas le cas, le client voit une erreur utile et claire.
* Vérifier qu'il existe un paramètre de requête facultatif nommé `q` (comme dans `http://127.0.0.1:8000/items/foo?q=somequery`) pour les requêtes `GET`.
* Puisque le paramètre `q` est déclaré avec `= None`, il est facultatif.
* Sans le `None`, il serait nécessaire (comme l'est <abbr title="en anglais : body">le corps</abbr> de la requête dans le cas du `PUT`).
* Pour les requêtes `PUT` vers `/items/{item_id}`, de lire <abbr title="en anglais : body">le corps</abbr> en <abbr title="JavaScript Object Notation">JSON</abbr> :
* Vérifier qu'il a un attribut obligatoire `name` qui devrait être un `str`.
* Vérifier qu'il a un attribut obligatoire `prix` qui doit être un `float`.
* Vérifier qu'il a un attribut facultatif `is_offer`, qui devrait être un `bool`, s'il est présent.
* Tout cela fonctionnerait également pour les objets <abbr title="JavaScript Object Notation">JSON</abbr> profondément imbriqués.
* Convertir de et vers <abbr title="JavaScript Object Notation">JSON</abbr> automatiquement.
* Documenter tout avec OpenAPI, qui peut être utilisé par :
* Les systèmes de documentation interactifs.
* Les systèmes de génération automatique de code client, pour de nombreuses langues.
* Fournir directement 2 interfaces web de documentation interactive.
---
Nous navons fait queffleurer la surface, mais vous comprenez déjà lidée de fonctionnement général.
Nous n'avons fait qu'effleurer la surface, mais vous avez déjà une idée de la façon dont tout cela fonctionne.
Essayez de changer la ligne :
Essayez de changer la ligne contenant :
```Python
return {"item_name": item.name, "item_id": item_id}
@@ -435,131 +418,55 @@ Essayez de changer la ligne :
... "item_price": item.price ...
```
... et voyez comment votre éditeur complètera automatiquement les attributs et connaîtra leurs types :
... et voyez comment votre éditeur complétera automatiquement les attributs et connaîtra leurs types :
![compatibilité éditeur](https://fastapi.tiangolo.com/img/vscode-completion.png)
![compatibilité IDE](https://fastapi.tiangolo.com/img/vscode-completion.png)
Pour un exemple plus complet incluant davantage de fonctionnalités, voir le <a href="https://fastapi.tiangolo.com/fr/tutorial/">Tutoriel - Guide utilisateur</a>.
Pour un exemple plus complet comprenant plus de fonctionnalités, voir le <a href="https://fastapi.tiangolo.com/fr/tutorial/">Tutoriel - Guide utilisateur</a>.
**Alerte spoiler** : le tutoriel - guide utilisateur inclut :
**Spoiler alert** : le tutoriel - guide utilisateur inclut :
* Déclaration de **paramètres** provenant dautres endroits tels que : **en-têtes**, **cookies**, **champs de formulaire** et **fichiers**.
* Comment définir des **contraintes de validation** comme `maximum_length` ou `regex`.
* Un système de **<abbr title="également connu sous le nom de composants, ressources, fournisseurs, services, injectables">Injection de dépendances</abbr>** très puissant et facile à utiliser.
* Sécurité et authentification, y compris la prise en charge de **OAuth2** avec les **jetons <abbr title="JSON Web Tokens">JWT</abbr>** et lauthentification **HTTP Basic**.
* Des techniques plus avancées (mais tout aussi simples) pour déclarer des **modèles JSON profondément imbriqués** (grâce à Pydantic).
* Intégration **GraphQL** avec <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> et dautres bibliothèques.
* De nombreuses fonctionnalités supplémentaires (grâce à Starlette) comme :
* Déclaration de **paramètres** provenant d'autres endroits différents comme : **<abbr title="en anglais : headers">en-têtes</abbr>.**, **cookies**, **champs de formulaire** et **fichiers**.
* L'utilisation de **contraintes de validation** comme `maximum_length` ou `regex`.
* Un **<abbr title="aussi connu sous le nom de composants, ressources, fournisseurs, services, injectables">système d'injection de dépendance </abbr>** très puissant et facile à utiliser .
* Sécurité et authentification, y compris la prise en charge de **OAuth2** avec les **<abbr title="en anglais : JWT tokens">jetons <abbr title="JSON Web Tokens">JWT</abbr></abbr>** et l'authentification **HTTP Basic**.
* Des techniques plus avancées (mais tout aussi faciles) pour déclarer les **modèles <abbr title="JavaScript Object Notation">JSON</abbr> profondément imbriqués** (grâce à Pydantic).
* Intégration de **GraphQL** avec <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> et d'autres bibliothèques.
* D'obtenir de nombreuses fonctionnalités supplémentaires (grâce à Starlette) comme :
* **WebSockets**
* des tests extrêmement faciles basés sur HTTPX et `pytest`
* **CORS**
* **Sessions Cookie**
* de tester le code très facilement avec `requests` et `pytest`
* **<abbr title="Cross-Origin Resource Sharing">CORS</abbr>**
* **Cookie Sessions**
* ... et plus encore.
### Déployer votre application (optionnel) { #deploy-your-app-optional }
## Performance
Vous pouvez éventuellement déployer votre application FastAPI sur <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>, allez vous inscrire sur la liste dattente si ce nest pas déjà fait. 🚀
Si vous avez déjà un compte **FastAPI Cloud** (nous vous avons invité depuis la liste dattente 😉), vous pouvez déployer votre application avec une seule commande.
Avant de déployer, assurez-vous dêtre connecté :
<div class="termy">
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
</div>
Puis déployez votre application :
<div class="termy">
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
</div>
Cest tout ! Vous pouvez maintenant accéder à votre application à cette URL. ✨
#### À propos de FastAPI Cloud { #about-fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** est créé par le même auteur et la même équipe derrière **FastAPI**.
Il simplifie le processus de **construction**, de **déploiement** et d**accès** à une API avec un minimum defforts.
Il apporte la même **expérience développeur** de la création dapplications avec FastAPI au **déploiement** dans le cloud. 🎉
FastAPI Cloud est le sponsor principal et le financeur des projets open source *FastAPI and friends*. ✨
#### Déployer sur dautres fournisseurs cloud { #deploy-to-other-cloud-providers }
FastAPI est open source et basé sur des standards. Vous pouvez déployer des applications FastAPI sur nimporte quel fournisseur cloud de votre choix.
Suivez les guides de votre fournisseur cloud pour y déployer des applications FastAPI. 🤓
## Performance { #performance }
Les benchmarks indépendants TechEmpower montrent que les applications **FastAPI** sexécutant sous Uvicorn sont <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">parmi les frameworks Python les plus rapides</a>, juste derrière Starlette et Uvicorn eux-mêmes (utilisés en interne par FastAPI). (*)
Les benchmarks TechEmpower indépendants montrent que les applications **FastAPI** s'exécutant sous Uvicorn sont <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank"> parmi les frameworks existants en Python les plus rapides </a>, juste derrière Starlette et Uvicorn (utilisés en interne par FastAPI). (*)
Pour en savoir plus, consultez la section <a href="https://fastapi.tiangolo.com/fr/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Dépendances { #dependencies }
## Dépendances facultatives
FastAPI dépend de Pydantic et Starlette.
Utilisées par Pydantic:
### Dépendances `standard` { #standard-dependencies }
Lorsque vous installez FastAPI avec `pip install "fastapi[standard]"`, cela inclut le groupe `standard` de dépendances optionnelles :
Utilisées par Pydantic :
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> — pour la validation des adresses email.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - pour la validation des adresses email.
Utilisées par Starlette :
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> — requis si vous souhaitez utiliser `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> — requis si vous souhaitez utiliser la configuration de template par défaut.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> — requis si vous souhaitez prendre en charge l<abbr title="conversion de la chaîne provenant dune requête HTTP en données Python">«décodage»</abbr> de formulaires avec `request.form()`.
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Obligatoire si vous souhaitez utiliser `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Obligatoire si vous souhaitez utiliser la configuration de template par défaut.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Obligatoire si vous souhaitez supporter le <abbr title="convertit la chaine de caractère d'une requête HTTP en donnée Python">"décodage"</abbr> de formulaire avec `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Obligatoire pour la prise en charge de `SessionMiddleware`.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Obligatoire pour le support `SchemaGenerator` de Starlette (vous n'en avez probablement pas besoin avec FastAPI).
Utilisées par FastAPI :
Utilisées par FastAPI / Starlette :
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> — pour le serveur qui charge et sert votre application. Cela inclut `uvicorn[standard]`, qui comprend certaines dépendances (par ex. `uvloop`) nécessaires pour un service à haute performance.
* `fastapi-cli[standard]` — pour fournir la commande `fastapi`.
* Cela inclut `fastapi-cloud-cli`, qui vous permet de déployer votre application FastAPI sur <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - Pour le serveur qui charge et sert votre application.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Obligatoire si vous voulez utiliser `ORJSONResponse`.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Obligatoire si vous souhaitez utiliser `UJSONResponse`.
### Sans dépendances `standard` { #without-standard-dependencies }
Vous pouvez tout installer avec `pip install fastapi[all]`.
Si vous ne souhaitez pas inclure les dépendances optionnelles `standard`, vous pouvez installer avec `pip install fastapi` au lieu de `pip install "fastapi[standard]"`.
### Sans `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
Si vous souhaitez installer FastAPI avec les dépendances standard mais sans `fastapi-cloud-cli`, vous pouvez installer avec `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Autres dépendances optionnelles { #additional-optional-dependencies }
Il existe des dépendances supplémentaires que vous pourriez vouloir installer.
Dépendances optionnelles supplémentaires pour Pydantic :
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> — pour la gestion des paramètres.
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> — pour des types supplémentaires à utiliser avec Pydantic.
Dépendances optionnelles supplémentaires pour FastAPI :
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> — requis si vous souhaitez utiliser `ORJSONResponse`.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> — requis si vous souhaitez utiliser `UJSONResponse`.
## Licence { #license }
## Licence
Ce projet est soumis aux termes de la licence MIT.

View File

@@ -1,4 +1,4 @@
# Apprendre { #learn }
# Apprendre
Voici les sections introductives et les tutoriels pour apprendre **FastAPI**.

View File

@@ -1,28 +1,84 @@
# Modèle Full Stack FastAPI { #full-stack-fastapi-template }
# Génération de projets - Modèle
Bien qu'ils soient généralement livrés avec une configuration spécifique, les modèles sont conçus pour être flexibles et personnalisables. Cela vous permet de les modifier et de les adapter aux besoins de votre projet, ce qui en fait un excellent point de départ. 🏁
Vous pouvez utiliser un générateur de projet pour commencer, qui réalisera pour vous la mise en place de bases côté architecture globale, sécurité, base de données et premières routes d'API.
Vous pouvez utiliser ce modèle pour démarrer, car il inclut une grande partie de la configuration initiale, la sécurité, la base de données et quelques endpoints d'API déjà prêts pour vous.
Un générateur de projet fera toujours une mise en place très subjective que vous devriez modifier et adapter suivant vos besoins, mais cela reste un bon point de départ pour vos projets.
Dépôt GitHub : <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Full Stack FastAPI Template</a>
## Full Stack FastAPI PostgreSQL
## Modèle Full Stack FastAPI - Stack technologique et fonctionnalités { #full-stack-fastapi-template-technology-stack-and-features }
GitHub : <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-postgresql</a>
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/fr) pour l'API backend Python.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) pour les interactions Python avec la base de données SQL (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), utilisé par FastAPI, pour la validation des données et la gestion des paramètres.
- 💾 [PostgreSQL](https://www.postgresql.org) comme base de données SQL.
- 🚀 [React](https://react.dev) pour le frontend.
- 💃 Utilisation de TypeScript, des hooks, de Vite et d'autres éléments d'une stack frontend moderne.
- 🎨 [Tailwind CSS](https://tailwindcss.com) et [shadcn/ui](https://ui.shadcn.com) pour les composants frontend.
- 🤖 Un client frontend généré automatiquement.
- 🧪 [Playwright](https://playwright.dev) pour les tests de bout en bout.
- 🦇 Prise en charge du mode sombre.
- 🐋 [Docker Compose](https://www.docker.com) pour le développement et la production.
- 🔒 Hachage sécurisé des mots de passe par défaut.
- 🔑 Authentification JWT (JSON Web Token).
- 📫 Récupération de mot de passe par e-mail.
- ✅ Tests avec [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) comme proxy inverse / équilibreur de charge.
- 🚢 Instructions de déploiement avec Docker Compose, y compris la configuration d'un proxy Traefik en frontal pour gérer les certificats HTTPS automatiques.
- 🏭 CI (intégration continue) et CD (déploiement continu) basées sur GitHub Actions.
### Full Stack FastAPI PostgreSQL - Fonctionnalités
* Intégration **Docker** complète (basée sur Docker).
* Déploiement Docker en mode <a href="https://docs.docker.com/engine/swarm/" class="external-link" target="_blank">Swarm</a>
* Intégration **Docker Compose** et optimisation pour développement local.
* Serveur web Python **prêt au déploiement** utilisant Uvicorn et Gunicorn.
* Backend Python <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">**FastAPI**</a> :
* **Rapide** : Très hautes performances, comparables à **NodeJS** ou **Go** (grâce à Starlette et Pydantic).
* **Intuitif** : Excellent support des éditeurs. <abbr title="aussi appelée auto-complétion, autocomplétion, IntelliSense...">Complétion</abbr> partout. Moins de temps passé à déboguer.
* **Facile** : Fait pour être facile à utiliser et apprendre. Moins de temps passé à lire de la documentation.
* **Concis** : Minimise la duplication de code. Plusieurs fonctionnalités à chaque déclaration de paramètre.
* **Robuste** : Obtenez du code prêt pour être utilisé en production. Avec de la documentation automatique interactive.
* **Basé sur des normes** : Basé sur (et totalement compatible avec) les normes ouvertes pour les APIs : <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> et <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
* <a href="https://fastapi.tiangolo.com/features/" class="external-link" target="_blank">**Et bien d'autres fonctionnalités**</a> comme la validation automatique, la sérialisation, l'authentification avec OAuth2 JWT tokens, etc.
* Hashage de **mots de passe sécurisé** par défaut.
* Authentification par **jetons JWT**.
* Modèles **SQLAlchemy** (indépendants des extensions Flask, afin qu'ils puissent être utilisés directement avec des *workers* Celery).
* Modèle de démarrages basiques pour les utilisateurs (à modifier et supprimer au besoin).
* Migrations **Alembic**.
* **CORS** (partage des ressources entre origines multiples, ou *Cross Origin Resource Sharing*).
* *Worker* **Celery** pouvant importer et utiliser les modèles et le code du reste du backend.
* Tests du backend REST basés sur **Pytest**, intégrés dans Docker, pour que vous puissiez tester toutes les interactions de l'API indépendamment de la base de données. Étant exécutés dans Docker, les tests peuvent utiliser un nouvel entrepôt de données créé de zéro à chaque fois (vous pouvez donc utiliser ElasticSearch, MongoDB, CouchDB, etc. et juste tester que l'API fonctionne).
* Intégration Python facile avec **Jupyter Kernels** pour le développement à distance ou intra-Docker avec des extensions comme Atom Hydrogen ou Visual Studio Code Jupyter.
* Frontend **Vue** :
* Généré avec Vue CLI.
* Gestion de l'**Authentification JWT**.
* Page de connexion.
* Après la connexion, page de tableau de bord principal.
* Tableau de bord principal avec création et modification d'utilisateurs.
* Modification de ses propres caractéristiques utilisateur.
* **Vuex**.
* **Vue-router**.
* **Vuetify** pour de magnifiques composants *material design*.
* **TypeScript**.
* Serveur Docker basé sur **Nginx** (configuré pour être facilement manipulé avec Vue-router).
* Utilisation de *Docker multi-stage building*, pour ne pas avoir besoin de sauvegarder ou *commit* du code compilé.
* Tests frontend exécutés à la compilation (pouvant être désactivés).
* Fait aussi modulable que possible, pour pouvoir fonctionner comme tel, tout en pouvant être utilisé qu'en partie grâce à Vue CLI.
* **PGAdmin** pour les bases de données PostgreSQL, facilement modifiable pour utiliser PHPMYAdmin ou MySQL.
* **Flower** pour la surveillance de tâches Celery.
* Équilibrage de charge entre le frontend et le backend avec **Traefik**, afin de pouvoir avoir les deux sur le même domaine, séparés par chemins, mais servis par différents conteneurs.
* Intégration Traefik, comprenant la génération automatique de certificat **HTTPS** Let's Encrypt.
* GitLab **CI** (intégration continue), comprenant des tests pour le frontend et le backend.
## Full Stack FastAPI Couchbase
GitHub : <a href="https://github.com/tiangolo/full-stack-fastapi-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-couchbase</a>
⚠️ **ATTENTION** ⚠️
Si vous démarrez un nouveau projet de zéro, allez voir les alternatives au début de cette page.
Par exemple, le générateur de projet <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">Full Stack FastAPI PostgreSQL</a> peut être une meilleure alternative, étant activement maintenu et utilisé et comprenant toutes les nouvelles fonctionnalités et améliorations.
Vous êtes toujours libre d'utiliser le générateur basé sur Couchbase si vous le voulez, cela devrait probablement fonctionner correctement, et si vous avez déjà un projet généré en utilisant ce dernier, cela devrait fonctionner aussi (et vous l'avez déjà probablement mis à jour suivant vos besoins).
Vous pouvez en apprendre plus dans la documentation du dépôt GithHub.
## Full Stack FastAPI MongoDB
...viendra surement plus tard, suivant le temps que j'ai. 😅 🎉
## Modèles d'apprentissage automatique avec spaCy et FastAPI
GitHub : <a href="https://github.com/microsoft/cookiecutter-spacy-fastapi" class="external-link" target="_blank">https://github.com/microsoft/cookiecutter-spacy-fastapi</a>
## Modèles d'apprentissage automatique avec spaCy et FastAPI - Fonctionnalités
* Intégration d'un modèle NER **spaCy**.
* Formatage de requête pour **Azure Cognitive Search**.
* Serveur Python web **prêt à utiliser en production** utilisant Uvicorn et Gunicorn.
* Déploiement CI/CD Kubernetes pour **Azure DevOps** (AKS).
* **Multilangues**. Choisissez facilement l'une des langues intégrées à spaCy durant la mise en place du projet.
* **Facilement généralisable** à d'autres bibliothèques similaires (Pytorch, Tensorflow), et non juste spaCy.

View File

@@ -1,68 +1,70 @@
# Introduction aux types Python { #python-types-intro }
# Introduction aux Types Python
Python prend en charge des «type hints» (également appelées «annotations de type») optionnels.
Python supporte des annotations de type (ou *type hints*) optionnelles.
Ces **«type hints»** ou annotations sont une syntaxe spéciale qui permet de déclarer le <abbr title="par exemple : str, int, float, bool">type</abbr> d'une variable.
Ces annotations de type constituent une syntaxe spéciale qui permet de déclarer le <abbr title="par exemple : str, int, float, bool">type</abbr> d'une variable.
En déclarant des types pour vos variables, les éditeurs et les outils peuvent vous offrir un meilleur support.
En déclarant les types de vos variables, cela permet aux différents outils comme les éditeurs de texte d'offrir un meilleur support.
Il s'agit d'un **tutoriel rapide / rappel** sur les «type hints» Python. Il ne couvre que le minimum nécessaire pour les utiliser avec **FastAPI** ... ce qui est en réalité très peu.
Ce chapitre n'est qu'un **tutoriel rapide / rappel** sur les annotations de type Python.
Seulement le minimum nécessaire pour les utiliser avec **FastAPI** sera couvert... ce qui est en réalité très peu.
**FastAPI** est entièrement basé sur ces «type hints», qui lui apportent de nombreux avantages et bénéfices.
**FastAPI** est totalement basé sur ces annotations de type, qui lui donnent de nombreux avantages.
Mais même si vous n'utilisez jamais **FastAPI**, vous auriez intérêt à en apprendre un peu à leur sujet.
Mais même si vous n'utilisez pas ou n'utiliserez jamais **FastAPI**, vous pourriez bénéficier d'apprendre quelques choses sur ces dernières.
/// note | Remarque
/// note
Si vous êtes un expert Python et que vous savez déjà tout sur les «type hints», passez au chapitre suivant.
Si vous êtes un expert Python, et que vous savez déjà **tout** sur les annotations de type, passez au chapitre suivant.
///
## Motivation { #motivation }
## Motivations
Commençons par un exemple simple :
Prenons un exemple simple :
{* ../../docs_src/python_types/tutorial001_py39.py *}
{*../../docs_src/python_types/tutorial001.py*}
Exécuter ce programme affiche :
Exécuter ce programe affiche :
```
John Doe
```
La fonction fait ce qui suit :
La fonction :
* Prend un `first_name` et un `last_name`.
* Convertit la première lettre de chacun en majuscule avec `title()`.
* <abbr title="Les met ensemble, en une seule. Avec le contenu de l'une après l'autre.">Concatène</abbr> les deux avec un espace au milieu.
* Convertit la première lettre de chaque paramètre en majuscules grâce à `title()`.
* Concatène les résultats avec un espace entre les deux.
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
{*../../docs_src/python_types/tutorial001.py hl[2] *}
### Modifiez-le { #edit-it }
### Limitations
C'est un programme très simple.
Mais maintenant, imaginez que vous l'écriviez à partir de zéro.
Mais maintenant imaginez que vous l'écriviez de zéro.
À un certain moment, vous auriez commencé la définition de la fonction, vous aviez les paramètres prêts...
À un certain point vous auriez commencé la définition de la fonction, vous aviez les paramètres prêts.
Mais ensuite, vous devez appeler «cette méthode qui convertit la première lettre en majuscule».
Mais vous aviez besoin de "cette méthode qui convertit la première lettre en majuscule".
Était-ce `upper` ? Était-ce `uppercase` ? `first_uppercase` ? `capitalize` ?
Était-ce `upper` ? `uppercase` ? `first_uppercase` ? `capitalize` ?
Alors, vous essayez l'ami de toujours du programmeur : l'autocomplétion de l'éditeur.
Vous essayez donc d'utiliser le vieil ami du programmeur, l'auto-complétion de l'éditeur.
Vous saisissez le premier paramètre de la fonction, `first_name`, puis un point (`.`) et appuyez sur `Ctrl+Espace` pour déclencher l'autocomplétion.
Vous écrivez le premier paramètre, `first_name`, puis un point (`.`) et appuyez sur `Ctrl+Espace` pour déclencher l'auto-complétion.
Mais, malheureusement, vous n'obtenez rien d'utile :
Mais malheureusement, rien d'utile n'en résulte :
<img src="/img/python-types/image01.png">
### Ajouter des types { #add-types }
### Ajouter des types
Modifions une seule ligne par rapport à la version précédente.
Modifions une seule ligne de la version précédente.
Nous allons changer seulement cet extrait, les paramètres de la fonction, de :
Nous allons changer exactement ce fragment, les paramètres de la fonction, de :
```Python
first_name, last_name
@@ -76,389 +78,222 @@ Nous allons changer exactement ce fragment, les paramètres de la fonction, de :
C'est tout.
Ce sont les «type hints» :
Ce sont des annotations de types :
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
{*../../docs_src/python_types/tutorial002.py hl[1] *}
Ce n'est pas la même chose que de déclarer des valeurs par défaut comme on le ferait avec :
À ne pas confondre avec la déclaration de valeurs par défaut comme ici :
```Python
first_name="john", last_name="doe"
```
C'est différent.
C'est une chose différente.
Nous utilisons des deux-points (`:`), pas des signes égal (`=`).
On utilise un deux-points (`:`), et pas un égal (`=`).
Et ajouter des «type hints» ne change normalement pas ce qui se passe par rapport à ce qui se passerait sans eux.
Et ajouter des annotations de types ne ce normalement pas de différence avec le comportement qui aurait eu lieu si elles n'étaient pas là.
Mais maintenant, imaginez que vous êtes à nouveau en train de créer cette fonction, mais avec des «type hints».
Maintenant, imaginez que vous êtes en train de créer cette fonction, mais avec des annotations de type cette fois.
Au même moment, vous essayez de déclencher l'autocomplétion avec `Ctrl+Espace` et vous voyez :
Au même moment que durant l'exemple précédent, vous essayez de déclencher l'auto-complétion et vous voyez :
<img src="/img/python-types/image02.png">
Avec cela, vous pouvez faire défiler, voir les options, jusqu'à trouver celle qui «vous dit quelque chose» :
Vous pouvez donc dérouler les options jusqu'à trouver la méthode à laquelle vous pensiez.
<img src="/img/python-types/image03.png">
## Plus de motivations { #more-motivation }
## Plus de motivations
Regardez cette fonction, elle a déjà des «type hints» :
Cette fonction possède déjà des annotations de type :
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
{*../../docs_src/python_types/tutorial003.py hl[1] *}
Comme l'éditeur connaît les types des variables, vous n'obtenez pas seulement l'autocomplétion, vous bénéficiez également de vérifications d'erreurs :
Comme l'éditeur connaît le type des variables, vous n'avez pas seulement l'auto-complétion, mais aussi de la détection d'erreurs :
<img src="/img/python-types/image04.png">
Maintenant vous savez qu'il faut corriger, convertir `age` en chaîne avec `str(age)` :
Maintenant que vous avez connaissance du problème, convertissez `age` en <abbr title="string">chaîne de caractères</abbr> grâce à `str(age)` :
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
{*../../docs_src/python_types/tutorial004.py hl[2] *}
## Déclarer des types { #declaring-types }
## Déclarer des types
Vous venez de voir l'endroit principal où déclarer des «type hints» : en tant que paramètres de fonctions.
Vous venez de voir là où les types sont généralement déclarés : dans les paramètres de fonctions.
C'est aussi l'endroit principal où vous les utiliserez avec **FastAPI**.
C'est aussi ici que vous les utiliseriez avec **FastAPI**.
### Types simples { #simple-types }
### Types simples
Vous pouvez déclarer tous les types standards de Python, pas seulement `str`.
Vous pouvez déclarer tous les types de Python, pas seulement `str`.
Vous pouvez utiliser, par exemple :
Comme par exemple :
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
{*../../docs_src/python_types/tutorial005.py hl[1] *}
### Types génériques avec paramètres de type { #generic-types-with-type-parameters }
### Types génériques avec des paramètres de types
Il existe des structures de données qui peuvent contenir d'autres valeurs, comme `dict`, `list`, `set` et `tuple`. Et les valeurs internes peuvent, elles aussi, avoir leur propre type.
Il existe certaines structures de données qui contiennent d'autres valeurs, comme `dict`, `list`, `set` et `tuple`. Et les valeurs internes peuvent elles aussi avoir leurs propres types.
Ces types qui ont des types internes sont appelés types «génériques». Et il est possible de les déclarer, même avec leurs types internes.
Pour déclarer ces types et les types internes, on utilise le module standard de Python `typing`.
Pour déclarer ces types et leurs types internes, vous pouvez utiliser le module standard Python `typing`. Il existe précisément pour prendre en charge ces «type hints».
Il existe spécialement pour supporter ces annotations de types.
#### Versions plus récentes de Python { #newer-versions-of-python }
#### `List`
La syntaxe utilisant `typing` est compatible avec toutes les versions, de Python 3.6 aux plus récentes, y compris Python 3.9, Python 3.10, etc.
Par exemple, définissons une variable comme `list` de `str`.
Au fur et à mesure que Python évolue, les versions plus récentes offrent un meilleur support pour ces annotations de type et, dans de nombreux cas, vous n'aurez même pas besoin d'importer et d'utiliser le module `typing` pour déclarer les annotations de type.
Importez `List` (avec un `L` majuscule) depuis `typing`.
Si vous pouvez choisir une version plus récente de Python pour votre projet, vous pourrez profiter de cette simplicité supplémentaire.
{*../../docs_src/python_types/tutorial006.py hl[1] *}
Dans toute la documentation, il y a des exemples compatibles avec chaque version de Python (lorsqu'il y a une différence).
Déclarez la variable, en utilisant la syntaxe des deux-points (`:`).
Par exemple, «Python 3.6+» signifie que c'est compatible avec Python 3.6 ou supérieur (y compris 3.7, 3.8, 3.9, 3.10, etc.). Et «Python 3.9+» signifie que c'est compatible avec Python 3.9 ou supérieur (y compris 3.10, etc.).
Et comme type, mettez `List`.
Si vous pouvez utiliser les dernières versions de Python, utilisez les exemples de la dernière version, ils auront la meilleure et la plus simple syntaxe, par exemple, «Python 3.10+».
Les listes étant un type contenant des types internes, mettez ces derniers entre crochets (`[`, `]`) :
#### List { #list }
{*../../docs_src/python_types/tutorial006.py hl[4] *}
Par exemple, définissons une variable comme étant une `list` de `str`.
/// tip | Astuce
Déclarez la variable, en utilisant la même syntaxe avec les deux-points (`:`).
Ces types internes entre crochets sont appelés des "paramètres de type".
Comme type, mettez `list`.
Comme la liste est un type qui contient des types internes, mettez-les entre crochets :
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
/// info
Ces types internes entre crochets sont appelés «paramètres de type».
Dans ce cas, `str` est le paramètre de type passé à `list`.
Ici, `str` est un paramètre de type passé à `List`.
///
Cela signifie : «la variable `items` est une `list`, et chacun des éléments de cette liste est un `str`».
Ce qui signifie : "la variable `items` est une `list`, et chacun de ses éléments a pour type `str`.
En faisant cela, votre éditeur peut vous offrir du support même pendant le traitement des éléments de la liste :
En faisant cela, votre éditeur pourra vous aider, même pendant que vous traitez des éléments de la liste.
<img src="/img/python-types/image05.png">
Sans types, c'est presque impossible à atteindre.
Sans types, c'est presque impossible à réaliser.
Notez que la variable `item` est l'un des éléments de la liste `items`.
Vous remarquerez que la variable `item` n'est qu'un des éléments de la list `items`.
Et pourtant, l'éditeur sait que c'est un `str` et fournit le support correspondant.
Et pourtant, l'éditeur sait qu'elle est de type `str` et pourra donc vous aider à l'utiliser.
#### Tuple et Set { #tuple-and-set }
#### `Tuple` et `Set`
Vous feriez la même chose pour déclarer des `tuple` et des `set` :
C'est le même fonctionnement pour déclarer un `tuple` ou un `set` :
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
{*../../docs_src/python_types/tutorial007.py hl[1,4] *}
Cela signifie :
Dans cet exemple :
* La variable `items_t` est un `tuple` avec 3 éléments, un `int`, un autre `int`, et un `str`.
* La variable `items_t` est un `tuple` avec 3 éléments, un `int`, un deuxième `int`, et un `str`.
* La variable `items_s` est un `set`, et chacun de ses éléments est de type `bytes`.
#### Dict { #dict }
#### `Dict`
Pour définir un `dict`, vous passez 2 paramètres de type, séparés par des virgules.
Pour définir un `dict`, il faut lui passer 2 paramètres, séparés par une virgule (`,`).
Le premier paramètre de type est pour les clés du `dict`.
Le premier paramètre de type est pour les clés et le second pour les valeurs du dictionnaire (`dict`).
Le deuxième paramètre de type est pour les valeurs du `dict` :
{*../../docs_src/python_types/tutorial008.py hl[1,4] *}
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
Dans cet exemple :
Cela signifie :
* La variable `prices` est de type `dict` :
* Les clés de ce dictionnaire sont de type `str`.
* Les valeurs de ce dictionnaire sont de type `float`.
* La variable `prices` est un `dict` :
* Les clés de ce `dict` sont de type `str` (disons, le nom de chaque article).
* Les valeurs de ce `dict` sont de type `float` (disons, le prix de chaque article).
#### `Optional`
#### Union { #union }
Vous pouvez aussi utiliser `Optional` pour déclarer qu'une variable a un type, comme `str` mais qu'il est "optionnel" signifiant qu'il pourrait aussi être `None`.
Vous pouvez déclarer qu'une variable peut être n'importe lequel de plusieurs types, par exemple, un `int` ou un `str`.
{*../../docs_src/python_types/tutorial009.py hl[1,4] *}
En Python 3.6 et supérieur (y compris Python 3.10), vous pouvez utiliser le type `Union` de `typing` et mettre entre crochets les types possibles à accepter.
Utiliser `Optional[str]` plutôt que `str` permettra à l'éditeur de vous aider à détecter les erreurs où vous supposeriez qu'une valeur est toujours de type `str`, alors qu'elle pourrait aussi être `None`.
En Python 3.10, il existe également une **nouvelle syntaxe** dans laquelle vous pouvez indiquer les types possibles séparés par une <abbr title='également appelé «bitwise or operator», mais cette signification nest pas pertinente ici'>barre verticale (`|`)</abbr>.
#### Types génériques
//// tab | Python 3.10+
Les types qui peuvent contenir des paramètres de types entre crochets, comme :
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
Dans les deux cas, cela signifie que `item` peut être un `int` ou un `str`.
#### Éventuellement `None` { #possibly-none }
Vous pouvez déclarer qu'une valeur peut avoir un type, comme `str`, mais qu'elle peut aussi être `None`.
En Python 3.6 et supérieur (y compris Python 3.10), vous pouvez le déclarer en important et en utilisant `Optional` depuis le module `typing`.
```Python hl_lines="1 4"
{!../../docs_src/python_types/tutorial009_py39.py!}
```
Utiliser `Optional[str]` au lieu de simplement `str` permettra à l'éditeur de vous aider à détecter les erreurs où vous pourriez supposer qu'une valeur est toujours un `str`, alors qu'elle pourrait en fait être `None`.
`Optional[Something]` est en réalité un raccourci pour `Union[Something, None]`, c'est équivalent.
Cela signifie aussi qu'en Python 3.10, vous pouvez utiliser `Something | None` :
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
//// tab | Python 3.9+ alternative
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
#### Utiliser `Union` ou `Optional` { #using-union-or-optional }
Si vous utilisez une version de Python inférieure à 3.10, voici un conseil de mon point de vue très **subjectif** :
* 🚨 Évitez d'utiliser `Optional[SomeType]`
* À la place, ✨ **utilisez `Union[SomeType, None]`** ✨.
Les deux sont équivalents et, en interne, c'est la même chose, mais je recommanderais `Union` plutôt que `Optional` parce que le mot «optional» semble impliquer que la valeur est optionnelle, alors qu'il signifie en réalité «elle peut être `None`», même si elle n'est pas optionnelle et reste requise.
Je pense que `Union[SomeType, None]` est plus explicite quant à ce que cela signifie.
Il ne s'agit que de mots et de noms. Mais ces mots peuvent influencer la manière dont vous et vos coéquipiers pensez au code.
Par exemple, prenons cette fonction :
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
Le paramètre `name` est défini comme `Optional[str]`, mais il n'est pas optionnel, vous ne pouvez pas appeler la fonction sans le paramètre :
```Python
say_hi() # Oh, non, cela lève une erreur ! 😱
```
Le paramètre `name` est toujours requis (pas optionnel) parce qu'il n'a pas de valeur par défaut. Néanmoins, `name` accepte `None` comme valeur :
```Python
say_hi(name=None) # Cela fonctionne, None est valide 🎉
```
La bonne nouvelle, c'est qu'une fois sur Python 3.10, vous n'aurez plus à vous en soucier, car vous pourrez simplement utiliser `|` pour définir des unions de types :
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
Et vous n'aurez plus à vous préoccuper de noms comme «Optional» et «Union». 😎
#### Types génériques { #generic-types }
Ces types qui prennent des paramètres de type entre crochets sont appelés **types génériques** ou **Generics**, par exemple :
//// tab | Python 3.10+
Vous pouvez utiliser les mêmes types intégrés comme génériques (avec des crochets et des types à l'intérieur) :
* `list`
* `tuple`
* `set`
* `dict`
Et, comme avec les versions précédentes de Python, ceux du module `typing` :
* `Union`
* `List`
* `Tuple`
* `Set`
* `Dict`
* `Optional`
* ... et d'autres.
* ...et d'autres.
En Python 3.10, comme alternative à l'utilisation des génériques `Union` et `Optional`, vous pouvez utiliser la <abbr title='également appelé «bitwise or operator», mais cette signification nest pas pertinente ici'>barre verticale (`|`)</abbr> pour déclarer des unions de types, c'est bien mieux et plus simple.
sont appelés des **types génériques** ou **Generics**.
////
### Classes en tant que types
//// tab | Python 3.9+
Vous pouvez aussi déclarer une classe comme type d'une variable.
Vous pouvez utiliser les mêmes types intégrés comme génériques (avec des crochets et des types à l'intérieur) :
Disons que vous avez une classe `Person`, avec une variable `name` :
* `list`
* `tuple`
* `set`
* `dict`
{*../../docs_src/python_types/tutorial010.py hl[1:3] *}
Et les génériques du module `typing` :
* `Union`
* `Optional`
* ... et d'autres.
////
### Classes comme types { #classes-as-types }
Vous pouvez également déclarer une classe comme type d'une variable.
Disons que vous avez une classe `Person`, avec un nom :
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
Vous pouvez ensuite déclarer une variable de type `Person` :
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
{*../../docs_src/python_types/tutorial010.py hl[6] *}
Et là encore, vous obtenez tout le support de l'éditeur :
Et vous aurez accès, encore une fois, au support complet offert par l'éditeur :
<img src="/img/python-types/image06.png">
Notez que cela signifie que «`one_person` est une **instance** de la classe `Person`».
## Les modèles Pydantic
Cela ne signifie pas «`one_person` est la **classe** appelée `Person.
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> est une bibliothèque Python pour effectuer de la validation de données.
## Modèles Pydantic { #pydantic-models }
Vous déclarez la forme de la donnée avec des classes et des attributs.
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> est une bibliothèque Python pour effectuer la validation de données.
Chaque attribut possède un type.
Vous déclarez la «forme» des données sous forme de classes avec des attributs.
Puis vous créez une instance de cette classe avec certaines valeurs et **Pydantic** validera les valeurs, les convertira dans le type adéquat (si c'est nécessaire et possible) et vous donnera un objet avec toute la donnée.
Et chaque attribut a un type.
Ainsi, votre éditeur vous offrira un support adapté pour l'objet résultant.
Ensuite, vous créez une instance de cette classe avec certaines valeurs et elle validera les valeurs, les convertira dans le type approprié (le cas échéant) et vous donnera un objet avec toutes les données.
Extrait de la documentation officielle de **Pydantic** :
Et vous bénéficiez de tout le support de l'éditeur avec cet objet résultant.
Un exemple tiré de la documentation officielle de Pydantic :
{* ../../docs_src/python_types/tutorial011_py310.py *}
{*../../docs_src/python_types/tutorial011.py*}
/// info
Pour en savoir plus à propos de <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, consultez sa documentation</a>.
Pour en savoir plus à propos de <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, allez jeter un coup d'oeil à sa documentation</a>.
///
**FastAPI** est entièrement basé sur Pydantic.
**FastAPI** est basé entièrement sur **Pydantic**.
Vous verrez beaucoup plus de tout cela en pratique dans le [Tutoriel - Guide utilisateur](tutorial/index.md){.internal-link target=_blank}.
Vous verrez bien plus d'exemples de son utilisation dans [Tutoriel - Guide utilisateur](tutorial/index.md){.internal-link target=_blank}.
/// tip | Astuce
## Les annotations de type dans **FastAPI**
Pydantic a un comportement particulier lorsque vous utilisez `Optional` ou `Union[Something, None]` sans valeur par défaut. Vous pouvez en lire davantage dans la documentation Pydantic à propos des <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">champs Optional requis</a>.
**FastAPI** utilise ces annotations pour faire différentes choses.
///
Avec **FastAPI**, vous déclarez des paramètres grâce aux annotations de types et vous obtenez :
## Annotations de type avec métadonnées { #type-hints-with-metadata-annotations }
* **du support de l'éditeur**
* **de la vérification de types**
Python propose aussi une fonctionnalité qui permet d'ajouter des **<abbr title="Des données à propos des données, dans ce cas, des informations sur le type, p. ex. une description.">métadonnées</abbr> supplémentaires** dans ces «type hints» en utilisant `Annotated`.
...et **FastAPI** utilise ces mêmes déclarations pour :
Depuis Python 3.9, `Annotated` fait partie de la bibliothèque standard, vous pouvez donc l'importer depuis `typing`.
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
Python lui-même ne fait rien avec cet `Annotated`. Et pour les éditeurs et autres outils, le type est toujours `str`.
Mais vous pouvez utiliser cet espace dans `Annotated` pour fournir à **FastAPI** des métadonnées supplémentaires sur la façon dont vous souhaitez que votre application se comporte.
L'important à retenir est que **le premier paramètre de type** que vous passez à `Annotated` est le **type réel**. Le reste n'est que des métadonnées pour d'autres outils.
Pour l'instant, vous avez seulement besoin de savoir que `Annotated` existe et que c'est du Python standard. 😎
Plus tard, vous verrez à quel point cela peut être **puissant**.
/// tip | Astuce
Le fait que ce soit du **Python standard** signifie que vous aurez toujours la **meilleure expérience développeur possible** dans votre éditeur, avec les outils que vous utilisez pour analyser et refactoriser votre code, etc. ✨
Et aussi que votre code sera très compatible avec de nombreux autres outils et bibliothèques Python. 🚀
///
## Les annotations de type dans **FastAPI** { #type-hints-in-fastapi }
**FastAPI** tire parti de ces «type hints» pour faire plusieurs choses.
Avec **FastAPI**, vous déclarez des paramètres avec des «type hints» et vous obtenez :
* **du support de l'éditeur**.
* **des vérifications de type**.
... et **FastAPI** utilise les mêmes déclarations pour :
* **Définir les prérequis** : depuis les paramètres de chemin des requêtes, les paramètres de requête, les en-têtes, les corps, les dépendances, etc.
* **Convertir les données** : depuis la requête vers le type requis.
* **Valider les données** : provenant de chaque requête :
* Générer des **erreurs automatiques** renvoyées au client lorsque les données sont invalides.
* **Définir les prérequis** : depuis les paramètres de chemins des requêtes, les entêtes, les corps, les dépendances, etc.
* **Convertir des données** : depuis la requête vers les types requis.
* **Valider des données** : venant de chaque requête :
* Générant automatiquement des **erreurs** renvoyées au client quand la donnée est invalide.
* **Documenter** l'API avec OpenAPI :
* ce qui est ensuite utilisé par les interfaces utilisateur de documentation interactive automatiques.
* ce qui ensuite utilisé par les interfaces utilisateur automatiques de documentation interactive.
Tout cela peut sembler abstrait. Ne vous inquiétez pas. Vous verrez tout cela en action dans le [Tutoriel - Guide utilisateur](tutorial/index.md){.internal-link target=_blank}.
Tout cela peut paraître bien abstrait, mais ne vous inquiétez pas, vous verrez tout ça en pratique dans [Tutoriel - Guide utilisateur](tutorial/index.md){.internal-link target=_blank}.
L'important, c'est qu'en utilisant les types standard de Python, en un seul endroit (au lieu d'ajouter davantage de classes, de décorateurs, etc.), **FastAPI** fera une grande partie du travail pour vous.
Ce qu'il faut retenir c'est qu'en utilisant les types standard de Python, à un seul endroit (plutôt que d'ajouter plus de classes, de décorateurs, etc.), **FastAPI** fera une grande partie du travail pour vous.
/// info
Si vous avez déjà parcouru tout le tutoriel et êtes revenu pour en savoir plus sur les types, une bonne ressource est <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">l'aide-mémoire de `mypy`</a>.
Si vous avez déjà lu le tutoriel et êtes revenus ici pour voir plus sur les types, une bonne ressource est la <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">"cheat sheet" de `mypy`</a>.
///

View File

@@ -1,86 +1,84 @@
# Tâches d'arrière-plan { #background-tasks }
# Tâches d'arrière-plan
Vous pouvez définir des tâches d'arrière-plan qui seront exécutées après avoir retourné une réponse.
Ceci est utile pour les opérations qui doivent avoir lieu après une requête, mais où le client n'a pas vraiment à attendre que l'opération soit terminée pour recevoir la réponse.
Ceci est utile pour les opérations qui doivent avoir lieu après une requête, mais où le client n'a pas réellement besoin d'attendre que l'opération soit terminée pour recevoir une réponse.
Cela comprend, par exemple :
* Les notifications par email envoyées après l'exécution d'une action :
* Étant donné que se connecter à un serveur email et envoyer un email a tendance à être «lent» (plusieurs secondes), vous pouvez retourner la réponse directement et envoyer la notification par email en arrière-plan.
* Étant donné que se connecter à un serveur et envoyer un email a tendance à être «lent» (plusieurs secondes), vous pouvez retourner la réponse directement et envoyer la notification en arrière-plan.
* Traiter des données :
* Par exemple, disons que vous recevez un fichier qui doit passer par un processus lent, vous pouvez retourner une réponse «Accepted» (HTTP 202) et traiter le fichier en arrière-plan.
* Par exemple, si vous recevez un fichier qui doit passer par un traitement lent, vous pouvez retourner une réponse «Accepted» (HTTP 202) puis faire le traitement en arrière-plan.
## Utiliser `BackgroundTasks` { #using-backgroundtasks }
Pour commencer, importez `BackgroundTasks` et définissez un paramètre dans votre *fonction de chemin* avec `BackgroundTasks` comme type déclaré :
## Utiliser `BackgroundTasks`
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
Pour commencer, importez `BackgroundTasks` et définissez un paramètre dans votre *fonction de chemin* avec `BackgroundTasks` comme type déclaré.
{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
**FastAPI** créera l'objet de type `BackgroundTasks` pour vous et le passera comme paramètre.
## Créer une fonction de tâche { #create-a-task-function }
## Créer une fonction de tâche
Créez une fonction à exécuter comme tâche d'arrière-plan.
C'est simplement une fonction standard qui peut recevoir des paramètres.
Une fonction à exécuter comme tâche d'arrière-plan est juste une fonction standard qui peut recevoir des paramètres.
Elle peut être une fonction asynchrone (`async def`) ou une fonction normale (`def`), **FastAPI** saura la gérer correctement.
Dans cet exemple, la fonction de tâche écrira dans un fichier (afin de simuler un envoi d'email).
Et comme l'opération d'écriture n'utilise ni `async` ni `await`, nous définissons la fonction avec un `def` normal :
L'opération d'écriture n'utilisant ni `async` ni `await`, on définit la fonction avec un `def` normal.
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
## Ajouter la tâche d'arrière-plan { #add-the-background-task }
## Ajouter une tâche d'arrière-plan
Dans votre *fonction de chemin*, passez votre fonction de tâche à l'objet de tâches d'arrière-plan grâce à la méthode `.add_task()` :
Dans votre *fonction de chemin*, passez votre fonction de tâche à l'objet de type `BackgroundTasks` (`background_tasks` ici) grâce à la méthode `.add_task()` :
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
`.add_task()` reçoit comme arguments :
* Une fonction de tâche à exécuter en arrière-plan (`write_notification`).
* Toute séquence d'arguments à passer à la fonction de tâche dans l'ordre (`email`).
* Tous les arguments nommés à passer à la fonction de tâche (`message="some notification"`).
* Les arguments positionnels à passer à la fonction de tâche dans l'ordre (`email`).
* Les arguments nommés à passer à la fonction de tâche (`message="some notification"`).
## Injection de dépendances { #dependency-injection }
## Injection de dépendances
Utiliser `BackgroundTasks` fonctionne aussi avec le système d'injection de dépendances, vous pouvez déclarer un paramètre de type `BackgroundTasks` à plusieurs niveaux : dans une *fonction de chemin*, dans une dépendance (dependable), dans une sous-dépendance, etc.
Utiliser `BackgroundTasks` fonctionne aussi avec le système d'injection de dépendances. Vous pouvez déclarer un paramètre de type `BackgroundTasks` à différents niveaux : dans une *fonction de chemin*, dans une dépendance, dans une sous-dépendance...
**FastAPI** sait quoi faire dans chaque cas et comment réutiliser le même objet, afin que toutes les tâches d'arrière-plan soient fusionnées et exécutées ensuite en arrière-plan :
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
**FastAPI** sait quoi faire dans chaque cas et comment réutiliser le même objet, afin que tous les paramètres de type `BackgroundTasks` soient fusionnés et que les tâches soient exécutées en arrière-plan :
{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *}
Dans cet exemple, les messages seront écrits dans le fichier `log.txt` après que la réponse soit envoyée.
S'il y avait un paramètre de requête dans la requête, il sera écrit dans le log via une tâche d'arrière-plan.
S'il y avait une `query` (paramètre nommé `q`) dans la requête, alors elle sera écrite dans `log.txt` via une tâche d'arrière-plan.
Ensuite, une autre tâche d'arrière-plan générée dans la *fonction de chemin* écrira un message utilisant le paramètre de chemin `email`.
Et ensuite une autre tâche d'arrière-plan (générée dans les paramètres de la *la fonction de chemin*) écrira un message dans `log.txt` comprenant le paramètre de chemin `email`.
## Détails techniques { #technical-details }
## Détails techniques
La classe `BackgroundTasks` provient directement de <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`starlette.background`</a>.
Elle est importée/incluse directement dans FastAPI afin que vous puissiez l'importer depuis `fastapi` et éviter d'importer par accident l'alternative `BackgroundTask` (sans le `s` final) depuis `starlette.background`.
Elle est importée/incluse directement dans **FastAPI** pour que vous puissiez l'importer depuis `fastapi` et éviter d'importer accidentellement `BackgroundTask` (sans `s` à la fin) depuis `starlette.background`.
En utilisant seulement `BackgroundTasks` (et non `BackgroundTask`), il est possible de l'utiliser en tant que paramètre de *fonction de chemin* et de laisser **FastAPI** gérer le reste pour vous, comme lorsque vous utilisez l'objet `Request` directement.
En utilisant seulement `BackgroundTasks` (et non `BackgroundTask`), il est possible de l'utiliser en tant que paramètre de *fonction de chemin* et de laisser **FastAPI** gérer le reste pour vous, comme en utilisant l'objet `Request` directement.
Il est toujours possible d'utiliser `BackgroundTask` seul dans FastAPI, mais vous devez créer l'objet dans votre code et renvoyer une `Response` Starlette qui l'inclut.
Il est tout de même possible d'utiliser `BackgroundTask` seul dans **FastAPI**, mais dans ce cas il faut créer l'objet dans le code et renvoyer une `Response` Starlette l'incluant.
Vous pouvez voir plus de détails dans <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">la documentation officielle de Starlette sur les tâches d'arrière-plan</a>.
Plus de détails sont disponibles dans <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">la documentation officielle de Starlette sur les tâches d'arrière-plan</a> (via leurs classes `BackgroundTasks`et `BackgroundTask`).
## Avertissement { #caveat }
## Avertissement
Si vous avez besoin d'effectuer des calculs lourds en arrière-plan et que vous n'avez pas nécessairement besoin qu'ils soient exécutés par le même processus (par exemple, vous n'avez pas besoin de partager la mémoire, les variables, etc.), vous pourriez bénéficier d'utiliser des outils plus importants comme <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
Si vous avez besoin de réaliser des traitements lourds en tâche d'arrière-plan et que vous n'avez pas besoin que ces traitements aient lieu dans le même process (par exemple, pas besoin de partager la mémoire, les variables, etc.), il peut s'avérer profitable d'utiliser des outils plus importants tels que <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
Ils nécessitent généralement des configurations plus complexes, un gestionnaire de file de messages/jobs, comme RabbitMQ ou Redis, mais ils permettent d'exécuter des tâches d'arrière-plan dans plusieurs processus, et surtout, sur plusieurs serveurs.
Ces outils nécessitent généralement des configurations plus complexes ainsi qu'un gestionnaire de queue de message, comme RabbitMQ ou Redis, mais ils permettent d'exécuter des tâches d'arrière-plan dans différents process, et potentiellement, sur plusieurs serveurs.
Mais si vous avez besoin d'accéder aux variables et objets de la même application **FastAPI**, ou si vous avez besoin d'effectuer de petites tâches d'arrière-plan (comme envoyer une notification par email), vous pouvez simplement utiliser `BackgroundTasks`.
Mais si vous avez besoin d'accéder aux variables et objets de la même application **FastAPI**, ou si vous avez besoin d'effectuer de petites tâches d'arrière-plan (comme envoyer des notifications par email), vous pouvez simplement vous contenter d'utiliser `BackgroundTasks`.
## Résumé { #recap }
## Résumé
Importez et utilisez `BackgroundTasks` avec des paramètres dans les *fonctions de chemin* et les dépendances pour ajouter des tâches d'arrière-plan.
Importez et utilisez `BackgroundTasks` grâce aux paramètres de *fonction de chemin* et les dépendances pour ajouter des tâches d'arrière-plan.

View File

@@ -1,8 +1,8 @@
# Body - Paramètres multiples { #body-multiple-parameters }
# Body - Paramètres multiples
Maintenant que nous avons vu comment utiliser `Path` et `Query`, voyons des utilisations plus avancées des déclarations du corps de la requête.
Maintenant que nous avons vu comment manipuler `Path` et `Query`, voyons comment faire pour le corps d'une requête, communément désigné par le terme anglais "body".
## Mélanger les paramètres `Path`, `Query` et body { #mix-path-query-and-body-parameters }
## Mélanger les paramètres `Path`, `Query` et body
Tout d'abord, sachez que vous pouvez mélanger les déclarations des paramètres `Path`, `Query` et body, **FastAPI** saura quoi faire.
@@ -10,15 +10,15 @@ Vous pouvez également déclarer des paramètres body comme étant optionnels, e
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | Remarque
/// note
Notez que, dans ce cas, le paramètre `item` provenant du body est optionnel. Sa valeur par défaut est `None`.
Notez que, dans ce cas, le paramètre `item` provenant du `Body` est optionnel (sa valeur par défaut est `None`).
///
## Paramètres multiples du body { #multiple-body-parameters }
## Paramètres multiples du body
Dans l'exemple précédent, les *opérations de chemin* attendaient un body JSON avec les attributs d'un `Item`, par exemple :
Dans l'exemple précédent, les opérations de routage attendaient un body JSON avec les attributs d'un `Item`, par exemple :
```JSON
{
@@ -29,13 +29,13 @@ Dans l'exemple précédent, les *opérations de chemin* attendaient un body JSON
}
```
Mais vous pouvez également déclarer plusieurs paramètres provenant de body, par exemple `item` et `user` :
Mais vous pouvez également déclarer plusieurs paramètres provenant de body, par exemple `item` et `user` simultanément :
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
Dans ce cas, **FastAPI** détectera qu'il y a plus d'un paramètre body dans la fonction (il y a deux paramètres qui sont des modèles Pydantic).
Dans ce cas, **FastAPI** détectera qu'il y a plus d'un paramètre dans le body (chacun correspondant à un modèle Pydantic).
Il utilisera alors les noms des paramètres comme clés (noms de champs) dans le body, et s'attendra à recevoir un body semblable à :
Il utilisera alors les noms des paramètres comme clés, et s'attendra à recevoir quelque chose de semblable à :
```JSON
{
@@ -52,23 +52,23 @@ Il utilisera alors les noms des paramètres comme clés (noms de champs) dans le
}
```
/// note | Remarque
/// note
Notez que, bien que `item` ait été déclaré de la même manière qu'auparavant, il est maintenant attendu dans le body sous la clé `item`.
"Notez que, bien que nous ayons déclaré le paramètre `item` de la même manière que précédemment, il est maintenant associé à la clé `item` dans le corps de la requête."`.
///
**FastAPI** effectue la conversion automatique à partir de la requête, de sorte que le paramètre `item` reçoive son contenu spécifique, et de même pour `user`.
**FastAPI** effectue la conversion de la requête de façon transparente, de sorte que les objets `item` et `user` se trouvent correctement définis.
Il effectuera la validation des données composées et les documentera ainsi dans le schéma OpenAPI et la documentation automatique.
Il effectue également la validation des données (même imbriquées les unes dans les autres), et permet de les documenter correctement (schéma OpenAPI et documentation auto-générée).
## Valeurs scalaires dans le body { #singular-values-in-body }
## Valeurs scalaires dans le body
De la même façon qu'il existe `Query` et `Path` pour définir des données supplémentaires pour les paramètres query et path, **FastAPI** fournit un équivalent `Body`.
Par exemple, en étendant le modèle précédent, vous pouvez vouloir ajouter un paramètre `importance` dans le même body, en plus des paramètres `item` et `user`.
Si vous le déclarez tel quel, comme c'est une valeur scalaire, **FastAPI** supposera qu'il s'agit d'un paramètre de requête.
Si vous le déclarez tel quel, comme c'est une valeur [scalaire](https://docs.github.com/fr/graphql/reference/scalars), **FastAPI** supposera qu'il s'agit d'un paramètre de requête (`Query`).
Mais vous pouvez indiquer à **FastAPI** de la traiter comme une variable de body en utilisant `Body` :
@@ -92,13 +92,13 @@ Dans ce cas, **FastAPI** s'attendra à un body semblable à :
}
```
Encore une fois, cela convertira les types de données, les validera, permettra de générer la documentation, etc.
Encore une fois, cela convertira les types de données, les validera, permettra de générer la documentation, etc...
## Paramètres multiples body et query { #multiple-body-params-and-query }
## Paramètres multiples body et query
Bien entendu, vous pouvez également déclarer des paramètres de requête supplémentaires quand vous en avez besoin, en plus de tout paramètre body.
Bien entendu, vous pouvez déclarer autant de paramètres que vous le souhaitez, en plus des paramètres body déjà déclarés.
Comme, par défaut, les valeurs scalaires sont interprétées comme des paramètres query, vous n'avez pas besoin d'ajouter explicitement `Query`. Vous pouvez simplement écrire :
Par défaut, les valeurs [scalaires](https://docs.github.com/fr/graphql/reference/scalars) sont interprétées comme des paramètres query, donc inutile d'ajouter explicitement `Query`. Vous pouvez juste écrire :
```Python
q: Union[str, None] = None
@@ -112,7 +112,7 @@ q: str | None = None
Par exemple :
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[27] *}
/// info
@@ -120,13 +120,13 @@ Par exemple :
///
## Inclure un seul paramètre body { #embed-a-single-body-parameter }
## Inclure un paramètre imbriqué dans le body
Disons que vous avez seulement un paramètre `item` dans le body, correspondant à un modèle Pydantic `Item`.
Par défaut, **FastAPI** attendra sa déclaration directement dans le body.
Cependant, si vous souhaitez qu'il interprète correctement un JSON avec une clé `item` associée au contenu du modèle, comme cela serait le cas si vous déclariez des paramètres body additionnels, vous pouvez utiliser le paramètre spécial `embed` de `Body` :
Cependant, si vous souhaitez qu'il interprête correctement un JSON avec une clé `item` associée au contenu du modèle, comme cela serait le cas si vous déclariez des paramètres body additionnels, vous pouvez utiliser le paramètre spécial `embed` de `Body` :
```Python
item: Item = Body(embed=True)
@@ -160,12 +160,12 @@ au lieu de :
}
```
## Pour résumer { #recap }
## Pour résumer
Vous pouvez ajouter plusieurs paramètres body à votre *fonction de chemin*, même si une requête ne peut avoir qu'un seul body.
Vous pouvez ajouter plusieurs paramètres body dans votre fonction de routage, même si une requête ne peut avoir qu'un seul body.
Cependant, **FastAPI** s'en chargera, vous fournira les bonnes données dans votre fonction, et validera et documentera le schéma correct dans l'*opération de chemin*.
Cependant, **FastAPI** se chargera de faire opérer sa magie, afin de toujours fournir à votre fonction des données correctes, les validera et documentera le schéma associé.
Vous pouvez également déclarer des valeurs scalaires à recevoir dans le body.
Vous pouvez également déclarer des valeurs [scalaires](https://docs.github.com/fr/graphql/reference/scalars) à recevoir dans le body.
Et vous pouvez indiquer à **FastAPI** d'inclure le body dans une clé, même lorsqu'un seul paramètre est déclaré.
Et vous pouvez indiquer à **FastAPI** d'inclure le body dans une autre variable, même lorsqu'un seul paramètre est déclaré.

View File

@@ -1,40 +1,40 @@
# Corps de la requête { #request-body }
# Corps de la requête
Quand vous avez besoin d'envoyer des données depuis un client (par exemple, un navigateur) vers votre API, vous les envoyez en tant que **corps de requête**.
Quand vous avez besoin d'envoyer de la donnée depuis un client (comme un navigateur) vers votre API, vous l'envoyez en tant que **corps de requête**.
Le corps d'une **requête** est la donnée envoyée par le client à votre API. Le corps d'une **réponse** est la donnée que votre API envoie au client.
Le corps d'une **requête** est de la donnée envoyée par le client à votre API. Le corps d'une **réponse** est la donnée envoyée par votre API au client.
Votre API doit presque toujours envoyer un corps de **réponse**. Mais les clients n'ont pas forcément besoin d'envoyer des **corps de requête** tout le temps, parfois ils ne demandent qu'un chemin, peut-être avec quelques paramètres de requête, mais n'envoient pas de corps.
Votre API aura presque toujours à envoyer un corps de **réponse**. Mais un client n'a pas toujours à envoyer un corps de **requête**.
Pour déclarer un corps de **requête**, vous utilisez des modèles <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> avec toute leur puissance et leurs avantages.
Pour déclarer un corps de **requête**, on utilise les modèles de <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> en profitant de tous leurs avantages et fonctionnalités.
/// info
Pour envoyer des données, vous devriez utiliser l'une de ces méthodes : `POST` (la plus courante), `PUT`, `DELETE` ou `PATCH`.
Pour envoyer de la donnée, vous devriez utiliser : `POST` (le plus populaire), `PUT`, `DELETE` ou `PATCH`.
Envoyer un corps avec une requête `GET` a un comportement non défini dans les spécifications ; néanmoins, c'est supporté par FastAPI, seulement pour des cas d'utilisation très complexes/extrêmes.
Envoyer un corps dans une requête `GET` a un comportement non défini dans les spécifications, cela est néanmoins supporté par **FastAPI**, seulement pour des cas d'utilisation très complexes/extrêmes.
Comme c'est découragé, la documentation interactive avec Swagger UI n'affichera pas la documentation pour le corps lors de l'utilisation de `GET`, et des proxys intermédiaires pourraient ne pas le supporter.
Ceci étant découragé, la documentation interactive générée par Swagger UI ne montrera pas de documentation pour le corps d'une requête `GET`, et les proxys intermédiaires risquent de ne pas le supporter.
///
## Importer `BaseModel` de Pydantic { #import-pydantics-basemodel }
## Importez le `BaseModel` de Pydantic
Commencez par importer la classe `BaseModel` depuis `pydantic` :
Commencez par importer la classe `BaseModel` du module `pydantic` :
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
{* ../../docs_src/body/tutorial001.py hl[4] *}
## Créer votre modèle de données { #create-your-data-model }
## Créez votre modèle de données
Déclarez ensuite votre modèle de données en tant que classe qui hérite de `BaseModel`.
Utilisez les types Python standards pour tous les attributs :
Utilisez les types Python standard pour tous les attributs :
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
{* ../../docs_src/body/tutorial001.py hl[7:11] *}
Comme pour la déclaration de paramètres de requête, lorsqu'un attribut de modèle a une valeur par défaut, il n'est pas requis. Sinon, il est requis. Utilisez `None` pour le rendre simplement optionnel.
Tout comme pour la déclaration de paramètres de requête, quand un attribut de modèle a une valeur par défaut, il n'est pas nécessaire. Sinon, cet attribut doit être renseigné dans le corps de la requête. Pour rendre ce champ optionnel simplement, utilisez `None` comme valeur par défaut.
Par exemple, le modèle ci-dessus déclare un «`object`» JSON (ou `dict` Python) tel que :
Par exemple, le modèle ci-dessus déclare un "objet" JSON (ou `dict` Python) tel que :
```JSON
{
@@ -45,7 +45,7 @@ Par exemple, le modèle ci-dessus déclare un «`object`» JSON (ou `dict` Pytho
}
```
... comme `description` et `tax` sont optionnels (avec une valeur par défaut de `None`), cet «`object`» JSON serait aussi valide :
...`description` et `tax` étant des attributs optionnels (avec `None` comme valeur par défaut), cet "objet" JSON serait aussi valide :
```JSON
{
@@ -54,111 +54,109 @@ Par exemple, le modèle ci-dessus déclare un «`object`» JSON (ou `dict` Pytho
}
```
## Le déclarer comme paramètre { #declare-it-as-a-parameter }
## Déclarez-le comme paramètre
Pour l'ajouter à votre *opération de chemin*, déclarez-le de la même manière que vous déclarez des paramètres de chemin et de requête :
Pour l'ajouter à votre *opération de chemin*, déclarez-le comme vous déclareriez des paramètres de chemin ou de requête :
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
{* ../../docs_src/body/tutorial001.py hl[18] *}
... et déclarez son type comme étant le modèle que vous avez créé, `Item`.
...et déclarez que son type est le modèle que vous avez créé : `Item`.
## Résultats { #results }
## Résultats
Avec cette seule déclaration de type Python, **FastAPI** va :
En utilisant uniquement les déclarations de type Python, **FastAPI** réussit à :
* Lire le corps de la requête en tant que JSON.
* Lire le contenu de la requête en tant que JSON.
* Convertir les types correspondants (si nécessaire).
* Valider la donnée.
* Si la donnée est invalide, renvoyer une erreur claire et explicite, indiquant exactement où et quelle donnée est incorrecte.
* Vous fournir la donnée reçue dans le paramètre `item`.
* Comme vous l'avez déclaré dans la fonction comme étant de type `Item`, vous aurez aussi tout le support de l'éditeur (autocomplétion, etc.) pour tous ses attributs et leurs types.
* Générer des définitions <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> pour votre modèle ; vous pouvez aussi les utiliser ailleurs si cela a du sens pour votre projet.
* Ces schémas feront partie du schéma OpenAPI généré et seront utilisés par la documentation automatique <abbr title="User Interfaces Interfaces utilisateur">UIs</abbr>.
* Si la donnée est invalide, une erreur propre et claire sera renvoyée, indiquant exactement où était la donnée incorrecte.
* Passer la donnée reçue dans le paramètre `item`.
* Ce paramètre ayant été déclaré dans la fonction comme étant de type `Item`, vous aurez aussi tout le support offert par l'éditeur (auto-complétion, etc.) pour tous les attributs de ce paramètre et les types de ces attributs.
* Générer des définitions <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> pour votre modèle, qui peuvent être utilisées où vous en avez besoin dans votre projet ensuite.
* Ces schémas participeront à la constitution du schéma généré OpenAPI, et seront donc utilisés par les documentations automatiquement générées.
## Documentation automatique { #automatic-docs }
## Documentation automatique
Les schémas JSON de vos modèles feront partie du schéma OpenAPI généré et seront affichés dans la documentation interactive de l'API :
Les schémas JSON de vos modèles seront intégrés au schéma OpenAPI global de votre application, et seront donc affichés dans la documentation interactive de l'API :
<img src="/img/tutorial/body/image01.png">
Ils seront aussi utilisés dans chaque *opération de chemin* de la documentation qui en a besoin :
Et seront aussi utilisés dans chaque *opération de chemin* de la documentation utilisant ces modèles :
<img src="/img/tutorial/body/image02.png">
## Support de l'éditeur { #editor-support }
## Support de l'éditeur
Dans votre éditeur, à l'intérieur de votre fonction, vous obtiendrez des annotations de type et de l'autocomplétion partout (ce qui n'arriverait pas si vous receviez un `dict` au lieu d'un modèle Pydantic) :
Dans votre éditeur, vous aurez des annotations de types et de l'auto-complétion partout dans votre fonction (ce qui n'aurait pas été le cas si vous aviez utilisé un classique `dict` plutôt qu'un modèle Pydantic) :
<img src="/img/tutorial/body/image03.png">
Vous obtenez aussi des vérifications d'erreurs pour les opérations incorrectes sur les types :
Et vous obtenez aussi de la vérification d'erreur pour les opérations incorrectes de types :
<img src="/img/tutorial/body/image04.png">
Ce n'est pas un hasard, tout le framework a été construit autour de ce design.
Ce n'est pas un hasard, ce framework entier a été bâti avec ce design comme objectif.
Et il a été rigoureusement testé lors de la phase de conception, avant toute implémentation, pour s'assurer qu'il fonctionne avec tous les éditeurs.
Et cela a été rigoureusement testé durant la phase de design, avant toute implémentation, pour s'assurer que cela fonctionnerait avec tous les éditeurs.
Des changements ont même été apportés à Pydantic luimême pour le supporter.
Des changements sur Pydantic ont même été faits pour supporter cela.
Les captures d'écran précédentes ont été prises avec <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
Les captures d'écrans précédentes ont été prises sur <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
Mais vous obtiendrez le même support de l'éditeur avec <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> et la plupart des autres éditeurs Python :
Mais vous auriez le même support de l'éditeur avec <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> et la majorité des autres éditeurs de code Python.
<img src="/img/tutorial/body/image05.png">
/// tip | Astuce
Si vous utilisez <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> comme éditeur, vous pouvez utiliser le <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">plug-in Pydantic pour PyCharm</a>.
Si vous utilisez <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> comme éditeur, vous pouvez utiliser le Plugin <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>.
Il améliore le support des modèles Pydantic avec :
Ce qui améliore le support pour les modèles Pydantic avec :
* autocomplétion
* vérifications de type
* refactoring
* recherche
* inspections
* de l'auto-complétion
* des vérifications de type
* du "refactoring" (ou remaniement de code)
* de la recherche
* de l'inspection
///
## Utiliser le modèle { #use-the-model }
## Utilisez le modèle
Dans la fonction, vous pouvez accéder directement à tous les attributs de l'objet du modèle :
Dans la fonction, vous pouvez accéder à tous les attributs de l'objet du modèle directement :
{* ../../docs_src/body/tutorial002_py310.py *}
{* ../../docs_src/body/tutorial002.py hl[21] *}
## Corps de la requête + paramètres de chemin { #request-body-path-parameters }
## Corps de la requête + paramètres de chemin
Vous pouvez déclarer des paramètres de chemin et un corps de requête en même temps.
Vous pouvez déclarer des paramètres de chemin et un corps de requête pour la même *opération de chemin*.
**FastAPI** reconnaîtra que les paramètres de la fonction qui correspondent aux paramètres de chemin doivent être **récupérés depuis le chemin**, et que les paramètres de fonction déclarés comme modèles Pydantic doivent être **récupérés depuis le corps de la requête**.
**FastAPI** est capable de reconnaître que les paramètres de la fonction qui correspondent aux paramètres de chemin doivent être **récupérés depuis le chemin**, et que les paramètres de fonctions déclarés comme modèles Pydantic devraient être **récupérés depuis le corps de la requête**.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
{* ../../docs_src/body/tutorial003.py hl[17:18] *}
## Corps de la requête + paramètres de chemin et de requête { #request-body-path-query-parameters }
## Corps de la requête + paramètres de chemin et de requête
Vous pouvez aussi déclarer des paramètres de **corps**, de **chemin** et de **requête**, tous en même temps.
Vous pouvez aussi déclarer un **corps**, et des paramètres de **chemin** et de **requête** dans la même *opération de chemin*.
**FastAPI** reconnaîtra chacun d'entre eux et récupérera la donnée au bon endroit.
**FastAPI** saura reconnaître chacun d'entre eux et récupérer la bonne donnée au bon endroit.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
{* ../../docs_src/body/tutorial004.py hl[18] *}
Les paramètres de la fonction seront reconnus comme suit :
Les paramètres de la fonction seront reconnus comme tel :
* Si le paramètre est aussi déclaré dans le **chemin**, il sera utilisé comme paramètre de chemin.
* Si le paramètre est d'un **type singulier** (comme `int`, `float`, `str`, `bool`, etc.), il sera interprété comme un paramètre de **requête**.
* Si le paramètre est déclaré comme étant du type d'un **modèle Pydantic**, il sera interprété comme faisant partie du **corps** de la requête.
* Si le paramètre est déclaré comme ayant pour type un **modèle Pydantic**, il sera interprété comme faisant partie du **corps** de la requête.
/// note | Remarque
/// note
FastAPI saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `= None`.
**FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `=None`.
L'annotation de type `str | None` (Python 3.10+) ou `Union` dans `Union[str, None]` (Python 3.9+) n'est pas utilisée par FastAPI pour déterminer que la valeur n'est pas requise, il le saura parce qu'il y a une valeur par défaut `= None`.
Mais ajouter des annotations de type permettra à votre éditeur de vous offrir un meilleur support et de détecter des erreurs.
Le type `Optional` dans `Optional[str]` n'est pas utilisé par **FastAPI**, mais sera utile à votre éditeur pour améliorer le support offert par ce dernier et détecter plus facilement des erreurs de type.
///
## Sans Pydantic { #without-pydantic }
## Sans Pydantic
Si vous ne voulez pas utiliser des modèles Pydantic, vous pouvez aussi utiliser des paramètres **Body**. Voir les documents pour [Corps de la requête - Paramètres multiples : Valeurs singulières dans le corps](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
Si vous ne voulez pas utiliser des modèles Pydantic, vous pouvez aussi utiliser des paramètres de **Corps**. Pour cela, allez voir la partie de la documentation sur [Corps de la requête - Paramètres multiples](body-multiple-params.md){.internal-link target=_blank}.

View File

@@ -1,14 +1,14 @@
# <abbr title="En anglais: Debugging">Débogage</abbr> { #debugging }
# <abbr title="En anglais: Debugging">Débogage</abbr>
Vous pouvez connecter le <abbr title="En anglais: debugger">débogueur</abbr> dans votre éditeur, par exemple avec Visual Studio Code ou PyCharm.
## Faites appel à `uvicorn` { #call-uvicorn }
## Faites appel à `uvicorn`
Dans votre application FastAPI, importez et exécutez directement `uvicorn` :
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
### À propos de `__name__ == "__main__"` { #about-name-main }
### À propos de `__name__ == "__main__"`
Le but principal de `__name__ == "__main__"` est d'avoir du code qui est exécuté lorsque votre fichier est appelé avec :
@@ -26,7 +26,7 @@ mais qui n'est pas appelé lorsqu'un autre fichier l'importe, comme dans :
from myapp import app
```
#### Pour davantage de détails { #more-details }
#### Pour davantage de détails
Imaginons que votre fichier s'appelle `myapp.py`.
@@ -78,7 +78,7 @@ Pour plus d'informations, consultez <a href="https://docs.python.org/3/library/_
///
## Exécutez votre code avec votre <abbr title="En anglais: debugger">débogueur</abbr> { #run-your-code-with-your-debugger }
## Exécutez votre code avec votre <abbr title="En anglais: debugger">débogueur</abbr>
Parce que vous exécutez le serveur Uvicorn directement depuis votre code, vous pouvez appeler votre programme Python (votre application FastAPI) directement depuis le <abbr title="En anglais: debugger">débogueur</abbr>.
@@ -86,10 +86,10 @@ Parce que vous exécutez le serveur Uvicorn directement depuis votre code, vous
Par exemple, dans Visual Studio Code, vous pouvez :
* Aller au panneau «Debug».
* «Add configuration ...».
* Sélectionnez «Python»
* Lancez le <abbr title="En anglais: debugger">débogueur</abbr> avec l'option «`Python: Current File (Integrated Terminal)`».
- Cliquer sur l'onglet "Debug" de la barre d'activités de Visual Studio Code.
- "Add configuration...".
- Sélectionnez "Python".
- Lancez le <abbr title="En anglais: debugger">débogueur</abbr> avec l'option "`Python: Current File (Integrated Terminal)`".
Il démarrera alors le serveur avec votre code **FastAPI**, s'arrêtera à vos points d'arrêt, etc.
@@ -101,10 +101,10 @@ Voici à quoi cela pourrait ressembler :
Si vous utilisez Pycharm, vous pouvez :
* Ouvrez le menu «Run».
* Sélectionnez l'option «Debug ...».
* Un menu contextuel s'affiche alors.
* Sélectionnez le fichier à déboguer (dans ce cas, `main.py`).
- Ouvrir le menu "Run".
- Sélectionnez l'option "Debug...".
- Un menu contextuel s'affiche alors.
- Sélectionnez le fichier à déboguer (dans ce cas, `main.py`).
Il démarrera alors le serveur avec votre code **FastAPI**, s'arrêtera à vos points d'arrêt, etc.

View File

@@ -1,122 +1,107 @@
# Premiers pas { #first-steps }
# Démarrage
Le fichier FastAPI le plus simple possible pourrait ressembler à ceci :
Le fichier **FastAPI** le plus simple possible pourrait ressembler à cela :
{* ../../docs_src/first_steps/tutorial001_py39.py *}
{* ../../docs_src/first_steps/tutorial001.py *}
Copiez cela dans un fichier `main.py`.
Copiez ce code dans un fichier nommé `main.py`.
Lancez le serveur de développement :
Démarrez le serveur :
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
$ uvicorn main:app --reload
<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.
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
<span style="color: green;">INFO</span>: Started reloader process [28720]
<span style="color: green;">INFO</span>: Started server process [28722]
<span style="color: green;">INFO</span>: Waiting for application startup.
<span style="color: green;">INFO</span>: Application startup complete.
```
</div>
Dans la sortie, il y a une ligne semblable à :
/// note
La commande `uvicorn main:app` fait référence à :
* `main` : le fichier `main.py` (le module Python).
* `app` : l'objet créé dans `main.py` via la ligne `app = FastAPI()`.
* `--reload` : l'option disant à uvicorn de redémarrer le serveur à chaque changement du code. À ne pas utiliser en production !
///
Vous devriez voir dans la console, une ligne semblable à la suivante :
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Cette ligne indique lURL à laquelle votre application est servie, sur votre machine locale.
Cette ligne montre l'URL par laquelle l'app est actuellement accessible, sur votre machine locale.
### Vérifiez { #check-it }
### Allez voir le résultat
Ouvrez votre navigateur à ladresse <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Ouvrez votre navigateur à l'adresse <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Vous verrez la réponse JSON suivante:
Vous obtiendrez cette réponse JSON :
```JSON
{"message": "Hello World"}
```
### Documentation interactive de lAPI { #interactive-api-docs }
### Documentation interactive de l'API
Rendez-vous maintenant sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Rendez-vous sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Vous verrez la documentation interactive automatique de lAPI (fournie par <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
Vous verrez la documentation interactive de l'API générée automatiquement (via <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)
### Documentation alternative de lAPI { #alternative-api-docs }
### Documentation alternative
Et maintenant, allez sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Ensuite, rendez-vous sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
Vous verrez la documentation alternative automatique (fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
Vous y verrez la documentation alternative (via <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 { #openapi }
### OpenAPI
**FastAPI** génère un «schéma» avec toute votre API en utilisant le standard **OpenAPI** pour définir des API.
**FastAPI** génère un "schéma" contenant toute votre API dans le standard de définition d'API **OpenAPI**.
#### «Schéma» { #schema }
#### "Schéma"
Un «schéma» est une définition ou description de quelque chose. Pas le code qui limplémente, mais juste une description abstraite.
Un "schéma" est une définition ou une description de quelque chose. Pas le code qui l'implémente, uniquement une description abstraite.
#### «Schéma» dAPI { #api-schema }
#### "Schéma" d'API
Ici, <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> est une spécification qui dicte comment définir le schéma de votre API.
Cette définition de schéma inclut les chemins de votre API, les paramètres possibles quils prennent, etc.
Le schéma inclut les chemins de votre API, les paramètres potentiels de chaque chemin, etc.
#### «Schéma» de données { #data-schema }
#### "Schéma" de données
Le terme «schéma» peut aussi se référer à la forme de certaines données, comme un contenu JSON.
Le terme "schéma" peut aussi faire référence à la forme de la donnée, comme un contenu JSON.
Dans ce cas, cela signerait les attributs JSON, et leurs types de données, etc.
Dans ce cas, cela signifierait les attributs JSON, ainsi que les types de ces attributs, etc.
#### OpenAPI et JSON Schema { #openapi-and-json-schema }
#### OpenAPI et JSON Schema
OpenAPI définit un schéma dAPI pour votre API. Et ce schéma inclut des définitions (ou «schémas») des données envoyées et reçues par votre API en utilisant **JSON Schema**, le standard pour les schémas de données JSON.
**OpenAPI** définit un schéma d'API pour votre API. Il inclut des définitions (ou "schémas") de la donnée envoyée et reçue par votre API en utilisant **JSON Schema**, le standard des schémas de données JSON.
#### Vérifier `openapi.json` { #check-the-openapi-json }
#### Allez voir `openapi.json`
Si vous êtes curieux de voir à quoi ressemble le schéma OpenAPI brut, FastAPI génère automatiquement un JSON (schéma) avec les descriptions de toute votre API.
Si vous êtes curieux d'à quoi ressemble le schéma brut **OpenAPI**, **FastAPI** génère automatiquement un (schéma) JSON avec les descriptions de toute votre API.
Vous pouvez le voir directement ici: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
Vous pouvez le voir directement à cette adresse : <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
Le schéma devrait ressembler à ceci :
Il affichera un JSON commençant par quelque chose comme:
```JSON
{
"openapi": "3.1.0",
"openapi": "3.0.2",
"info": {
"title": "FastAPI",
"version": "0.1.0"
@@ -135,87 +120,79 @@ Il affichera un JSON commençant par quelque chose comme:
...
```
#### À quoi sert OpenAPI { #what-is-openapi-for }
#### À quoi sert OpenAPI
Le schéma OpenAPI alimente les deux systèmes de documentation interactive inclus.
Le schéma **OpenAPI** est ce qui alimente les deux systèmes de documentation interactive.
Et il existe des dizaines dalternatives, toutes basées sur OpenAPI. Vous pourriez facilement ajouter lune de ces alternatives à votre application construite avec **FastAPI**.
Et il existe des dizaines d'alternatives, toutes basées sur **OpenAPI**. Vous pourriez facilement ajouter n'importe laquelle de ces alternatives à votre application **FastAPI**.
Vous pourriez également lutiliser pour générer automatiquement du code pour des clients qui communiquent avec votre API. Par exemple, des applications frontend, mobiles ou IoT.
Vous pourriez aussi l'utiliser pour générer du code automatiquement, pour les clients qui communiquent avec votre API. Comme par exemple, des applications frontend, mobiles ou IOT.
### Déployer votre application (optionnel) { #deploy-your-app-optional }
## Récapitulatif, étape par étape
Vous pouvez, si vous le souhaitez, déployer votre application FastAPI sur <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>, rejoignez la liste dattente si ce nest pas déjà fait. 🚀
### Étape 1 : import `FastAPI`
Si vous avez déjà un compte **FastAPI Cloud** (nous vous avons invité depuis la liste dattente 😉), vous pouvez déployer votre application avec une seule commande.
{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
Avant de déployer, assurez-vous dêtre connecté:
<div class="termy">
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
</div>
Puis déployez votre application:
<div class="termy">
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
</div>
Et voilà! Vous pouvez maintenant accéder à votre application à cette URL. ✨
## Récapitulatif, étape par étape { #recap-step-by-step }
### Étape 1 : importer `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
`FastAPI` est une classe Python qui fournit toutes les fonctionnalités pour votre API.
`FastAPI` est une classe Python qui fournit toutes les fonctionnalités nécessaires au lancement de votre API.
/// note | Détails techniques
`FastAPI` est une classe qui hérite directement de `Starlette`.
`FastAPI` est une classe héritant directement de `Starlette`.
Vous pouvez donc aussi utiliser toutes les fonctionnalités de <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> avec `FastAPI`.
Vous pouvez donc aussi utiliser toutes les fonctionnalités de <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> depuis `FastAPI`.
///
### Étape 2 : créer une «instance» `FastAPI` { #step-2-create-a-fastapi-instance }
### Étape 2 : créer une "instance" `FastAPI`
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
Ici, la variable `app` sera une «instance» de la classe `FastAPI`.
Ici la variable `app` sera une "instance" de la classe `FastAPI`.
Ce sera le point principal dinteraction pour créer toute votre API.
Ce sera le point principal d'interaction pour créer toute votre API.
### Étape 3 : créer une *opération de chemin* { #step-3-create-a-path-operation }
Cette `app` est la même que celle à laquelle fait référence `uvicorn` dans la commande :
#### Chemin { #path }
<div class="termy">
«Chemin» (ou «path») fait ici référence à la dernière partie de lURL à partir du premier `/`.
```console
$ uvicorn main:app --reload
Ainsi, dans une URL comme:
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Si vous créez votre app avec :
{* ../../docs_src/first_steps/tutorial002.py hl[3] *}
Et la mettez dans un fichier `main.py`, alors vous appelleriez `uvicorn` avec :
<div class="termy">
```console
$ uvicorn main:my_awesome_api --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Étape 3: créer une *opération de chemin*
#### Chemin
Chemin, ou "path" fait référence ici à la dernière partie de l'URL démarrant au premier `/`.
Donc, dans un URL tel que :
```
https://example.com/items/foo
```
... le chemin serait:
...le "path" serait :
```
/items/foo
@@ -223,77 +200,76 @@ https://example.com/items/foo
/// info
Un «chemin» est aussi couramment appelé un «endpoint» ou une «route».
Un chemin, ou "path" est aussi souvent appelé route ou "endpoint".
///
Lors de la construction dune API, le «chemin» est le principal moyen de séparer les «responsabilités» et les «ressources».
#### Opération
#### Opération { #operation }
"Opération" fait référence à une des "méthodes" HTTP.
«Opération» fait ici référence à lune des «méthodes» HTTP.
Lune de:
Une de :
* `POST`
* `GET`
* `PUT`
* `DELETE`
... et les plus exotiques:
...ou une des plus exotiques :
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
Dans le protocole HTTP, vous pouvez communiquer avec chaque chemin en utilisant une (ou plusieurs) de ces «méthodes».
Dans le protocol HTTP, vous pouvez communiquer avec chaque chemin en utilisant une (ou plus) de ces "méthodes".
---
En construisant des API, vous utilisez normalement ces méthodes HTTP spécifiques pour effectuer une action spécifique.
En construisant des APIs, vous utilisez généralement ces méthodes HTTP spécifiques pour effectuer une action précise.
Normalement, vous utilisez:
Généralement vous utilisez :
* `POST` : pour créer des données.
* `GET` : pour lire des données.
* `PUT` : pour mettre à jour des données.
* `DELETE` : pour supprimer des données.
* `POST` : pour créer de la donnée.
* `GET` : pour lire de la donnée.
* `PUT` : pour mettre à jour de la donnée.
* `DELETE` : pour supprimer de la donnée.
Ainsi, dans OpenAPI, chacune des méthodes HTTP est appelée une «opération».
Donc, dans **OpenAPI**, chaque méthode HTTP est appelée une "opération".
Nous allons donc aussi les appeler «opérations».
Nous allons donc aussi appeler ces dernières des "**opérations**".
#### Définir un *décorateur dopération de chemin* { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
#### Définir un *décorateur d'opération de chemin*
Le `@app.get("/")` indique à **FastAPI** que la fonction juste en dessous est chargée de traiter les requêtes qui vont vers:
{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
Le `@app.get("/")` dit à **FastAPI** que la fonction en dessous est chargée de gérer les requêtes qui vont sur :
* le chemin `/`
* en utilisant une <abbr title="une méthode HTTP GET">opération <code>get</code></abbr>
* en utilisant une <abbr title="une méthode GET HTTP">opération <code>get</code></abbr>
/// info | `@décorateur` Info
Cette syntaxe `@something` en Python est appelée un «décorateur».
Cette syntaxe `@something` en Python est appelée un "décorateur".
Vous la placez au-dessus dune fonction. Comme un joli chapeau décoratif (je suppose que cest de là que vient le terme 🤷🏻‍♂).
Vous la mettez au dessus d'une fonction. Comme un joli chapeau décoratif (j'imagine que ce terme vient de 🤷🏻‍♂).
Un «décorateur» prend la fonction en dessous et fait quelque chose avec.
Un "décorateur" prend la fonction en dessous et en fait quelque chose.
Dans notre cas, ce décorateur indique à **FastAPI** que la fonction en dessous correspond au **chemin** `/` avec l**opération** `get`.
Dans notre cas, ce décorateur dit à **FastAPI** que la fonction en dessous correspond au **chemin** `/` avec l'**opération** `get`.
Cest le «décorateur dopération de chemin».
C'est le "**décorateur d'opération de chemin**".
///
Vous pouvez aussi utiliser les autres opérations:
Vous pouvez aussi utiliser les autres opérations :
* `@app.post()`
* `@app.put()`
* `@app.delete()`
Et les plus exotiques:
Tout comme celles les plus exotiques :
* `@app.options()`
* `@app.head()`
@@ -302,79 +278,58 @@ Et les plus exotiques:
/// tip | Astuce
Vous êtes libre dutiliser chaque opération (méthode HTTP) comme vous le souhaitez.
Vous êtes libres d'utiliser chaque opération (méthode HTTP) comme vous le désirez.
**FastAPI** nimpose aucune signification spécifique.
**FastAPI** n'impose pas de sens spécifique à chacune d'elle.
Les informations ici sont présentées comme des lignes directrices, pas comme une exigence.
Les informations qui sont présentées ici forment une directive générale, pas des obligations.
Par exemple, lorsque vous utilisez GraphQL, vous effectuez normalement toutes les actions en utilisant uniquement des opérations `POST`.
Par exemple, quand l'on utilise **GraphQL**, toutes les actions sont effectuées en utilisant uniquement des opérations `POST`.
///
### Étape 4 : définir la **fonction dopération de chemin** { #step-4-define-the-path-operation-function }
### Étape 4 : définir la **fonction de chemin**.
Voici notre «fonction dopération de chemin» :
Voici notre "**fonction de chemin**" (ou fonction d'opération de chemin) :
* **chemin** : `/`.
* **opération** : `get`.
* **fonction** : la fonction sous le «décorateur» (sous `@app.get("/")`).
* **fonction** : la fonction sous le "décorateur" (sous `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
Cest une fonction Python.
C'est une fonction Python.
Elle sera appelée par **FastAPI** chaque fois quil reçoit une requête vers lURL «`/`» en utilisant une opération `GET`.
Elle sera appelée par **FastAPI** quand une requête sur l'URL `/` sera reçue via une opération `GET`.
Dans ce cas, cest une fonction `async`.
Ici, c'est une fonction asynchrone (définie avec `async def`).
---
Vous pourriez aussi la définir comme une fonction normale au lieu de `async def`:
Vous pourriez aussi la définir comme une fonction classique plutôt qu'avec `async def` :
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
/// note | Remarque
/// note
Si vous ne connaissez pas la différence, consultez [Asynchrone : *«Pressé ?»*](../async.md#in-a-hurry){.internal-link target=_blank}.
Si vous ne connaissez pas la différence, allez voir la section [Concurrence : *"Vous êtes pressés ?"*](../async.md#vous-etes-presses){.internal-link target=_blank}.
///
### Étape 5 : retourner le contenu { #step-5-return-the-content }
### Étape 5 : retourner le contenu
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
Vous pouvez retourner un `dict`, une `list`, des valeurs simples comme `str`, `int`, etc.
Vous pouvez retourner un dictionnaire (`dict`), une liste (`list`), des valeurs seules comme des chaines de caractères (`str`) et des entiers (`int`), etc.
Vous pouvez également retourner des modèles Pydantic (vous en verrez plus à ce sujet plus tard).
Vous pouvez aussi retourner des models **Pydantic** (qui seront détaillés plus tard).
Il existe de nombreux autres objets et modèles qui seront automatiquement convertis en JSON (y compris des ORM, etc.). Essayez dutiliser vos préférés, il est très probable quils soient déjà pris en charge.
Il y a de nombreux autres objets et modèles qui seront automatiquement convertis en JSON. Essayez d'utiliser vos favoris, il est fort probable qu'ils soient déjà supportés.
### Étape 6 : la déployer { #step-6-deploy-it }
Déployez votre application sur **<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** avec une seule commande: `fastapi deploy`. 🎉
#### À propos de FastAPI Cloud { #about-fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** est développé par le même auteur et la même équipe derrière **FastAPI**.
Il rationalise le processus de **construction**, **déploiement** et **accès** à une API avec un effort minimal.
Il apporte la même **expérience développeur** que pour la création dapplications avec FastAPI au **déploiement** dans le cloud. 🎉
FastAPI Cloud est le sponsor principal et le financeur des projets open source *FastAPI and friends*. ✨
#### Déployer sur dautres fournisseurs cloud { #deploy-to-other-cloud-providers }
FastAPI est open source et basé sur des standards. Vous pouvez déployer des applications FastAPI chez nimporte quel fournisseur cloud de votre choix.
Suivez les guides de votre fournisseur cloud pour y déployer des applications FastAPI. 🤓
## Récapitulatif { #recap }
## Récapitulatif
* Importez `FastAPI`.
* Créez une instance `app`.
* Écrivez un **décorateur dopération de chemin** avec des décorateurs comme `@app.get("/")`.
* Définissez une **fonction dopération de chemin** ; par exemple, `def root(): ...`.
* Lancez le serveur de développement avec la commande `fastapi dev`.
* Déployez éventuellement votre application avec `fastapi deploy`.
* Créez une instance d'`app`.
* Ajoutez une **décorateur d'opération de chemin** (tel que `@app.get("/")`).
* Ajoutez une **fonction de chemin** (telle que `def root(): ...` comme ci-dessus).
* Lancez le serveur de développement (avec `uvicorn main:app --reload`).

View File

@@ -1,53 +1,29 @@
# Tutoriel - Guide d'utilisation { #tutorial-user-guide }
# Tutoriel - Guide utilisateur - Introduction
Ce tutoriel vous montre comment utiliser **FastAPI** avec la plupart de ses fonctionnalités, étape par étape.
Chaque section s'appuie progressivement sur les précédentes, mais elle est structurée de manière à séparer les sujets, afin que vous puissiez aller directement à l'un d'entre eux pour résoudre vos besoins spécifiques en matière d'API.
Il est également conçu pour servir de référence future afin que vous puissiez revenir et voir exactement ce dont vous avez besoin.
Il est également conçu pour fonctionner comme une référence future.
## Exécuter le code { #run-the-code }
Vous pouvez donc revenir et voir exactement ce dont vous avez besoin.
## Exécuter le code
Tous les blocs de code peuvent être copiés et utilisés directement (il s'agit en fait de fichiers Python testés).
Pour exécuter l'un des exemples, copiez le code dans un fichier `main.py`, et lancez `fastapi dev` avec :
Pour exécuter l'un de ces exemples, copiez le code dans un fichier `main.py`, et commencez `uvicorn` avec :
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
$ uvicorn main:app --reload
<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.
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
<span style="color: green;">INFO</span>: Started reloader process [28720]
<span style="color: green;">INFO</span>: Started server process [28722]
<span style="color: green;">INFO</span>: Waiting for application startup.
<span style="color: green;">INFO</span>: Application startup complete.
```
</div>
@@ -58,38 +34,50 @@ L'utiliser dans votre éditeur est ce qui vous montre vraiment les avantages de
---
## Installer FastAPI { #install-fastapi }
## Installer FastAPI
La première étape consiste à installer FastAPI.
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, de l'activer, puis **d'installer FastAPI** :
Pour le tutoriel, vous voudrez peut-être l'installer avec toutes les dépendances et fonctionnalités optionnelles :
<div class="termy">
```console
$ pip install "fastapi[standard]"
$ pip install fastapi[all]
---> 100%
```
</div>
/// note | Remarque
... qui comprend également `uvicorn`, que vous pouvez utiliser comme serveur pour exécuter votre code.
Lorsque vous installez avec `pip install "fastapi[standard]"`, cela inclut certaines dépendances standard optionnelles par défaut, y compris `fastapi-cloud-cli`, qui vous permet de déployer sur <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
/// note
Si vous ne souhaitez pas avoir ces dépendances optionnelles, vous pouvez installer à la place `pip install fastapi`.
Vous pouvez également l'installer pièce par pièce.
Si vous voulez installer les dépendances standard mais sans `fastapi-cloud-cli`, vous pouvez installer avec `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
C'est ce que vous feriez probablement une fois que vous voudrez déployer votre application en production :
```
pip install fastapi
```
Installez également `uvicorn` pour qu'il fonctionne comme serveur :
```
pip install uvicorn
```
Et la même chose pour chacune des dépendances facultatives que vous voulez utiliser.
///
## Guide d'utilisation avancé { #advanced-user-guide }
## Guide utilisateur avancé
Il existe également un **Guide d'utilisation avancé** que vous pouvez lire plus tard après ce **Tutoriel - Guide d'utilisation**.
Le **Guide d'utilisation avancé** s'appuie sur celui-ci, utilise les mêmes concepts et vous apprend quelques fonctionnalités supplémentaires.
Le **Guide d'utilisation avancé**, qui s'appuie sur cette base, utilise les mêmes concepts et vous apprend quelques fonctionnalités supplémentaires.
Mais vous devriez d'abord lire le **Tutoriel - Guide d'utilisation** (ce que vous êtes en train de lire en ce moment).
Mais vous devez d'abord lire le **Tutoriel - Guide d'utilisation** (ce que vous êtes en train de lire en ce moment).
Il est conçu pour que vous puissiez construire une application complète avec seulement le **Tutoriel - Guide d'utilisation**, puis l'étendre de différentes manières, selon vos besoins, en utilisant certaines des idées supplémentaires du **Guide d'utilisation avancé**.
Il est conçu pour que vous puissiez construire une application complète avec seulement le **Tutoriel - Guide d'utilisation**, puis l'étendre de différentes manières, en fonction de vos besoins, en utilisant certaines des idées supplémentaires du **Guide d'utilisation avancé**.

View File

@@ -1,8 +1,8 @@
# Paramètres de chemin et validations numériques { #path-parameters-and-numeric-validations }
# Paramètres de chemin et validations numériques
De la même façon que vous pouvez déclarer plus de validations et de métadonnées pour les paramètres de requête avec `Query`, vous pouvez déclarer le même type de validations et de métadonnées pour les paramètres de chemin avec `Path`.
## Importer `Path` { #import-path }
## Importer Path
Tout d'abord, importez `Path` de `fastapi`, et importez `Annotated` :
@@ -14,11 +14,11 @@ FastAPI a ajouté le support pour `Annotated` (et a commencé à le recommander)
Si vous avez une version plus ancienne, vous obtiendrez des erreurs en essayant d'utiliser `Annotated`.
Assurez-vous de [mettre à niveau la version de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} à la version 0.95.1 au minimum avant d'utiliser `Annotated`.
Assurez-vous de [Mettre à jour la version de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} à la version 0.95.1 à minima avant d'utiliser `Annotated`.
///
## Déclarer des métadonnées { #declare-metadata }
## Déclarer des métadonnées
Vous pouvez déclarer les mêmes paramètres que pour `Query`.
@@ -26,15 +26,15 @@ Par exemple, pour déclarer une valeur de métadonnée `title` pour le paramètr
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | Remarque
/// note
Un paramètre de chemin est toujours requis car il doit faire partie du chemin. Même si vous l'avez déclaré avec `None` ou défini une valeur par défaut, cela ne changerait rien, il serait toujours requis.
///
## Ordonnez les paramètres comme vous le souhaitez { #order-the-parameters-as-you-need }
## Ordonnez les paramètres comme vous le souhaitez
/// tip | Astuce
/// tip
Ce n'est probablement pas aussi important ou nécessaire si vous utilisez `Annotated`.
@@ -46,7 +46,7 @@ Et vous n'avez pas besoin de déclarer autre chose pour ce paramètre, donc vous
Mais vous avez toujours besoin d'utiliser `Path` pour le paramètre de chemin `item_id`. Et vous ne voulez pas utiliser `Annotated` pour une raison quelconque.
Python se plaindra si vous mettez une valeur avec une «défaut» avant une valeur qui n'a pas de «défaut».
Python se plaindra si vous mettez une valeur avec une "défaut" avant une valeur qui n'a pas de "défaut".
Mais vous pouvez les réorganiser, et avoir la valeur sans défaut (le paramètre de requête `q`) en premier.
@@ -54,15 +54,15 @@ Cela n'a pas d'importance pour **FastAPI**. Il détectera les paramètres par le
Ainsi, vous pouvez déclarer votre fonction comme suit :
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
Mais gardez à l'esprit que si vous utilisez `Annotated`, vous n'aurez pas ce problème, cela n'aura pas d'importance car vous n'utilisez pas les valeurs par défaut des paramètres de fonction pour `Query()` ou `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py hl[10] *}
## Ordonnez les paramètres comme vous le souhaitez (astuces) { #order-the-parameters-as-you-need-tricks }
## Ordonnez les paramètres comme vous le souhaitez (astuces)
/// tip | Astuce
/// tip
Ce n'est probablement pas aussi important ou nécessaire si vous utilisez `Annotated`.
@@ -83,23 +83,32 @@ Passez `*`, comme premier paramètre de la fonction.
Python ne fera rien avec ce `*`, mais il saura que tous les paramètres suivants doivent être appelés comme arguments "mots-clés" (paires clé-valeur), également connus sous le nom de <abbr title="De : K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Même s'ils n'ont pas de valeur par défaut.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
### Mieux avec `Annotated` { #better-with-annotated }
# Avec `Annotated`
Gardez à l'esprit que si vous utilisez `Annotated`, comme vous n'utilisez pas les valeurs par défaut des paramètres de fonction, vous n'aurez pas ce problème, et vous n'aurez probablement pas besoin d'utiliser `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
## Validations numériques : supérieur ou égal { #number-validations-greater-than-or-equal }
## Validations numériques : supérieur ou égal
Avec `Query` et `Path` (et d'autres que vous verrez plus tard) vous pouvez déclarer des contraintes numériques.
Ici, avec `ge=1`, `item_id` devra être un nombre entier «`g`reater than or `e`qual» à `1`.
Ici, avec `ge=1`, `item_id` devra être un nombre entier "`g`reater than or `e`qual" à `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
## Validations numériques : supérieur et inférieur ou égal { #number-validations-greater-than-and-less-than-or-equal }
## Validations numériques : supérieur ou égal et inférieur ou égal
La même chose s'applique pour :
* `gt` : `g`reater `t`han
* `le` : `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
## Validations numériques : supérieur et inférieur ou égal
La même chose s'applique pour :
@@ -108,7 +117,7 @@ La même chose s'applique pour :
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
## Validations numériques : flottants, supérieur et inférieur { #number-validations-floats-greater-than-and-less-than }
## Validations numériques : flottants, supérieur et inférieur
Les validations numériques fonctionnent également pour les valeurs `float`.
@@ -120,7 +129,7 @@ Et la même chose pour <abbr title="less than"><code>lt</code></abbr>.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
## Pour résumer { #recap }
## Pour résumer
Avec `Query`, `Path` (et d'autres que vous verrez plus tard) vous pouvez déclarer des métadonnées et des validations de chaînes de la même manière qu'avec les [Paramètres de requête et validations de chaînes](query-params-str-validations.md){.internal-link target=_blank}.

View File

@@ -1,32 +1,37 @@
# Paramètres de chemin { #path-parameters }
# Paramètres de chemin
Vous pouvez déclarer des «paramètres» ou «variables» de chemin avec la même syntaxe que celle utilisée par les chaînes de formatage Python :
Vous pouvez déclarer des "paramètres" ou "variables" de chemin avec la même syntaxe que celle utilisée par le
<a href="https://docs.python.org/fr/3/library/string.html#format-string-syntax" class="external-link" target="_blank">formatage de chaîne Python</a> :
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
La valeur du paramètre de chemin `item_id` sera transmise à la fonction dans l'argument `item_id`.
{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
Donc, si vous exécutez cet exemple et allez sur <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, vous verrez comme réponse :
La valeur du paramètre `item_id` sera transmise à la fonction dans l'argument `item_id`.
Donc, si vous exécutez cet exemple et allez sur <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>,
vous verrez comme réponse :
```JSON
{"item_id":"foo"}
```
## Paramètres de chemin typés { #path-parameters-with-types }
## Paramètres de chemin typés
Vous pouvez déclarer le type d'un paramètre de chemin dans la fonction, en utilisant les annotations de type Python standard :
Vous pouvez déclarer le type d'un paramètre de chemin dans la fonction, en utilisant les annotations de type Python :
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
{* ../../docs_src/path_params/tutorial002.py hl[7] *}
Ici, `item_id` est déclaré comme `int`.
/// check | vérifier
Ceci vous permettra d'obtenir des fonctionnalités de l'éditeur dans votre fonction, telles que des vérifications d'erreurs, de l'autocomplétion, etc.
Ceci vous permettra d'obtenir des fonctionnalités de l'éditeur dans votre fonction, telles
que des vérifications d'erreur, de l'auto-complétion, etc.
///
## <abbr title="aussi connu sous le nom : sérialisation, parsing, marshalling">Conversion</abbr> de données { #data-conversion }
## <abbr title="aussi appelé sérialisation, ou parfois parsing ou marshalling en anglais">Conversion</abbr> de données
Si vous exécutez cet exemple et allez sur <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, vous aurez comme réponse :
@@ -37,37 +42,38 @@ Si vous exécutez cet exemple et allez sur <a href="http://127.0.0.1:8000/items/
/// check | vérifier
Comme vous l'avez remarqué, la valeur reçue par la fonction (et renvoyée ensuite) est `3`,
en tant qu'entier (`int`) Python, pas la chaîne de caractères (`string`) «3».
en tant qu'entier (`int`) Python, pas la chaîne de caractères (`string`) `"3"`.
Donc, avec cette déclaration de type, **FastAPI** vous fournit un <abbr title="conversion de la chaîne de caractères venant de la requête HTTP en données Python">«parsing»</abbr> de la requête automatique.
Grâce aux déclarations de types, **FastAPI** fournit du
<abbr title="conversion de la chaîne de caractères venant de la requête HTTP en données Python">"parsing"</abbr> automatique.
///
## Validation de données { #data-validation }
## Validation de données
Si vous allez sur <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, vous aurez une belle erreur 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"
}
]
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
car le paramètre de chemin `item_id` possède comme valeur «foo», qui n'est pas un `int`.
car le paramètre de chemin `item_id` possède comme valeur `"foo"`, qui ne peut pas être convertie en entier (`int`).
La même erreur se produira si vous passez un nombre flottant (`float`) et non un entier, comme ici
<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 | vérifier
Donc, avec ces mêmes déclarations de type Python, **FastAPI** vous fournit de la validation de données.
@@ -78,24 +84,24 @@ Ce qui est incroyablement utile au moment de développer et débugger du code qu
///
## Documentation { #documentation }
## Documentation
Et quand vous vous rendez sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, vous verrez la
documentation générée automatiquement et interactive :
<img src="/img/tutorial/path-params/image01.png">
/// check | vérifier
/// info
À nouveau, en utilisant uniquement les déclarations de type Python, **FastAPI** vous fournit automatiquement une documentation interactive (intégrant Swagger UI).
À nouveau, en utilisant uniquement les déclarations de type Python, **FastAPI** vous fournit automatiquement une documentation interactive (via Swagger UI).
On voit bien dans la documentation que `item_id` est déclaré comme entier.
///
## Les avantages d'avoir une documentation basée sur une norme, et la documentation alternative { #standards-based-benefits-alternative-documentation }
## Les avantages d'avoir une documentation basée sur une norme, et la documentation alternative.
Le schéma généré suivant la norme <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a>,
Le schéma généré suivant la norme <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a>,
il existe de nombreux outils compatibles.
Grâce à cela, **FastAPI** lui-même fournit une documentation alternative (utilisant ReDoc), qui peut être lue
@@ -106,96 +112,92 @@ sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">
De la même façon, il existe bien d'autres outils compatibles, y compris des outils de génération de code
pour de nombreux langages.
## Pydantic { #pydantic }
## Pydantic
Toute la validation de données est effectuée en arrière-plan avec <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>,
Toute la validation de données est effectué en arrière-plan avec <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>,
dont vous bénéficierez de tous les avantages. Vous savez donc que vous êtes entre de bonnes mains.
Vous pouvez utiliser les mêmes déclarations de type avec `str`, `float`, `bool` et de nombreux autres types de données complexes.
## L'ordre importe
Plusieurs d'entre eux sont explorés dans les prochains chapitres du tutoriel.
## L'ordre importe { #order-matters }
Quand vous créez des *opérations de chemin*, vous pouvez vous retrouver dans une situation où vous avez un chemin fixe.
Quand vous créez des *fonctions de chemins*, vous pouvez vous retrouver dans une situation où vous avez un chemin fixe.
Tel que `/users/me`, disons pour récupérer les données sur l'utilisateur actuel.
Et vous avez un second chemin : `/users/{user_id}` pour récupérer de la donnée sur un utilisateur spécifique grâce à son identifiant d'utilisateur
Les *opérations de chemin* étant évaluées dans l'ordre, il faut s'assurer que la fonction correspondant à `/users/me` est déclarée avant celle de `/users/{user_id}` :
Les *fonctions de chemin* étant évaluées dans l'ordre, il faut s'assurer que la fonction correspondant à `/users/me` est déclarée avant celle de `/users/{user_id}` :
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
Sinon, le chemin `/users/{user_id}` correspondrait aussi à `/users/me`, la fonction «croyant» qu'elle a reçu un paramètre `user_id` avec pour valeur «me».
Sinon, le chemin `/users/{user_id}` correspondrait aussi à `/users/me`, la fonction "croyant" qu'elle a reçu un paramètre `user_id` avec pour valeur `"me"`.
De même, vous ne pouvez pas redéfinir une opération de chemin :
## Valeurs prédéfinies
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
Si vous avez une *fonction de chemin* qui reçoit un *paramètre de chemin*, mais que vous voulez que les valeurs possibles des paramètres soient prédéfinies, vous pouvez utiliser les <abbr title="Enumeration">`Enum`</abbr> de Python.
La première sera toujours utilisée car le chemin correspond d'abord.
### Création d'un `Enum`
## Valeurs prédéfinies { #predefined-values }
Importez `Enum` et créez une sous-classe qui hérite de `str` et `Enum`.
Si vous avez une *opération de chemin* qui reçoit un *paramètre de chemin*, mais que vous voulez que les valeurs possibles des paramètres soient prédéfinies, vous pouvez utiliser les <abbr title="Enumeration">`Enum`</abbr> de Python.
### Créer une classe `Enum` { #create-an-enum-class }
Importez `Enum` et créez une sous-classe qui hérite de `str` et de `Enum`.
En héritant de `str`, la documentation de l'API saura que les valeurs doivent être de type `string` et pourra donc l'afficher correctement.
En héritant de `str` la documentation sera capable de savoir que les valeurs doivent être de type `string` et pourra donc afficher cette `Enum` correctement.
Créez ensuite des attributs de classe avec des valeurs fixes, qui seront les valeurs autorisées pour cette énumération.
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
/// tip | Astuce
/// info
Pour ceux qui se demandent, «AlexNet», «ResNet», et «LeNet» sont juste des noms de <abbr title="Techniquement, des architectures de modèles de Deep Learning">modèles</abbr> de Machine Learning.
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Les énumérations (ou enums) sont disponibles en Python</a> depuis la version 3.4.
///
### Déclarer un *paramètre de chemin* { #declare-a-path-parameter }
/// tip | Astuce
Pour ceux qui se demandent, "AlexNet", "ResNet", et "LeNet" sont juste des noms de <abbr title="Techniquement, des architectures de modèles">modèles</abbr> de Machine Learning.
///
### Déclarer un paramètre de chemin
Créez ensuite un *paramètre de chemin* avec une annotation de type désignant l'énumération créée précédemment (`ModelName`) :
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
{* ../../docs_src/path_params/tutorial005.py hl[16] *}
### Vérifier la documentation { #check-the-docs }
### Documentation
Les valeurs disponibles pour le *paramètre de chemin* sont bien prédéfinies, la documentation les affiche correctement :
<img src="/img/tutorial/path-params/image03.png">
### Manipuler les *énumérations* Python { #working-with-python-enumerations }
### Manipuler les *énumérations* Python
La valeur du *paramètre de chemin* sera un des «membres» de l'énumération.
La valeur du *paramètre de chemin* sera un des "membres" de l'énumération.
#### Comparer les *membres d'énumération* { #compare-enumeration-members }
#### Comparer les *membres d'énumération*
Vous pouvez comparer ce paramètre avec les membres de votre énumération `ModelName` :
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
#### Récupérer la *valeur de l'énumération* { #get-the-enumeration-value }
#### Récupérer la *valeur de l'énumération*
Vous pouvez obtenir la valeur réelle d'un membre (une chaîne de caractères ici), avec `model_name.value`, ou en général, `votre_membre_d'enum.value` :
Vous pouvez obtenir la valeur réel d'un membre (une chaîne de caractères ici), avec `model_name.value`, ou en général, `votre_membre_d'enum.value` :
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
{* ../../docs_src/path_params/tutorial005.py hl[20] *}
/// tip | Astuce
Vous pouvez aussi accéder à la valeur «lenet» avec `ModelName.lenet.value`.
Vous pouvez aussi accéder la valeur `"lenet"` avec `ModelName.lenet.value`.
///
#### Retourner des *membres d'énumération* { #return-enumeration-members }
#### Retourner des *membres d'énumération*
Vous pouvez retourner des *membres d'énumération* dans vos *opérations de chemin*, même imbriqués dans un corps JSON (e.g. un `dict`).
Vous pouvez retourner des *membres d'énumération* dans vos *fonctions de chemin*, même imbriquée dans un JSON (e.g. un `dict`).
Ils seront convertis vers leurs valeurs correspondantes (chaînes de caractères ici) avant d'être transmis au client :
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
Le client recevra une réponse JSON comme celle-ci :
@@ -206,23 +208,23 @@ Le client recevra une réponse JSON comme celle-ci :
}
```
## Paramètres de chemin contenant des chemins { #path-parameters-containing-paths }
## Paramètres de chemin contenant des chemins
Disons que vous avez une *opération de chemin* liée au chemin `/files/{file_path}`.
Disons que vous avez une *fonction de chemin* liée au chemin `/files/{file_path}`.
Mais que `file_path` lui-même doit contenir un *chemin*, comme `home/johndoe/myfile.txt` par exemple.
Donc, l'URL pour ce fichier pourrait être : `/files/home/johndoe/myfile.txt`.
### Support d'OpenAPI { #openapi-support }
### Support d'OpenAPI
OpenAPI ne supporte pas de manière de déclarer un *paramètre de chemin* contenant un *chemin* à l'intérieur, cela pouvant causer des scénarios difficiles à tester et définir.
OpenAPI ne supporte pas de manière de déclarer un paramètre de chemin contenant un *chemin*, cela pouvant causer des scénarios difficiles à tester et définir.
Néanmoins, cela reste faisable dans **FastAPI**, via les outils internes de Starlette.
Et la documentation fonctionne quand même, bien qu'aucune section ne soit ajoutée pour dire que le paramètre devrait contenir un *chemin*.
Et la documentation fonctionne quand même, bien qu'aucune section ne soit ajoutée pour dire que la paramètre devrait contenir un *chemin*.
### Convertisseur de *chemin* { #path-convertor }
### Convertisseur de *chemin*
En utilisant une option de Starlette directement, vous pouvez déclarer un *paramètre de chemin* contenant un *chemin* avec une URL comme :
@@ -230,11 +232,11 @@ En utilisant une option de Starlette directement, vous pouvez déclarer un *para
/files/{file_path:path}
```
Dans ce cas, le nom du paramètre est `file_path`, et la dernière partie, `:path`, indique que le paramètre doit correspondre à n'importe quel *chemin*.
Dans ce cas, le nom du paramètre est `file_path`, et la dernière partie, `:path`, indique à Starlette que le paramètre devrait correspondre à un *chemin*.
Vous pouvez donc l'utiliser comme tel :
Vous pouvez donc l'utilisez comme tel :
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
{* ../../docs_src/path_params/tutorial004.py hl[6] *}
/// tip | Astuce
@@ -244,15 +246,15 @@ Dans ce cas, l'URL serait : `/files//home/johndoe/myfile.txt`, avec un double sl
///
## Récapitulatif { #recap }
## Récapitulatif
Avec **FastAPI**, en utilisant les déclarations de type rapides, intuitives et standards de Python, vous bénéficiez de :
* Support de l'éditeur : vérification d'erreurs, autocomplétion, etc.
* «<abbr title="conversion de la chaîne de caractères venant de la requête HTTP en données Python">Parsing</abbr>» de données
* Validation de données
* Annotations d'API et documentation automatique
* Support de l'éditeur : vérification d'erreurs, auto-complétion, etc.
* <abbr title="conversion de la chaîne de caractères venant de la requête HTTP en données Python">"Parsing"</abbr> de données.
* Validation de données.
* Annotations d'API et documentation automatique.
Et vous n'avez besoin de le déclarer qu'une fois.
C'est probablement l'avantage visible principal de **FastAPI** comparé aux autres frameworks (outre les performances pures).
C'est probablement l'avantage visible principal de **FastAPI** comparé aux autres *frameworks* (outre les performances pures).

View File

@@ -1,273 +1,166 @@
# Paramètres de requête et validations de chaînes de caractères { #query-parameters-and-string-validations }
# Paramètres de requête et validations de chaînes de caractères
**FastAPI** vous permet de déclarer des informations et des validations supplémentaires pour vos paramètres.
**FastAPI** vous permet de déclarer des informations et des validateurs additionnels pour vos paramètres de requêtes.
Prenons cette application comme exemple :
Commençons avec cette application pour exemple :
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
{* ../../docs_src/query_params_str_validations/tutorial001.py hl[9] *}
Le paramètre de requête `q` est de type `str | None`, ce qui signifie qu'il est de type `str` mais peut aussi être `None`. D'ailleurs, la valeur par défaut est `None`, donc FastAPI saura qu'il n'est pas requis.
Le paramètre de requête `q` a pour type `Union[str, None]` (ou `str | None` en Python 3.10), signifiant qu'il est de type `str` mais pourrait aussi être égal à `None`, et bien sûr, la valeur par défaut est `None`, donc **FastAPI** saura qu'il n'est pas requis.
/// note | Remarque
/// note
FastAPI saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `= None`.
**FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `= None`.
Le fait d'avoir `str | None` permettra à votre éditeur de vous offrir un meilleur support et de détecter les erreurs.
Le `Union` dans `Union[str, None]` permettra à votre éditeur de vous offrir un meilleur support et de détecter les erreurs.
///
## Validation additionnelle { #additional-validation }
## Validation additionnelle
Nous allons imposer que, même si `q` est optionnel, dès qu'il est fourni, **sa longueur n'excède pas 50 caractères**.
Nous allons imposer que bien que `q` soit un paramètre optionnel, dès qu'il est fourni, **sa longueur n'excède pas 50 caractères**.
### Importer `Query` et `Annotated` { #import-query-and-annotated }
## Importer `Query`
Pour cela, importez d'abord :
Pour cela, importez d'abord `Query` depuis `fastapi` :
* `Query` depuis `fastapi`
* `Annotated` depuis `typing`
{* ../../docs_src/query_params_str_validations/tutorial002.py hl[3] *}
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
## Utiliser `Query` comme valeur par défaut
/// info
Construisez ensuite la valeur par défaut de votre paramètre avec `Query`, en choisissant 50 comme `max_length` :
FastAPI a ajouté la prise en charge de `Annotated` (et a commencé à le recommander) dans la version 0.95.0.
{* ../../docs_src/query_params_str_validations/tutorial002.py hl[9] *}
Si vous avez une version plus ancienne, vous obtiendrez des erreurs en essayant d'utiliser `Annotated`.
Comme nous devons remplacer la valeur par défaut `None` dans la fonction par `Query()`, nous pouvons maintenant définir la valeur par défaut avec le paramètre `Query(default=None)`, il sert le même objectif qui est de définir cette valeur par défaut.
Assurez-vous de [mettre à niveau la version de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} vers au moins la 0.95.1 avant d'utiliser `Annotated`.
///
## Utiliser `Annotated` dans le type pour le paramètre `q` { #use-annotated-in-the-type-for-the-q-parameter }
Vous vous souvenez, je vous ai dit plus tôt que `Annotated` pouvait être utilisé pour ajouter des métadonnées à vos paramètres dans l[introduction aux types Python](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank} ?
Il est temps de l'utiliser avec FastAPI. 🚀
Nous avions cette annotation de type :
//// tab | Python 3.10+
Donc :
```Python
q: str | None = None
q: Union[str, None] = Query(default=None)
```
////
//// tab | Python 3.9+
... rend le paramètre optionnel, et est donc équivalent à :
```Python
q: Union[str, None] = None
```
////
Mais déclare explicitement `q` comme étant un paramètre de requête.
Nous allons l'envelopper avec `Annotated`, ce qui donne :
/// info
//// tab | Python 3.10+
Gardez à l'esprit que la partie la plus importante pour rendre un paramètre optionnel est :
```Python
q: Annotated[str | None] = None
= None
```
////
//// tab | Python 3.9+
ou :
```Python
q: Annotated[Union[str, None]] = None
= Query(None)
```
////
et utilisera ce `None` pour détecter que ce paramètre de requête **n'est pas requis**.
Ces deux versions signifient la même chose : `q` est un paramètre qui peut être un `str` ou `None`, et par défaut, il vaut `None`.
Passons maintenant aux choses amusantes. 🎉
## Ajouter `Query` à `Annotated` dans le paramètre `q` { #add-query-to-annotated-in-the-q-parameter }
Maintenant que nous avons ce `Annotated` dans lequel nous pouvons mettre plus d'informations (dans ce cas une validation supplémentaire), ajoutez `Query` à l'intérieur de `Annotated`, et définissez le paramètre `max_length` à `50` :
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Notez que la valeur par défaut est toujours `None`, donc le paramètre est toujours optionnel.
Mais maintenant, avec `Query(max_length=50)` à l'intérieur de `Annotated`, nous indiquons à FastAPI que nous voulons une **validation supplémentaire** pour cette valeur, nous voulons qu'elle ait au maximum 50 caractères. 😎
/// tip | Astuce
Ici, nous utilisons `Query()` parce qu'il s'agit d'un **paramètre de requête**. Plus tard, nous verrons d'autres objets comme `Path()`, `Body()`, `Header()` et `Cookie()`, qui acceptent aussi les mêmes arguments que `Query()`.
Le `Union[str, None]` est uniquement là pour permettre à votre éditeur un meilleur support.
///
FastAPI va maintenant :
* **Valider** les données en s'assurant que la longueur maximale est de 50 caractères
* Afficher une **erreur claire** au client lorsque les données ne sont pas valides
* **Documenter** le paramètre dans l*opération de chemin* du schéma OpenAPI (de sorte qu'il apparaisse dans **l'interface de documentation automatique**)
## Alternative (ancien) : `Query` comme valeur par défaut { #alternative-old-query-as-the-default-value }
Les versions précédentes de FastAPI (avant <abbr title="avant 2023-03">0.95.0</abbr>) vous obligeaient à utiliser `Query` comme valeur par défaut de votre paramètre, au lieu de le mettre dans `Annotated`. Il y a de fortes chances que vous voyiez du code qui l'utilise encore, donc je vais vous l'expliquer.
/// tip | Astuce
Pour un nouveau code et dès que possible, utilisez `Annotated` comme expliqué ci-dessus. Il y a de multiples avantages (expliqués ci-dessous) et aucun inconvénient. 🍰
///
Voici comment vous utiliseriez `Query()` comme valeur par défaut du paramètre de votre fonction, en définissant le paramètre `max_length` à 50 :
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
Comme dans ce cas (sans utiliser `Annotated`) nous devons remplacer la valeur par défaut `None` dans la fonction par `Query()`, nous devons maintenant définir la valeur par défaut avec le paramètre `Query(default=None)`, cela sert le même objectif de définir cette valeur par défaut (au moins pour FastAPI).
Donc :
Ensuite, nous pouvons passer d'autres paramètres à `Query`. Dans cet exemple, le paramètre `max_length` qui s'applique aux chaînes de caractères :
```Python
q: str | None = Query(default=None)
q: Union[str, None] = Query(default=None, max_length=50)
```
... rend le paramètre optionnel, avec une valeur par défaut de `None`, ce qui est équivalent à :
Cela va valider les données, montrer une erreur claire si ces dernières ne sont pas valides, et documenter le paramètre dans le schéma `OpenAPI` de cette *path operation*.
```Python
q: str | None = None
```
## Rajouter plus de validation
Mais la version avec `Query` le déclare explicitement comme étant un paramètre de requête.
Vous pouvez aussi rajouter un second paramètre `min_length` :
Ensuite, nous pouvons passer d'autres paramètres à `Query`. Dans ce cas, le paramètre `max_length` qui s'applique aux chaînes de caractères :
{* ../../docs_src/query_params_str_validations/tutorial003.py hl[9] *}
```Python
q: str | None = Query(default=None, max_length=50)
```
## Ajouter des validations par expressions régulières
Cela va valider les données, afficher une erreur claire lorsque les données ne sont pas valides, et documenter le paramètre dans l*opération de chemin* du schéma OpenAPI.
On peut définir une <abbr title="Une expression régulière, regex ou regexp est une suite de caractères qui définit un pattern de correspondance pour les chaînes de caractères.">expression régulière</abbr> à laquelle le paramètre doit correspondre :
### `Query` comme valeur par défaut ou dans `Annotated` { #query-as-the-default-value-or-in-annotated }
{* ../../docs_src/query_params_str_validations/tutorial004.py hl[10] *}
Gardez à l'esprit qu'en utilisant `Query` à l'intérieur de `Annotated`, vous ne pouvez pas utiliser le paramètre `default` de `Query`.
Cette expression régulière vérifie que la valeur passée comme paramètre :
Utilisez plutôt la véritable valeur par défaut du paramètre de fonction. Sinon, ce serait incohérent.
Par exemple, ceci n'est pas autorisé :
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
... parce qu'il n'est pas clair si la valeur par défaut devrait être «rick» ou «morty».
Ainsi, vous utiliseriez (de préférence) :
```Python
q: Annotated[str, Query()] = "rick"
```
... ou dans des bases de code plus anciennes, vous trouverez :
```Python
q: str = Query(default="rick")
```
### Avantages de `Annotated` { #advantages-of-annotated }
**Utiliser `Annotated` est recommandé** plutôt que la valeur par défaut dans les paramètres de fonction, c'est **mieux** pour plusieurs raisons. 🤓
La **valeur par défaut** du **paramètre de fonction** est la **véritable valeur par défaut**, c'est plus intuitif en Python en général. 😌
Vous pourriez **appeler** cette même fonction **ailleurs** sans FastAPI, et elle **fonctionnerait comme prévu**. S'il y a un paramètre **requis** (sans valeur par défaut), votre **éditeur** vous le signalera avec une erreur, **Python** se plaindra aussi si vous l'exécutez sans passer le paramètre requis.
Lorsque vous n'utilisez pas `Annotated` et que vous utilisez à la place l**(ancien) style de valeur par défaut**, si vous appelez cette fonction sans FastAPI **ailleurs**, vous devez **penser** à passer les arguments à la fonction pour qu'elle fonctionne correctement, sinon les valeurs seront différentes de ce à quoi vous vous attendez (par exemple `QueryInfo` ou quelque chose de similaire au lieu de `str`). Et votre éditeur ne se plaindra pas, et Python ne se plaindra pas en exécutant cette fonction, seulement lorsque les opérations internes échoueront.
Comme `Annotated` peut avoir plus d'une annotation de métadonnées, vous pouvez même utiliser la même fonction avec d'autres outils, comme <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
## Ajouter plus de validations { #add-more-validations }
Vous pouvez aussi ajouter un paramètre `min_length` :
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Ajouter des expressions régulières { #add-regular-expressions }
Vous pouvez définir un `pattern` d<abbr title="Une expression régulière, regex ou regexp est une suite de caractères qui définit un motif de recherche pour les chaînes de caractères.">expression régulière</abbr> auquel le paramètre doit correspondre :
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Ce motif d'expression régulière vérifie que la valeur de paramètre reçue :
* `^` : commence avec les caractères qui suivent, n'a pas de caractères avant.
* `^` : commence avec les caractères qui suivent, avec aucun caractère avant ceux-là.
* `fixedquery` : a pour valeur exacte `fixedquery`.
* `$` : se termine , n'a pas d'autres caractères après `fixedquery`.
* `$` : se termine directement ensuite, n'a pas d'autres caractères après `fixedquery`.
Si vous vous sentez perdu avec toutes ces idées d**«expression régulière»**, pas d'inquiétude. C'est un sujet difficile pour beaucoup. Vous pouvez déjà faire beaucoup de choses sans avoir besoin des expressions régulières.
Si vous vous sentez perdu avec le concept d'**expression régulière**, pas d'inquiétudes. Il s'agit d'une notion difficile pour beaucoup, et l'on peut déjà réussir à faire beaucoup sans jamais avoir à les manipuler.
Maintenant vous savez que, lorsque vous en avez besoin, vous pouvez les utiliser dans **FastAPI**.
Mais si vous décidez d'apprendre à les utiliser, sachez qu'ensuite vous pouvez les utiliser directement dans **FastAPI**.
## Valeurs par défaut { #default-values }
## Valeurs par défaut
Vous pouvez, bien sûr, utiliser des valeurs par défaut autres que `None`.
De la même façon que vous pouvez passer `None` comme premier argument pour l'utiliser comme valeur par défaut, vous pouvez passer d'autres valeurs.
Disons que vous voulez déclarer le paramètre de requête `q` avec un `min_length` de `3`, et avec une valeur par défaut de «fixedquery» :
Disons que vous déclarez le paramètre `q` comme ayant une longueur minimale de `3`, et une valeur par défaut étant `"fixedquery"` :
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial005.py hl[7] *}
/// note | Remarque
/// note | Rappel
Avoir une valeur par défaut de n'importe quel type, y compris `None`, rend le paramètre optionnel (non requis).
Avoir une valeur par défaut rend le paramètre optionnel.
///
## Paramètres requis { #required-parameters }
## Rendre ce paramètre requis
Quand nous n'avons pas besoin de déclarer plus de validations ou de métadonnées, nous pouvons rendre le paramètre de requête `q` requis simplement en ne déclarant pas de valeur par défaut, comme :
Quand on ne déclare ni validation, ni métadonnée, on peut rendre le paramètre `q` requis en ne lui déclarant juste aucune valeur par défaut :
```Python
q: str
```
au lieu de :
à la place de :
```Python
q: str | None = None
q: Union[str, None] = None
```
Mais nous le déclarons maintenant avec `Query`, par exemple ainsi :
Mais maintenant, on déclare `q` avec `Query`, comme ceci :
```Python
q: Annotated[str | None, Query(min_length=3)] = None
q: Union[str, None] = Query(default=None, min_length=3)
```
Donc, lorsque vous avez besoin de déclarer une valeur comme requise tout en utilisant `Query`, vous pouvez simplement ne pas déclarer de valeur par défaut :
Donc pour déclarer une valeur comme requise tout en utilisant `Query`, il faut utiliser `...` comme premier argument :
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial006.py hl[7] *}
### Requis, peut être `None` { #required-can-be-none }
/// info
Vous pouvez déclarer qu'un paramètre peut accepter `None`, mais qu'il est tout de même requis. Cela obligera les clients à envoyer une valeur, même si la valeur est `None`.
Si vous n'avez jamais vu ce `...` auparavant : c'est une des constantes natives de Python <a href="https://docs.python.org/fr/3/library/constants.html#Ellipsis" class="external-link" target="_blank">appelée "Ellipsis"</a>.
Pour cela, vous pouvez déclarer que `None` est un type valide mais ne pas déclarer de valeur par défaut :
///
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
Cela indiquera à **FastAPI** que la présence de ce paramètre est obligatoire.
## Liste de paramètres de requête / valeurs multiples { #query-parameter-list-multiple-values }
## Liste de paramètres / valeurs multiples via Query
Quand vous définissez un paramètre de requête explicitement avec `Query`, vous pouvez aussi déclarer qu'il reçoit une liste de valeurs, autrement dit, des valeurs multiples.
Quand on définit un paramètre de requête explicitement avec `Query` on peut aussi déclarer qu'il reçoit une liste de valeur, ou des "valeurs multiples".
Par exemple, pour déclarer un paramètre de requête `q` qui peut apparaître plusieurs fois dans l'URL, vous pouvez écrire :
Par exemple, pour déclarer un paramètre de requête `q` qui peut apparaître plusieurs fois dans une URL, on écrit :
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial011.py hl[9] *}
Avec une URL comme :
Ce qui fait qu'avec une URL comme :
```
http://localhost:8000/items/?q=foo&q=bar
```
vous recevrez les valeurs des multiples paramètres de requête `q` (`foo` et `bar`) dans une `list` Python au sein de votre *fonction d'opération de chemin*, dans le *paramètre de fonction* `q`.
vous recevriez les valeurs des multiples paramètres de requête `q` (`foo` et `bar`) dans une `list` Python au sein de votre fonction de **path operation**, dans le paramètre de fonction `q`.
Donc, la réponse à cette URL serait :
Donc la réponse de cette URL serait :
```JSON
{
@@ -280,19 +173,19 @@ Donc, la réponse à cette URL serait :
/// tip | Astuce
Pour déclarer un paramètre de requête de type `list`, comme dans l'exemple ci-dessus, vous devez explicitement utiliser `Query`, sinon cela sera interprété comme faisant partie du corps de la requête.
Pour déclarer un paramètre de requête de type `list`, comme dans l'exemple ci-dessus, il faut explicitement utiliser `Query`, sinon cela sera interprété comme faisant partie du corps de la requête.
///
Les documents interactifs de l'API seront mis à jour en conséquence, pour autoriser plusieurs valeurs :
La documentation sera donc mise à jour automatiquement pour autoriser plusieurs valeurs :
<img src="/img/tutorial/query-params-str-validations/image02.png">
### Liste de paramètres de requête / valeurs multiples avec valeurs par défaut { #query-parameter-list-multiple-values-with-defaults }
### Combiner liste de paramètres et valeurs par défaut
Vous pouvez aussi définir une `list` de valeurs par défaut si aucune n'est fournie :
Et l'on peut aussi définir une liste de valeurs par défaut si aucune n'est fournie :
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial012.py hl[9] *}
Si vous allez à :
@@ -300,7 +193,9 @@ Si vous allez à :
http://localhost:8000/items/
```
la valeur par défaut de `q` sera : `["foo", "bar"]` et votre réponse sera :
la valeur par défaut de `q` sera : `["foo", "bar"]`
et la réponse sera :
```JSON
{
@@ -311,163 +206,93 @@ la valeur par défaut de `q` sera : `["foo", "bar"]` et votre réponse sera :
}
```
#### Utiliser simplement `list` { #using-just-list }
#### Utiliser `list`
Vous pouvez aussi utiliser `list` directement au lieu de `list[str]` :
Il est aussi possible d'utiliser directement `list` plutôt que `List[str]` :
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial013.py hl[7] *}
/// note | Remarque
/// note
Gardez à l'esprit que dans ce cas, FastAPI ne vérifiera pas le contenu de la liste.
Dans ce cas-là, **FastAPI** ne vérifiera pas le contenu de la liste.
Par exemple, `list[int]` vérifierait (et documenterait) que le contenu de la liste est composé d'entiers. Mais `list` seul ne le ferait pas.
Par exemple, `List[int]` vérifiera (et documentera) que la liste est bien entièrement composée d'entiers. Alors qu'un simple `list` ne ferait pas cette vérification.
///
## Déclarer plus de métadonnées { #declare-more-metadata }
## Déclarer des métadonnées supplémentaires
Vous pouvez ajouter plus d'informations sur le paramètre.
On peut aussi ajouter plus d'informations sur le paramètre.
Ces informations seront incluses dans l'OpenAPI généré et utilisées par les interfaces utilisateur de la documentation et les outils externes.
Ces informations seront incluses dans le schéma `OpenAPI` généré et utilisées par la documentation interactive ou les outils externes utilisés.
/// note | Remarque
/// note
Gardez à l'esprit que différents outils peuvent avoir des niveaux de prise en charge d'OpenAPI différents.
Gardez en tête que les outils externes utilisés ne supportent pas forcément tous parfaitement OpenAPI.
Certains d'entre eux peuvent ne pas encore afficher toutes les informations supplémentaires déclarées, bien que, dans la plupart des cas, la fonctionnalité manquante soit déjà planifiée.
Il se peut donc que certains d'entre eux n'utilisent pas toutes les métadonnées que vous avez déclarées pour le moment, bien que dans la plupart des cas, les fonctionnalités manquantes ont prévu d'être implémentées.
///
Vous pouvez ajouter un `title` :
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
{* ../../docs_src/query_params_str_validations/tutorial007.py hl[10] *}
Et une `description` :
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
{* ../../docs_src/query_params_str_validations/tutorial008.py hl[13] *}
## Alias de paramètres { #alias-parameters }
## Alias de paramètres
Imaginez que vous vouliez que le paramètre soit `item-query`.
Imaginez que vous vouliez que votre paramètre se nomme `item-query`.
Comme dans :
Comme dans la requête :
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Mais `item-query` n'est pas un nom de variable Python valide.
Mais `item-query` n'est pas un nom de variable valide en Python.
Le plus proche serait `item_query`.
Le nom le plus proche serait `item_query`.
Mais vous avez quand même besoin que ce soit exactement `item-query`...
Mais vous avez vraiment envie que ce soit exactement `item-query`...
Vous pouvez alors déclarer un `alias`, et cet alias sera utilisé pour trouver la valeur du paramètre :
Pour cela vous pouvez déclarer un `alias`, et cet alias est ce qui sera utilisé pour trouver la valeur du paramètre :
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial009.py hl[9] *}
## Déprécier des paramètres { #deprecating-parameters }
## Déprécier des paramètres
Disons maintenant que vous n'aimez plus ce paramètre.
Disons que vous ne vouliez plus utiliser ce paramètre désormais.
Vous devez le laisser pendant un certain temps car des clients l'utilisent, mais vous voulez que les documents l'affichent clairement comme <abbr title="obsolète, il est recommandé de ne pas l'utiliser">déprécié</abbr>.
Il faut qu'il continue à exister pendant un certain temps car vos clients l'utilisent, mais vous voulez que la documentation mentionne clairement que ce paramètre est <abbr title="obsolète, recommandé de ne pas l'utiliser">déprécié</abbr>.
Passez alors le paramètre `deprecated=True` à `Query` :
On utilise alors l'argument `deprecated=True` de `Query` :
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
{* ../../docs_src/query_params_str_validations/tutorial010.py hl[18] *}
Les documents l'afficheront ainsi :
La documentation le présentera comme il suit :
<img src="/img/tutorial/query-params-str-validations/image01.png">
## Exclure des paramètres dOpenAPI { #exclude-parameters-from-openapi }
## Pour résumer
Pour exclure un paramètre de requête du schéma OpenAPI généré (et donc, des systèmes de documentation automatiques), définissez le paramètre `include_in_schema` de `Query` à `False` :
Il est possible d'ajouter des validateurs et métadonnées pour vos paramètres.
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Validation personnalisée { #custom-validation }
Il peut y avoir des cas où vous avez besoin d'une **validation personnalisée** qui ne peut pas être réalisée avec les paramètres présentés ci-dessus.
Dans ces cas, vous pouvez utiliser une **fonction de validation personnalisée** qui est appliquée après la validation normale (par exemple, après avoir validé que la valeur est une `str`).
Vous pouvez y parvenir en utilisant <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">`AfterValidator` de Pydantic</a> à l'intérieur de `Annotated`.
/// tip | Astuce
Pydantic dispose aussi de <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator" class="external-link" target="_blank">`BeforeValidator`</a> et d'autres. 🤓
///
Par exemple, ce validateur personnalisé vérifie que l'identifiant d'item commence par `isbn-` pour un numéro de livre <abbr title="ISBN signifie International Standard Book Number Numéro international normalisé du livre">ISBN</abbr> ou par `imdb-` pour un identifiant d'URL de film <abbr title="IMDB (Internet Movie Database) est un site web avec des informations sur les films">IMDB</abbr> :
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info
Ceci est disponible avec Pydantic version 2 ou supérieure. 😎
///
/// tip | Astuce
Si vous devez effectuer un type de validation nécessitant une communication avec un **composant externe**, comme une base de données ou une autre API, vous devriez plutôt utiliser les **dépendances FastAPI**, que vous apprendrez à connaître plus tard.
Ces validateurs personnalisés sont destinés aux choses qui peuvent être vérifiées **uniquement** avec **les mêmes données** fournies dans la requête.
///
### Comprendre ce code { #understand-that-code }
Le point important est simplement d'utiliser **`AfterValidator` avec une fonction à l'intérieur de `Annotated`**. N'hésitez pas à sauter cette partie. 🤸
---
Mais si vous êtes curieux à propos de cet exemple de code spécifique et que vous êtes toujours captivé, voici quelques détails supplémentaires.
#### Chaîne avec `value.startswith()` { #string-with-value-startswith }
L'avez-vous remarqué ? une chaîne utilisant `value.startswith()` peut prendre un tuple, et elle vérifiera chaque valeur du tuple :
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Un élément aléatoire { #a-random-item }
Avec `data.items()` nous obtenons un <abbr title="Quelque chose que l'on peut parcourir avec une boucle for, comme une liste, un set, etc.">objet itérable</abbr> avec des tuples contenant la clé et la valeur pour chaque élément du dictionnaire.
Nous convertissons cet objet itérable en une véritable `list` avec `list(data.items())`.
Ensuite, avec `random.choice()` nous pouvons obtenir une **valeur aléatoire** depuis la liste, donc nous obtenons un tuple `(id, name)`. Ce sera quelque chose comme («imdb-tt0371724», «The Hitchhiker's Guide to the Galaxy»).
Nous **affectons ensuite ces deux valeurs** du tuple aux variables `id` et `name`.
Ainsi, si l'utilisateur n'a pas fourni d'identifiant d'item, il recevra tout de même une suggestion aléatoire.
... nous faisons tout cela en **une seule ligne simple**. 🤯 N'adorez-vous pas Python ? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Récapitulatif { #recap }
Vous pouvez déclarer des validations supplémentaires et des métadonnées pour vos paramètres.
Validations et métadonnées génériques :
Validateurs et métadonnées génériques:
* `alias`
* `title`
* `description`
* `deprecated`
Validations spécifiques aux chaînes de caractères :
Validateurs spécifiques aux chaînes de caractères :
* `min_length`
* `max_length`
* `pattern`
* `regex`
Validations personnalisées en utilisant `AfterValidator`.
Parmi ces exemples, vous avez pu voir comment déclarer des validateurs pour les chaînes de caractères.
Dans ces exemples, vous avez vu comment déclarer des validations pour des valeurs de `str`.
Voir les prochains chapitres pour apprendre à déclarer des validations pour d'autres types, comme les nombres.
Dans les prochains chapitres, vous verrez comment déclarer des validateurs pour d'autres types, comme les nombres.

View File

@@ -1,10 +1,10 @@
# Paramètres de requête { #query-parameters }
# Paramètres de requête
Lorsque vous déclarez d'autres paramètres de fonction qui ne font pas partie des paramètres de chemin, ils sont automatiquement interprétés comme des paramètres de «requête».
Quand vous déclarez des paramètres dans votre fonction de chemin qui ne font pas partie des paramètres indiqués dans le chemin associé, ces paramètres sont automatiquement considérés comme des paramètres de "requête".
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
{* ../../docs_src/query_params/tutorial001.py hl[9] *}
La partie appelée requête (ou «query») dans une URL est l'ensemble des paires clé-valeur placées après le `?`, séparées par des `&`.
La partie appelée requête (ou **query**) dans une URL est l'ensemble des paires clés-valeurs placées après le `?` , séparées par des `&`.
Par exemple, dans l'URL :
@@ -12,9 +12,9 @@ Par exemple, dans l'URL :
http://127.0.0.1:8000/items/?skip=0&limit=10
```
... les paramètres de requête sont :
...les paramètres de requête sont :
* `skip` : avec une valeur de `0`
* `skip` : avec une valeur de`0`
* `limit` : avec une valeur de `10`
Faisant partie de l'URL, ces valeurs sont des chaînes de caractères (`str`).
@@ -23,12 +23,12 @@ Mais quand on les déclare avec des types Python (dans l'exemple précédent, en
Toutes les fonctionnalités qui s'appliquent aux paramètres de chemin s'appliquent aussi aux paramètres de requête :
* Prise en charge par l'éditeur (évidemment)
* <abbr title="conversion de la chaîne de caractères venant de la requête HTTP en données Python">«parsing»</abbr> des données
* Validation des données
* Documentation automatique
* Support de l'éditeur : vérification d'erreurs, auto-complétion, etc.
* <abbr title="conversion de la chaîne de caractères venant de la requête HTTP en données Python">"Parsing"</abbr> de données.
* Validation de données.
* Annotations d'API et documentation automatique.
## Valeurs par défaut { #defaults }
## Valeurs par défaut
Les paramètres de requête ne sont pas une partie fixe d'un chemin, ils peuvent être optionnels et avoir des valeurs par défaut.
@@ -57,25 +57,33 @@ Les valeurs des paramètres de votre fonction seront :
* `skip=20` : car c'est la valeur déclarée dans l'URL.
* `limit=10` : car `limit` n'a pas été déclaré dans l'URL, et que la valeur par défaut était `10`.
## Paramètres optionnels { #optional-parameters }
## Paramètres optionnels
De la même façon, vous pouvez définir des paramètres de requête comme optionnels, en leur donnant comme valeur par défaut `None` :
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
{* ../../docs_src/query_params/tutorial002.py hl[9] *}
Ici, le paramètre `q` sera optionnel, et aura `None` comme valeur par défaut.
/// check | vérifier
/// check | Remarque
On peut voir que **FastAPI** est capable de détecter que le paramètre de chemin `item_id` est un paramètre de chemin et que `q` n'en est pas un, c'est donc un paramètre de requête.
///
## Conversion des types des paramètres de requête { #query-parameter-type-conversion }
/// note
Vous pouvez aussi déclarer des types `bool`, ils seront convertis :
**FastAPI** saura que `q` est optionnel grâce au `=None`.
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
Le `Optional` dans `Optional[str]` n'est pas utilisé par **FastAPI** (**FastAPI** n'en utilisera que la partie `str`), mais il servira tout de même à votre éditeur de texte pour détecter des erreurs dans votre code.
///
## Conversion des types des paramètres de requête
Vous pouvez aussi déclarer des paramètres de requête comme booléens (`bool`), **FastAPI** les convertira :
{* ../../docs_src/query_params/tutorial003.py hl[9] *}
Avec ce code, en allant sur :
@@ -109,7 +117,7 @@ http://127.0.0.1:8000/items/foo?short=yes
ou n'importe quelle autre variation de casse (tout en majuscules, uniquement la première lettre en majuscule, etc.), votre fonction considérera le paramètre `short` comme ayant une valeur booléenne à `True`. Sinon la valeur sera à `False`.
## Multiples paramètres de chemin et de requête { #multiple-path-and-query-parameters }
## Multiples paramètres de chemin et de requête
Vous pouvez déclarer plusieurs paramètres de chemin et paramètres de requête dans la même fonction, **FastAPI** saura comment les gérer.
@@ -117,9 +125,9 @@ Et vous n'avez pas besoin de les déclarer dans un ordre spécifique.
Ils seront détectés par leurs noms :
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
{* ../../docs_src/query_params/tutorial004.py hl[8,10] *}
## Paramètres de requête requis { #required-query-parameters }
## Paramètres de requête requis
Quand vous déclarez une valeur par défaut pour un paramètre qui n'est pas un paramètre de chemin (actuellement, nous n'avons vu que les paramètres de requête), alors ce paramètre n'est pas requis.
@@ -127,7 +135,7 @@ Si vous ne voulez pas leur donner de valeur par défaut mais juste les rendre op
Mais si vous voulez rendre un paramètre de requête obligatoire, vous pouvez juste ne pas y affecter de valeur par défaut :
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
Ici le paramètre `needy` est un paramètre requis (ou obligatoire) de type `str`.
@@ -137,21 +145,20 @@ Si vous ouvrez une URL comme :
http://127.0.0.1:8000/items/foo-item
```
... sans ajouter le paramètre requis `needy`, vous verrez une erreur semblable à :
...sans ajouter le paramètre requis `needy`, vous aurez une erreur :
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
"detail": [
{
"loc": [
"query",
"needy"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
```
@@ -161,7 +168,7 @@ La présence de `needy` étant nécessaire, vous auriez besoin de l'insérer dan
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
... cela fonctionnerait :
...ce qui fonctionnerait :
```JSON
{
@@ -170,11 +177,11 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
}
```
Et bien sûr, vous pouvez définir certains paramètres comme requis, certains avec des valeurs par défaut et certains entièrement optionnels :
Et bien sur, vous pouvez définir certains paramètres comme requis, certains avec des valeurs par défaut et certains entièrement optionnels :
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
{* ../../docs_src/query_params/tutorial006.py hl[10] *}
Dans ce cas, on a 3 paramètres de requête :
Ici, on a donc 3 paramètres de requête :
* `needy`, requis et de type `str`.
* `skip`, un `int` avec comme valeur par défaut `0`.
@@ -182,6 +189,6 @@ Dans ce cas, on a 3 paramètres de requête :
/// tip | Astuce
Vous pouvez utiliser les `Enum`s de la même façon qu'avec les [Paramètres de chemin](path-params.md#predefined-values){.internal-link target=_blank}.
Vous pouvez utiliser les `Enum`s de la même façon qu'avec les [Paramètres de chemin](path-params.md#valeurs-predefinies){.internal-link target=_blank}.
///

View File

@@ -1,3 +1,3 @@
# 소개
# 소개 { #about }
FastAPI에 대한 디자인, 영감 등에 대해 🤓
FastAPI, 그 디자인, 영감 등에 대해 🤓

View File

@@ -1,16 +1,16 @@
# 추가 상태 코드
# 추가 상태 코드 { #additional-status-codes }
기본적으로 **FastAPI**는 응답을 `JSONResponse`를 사용하여 반환하며, *경로 작업(path operation)*에서 반환한 내용을 해당 `JSONResponse` 안에 넣어 반환합니다.
기본 상태 코드 또는 *경로 작업*에서 설정한 상태 코드를 사용합니다.
## 추가 상태 코드
## 추가 상태 코드 { #additional-status-codes_1 }
기본 상태 코드와 별도로 추가 상태 코드를 반환하려면 `JSONResponse`와 같이 `Response`를 직접 반환하고 추가 상태 코드를 직접 설정할 수 있습니다.
예를 들어 항목을 업데이트할 수 있는 *경로 작업*이 있고 성공 시 200 “OK”의 HTTP 상태 코드를 반환한다고 가정해 보겠습니다.
하지만 새로운 항목을 허용하기를 원할 것입니다. 항목이 이전에 존재하지 않았다면 이를 생성하고 HTTP 상태 코드 201 "Created"를 반환합니다.
하지만 새로운 항목을 허용하기를 원할 것입니다. 그리고 항목이 이전에 존재하지 않았다면 이를 생성하고 HTTP 상태 코드 201 "Created"를 반환합니다.
이를 위해서는 `JSONResponse`를 가져와서 원하는 `status_code`를 설정하여 콘텐츠를 직접 반환합니다:
@@ -26,7 +26,7 @@
///
/// note | 기술 세부 정보
/// note | 기술 세부사항
`from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
@@ -34,7 +34,7 @@
///
## OpenAPI 및 API 문서
## OpenAPI 및 API 문서 { #openapi-and-api-docs }
추가 상태 코드와 응답을 직접 반환하는 경우, FastAPI는 반환할 내용을 미리 알 수 있는 방법이 없기 때문에 OpenAPI 스키마(API 문서)에 포함되지 않습니다.

View File

@@ -1,6 +1,6 @@
# 고급 의존성
# 고급 의존성 { #advanced-dependencies }
## 매개변수화된 의존성
## 매개변수화된 의존성 { #parameterized-dependencies }
지금까지 본 모든 의존성은 고정된 함수 또는 클래스입니다.
@@ -10,7 +10,7 @@
이때 해당 고정된 내용을 매개변수화할 수 있길 바랍니다.
## "호출 가능한" 인스턴스
## "호출 가능한" 인스턴스 { #a-callable-instance }
Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법이 있습니다.
@@ -21,9 +21,9 @@ Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
이 경우, **FastAPI**는 추가 매개변수와 하위 의존성을 확인하기 위해 `__call__`을 사용하게 되며,
나중에 *경로 연산 함수*에서 매개변수에 값을 전달할 때 이를 호출하게 됩니다.
나중에 *경로 처리 함수*에서 매개변수에 값을 전달할 때 이를 호출하게 됩니다.
## 인스턴스 매개변수화하기
## 인스턴스 매개변수화하기 { #parameterize-the-instance }
이제 `__init__`을 사용하여 의존성을 "매개변수화"할 수 있는 인스턴스의 매개변수를 선언할 수 있습니다:
@@ -31,7 +31,7 @@ Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법
이 경우, **FastAPI**는 `__init__`에 전혀 관여하지 않으며, 우리는 이 메서드를 코드에서 직접 사용하게 됩니다.
## 인스턴스 생성하기
## 인스턴스 생성하기 { #create-an-instance }
다음과 같이 이 클래스의 인스턴스를 생성할 수 있습니다:
@@ -39,10 +39,9 @@ Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법
이렇게 하면 `checker.fixed_content` 속성에 `"bar"`라는 값을 담아 의존성을 "매개변수화"할 수 있습니다.
## 인스턴스를 의존성으로 사용하기
## 인스턴스를 의존성으로 사용하기 { #use-the-instance-as-a-dependency }
그런 다음, `Depends(FixedContentQueryChecker)` 대신 `Depends(checker)`에서 이 `checker` 인스턴스를 사용할 수 있으며,
클래스 자체가 아닌 인스턴스 `checker`가 의존성이 됩니다.
그런 다음, 클래스 자체가 아닌 인스턴스 `checker`가 의존성이 되므로, `Depends(FixedContentQueryChecker)` 대신 `Depends(checker)`에서 이 `checker` 인스턴스를 사용할 수 있습니다.
의존성을 해결할 때 **FastAPI**는 이 `checker`를 다음과 같이 호출합니다:
@@ -50,18 +49,116 @@ Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법
checker(q="somequery")
```
...그리고 이때 반환되는 값을 *경로 연산 함수*의 `fixed_content_included` 매개변수 전달합니다:
...그리고 이때 반환되는 값을 *경로 처리 함수*의 의존성 값으로, `fixed_content_included` 매개변수 전달합니다:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
/// tip | 참고
/// tip |
이 모든 과정이 복잡하게 느껴질 수 있습니다. 그리고 지금은 이 방법이 얼마나 유용한지 명확하지 않을 수도 있습니다.
이 예시는 의도적으로 간단하게 만들었지만, 전체 구조가 어떻게 작동하는지 보여줍니다.
보안 관련 장에서는 이와 같은 방식으로 구현된 편의 함수들이 있습니다.
보안 관련 장에서는 이와 같은 방식으로 구현된 유틸리티 함수들이 있습니다.
이 모든 과정을 이해했다면, 이러한 보안 도구들이 내부적으로 어떻게 작동하는지 이미 파악한 것입니다.
이 모든 과정을 이해했다면, 이러한 보안용 유틸리티 도구들이 내부적으로 어떻게 작동하는지 이미 파악한 것입니다.
///
## `yield`, `HTTPException`, `except`, 백그라운드 태스크가 있는 의존성 { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | 경고
대부분의 경우 이러한 기술 세부사항이 필요하지 않을 것입니다.
이 세부사항은 주로 0.121.0 이전의 FastAPI 애플리케이션이 있고 `yield`가 있는 의존성에서 문제가 발생하는 경우에 유용합니다.
///
`yield`가 있는 의존성은 여러 사용 사례를 수용하고 일부 문제를 해결하기 위해 시간이 지나며 발전해 왔습니다. 다음은 변경된 내용의 요약입니다.
### `yield`와 `scope`가 있는 의존성 { #dependencies-with-yield-and-scope }
0.121.0 버전에서 FastAPI는 `yield`가 있는 의존성에 대해 `Depends(scope="function")` 지원을 추가했습니다.
`Depends(scope="function")`를 사용하면, `yield` 이후의 종료 코드는 *경로 처리 함수*가 끝난 직후(클라이언트에 응답이 반환되기 전)에 실행됩니다.
그리고 `Depends(scope="request")`(기본값)를 사용하면, `yield` 이후의 종료 코드는 응답이 전송된 후에 실행됩니다.
자세한 내용은 [Dependencies with `yield` - Early exit and `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope) 문서를 참고하세요.
### `yield`가 있는 의존성과 `StreamingResponse`, 기술 세부사항 { #dependencies-with-yield-and-streamingresponse-technical-details }
FastAPI 0.118.0 이전에는 `yield`가 있는 의존성을 사용하면, *경로 처리 함수*가 반환된 뒤 응답을 보내기 직전에 `yield` 이후의 종료 코드가 실행되었습니다.
의도는 응답이 네트워크를 통해 전달되기를 기다리면서 필요한 것보다 더 오래 리소스를 점유하지 않도록 하는 것이었습니다.
이 변경은 `StreamingResponse`를 반환하는 경우에도 `yield`가 있는 의존성의 종료 코드가 이미 실행된다는 의미이기도 했습니다.
예를 들어, `yield`가 있는 의존성에 데이터베이스 세션이 있다면, `StreamingResponse`는 데이터를 스트리밍하는 동안 해당 세션을 사용할 수 없게 됩니다. `yield` 이후의 종료 코드에서 세션이 이미 닫혔기 때문입니다.
이 동작은 0.118.0에서 되돌려져, `yield` 이후의 종료 코드가 응답이 전송된 뒤 실행되도록 변경되었습니다.
/// info | 정보
아래에서 보시겠지만, 이는 0.106.0 버전 이전의 동작과 매우 비슷하지만, 여러 개선 사항과 코너 케이스에 대한 버그 수정이 포함되어 있습니다.
///
#### 종료 코드를 조기에 실행하는 사용 사례 { #use-cases-with-early-exit-code }
특정 조건의 일부 사용 사례에서는 응답을 보내기 전에 `yield`가 있는 의존성의 종료 코드를 실행하던 예전 동작이 도움이 될 수 있습니다.
예를 들어, `yield`가 있는 의존성에서 데이터베이스 세션을 사용해 사용자를 검증만 하고, *경로 처리 함수*에서는 그 데이터베이스 세션을 다시는 사용하지 않으며(의존성에서만 사용), **그리고** 응답을 전송하는 데 오랜 시간이 걸리는 경우를 생각해 봅시다. 예를 들어 데이터를 천천히 보내는 `StreamingResponse`인데, 어떤 이유로든 데이터베이스를 사용하지는 않는 경우입니다.
이 경우 데이터베이스 세션은 응답 전송이 끝날 때까지 유지되지만, 사용하지 않는다면 굳이 유지할 필요가 없습니다.
다음과 같이 보일 수 있습니다:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
다음에서 `Session`을 자동으로 닫는 종료 코드는:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...응답이 느린 데이터 전송을 마친 뒤에 실행됩니다:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
하지만 `generate_stream()`는 데이터베이스 세션을 사용하지 않으므로, 응답을 전송하는 동안 세션을 열린 채로 유지할 필요는 없습니다.
SQLModel(또는 SQLAlchemy)을 사용하면서 이런 특정 사용 사례가 있다면, 더 이상 필요하지 않을 때 세션을 명시적으로 닫을 수 있습니다:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
그러면 세션이 데이터베이스 연결을 해제하여, 다른 요청들이 이를 사용할 수 있게 됩니다.
`yield`가 있는 의존성에서 조기 종료가 필요한 다른 사용 사례가 있다면, 여러분의 구체적인 사용 사례와 `yield`가 있는 의존성에 대한 조기 종료가 어떤 점에서 이득이 되는지를 포함해 <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussion Question</a>을 생성해 주세요.
`yield`가 있는 의존성에서 조기 종료에 대한 설득력 있는 사용 사례가 있다면, 조기 종료를 선택적으로 활성화할 수 있는 새로운 방법을 추가하는 것을 고려하겠습니다.
### `yield`가 있는 의존성과 `except`, 기술 세부사항 { #dependencies-with-yield-and-except-technical-details }
FastAPI 0.110.0 이전에는 `yield`가 있는 의존성을 사용한 다음 그 의존성에서 `except`로 예외를 잡고, 예외를 다시 발생시키지 않으면, 예외가 자동으로 어떤 예외 핸들러 또는 내부 서버 오류 핸들러로 raise/forward 되었습니다.
이는 핸들러 없이 전달된 예외(내부 서버 오류)로 인해 처리되지 않은 메모리 사용이 발생하는 문제를 수정하고, 일반적인 Python 코드의 동작과 일관되게 하기 위해 0.110.0 버전에서 변경되었습니다.
### 백그라운드 태스크와 `yield`가 있는 의존성, 기술 세부사항 { #background-tasks-and-dependencies-with-yield-technical-details }
FastAPI 0.106.0 이전에는 `yield` 이후에 예외를 발생시키는 것이 불가능했습니다. `yield`가 있는 의존성의 종료 코드는 응답이 전송된 *후에* 실행되었기 때문에, [Exception Handlers](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}가 이미 실행된 뒤였습니다.
이는 주로 백그라운드 태스크 안에서 의존성이 "yield"한 동일한 객체들을 사용할 수 있게 하기 위한 설계였습니다. 백그라운드 태스크가 끝난 뒤에 종료 코드가 실행되었기 때문입니다.
이는 응답이 네트워크를 통해 전달되기를 기다리는 동안 리소스를 점유하지 않기 위한 의도로 FastAPI 0.106.0에서 변경되었습니다.
/// tip | 팁
추가로, 백그라운드 태스크는 보통 별도의 리소스(예: 자체 데이터베이스 연결)를 가지고 따로 처리되어야 하는 독립적인 로직 집합입니다.
따라서 이 방식이 코드를 더 깔끔하게 만들어줄 가능성이 큽니다.
///
이 동작에 의존하던 경우라면, 이제는 백그라운드 태스크를 위한 리소스를 백그라운드 태스크 내부에서 생성하고, 내부적으로는 `yield`가 있는 의존성의 리소스에 의존하지 않는 데이터만 사용해야 합니다.
예를 들어, 동일한 데이터베이스 세션을 사용하는 대신, 백그라운드 태스크 내부에서 새 데이터베이스 세션을 생성하고, 이 새 세션을 사용해 데이터베이스에서 객체를 가져오면 됩니다. 그리고 데이터베이스에서 가져온 객체를 백그라운드 태스크 함수의 매개변수로 전달하는 대신, 해당 객체의 ID를 전달한 다음 백그라운드 태스크 함수 내부에서 객체를 다시 가져오면 됩니다.

View File

@@ -1,31 +1,26 @@
# 비동기 테스트 코드 작성
# 비동기 테스트 { #async-tests }
이전 장에서 `TestClient` 이용해 **FastAPI** 플리케이션 테스트를 작성하는 법을 배우셨을텐데요.
지금까지는 `async` 키워드 사용없이 동기 함수의 테스트 코드를 작성하는 법만 익혔습니다.
제공된 `TestClient`사용하여 **FastAPI** 플리케이션을 테스트하는 방법을 이미 살펴보았습니다. 지금까지는 `async` 함수를 사용하지 않고, 동기 테스트를 작성하는 방법만 보았습니다.
하지만 비동기 함수를 사용하여 테스트 코드를 작성하는 것은 매우 유용할 수 있습니다.
예를 들면 데이터베이스에 비동기로 쿼리하는 경우를 생각해봅시다.
FastAPI 애플리케이션에 요청을 보내고, 비동기 데이터베이스 라이브러리를 사용하여 백엔드가 데이터베이스에 올바르게 데이터를 기록했는지 확인하고 싶을 때가 있을 겁니다.
테스트에서 비동기 함수를 사용할 수 있으면 유용할 수 있습니다. 예를 들어 데이터베이스를 비동기로 쿼리하는 경우를 생각해 보세요. FastAPI 애플리케이션에 요청을 보낸 다음, async 데이터베이스 라이브러리를 사용하면서 백엔드가 데이터베이스에 올바른 데이터를 성공적으로 기록했는지 검증하고 싶을 수 있습니다.
이런 경우의 테스트 코드를 어떻게 비동기로 작성하는지 알아봅시다.
어떻게 동작하게 만들 수 있는지 살펴보겠습니다.
## pytest.mark.anyio
## pytest.mark.anyio { #pytest-mark-anyio }
앞에서 작성한 테스트 함수에서 비동기 함수를 호출하고 싶다면, 테스트 코드도 비동기 함수여야합니다.
AnyIO는 특정 테스트 함수를 비동기 함수로 호출 할 수 있는 깔끔한 플러그인을 제공합니다.
테스트에서 비동기 함수를 호출하면, 테스트 함수도 비동기여야 합니다. AnyIO는 이를 위한 깔끔한 플러그인을 제공하며, 일부 테스트 함수를 비동기로 호출하도록 지정할 수 있습니다.
## HTTPX { #httpx }
## HTTPX
**FastAPI** 애플리케이션이 `async def` 대신 일반 `def` 함수를 사용하더라도, 내부적으로는 여전히 `async` 애플리케이션입니다.
**FastAPI** 애플리케이션`async def` 대신 `def` 키워드로 선언된 함수를 사용하더라도, 내부적으로는 여전히 `비동기` 애플리케이션입니다.
`TestClient`는 표준 pytest를 사용하여, 일반 `def` 테스트 함수 안에서 비동기 FastAPI 애플리케이션을 호출하도록 내부에서 마법 같은 처리를 합니다. 하지만 비동기 함수 안에서 이를 사용하면 그 마법은 더 이상 동작하지 않습니다. 테스트를 비동기로 실행하면, 테스트 함수 안에서 `TestClient`를 더 이상 사용할 수 없습니다.
`TestClient`pytest 표준을 사용하여 비동기 FastAPI 애플리케이션을 일반적인 `def` 테스트 함수 내에서 호출할 수 있도록 내부에서 마술을 부립니다. 하지만 이 마술은 비동기 함수 내부에서 사용할 때는 더 이상 작동하지 않습니다. 테스트를 비동기로 실행하면, 더 이상 테스트 함수 내부에서 `TestClient`를 사용할 수 습니다.
`TestClient`<a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>를 기반으로 하며, 다행히 HTTPX를 직접 사용해 API를 테스트할 수 습니다.
`TestClient`는 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>를 기반으로 하고 있으며, 다행히 이를 직접 사용하여 API를 테스트할 수 있습니다.
## 예시 { #example }
## 예시
간단한 예시를 위해 [더 큰 어플리케이션 만들기](../ko/tutorial/bigger-applications.md){.internal-link target=_blank} 와 [테스트](../ko/tutorial/testing.md){.internal-link target=_blank}:에서 다룬 파일 구조와 비슷한 형태를 확인해봅시다:
간단한 예시로, [더 큰 애플리케이션](../tutorial/bigger-applications.md){.internal-link target=_blank}과 [테스트](../tutorial/testing.md){.internal-link target=_blank}에서 설명한 것과 비슷한 파일 구조를 살펴보겠습니다:
```
.
@@ -35,17 +30,17 @@ AnyIO는 특정 테스트 함수를 비동기 함수로 호출 할 수 있는
│   └── test_main.py
```
`main.py`는 아래와 같아야 합니다:
`main.py` 파일은 다음과 같습니다:
{* ../../docs_src/async_tests/main.py *}
{* ../../docs_src/async_tests/app_a_py39/main.py *}
`test_main.py` 파일 `main.py`에 대한 테스트가 있을 텐데, 다음과 같 수 있습니다:
`test_main.py` 파일에는 `main.py`에 대한 테스트가 있으며, 이제 다음과 같이 보일 수 있습니다:
{* ../../docs_src/async_tests/test_main.py *}
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
## 실행하기
## 실행하기 { #run-it }
아래의 명령어로 테스트 코드를 실행니다:
다음과 같이 평소처럼 테스트를 실행할 수 있습니다:
<div class="termy">
@@ -57,52 +52,48 @@ $ pytest
</div>
## 자세히 보기
## 자세히 보기 { #in-detail }
`@pytest.mark.anyio` 마커는 pytest에게 이 테스트 함수가 비동기로 호출되어야 함을 알려줍니다:
`@pytest.mark.anyio` 마커는 pytest에게 이 테스트 함수가 비동기로 호출되어야 한다고 알려줍니다:
{* ../../docs_src/async_tests/test_main.py hl[7] *}
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
/// tip | 팁
테스트 함수가 이제 `TestClient`를 사용할 때처럼 단순히 `def`가 아니라 `async def`로 작성된 점에 주목해주세요.
`TestClient`를 사용할 때처럼 단순히 `def`가 아니라, 이제 테스트 함수가 `async def`라는 점에 주목세요.
///
그 다음 `AsyncClient` 로 앱을 만들고 비동기 요청을 `await` 키워드로 보낼 수 있습니다:
그 다음 앱으로 `AsyncClient`를 만들고, `await`를 사용해 비동기 요청을 보낼 수 있습니다.
{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
위의 코드는:
이는 다음과 동등합니다:
```Python
response = client.get('/')
```
`TestClient` 요청을 보내던 것과 동일합니다.
`TestClient` 요청을 보내기 위해 사용하던 코드입니다.
/// tip | 팁
로운 `AsyncClient`를 사용할 때 async/await를 사용하고 있다는 점에 주목하세요. 요청은 비동기적으로 처리됩니다.
`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>.확인해주세요.
플리케이션이 lifespan 이벤트에 의존다면, `AsyncClient` 이러한 이벤트를 트리거하지 않습니다. 이벤트가 트리거되도록 하려면 <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a>의 `LifespanManager`를 사용하세요.
///
## 그 외의 비동기 함수 호출
## 기타 비동기 함수 호출 { #other-asynchronous-function-calls }
테스트 함수가 이제 비동기 함수이므로, FastAPI 애플리케이션에 요청을 보내는 것 외에도 다른 `async` 함수를 호출하고 `await` 키워드를 사용 할 수 있습니다.
테스트 함수가 이제 비동기이므로, 테스트에서 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")` 콜백 내에서 인스턴스화하는 것이 좋습니다.
테스트에 비동기 함수 호출을 통합할 때(예: <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`를 마주친다면, 이벤트 루프가 필요한 객체는 async 함수 에서만 인스턴스화해야 한다는 점을 기억하세요. 예를 들어 `@app.on_event("startup")` 콜백에서 인스턴스화할 수 있습니다.
///

View File

@@ -1,4 +1,4 @@
# 사용자 정의 응답 - HTML, Stream, 파일, 기타
# 사용자 정의 응답 - HTML, Stream, 파일, 기타 { #custom-response-html-stream-file-others }
기본적으로, **FastAPI** 응답을 `JSONResponse`를 사용하여 반환합니다.
@@ -6,11 +6,11 @@
그러나 `Response` (또는 `JSONResponse`와 같은 하위 클래스)를 직접 반환하면, 데이터가 자동으로 변환되지 않으며 (심지어 `response_model`을 선언했더라도), 문서화가 자동으로 생성되지 않습니다(예를 들어, 생성된 OpenAPI의 일부로 HTTP 헤더 `Content-Type`에 특정 "미디어 타입"을 포함하는 경우).
하지만 *경로 작업 데코레이터*에서 `response_class` 매개변수를 사용하여 원하는 `Response`(예: 모든 `Response` 하위 클래스)를 선언할 수도 있습니다.
하지만 *경로 처리 데코레이터*에서 `response_class` 매개변수를 사용하여 원하는 `Response`(예: 모든 `Response` 하위 클래스)를 선언할 수도 있습니다.
*경로 작업 함수*에서 반환하는 내용은 해당 `Response`안에 포함됩니다.
*경로 처리 함수*에서 반환하는 내용은 해당 `Response`안에 포함됩니다.
그리고 만약 그 `Response``JSONResponse``UJSONResponse`의 경우 처럼 JSON 미디어 타입(`application/json`)을 가지고 있다면, *경로 작업 데코레이터*에서 선언한 Pydantic의 `response_model`을 사용해 자동으로 변환(및 필터링) 됩니다.
그리고 만약 그 `Response``JSONResponse``UJSONResponse`의 경우 처럼 JSON 미디어 타입(`application/json`)을 가지고 있다면, *경로 처리 데코레이터*에서 선언한 Pydantic의 `response_model`을 사용해 자동으로 변환(및 필터링) 됩니다.
/// note | 참고
@@ -18,11 +18,11 @@
///
## `ORJSONResponse` 사용하기
## `ORJSONResponse` 사용하기 { #use-orjsonresponse }
예를 들어, 성능을 극대화하려는 경우, <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">orjson</a>을 설치하여 사용하고 응답을 `ORJSONResponse`로 설정할 수 있습니다.
예를 들어, 성능을 극대화하려는 경우, <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>을 설치하여 사용하고 응답을 `ORJSONResponse`로 설정할 수 있습니다.
사용하고자 하는 `Response` 클래스(하위 클래스)를 임포트한 후, **경로 작업 데코레이터*에서 선언하세요.
사용하고자 하는 `Response` 클래스(하위 클래스)를 임포트한 후, *경로 처리 데코레이터*에서 선언하세요.
대규모 응답의 경우, 딕셔너리를 반환하는 것보다 `Response`를 반환하는 것이 훨씬 빠릅니다.
@@ -30,7 +30,7 @@
하지만 반환하는 내용이 **JSON으로 직렬화 가능**하다고 확신하는 경우, 해당 내용을 응답 클래스에 직접 전달할 수 있으며, FastAPI가 반환 내용을 `jsonable_encoder`를 통해 처리한 뒤 응답 클래스에 전달하는 오버헤드를 피할 수 있습니다.
{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
/// info | 정보
@@ -48,14 +48,14 @@
///
## HTML 응답
## HTML 응답 { #html-response }
**FastAPI**에서 HTML 응답을 직접 반환하려면 `HTMLResponse`를 사용하세요.
* `HTMLResponse`를 임포트 합니다.
* *경로 작업 데코레이터*의 `response_class` 매개변수로 `HTMLResponse`를 전달합니다.
* *경로 처리 데코레이터*의 `response_class` 매개변수로 `HTMLResponse`를 전달합니다.
{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
/// info | 정보
@@ -67,17 +67,17 @@
///
### `Response` 반환하기
### `Response` 반환하기 { #return-a-response }
[응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 본 것 처럼, *경로 작업*에서 응답을 직접 반환하여 재정의할 수도 있습니다.
[응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 본 것 처럼, *경로 처리*에서 응답을 직접 반환하여 재정의할 수도 있습니다.
위의 예제와 동일하게 `HTMLResponse`를 반환하는 코드는 다음과 같을 수 있습니다:
{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
/// warning | 경고
*경로 작업 함수*에서 직접 반환된 `Response`는 OpenAPI에 문서화되지 않습니다(예를들어, `Content-Type`이 문서화되지 않음) 자동 대화형 문서에서도 표시되지 않습니다.
*경로 처리 함수*에서 직접 반환된 `Response`는 OpenAPI에 문서화되지 않습니다(예를들어, `Content-Type`이 문서화되지 않음) 자동 대화형 문서에서도 표시되지 않습니다.
///
@@ -87,27 +87,27 @@
///
### OpenAPI에 문서화하고 `Response` 재정의 하기
### OpenAPI에 문서화하고 `Response` 재정의 하기 { #document-in-openapi-and-override-response }
함수 내부에서 응답을 재정의하면서 동시에 OpenAPI에서 "미디어 타입"을 문서화하고 싶다면, `response_class` 매게변수를 사용하면서 `Response` 객체를 반환할 수 있습니다.
이 경우 `response_class`는 OpenAPI *경로 작업*을 문서화하는 데만 사용되고, 실제로는 여러분이 반환한 `Response`가 그대로 사용됩니다.
이 경우 `response_class`는 OpenAPI *경로 처리*를 문서화하는 데만 사용되고, 실제로는 여러분이 반환한 `Response`가 그대로 사용됩니다.
### `HTMLResponse`직접 반환하기
#### `HTMLResponse`직접 반환하기 { #return-an-htmlresponse-directly }
예를 들어, 다음과 같이 작성할 수 있습니다:
{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
이 예제에서, `generate_html_response()` 함수는 HTML을 `str`로 반환하는 대신 이미 `Response`를 생성하고 반환합니다.
`generate_html_response()`를 호출한 결과를 반환함으로써, 기본적인 **FastAPI** 기본 동작을 재정의 하는 `Response`를 이미 반환하고 있습니다.
하지만 `response_class``HTMLResponse`를 함께 전달했기 때문에, FastAPI는 이를 OpenAPI 및 대화형 문서에서 `text/html`로 HTML을 문서화 하는 방법을 알 수 있습니다.
하지만 `response_class``HTMLResponse`를 함께 전달했기 때문에, **FastAPI**는 이를 OpenAPI 및 대화형 문서에서 `text/html`로 HTML을 문서화 하는 방법을 알 수 있습니다.
<img src="/img/tutorial/custom-response/image01.png">
## 사용 가능한 응답들
## 사용 가능한 응답들 { #available-responses }
다음은 사용할 수 있는 몇가지 응답들 입니다.
@@ -121,7 +121,7 @@
///
### `Response`
### `Response` { #response }
기본 `Response` 클래스는 다른 모든 응답 클래스의 부모 클래스 입니다.
@@ -134,27 +134,27 @@
* `headers` - 문자열로 이루어진 `dict`.
* `media_type` - 미디어 타입을 나타내는 `str` 예: `"text/html"`.
FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포함시킵니다. 또한 `media_type`에 기반하여 `Content-Type` 헤더를 포함하며, 텍스트 타입의 경우 문자 집합을 추가 합니다.
FastAPI (실제로는 Starlette)가 자동으로 Content-Length 헤더를 포함시킵니다. 또한 `media_type`에 기반하여 Content-Type 헤더를 포함하며, 텍스트 타입의 경우 문자 집합을 추가 합니다.
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
### `HTMLResponse`
### `HTMLResponse` { #htmlresponse }
텍스트 또는 바이트를 받아 HTML 응답을 반환합니다. 위에서 설명한 내용과 같습니다.
### `PlainTextResponse`
### `PlainTextResponse` { #plaintextresponse }
텍스트 또는 바이트를 받아 일반 텍스트 응답을 반환합니다.
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
### `JSONResponse`
### `JSONResponse` { #jsonresponse }
데이터를 받아 `application/json`으로 인코딩된 응답을 반환합니다.
이는 위에서 설명했듯이 **FastAPI**에서 기본적으로 사용되는 응답 형식입니다.
### `ORJSONResponse`
### `ORJSONResponse` { #orjsonresponse }
<a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>을 사용하여 빠른 JSON 응답을 제공하는 대안입니다. 위에서 설명한 내용과 같습니다.
@@ -164,13 +164,13 @@ FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포
///
### `UJSONResponse`
### `UJSONResponse` { #ujsonresponse }
<a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>을 사용한 또 다른 JSON 응답 형식입니다.
/// info | 정보
이 응답을 사용하려면 `ujson`을 설치해야합니다. 예: 'pip install ujson`.
이 응답을 사용하려면 `ujson`을 설치해야합니다. 예: `pip install ujson`.
///
@@ -180,7 +180,7 @@ FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포
///
{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
/// tip | 팁
@@ -188,22 +188,22 @@ FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포
///
### `RedirectResponse`
### `RedirectResponse` { #redirectresponse }
HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 307(임시 리디렉션)으로 설정됩니다.
`RedirectResponse`를 직접 반환할 수 있습니다.
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
---
또는 `response_class` 매개변수에서 사용할 수도 있습니다:
{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
이 경우, *경로 작업* 함수에서 URL을 직접 반환할 수 있습니다.
이 경우, *경로 처리* 함수에서 URL을 직접 반환할 수 있습니다.
이 경우, 사용되는 `status_code``RedirectResponse`의 기본값인 `307` 입니다.
@@ -211,23 +211,23 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
`status_code` 매개변수를 `response_class` 매개변수와 함께 사용할 수도 있습니다:
{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
### `StreamingResponse`
### `StreamingResponse` { #streamingresponse }
비동기 제너레이터 또는 일반 제너레이터/이터레이터를 받아 응답 본문을 스트리밍 합니다.
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
#### 파일과 같은 객체를 사용한 `StreamingResponse`
#### 파일과 같은 객체를 사용한 `StreamingResponse` { #using-streamingresponse-with-file-like-objects }
파일과 같은 객체(예: `open()`으로 반환된 객체)가 있는 경우, 해당 파일과 같은 객체를 반복(iterate)하는 제너레이터 함수를 만들 수 있습니다.
<a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> 객체(예: `open()`으로 반환된 객체)가 있는 경우, 해당 file-like 객체를 반복(iterate)하는 제너레이터 함수를 만들 수 있습니다.
이 방식으로, 파일 전체를 메모리에 먼저 읽어들일 필요 없이, 제너레이터 함수를 `StreamingResponse`에 전달하여 반환할 수 있습니다.
이 방식은 클라우드 스토리지, 비디오 처리 등의 다양한 라이브러리와 함께 사용할 수 있습니다.
{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
1. 이것이 제너레이터 함수입니다. `yield` 문을 포함하고 있으므로 "제너레이터 함수"입니다.
2. `with` 블록을 사용함으로써, 제너레이터 함수가 완료된 후 파일과 같은 객체가 닫히도록 합니다. 즉, 응답 전송이 끝난 후 닫힙니다.
@@ -235,15 +235,15 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
이렇게 하면 "생성(generating)" 작업을 내부적으로 다른 무언가에 위임하는 제너레이터 함수가 됩니다.
이 방식을 사용하면 `with` 블록 안에서 파일을 열 수 있어, 작업이 완료된 후 파일과 같은 객체가 닫히는 것을 보장할 수 있습니다.
이 방식을 사용하면 `with` 블록 안에서 파일을 열 수 있어, 작업이 완료된 후 파일과 같은 객체가 닫히는 것을 보장할 수 있습니다.
/// tip | 팁
여기서 표준 `open()`을 사용하고 있기 때문에 `async`와 `await`를 지원하지 않습니다. 따라서 경로 작업은 일반 `def`로 선언합니다.
여기서 표준 `open()`을 사용하고 있기 때문에 `async``await`를 지원하지 않습니다. 따라서 경로 처리는 일반 `def`로 선언합니다.
///
### `FileResponse`
### `FileResponse` { #fileresponse }
파일을 비동기로 스트리밍하여 응답합니다.
@@ -256,25 +256,25 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
파일 응답에는 적절한 `Content-Length`, `Last-Modified`, 및 `ETag` 헤더가 포함됩니다.
{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
또한 `response_class` 매개변수를 사용할 수도 있습니다:
{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
이 경우, 경로 작업 함수에서 파일 경로를 직접 반환할 수 있습니다.
이 경우, 경로 처리 함수에서 파일 경로를 직접 반환할 수 있습니다.
## 사용자 정의 응답 클래스
## 사용자 정의 응답 클래스 { #custom-response-class }
`Response`를 상속받아 사용자 정의 응답 클래스를 생성하고 사용할 수 있습니다.
예를 들어, 포함된 `ORJSONResponse` 클래스에서 사용되지 않는 설정으로 <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">orjson</a>을 사용하고 싶다고 가정해봅시다.
예를 들어, 포함된 `ORJSONResponse` 클래스에서 사용되지 않는 설정으로 <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>을 사용하고 싶다고 가정해봅시다.
만약 들여쓰기 및 포맷된 JSON을 반환하고 싶다면, `orjson.OPT_INDENT_2` 옵션을 사용할 수 있습니다.
`CustomORJSONResponse`를 생성할 수 있습니다. 여기서 핵심은 `Response.render(content)` 메서드를 생성하여 내용을 `bytes`로 반환하는 것입니다:
{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
이제 다음 대신:
@@ -282,7 +282,7 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
{"message": "Hello World"}
```
이 응답은 이렇게 반환됩니다:
...이 응답은 이렇게 반환됩니다:
```json
{
@@ -292,22 +292,22 @@ HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 30
물론 JSON 포맷팅보다 더 유용하게 활용할 방법을 찾을 수 있을 것입니다. 😉
## 기본 응답 클래스
## 기본 응답 클래스 { #default-response-class }
**FastAPI** 클래스 객체 또는 `APIRouter`를 생성할 때 기본적으로 사용할 응답 클래스를 지정할 수 있습니다.
이를 정의하는 매개변수는 `default_response_class`입니다.
아래 예제에서 **FastAPI**는 모든 경로 작업에서 기본적으로 `JSONResponse` 대신 `ORJSONResponse`를 사용합니다.
아래 예제에서 **FastAPI**는 모든 *경로 처리*에서 기본적으로 `JSONResponse` 대신 `ORJSONResponse`를 사용합니다.
{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
/// tip | 팁
여전히 이전처럼 *경로 작업*에서 `response_class`를 재정의할 수 있습니다.
여전히 이전처럼 *경로 처리*에서 `response_class`를 재정의할 수 있습니다.
///
## 추가 문서화
## 추가 문서화 { #additional-documentation }
OpenAPI에서 `responses`를 사용하여 미디어 타입 및 기타 세부 정보를 선언할 수도 있습니다: [OpenAPI에서 추가 응답](additional-responses.md){.internal-link target=_blank}.

View File

@@ -1,67 +1,66 @@
# Lifespan 이벤트
# Lifespan 이벤트 { #lifespan-events }
애플리케이션 **시작 전**에 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이는 이 코드가 **한 번**만 실행되며, **애플리케이션이 요청을 받기 시작하기 전**에 실행된다는 의미입니다.
애플리케이션 **시작**하기 전에 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이는 이 코드가 **한 번**만 실행되며, 애플리케이션이 **요청을 받기 시작하기 전**에 실행된다는 의미입니다.
마찬가지로, 애플리케이션이 **종료될 때** 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이 경우, 이 코드는 **한 번**만 실행되며, **여러 요청을 처리한 후**에 실행됩니다.
마찬가지로, 애플리케이션이 **종료**될 때 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이 경우, 이 코드는 **한 번**만 실행되며, **여러 요청을 처리한 후**에 실행됩니다.
이 코드 애플리케이션이 **요청을 받기 시작하기 전에** 실행되고, 요청 처리가 끝난 후 **종료 직전에** 실행되기 때문에 전체 애플리케이션의 **수명(Lifespan)**을 다룹니다. (잠시 후 "수명"이라는 단어가 중요해집니다 😉)
이 코드 애플리케이션이 요청을 받기 **시작**하기 전에 실행되고, 요청 처리를 **끝낸 직후** 실행되기 때문에 전체 애플리케이션의 **수명(lifespan)**을 다룹니다(잠시 후 "lifespan"이라는 단어가 중요해집니다 😉).
방법은 전체 애플리케이션에서 사용해야 하는 **자원**을 설정하거나 요청 간에 **공유되는** 자원을 설정하고, 또는 후에 **정리**하는 데 매우 유용할 수 있습니다. 예를 들어, 데이터베이스 연결 풀 또는 공유되는 머신러닝 모델을 로드하는 경우입니다.
는 전체 앱에서 사용해야 하는 **자원**을 설정하고, 요청 간에 **공유되는** 자원을 설정하고, 그리고/또는 후에 **정리**하는 데 매우 유용할 수 있습니다. 예를 들어, 데이터베이스 연결 풀 또는 공유 머신러닝 모델을 로드하는 경우입니다.
## 사용 사례 { #use-case }
## 사용 사례
먼저 **사용 사례** 예시로 시작한 다음, 이를 어떻게 해결할지 살펴보겠습니다.
먼저 **사용 사례**를 예로 들어보고, 이를 어떻게 해결할 수 있는지 살펴보겠습니다.
요청을 처리하는 데 사용하고 싶은 **머신러닝 모델**이 있다고 상상해 봅시다. 🤖
우리가 요청을 처리하기 위해 사용하고 싶은 **머신러닝 모델**이 있다고 상상해 봅시다. 🤖
동일한 모델이 요청 간에 공유되므로, 요청마다 모델이 하나씩 있거나 사용자마다 하나씩 있는 등의 방식이 아닙니다.
이 모델들은 요청 간에 공유되므로, 요청마다 모델이 하나씩 있는 것이 아니라, 여러 요청에서 동일한 모델을 사용합니다.
모델을 로드하는 데 **상당한 시간이 걸린다고 상상해 봅시다**, 왜냐하면 모델이 **디스크에서 많은 데이터를 읽어야** 하기 때문입니다. 그래서 모든 요청마다 이를 수행하고 싶지는 않습니다.
델을 로드하는 데 **상당한 시간이 걸린다고 상상해 봅시다**, 왜냐하면 모델이 **디스크에서 많은 데이터를 읽어야** 하기 때문입니다. 따라서 모든 요청에 대해 모델을 매번 로드하고 싶지 않습니다.
듈/파일의 최상위에서 로드할 수도 있지만, 그러면 단순한 자동화된 테스트를 실행하는 경우에도 **모델을 로드**하게 되고, 테스트가 코드의 독립적인 부분을 실행하기 전에 모델이 로드될 때까지 기다려야 하므로 **느려집니다**.
모듈/파일의 최상위에서 모델을 로드할 수도 있지만, 그러면 **모델을 로드하는데** 시간이 걸리기 때문에, 단순한 자동화된 테스트를 실행할 때도 모델이 로드될 때까지 기다려야 해서 **테스트 속도가 느려집니다**.
이것이 우리가 해결할 문제입니다. 요청을 처리하기 전에 모델을 로드하되, 코드가 로드되는 동안이 아니라 애플리케이션이 요청을 받기 시작하기 직전에만 로드하겠습니다.
이 문제를 해결하려고 하는 것입니다. 요청을 처리하기 전에 모델을 로드하되, 애플리케이션이 요청을 받기 시작하기 직전에만 로드하고, 코드가 로드되는 동안은 로드하지 않도록 하겠습니다.
## Lifespan { #lifespan }
## Lifespan
`FastAPI` 앱의 `lifespan` 매개변수와 "컨텍스트 매니저"를 사용하여 *시작*과 *종료* 로직을 정의할 수 있습니다(컨텍스트 매니저가 무엇인지 잠시 후에 보여드리겠습니다).
`FastAPI` 애플리케이션의 `lifespan` 매개변수와 "컨텍스트 매니저"를 사용하여 *시작*과 *종료* 로직을 정의할 수 있습니다. (컨텍스트 매니저가 무엇인지 잠시 후에 설명드리겠습니다.)
예제로 시작한 다음 자세히 살펴보겠습니다.
예제를 통해 시작하고, 그 후에 자세히 살펴보겠습니다.
`yield`를 사용해 비동기 함수 `lifespan()`을 다음과 같이 생성합니다:
우리는 `yield`를 사용하여 비동기 함수 `lifespan()`을 다음과 같이 생성합니다:
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
{* ../../docs_src/events/tutorial003.py hl[16,19] *}
여기서는 `yield` 이전에 (가짜) 모델 함수를 머신러닝 모델이 들어 있는 딕셔너리에 넣어 모델을 로드하는 비용이 큰 *시작* 작업을 시뮬레이션합니다. 이 코드는 애플리케이션이 **요청을 받기 시작하기 전**, *시작* 동안에 실행됩니다.
여기서 우리는 모델을 로드하는 비싼 *시작* 작업을 시뮬레이션하고 있습니다. `yield` 앞에서 (가짜) 모델 함수를 머신러닝 모델이 담긴 딕셔너리에 넣습니다. 이 코드는 **애플리케이션이 요청을 받기 시작하기 전**, *시작* 동안에 실행됩니다.
그리고 `yield` 직후에는 모델을 언로드합니다. 이 코드는 **애플리케이션이 요청 처리 완료 후**, *종료* 직전에 실행됩니다. 예를 들어, 메모리나 GPU와 같은 자원을 해제하는 작업을 할 수 있습니다.
그리고 `yield` 직후에는 모델을 언로드합니다. 이 코드는 애플리케이션이 **요청 처리를 마친 후**, *종료* 직전에 실행됩니다. 예를 들어 메모리나 GPU 같은 자원을 해제할 수 있습니다.
/// tip | 팁
`shutdown`은 애플리케이션을 **종료**할 때 발생합니다.
`shutdown`은 애플리케이션을 **중지**할 때 발생합니다.
로운 버전을 시작해야 하거나, 그냥 실행을 멈추고 싶을 수도 있습니다. 🤷
새 버전을 시작해야 할 수도 있고, 그냥 실행하는 게 지겨워졌을 수도 있습니다. 🤷
///
### Lifespan 함수
### Lifespan 함수 { #lifespan-function }
먼저 주목할 점은, `yield`를 사용하여 비동기 함수(async function)를 정의하고 있다는 것입니다. 이는 `yield`를 사용 의존성과 매우 유사합니다.
먼저 주목할 점은 `yield`를 사용하여 비동기 함수를 정의하고 있다는 것입니다. 이는 `yield`를 사용하는 의존성과 매우 유사합니다.
{* ../../docs_src/events/tutorial003.py hl[14:19] *}
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
함수의 첫 번째 부분, 즉 `yield` 이전의 코드는 애플리케이션이 시작되기 **전에** 실행됩니다.
그리고 `yield` 이후의 부분은 애플리케이션이 료된 **나중** 실행됩니다.
그리고 `yield` 이후의 부분은 애플리케이션이 료된 **** 실행됩니다.
### 비동기 컨텍스트 매니저
### 비동기 컨텍스트 매니저 { #async-context-manager }
함수를 확인해보면, `@asynccontextmanager`장식되어 있습니다.
확인해 보면, 함수는 `@asynccontextmanager`데코레이션되어 있습니다.
것은 함수를 "**비동기 컨텍스트 매니저**"라고 불리는 것으로 변환시킵니다.
함수를 "**비동기 컨텍스트 매니저**"라고 불리는 것으로 변환니다.
{* ../../docs_src/events/tutorial003.py hl[1,13] *}
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
파이썬에서 **컨텍스트 매니저**는 `with` 문에서 사용할 수 있는 것입니다. 예를 들어, `open()`은 컨텍스트 매니저로 사용할 수 있습니다:
@@ -69,97 +68,98 @@
with open("file.txt") as file:
file.read()
```
최근 버전의 파이썬에서는 **비동기 컨텍스트 매니저**도 있습니다. 이를 `async with`와 함께 사용합니다:
최근 버전의 파이썬에는 **비동기 컨텍스트 매니저**도 있습니다. 이를 `async with`와 함께 사용합니다:
```Python
async with lifespan(app):
await do_stuff()
```
컨텍스트 매니저나 위와 같은 비동기 컨텍스트 매니저를 만들면, `with` 블록에 들어가기 전에 `yield` 이전의 코드 실행고, `with` 블록을 벗어난 후에는 `yield` 이후의 코드 실행니다.
위와 같은 컨텍스트 매니저 또는 비동기 컨텍스트 매니저를 만들면, `with` 블록에 들어가기 전에 `yield` 이전의 코드 실행고, `with` 블록을 벗어난 후에는 `yield` 이후의 코드 실행니다.
위의 코드 예제에서는 직접 사용하지 않고, FastAPI에 전달하여 사용하도록 합니다.
위의 코드 예제에서는 직접 사용하지 않고, FastAPI에 전달하여 FastAPI가 이를 사용하도록 합니다.
`FastAPI` 애플리케이션`lifespan` 매개변수는 **비동기 컨텍스트 매니저**를 받기 때문에, 새로운 `lifespan` 비동기 컨텍스트 매니저를 FastAPI에 전달할 수 있습니다.
`FastAPI` `lifespan` 매개변수는 **비동기 컨텍스트 매니저**를 받으므로, 새 `lifespan` 비동기 컨텍스트 매니저를 전달할 수 있습니다.
{* ../../docs_src/events/tutorial003.py hl[22] *}
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
## 대체 이벤트 (사용 중단)
## 대체 이벤트(사용 중단) { #alternative-events-deprecated }
/// warning | 경고
*시작*과 *종료*를 처리하는 권장 방법은 위에서 설명한 대로 `FastAPI` 애플리케이션`lifespan` 매개변수를 사용하는 것입니다. `lifespan` 매개변수를 제공하면 `startup``shutdown` 이벤트 핸들러는 더 이상 호출되지 않습니다. `lifespan`을 사용할지, 모든 이벤트를 사용할지 선택해야 하며 둘 다 사용할 수는 없습니다.
*시작*과 *종료*를 처리하는 권장 방법은 위에서 설명한 대로 `FastAPI` `lifespan` 매개변수를 사용하는 것입니다. `lifespan` 매개변수를 제공하면 `startup``shutdown` 이벤트 핸들러는 더 이상 호출되지 않습니다. `lifespan`만 쓰거나 이벤트만 쓰거나 둘 중 하나이지, 둘 다는 아닙니다.
이 부분은 건너뛰셔도 좋습니다.
이 부분은 아마 건너뛰셔도 니다.
///
*시작*과 *종료* 동안 실행될 이 로직을 정의하는 대체 방법이 있습니다.
애플리케이션이 시작되기 전에 또는 종료될 때 실행야 하는 이벤트 핸들러(함수)를 정의할 수 있습니다.
애플리케이션이 시작되기 전에 또는 애플리케이션이 종료될 때 실행되어야 하는 이벤트 핸들러(함수)를 정의할 수 있습니다.
이 함수들은 `async def` 또는 일반 `def`로 선언할 수 있습니다.
### `startup` 이벤트
### `startup` 이벤트 { #startup-event }
애플리케이션이 시작되기 전에 실행되어야 하는 함수를 추가하려면, `"startup"` 이벤트로 선언합니다:
{* ../../docs_src/events/tutorial001.py hl[8] *}
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
이 경우, `startup` 이벤트 핸들러 함수는 "database"라는 항목(단지 `dict`)을 일부 값으로 초기화합니다.
이 경우, `startup` 이벤트 핸들러 함수는 "database"(그냥 `dict`) 항목을 일부 값으로 초기화합니다.
여러 개의 이벤트 핸들러 함수를 추가할 수 있습니다.
애플리케이션은 모든 `startup` 이벤트 핸들러가 완료될 때까지 요청을 받기 시작하지 않습니다.
그리고 모든 `startup` 이벤트 핸들러가 완료될 때까지 애플리케이션은 요청을 받기 시작하지 않습니다.
### `shutdown` 이벤트
### `shutdown` 이벤트 { #shutdown-event }
애플리케이션이 종료될 때 실행되어야 하는 함수를 추가하려면, `"shutdown"` 이벤트로 선언합니다:
{* ../../docs_src/events/tutorial002.py hl[6] *}
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
여기서, `shutdown` 이벤트 핸들러 함수는 `"Application shutdown"`이라는 텍스트를 `log.txt` 파일에 기록합니다.
여기서 `shutdown` 이벤트 핸들러 함수는 텍스트 한 줄 `"Application shutdown"` `log.txt` 파일에 기록합니다.
/// info | 정보
`open()` 함수에서 `mode="a"`는 "추가"를 의미하므로, 파일에 있는 기존 내용 덮어쓰지 않고 새로운 줄이 추가됩니다.
`open()` 함수에서 `mode="a"`는 "append"(추가)를 의미하므로, 기존 내용 덮어쓰지 않고 파일에 있던 내용 뒤에 줄이 추가됩니다.
///
/// tip | 팁
이 경우, 우리는 표준 파이썬 `open()` 함수를 사용하여 파일과 상호작용하고 있습니다.
이 경우에는 파일과 상호작용하는 표준 파이썬 `open()` 함수를 사용하고 있습니다.
따라서 I/O(입출력) 작업이 포함되어 있어 디스크에 기록되는 것을 "기다리는" 과정이 필요합니다.
따라서 I/O(input/output)가 포함되어 있어 디스크에 기록되는 것을 "기다리는" 과정이 필요합니다.
하지만 `open()``async``await`를 사용하지 않습니다.
그래서 우리는 이벤트 핸들러 함수 `async def` 대신 일반 `def`로 선언합니다.
그래서 이벤트 핸들러 함수 `async def` 대신 표준 `def`로 선언합니다.
///
### `startup`과 `shutdown`을 함께 사용
### `startup`과 `shutdown`을 함께 { #startup-and-shutdown-together }
*시작*과 *종료* 로직 연결 가능성이 높습니다. 예를 들어, 무언가를 시작한 후 끝내거나, 자원을 획득한 후 해제하는 등의 작업할 수 있습니다.
*시작*과 *종료* 로직 연결되어 있을 가능성이 높습니다. 무언가를 시작했다가 끝내거나, 자원을 획득했다가 해제하는 등의 작업이 필요할 수 있습니다.
이러한 작업을 별도의 함수로 처리하면 서로 로직이나 변수를 공유하지 않기 때문에 더 어려워집니다. 값들을 전역 변수에 저장하거나 비슷한 트릭을 사용해야 할 수 있습니다.
로직이나 변수를 함께 공유하지 않는 분리된 함수에서 이를 처리하면, 전역 변수에 값을 저장하거나 비슷한 트릭이 필요해져 더 어렵습니다.
렇기 때문에 위에서 설명한 대로 `lifespan`을 사용하는 것이 권장됩니다.
그 때문에, 이제는 위에서 설명한 대로 `lifespan`을 사용하는 것이 권장됩니다.
## 기술적 세부사항
## 기술적 세부사항 { #technical-details }
호기심 많은 분들을 위한 기술적인 세부사항입니다. 🤓
ASGI 기술 사양에 따르면, 이는 <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Lifespan Protocol</a>의 일부이며, `startup``shutdown`이라는 이벤트를 정의합니다.
내부적으로 ASGI 기술 사양에서는 이것이 <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Lifespan Protocol</a>의 일부이며, `startup``shutdown`이라는 이벤트를 정의합니다.
/// info | 정보
Starlette `lifespan` 핸들러에 대해 더 읽고 싶다면 <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">Starlette의 Lifespan 문서</a>에서 확인할 수 있습니다.
Starlette `lifespan` 핸들러에 대해서는 <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">Starlette의 Lifespan 문서</a>에서 더 읽어볼 수 있습니다.
이 문서에는 코드의 다른 영역에서 사용할 수 있는 lifespan 상태를 처리하는 방법도 포함되어 있습니다.
또한 코드의 다른 영역에서 사용할 수 있는 lifespan 상태를 처리하는 방법도 포함되어 있습니다.
///
## 서브 애플리케이션
## 서브 애플리케이션 { #sub-applications }
🚨 이 lifespan 이벤트(`startup``shutdown`)는 메인 애플리케이션에 대해서만 실행되며, [서브 애플리케이션 - Mounts](sub-applications.md){.internal-link target=_blank}에는 실행되지 않음을 유의하세요.
🚨 이 lifespan 이벤트(startupshutdown)는 메인 애플리케이션에 대해서만 실행되며, [서브 애플리케이션 - Mounts](sub-applications.md){.internal-link target=_blank}에는 실행되지 않음을 유의하세요.

View File

@@ -1,6 +1,6 @@
# 심화 사용자 안내서 - 도입부
# 심화 사용자 안내서 - 도입부 { #advanced-user-guide }
## 추가 기능
## 추가 기능 { #additional-features }
메인 [자습서 - 사용자 안내서](../tutorial/index.md){.internal-link target=_blank}는 여러분이 **FastAPI**의 모든 주요 기능을 둘러보시기에 충분할 것입니다.
@@ -14,14 +14,8 @@
///
## 자습서를 먼저 읽으십시오
## 자습서를 먼저 읽으십시오 { #read-the-tutorial-first }
여러분은 메인 [자습서 - 사용자 안내서](../tutorial/index.md){.internal-link target=_blank}의 지식으로 **FastAPI**의 대부분의 기능을 사용하실 수 있습니다.
이어지는 장들은 여러분이 메인 자습서 - 사용자 안내서를 이미 읽으셨으며 주요 아이디어를 알고 계신다고 가정합니다.
## TestDriven.io 강좌
여러분이 문서의 이 부분을 보완하시기 위해 심화-기초 강좌 수강을 희망하신다면 다음을 참고 하시기를 바랍니다: **TestDriven.io**의 <a href="https://testdriven.io/courses/tdd-fastapi/" class="external-link" target="_blank">FastAPI와 Docker를 사용한 테스트 주도 개발</a>.
그들은 현재 전체 수익의 10퍼센트를 **FastAPI** 개발에 기부하고 있습니다. 🎉 😄

View File

@@ -1,31 +1,31 @@
# 응답 - 상태 코드 변경
# 응답 - 상태 코드 변경 { #response-change-status-code }
기본 [응답 상태 코드 설정](../tutorial/response-status-code.md){.internal-link target=_blank}이 가능하다는 걸 이미 알고 계실 겁니다.
하지만 경우에 따라 기본 설정과 다른 상태 코드를 반환해야 할 때가 있습니다.
## 사용 예
## 사용 예 { #use-case }
예를 들어 기본적으로 HTTP 상태 코드 "OK" `200`을 반환하고 싶다고 가정해 봅시다.
하지만 데이터가 존재하지 않으면 이를 새로 생성하고, HTTP 상태 코드 "CREATED" `201`을 반환하고자 할 때가 있을 수 있습니다.
이때도 여전히 `response_model`을 사용하여 반환하는 데이터를 필터링하고 변환하고 싶을 수 있습니다.
하지만 여전히 `response_model`을 사용하여 반환하는 데이터를 필터링하고 변환할 수 있기를 원합니다.
이런 경우에는 `Response` 파라미터를 사용할 수 있습니다.
## `Response` 파라미터 사용하기
## `Response` 파라미터 사용하기 { #use-a-response-parameter }
*경로 작동 함수*에 `Response` 타입의 파라미터를 선언할 수 있습니다. (쿠키와 헤더에 대해 선언하는 것과 유사하게)
*경로 처리 함수*에 `Response` 타입의 파라미터를 선언할 수 있습니다. (쿠키와 헤더에 대해 선언하는 것과 유사하게)
그리고 이 *임시* 응답 객체에서 `status_code`를 설정할 수 있습니다.
{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
그리고 평소처럼 원하는 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
그리고 평소처럼 필요한 어떤 객체든 반환할 수 있습니다(`dict`, 데이터베이스 모델 등).
`response_model`을 선언했다면 반환된 객체는 여전히 필터링되고 변환됩니다.
**FastAPI**는 이 *임시* 응답 객체에서 상태 코드(쿠키와 헤더 포함)를 추출하여, `response_model`로 필터링된 반환 값을 최종 응답에 넣습니다.
**FastAPI**는 이 *임시* 응답 객체에서 상태 코드(쿠키와 헤더 포함)를 추출하여, `response_model`로 필터링된 반환 값을 포함하는 최종 응답에 넣습니다.
또한, 의존성에서도 `Response` 파라미터를 선언하고 그 안에서 상태 코드를 설정할 수 있습니다. 단, 마지막으로 설정된 상태 코드가 우선 적용된다는 점을 유의하세요.

View File

@@ -1,49 +1,51 @@
# 응답 쿠키
# 응답 쿠키 { #response-cookies }
## `Response` 매개변수 사용하기
## `Response` 매개변수 사용하기 { #use-a-response-parameter }
*경로 작동 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다.
*경로 처리 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다.
그런 다음 해당 *임시* 응답 객체에서 쿠키를 설정할 수 있습니다.
{* ../../docs_src/response_cookies/tutorial002.py hl[1,8:9] *}
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
그런 다음 필요한 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
그런 다음 일반적으로 하듯이 필요한 어떤 객체든 반환할 수 있습니다(`dict`, 데이터베이스 모델 등).
그리고 `response_model`을 선언했다면 반환한 객체를 거르고 변환하는 데 여전히 사용됩니다.
**FastAPI**는 그 *임시* 응답에서 쿠키(또한 헤더 및 상태 코드)를 추출하고, 반환 값이 포함된 최종 응답에 이를 넣습니다. 이 값은 `response_model`로 걸러지게 됩니다.
**FastAPI**는 그 *임시* 응답에서 쿠키(또한 헤더 및 상태 코드)를 추출하고, `response_model`로 필터링된 반환 값이 포함된 최종 응답에 이를 넣습니다.
또한 의존관계에서 `Response` 매개변수를 선언하고, 해당 의존성에서 쿠키(및 헤더)를 설정할 수도 있습니다.
## `Response`를 직접 반환하기
## `Response`를 직접 반환하기 { #return-a-response-directly }
코드에서 `Response`를 직접 반환할 때도 쿠키를 생성할 수 있습니다.
이를 위해 [Response를 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 설명한 대로 응답을 생성할 수 있습니다.
그런 다음 쿠키를 설정하고 반환하면 됩니다:
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
/// tip
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
/// tip | 팁
`Response` 매개변수를 사용하지 않고 응답을 직접 반환하는 경우, FastAPI는 이를 직접 반환한다는 점에 유의하세요.
따라서 데이터가 올바른 유형인지 확인해야 합니다. 예: `JSONResponse`를 반환하는 경우, JSON과 호환되는지 확인하세요.
또한 `response_model`걸러져야 할 데이터달되지 않도록 확인하세요.
또한 `response_model`필터링되어야 했던 데이터송하지 않도록 하세요.
///
### 추가 정보
### 추가 정보 { #more-info }
/// note | 기술 세부사항
/// note | 기술 세부사항
`from starlette.responses import Response` 또는 `from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자의 편의를 위해 `fastapi.responses`로 동일한 `starlette.responses`를 제공합니다. 그러나 대부분의 응답은 Starlette에서 직접 제공됩니다.
**FastAPI**는 개발자의 편의를 위해 `fastapi.responses`로 동일한 `starlette.responses`를 제공합니다. 하지만 사용 가능한 대부분의 응답은 Starlette에서 직접 제공됩니다.
또한 `Response`는 헤더와 쿠키를 설정하는 데 자주 사용되므로, **FastAPI**는 이를 `fastapi.Response`로도 제공합니다.
///
사용 가능한 모든 매개변수와 옵션은 <a href="https://www.starlette.dev/responses/#set-cookie" class="external-link" target="_blank">Starlette 문서</a>에서 확인할 수 있습니다.
사용 가능한 모든 매개변수와 옵션은 <a href="https://www.starlette.dev/responses/#set-cookie" class="external-link" target="_blank">Starlette 문서</a>에서 확인할 수 있습니다.

View File

@@ -1,20 +1,20 @@
# 응답을 직접 반환하기
# 응답을 직접 반환하기 { #return-a-response-directly }
**FastAPI**에서 *경로 작업(path operation)* 생성할 때, 일반적으로 `dict`, `list`, Pydantic 모델, 데이터베이스 모델 등의 데이터를 반환할 수 있습니다.
**FastAPI**에서 *경로 처리(path operation)* 생성할 때, 일반적으로 `dict`, `list`, Pydantic 모델, 데이터베이스 모델 등의 데이터를 반환할 수 있습니다.
기본적으로 **FastAPI**는 [JSON 호환 가능 인코더](../tutorial/encoder.md){.internal-link target=_blank}에 설명된 `jsonable_encoder`를 사용해 해당 반환 값을 자동으로 `JSON`으로 변환합니다.
기본적으로 **FastAPI**는 [JSON 호환 가능 인코더](../tutorial/encoder.md){.internal-link target=_blank}에 설명된 `jsonable_encoder`를 사용해 해당 반환 값을 자동으로 JSON으로 변환합니다.
그런 다음, JSON 호환 데이터(예: `dict`)를 `JSONResponse`에 넣어 사용자의 응답을 전송하는 방식으로 처리됩니다.
그런 다음, 내부적으로는 JSON 호환 데이터(예: `dict`)를 `JSONResponse`에 넣어 클라이언트로 응답을 전송하는 데 사용합니다.
그러나 *경로 작업*에서 `JSONResponse`를 직접 반환할 수도 있습니다.
하지만 *경로 처리*에서 `JSONResponse`를 직접 반환할 수도 있습니다.
예를 들어, 사용자 정의 헤더나 쿠키를 반환해야 하는 경우에 유용할 수 있습니다.
## `Response` 반환하기
## `Response` 반환하기 { #return-a-response }
사실, `Response` 또는 그 하위 클래스를 반환할 수 있습니다.
/// tip
/// tip | 팁
`JSONResponse` 자체도 `Response`의 하위 클래스입니다.
@@ -26,38 +26,40 @@ Pydantic 모델로 데이터 변환을 수행하지 않으며, 내용을 다른
이로 인해 많은 유연성을 얻을 수 있습니다. 어떤 데이터 유형이든 반환할 수 있고, 데이터 선언이나 유효성 검사를 재정의할 수 있습니다.
## `Response`에서 `jsonable_encoder` 사용하기
## `Response`에서 `jsonable_encoder` 사용하기 { #using-the-jsonable-encoder-in-a-response }
**FastAPI**는 반환하는 `Response`에 아무런 변환을 하지 않으므로, 그 내용이 준비되어 있야 합니다.
**FastAPI**는 반환하는 `Response`에 아무런 변경도 하지 않으므로, 그 내용이 준비되어 있는지 확인해야 합니다.
예를 들어, Pydantic 모델을 `dict`로 변환 `JSONResponse`에 넣지 않으면 JSON 호환 유형으로 변환된 데이터 유형(예: `datetime`, `UUID` 등)이 사용되지 않습니다.
예를 들어, Pydantic 모델을 먼저 `dict`로 변환하고 `datetime`, `UUID` 등의 모든 데이터 타입을 JSON 호환 타입으로 변환하지 않으면 Pydantic 모델을 `JSONResponse`에 넣을 수 없습니다.
이러한 경우, 데이터를 응답에 전달하기 전에 `jsonable_encoder`를 사용하여 변환할 수 있습니다:
{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | 기술 세부 사항
/// note | 기술 세부사항
`from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자의 편의를 위해 `starlette.responses``fastapi.responses`로 제공합니다. 그러나 대부분의 가능한 응답은 Starlette에서 직접 제공합니다.
**FastAPI**는 개발자의 편의를 위해 `starlette.responses``fastapi.responses`로 제공합니다. 하지만 대부분의 사용 가능한 응답은 Starlette에서 직접 제공합니다.
///
## 사용자 정의 `Response` 반환하기
위 예제는 필요한 모든 부분을 보여주지만, 아직 유용하지는 않습니다. 사실 데이터를 직접 반환하면 **FastAPI**가 이를 `JSONResponse`에 넣고 `dict`로 변환하는 등 모든 작업을 자동으로 처리합니다.
## 사용자 정의 `Response` 반환하기 { #returning-a-custom-response }
이제, 사용자 정의 응답을 반환하는 방법을 알아보겠습니다.
위 예제는 필요한 모든 부분을 보여주지만, 아직은 그다지 유용하지 않습니다. `item`을 그냥 직접 반환했어도 **FastAPI**가 기본으로 이를 `JSONResponse`에 넣고 `dict`로 변환하는 등의 작업을 모두 수행해 주었을 것이기 때문입니다.
예를 들어 <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a> 응답을 반환하고 싶다고 가정해보겠습니다.
이제, 이를 사용해 사용자 정의 응답을 반환하는 방법을 알아보겠습니다.
예를 들어 <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a> 응답을 반환하고 싶다고 가정해 보겠습니다.
XML 내용을 문자열에 넣고, 이를 `Response`에 넣어 반환할 수 있습니다:
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
## 참고 사항 { #notes }
## 참고 사항
`Response`를 직접 반환할 때, 그 데이터는 자동으로 유효성 검사되거나, 변환(직렬화)되거나, 문서화되지 않습니다.
그러나 [OpenAPI에서 추가 응답](additional-responses.md){.internal-link target=_blank}에서 설명된 대로 문서화할 수 있습니다.
이후 단락에서 자동 데이터 변환, 문서화 등을 사용하면서 사용자 정의 `Response`를 선언하는 방법을 확인할 수 있습니다.
이후 섹션에서 자동 데이터 변환, 문서화 등을 계속 사용하면서 이러한 사용자 정의 `Response`사용하는/선언하는 방법을 확인할 수 있습니다.

View File

@@ -1,12 +1,12 @@
# 응답 헤더
# 응답 헤더 { #response-headers }
## `Response` 매개변수 사용하기
## `Response` 매개변수 사용하기 { #use-a-response-parameter }
여러분은 *경로 작동 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다 (쿠키와 같이 사용할 수 있습니다).
여러분은 *경로 처리 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다 (쿠키와 같이 사용할 수 있습니다).
그런 다음, 여러분은 해당 *임시* 응답 객체에서 헤더를 설정할 수 있습니다.
{* ../../docs_src/response_headers/tutorial002.py hl[1,7:8] *}
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
그 후, 일반적으로 사용하듯이 필요한 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
@@ -16,26 +16,26 @@
또한, 종속성에서 `Response` 매개변수를 선언하고 그 안에서 헤더(및 쿠키)를 설정할 수 있습니다.
## `Response` 직접 반환하기
## `Response` 직접 반환하기 { #return-a-response-directly }
`Response`를 직접 반환할 때에도 헤더를 추가할 수 있습니다.
[응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 설명한 대로 응답을 생성하고, 헤더를 추가 매개변수로 전달하세요.
{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
/// note | 기술 세부사항
/// note | 기술 세부사항
`from starlette.responses import Response``from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 `starlette.responses``fastapi.responses`로 개발자의 편의를 위해 직접 제공하지만, 대부분의 응답은 Starlette에서 직접 제공됩니다.
**FastAPI**는 해당 *임시* 응답에서 헤더(쿠키와 상태 코드도 포함)를 추출하여, 여러분이 반환한 값을 포함하는 최종 응답에 `response_model`로 필터링된 값을 넣습니다.
그리고 `Response`는 헤더와 쿠키를 설정하는 데 자주 사용될 수 있으므로, **FastAPI**는 `fastapi.Response`로도 이를 제공합니다.
///
## 커스텀 헤더
## 커스텀 헤더 { #custom-headers }
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">X- 접두어를 사용하여</a> 커스텀 사설 헤더를 추가할 수 있습니다.
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">`X-` 접두어를 사용하여</a> 커스텀 사설 헤더를 추가할 수 있다는 점을 기억하세요.
하지만, 여러분이 브라우저에서 클라이언트가 볼 수 있기를 원하는 커스텀 헤더가 있는 경우, CORS 설정에 이를 추가해야 합니다([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}에서 자세히 알아보세요). `expose_headers` 매개변수를 사용하여 <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette의 CORS 설명서</a>에 문서화된 대로 설정할 수 있습니다.
하지만, 여러분이 브라우저에서 클라이언트가 볼 수 있기를 원하는 커스텀 헤더가 있는 경우, CORS 설정에 이를 추가해야 합니다([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}에서 자세히 알아보세요). <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette의 CORS 서</a>에 문서화된 `expose_headers` 매개변수를 사용하세요.

View File

@@ -1,67 +1,67 @@
# 하위 응용프로그램 - 마운트
# 하위 응용프로그램 - 마운트 { #sub-applications-mounts }
만약 각각의 독립적인 OpenAPI와 문서 UI를 갖는 두 개의 독립적인 FastAPI 응용프로그램이 필요하다면, 메인 어플리케이션에 하나 (또는 그 이상의) 하위-응용프로그램(들)마운트"해서 사용할 수 있습니다.
각각의 독립적인 OpenAPI와 문서 UI를 갖는 두 개의 독립적인 FastAPI 애플리케이션이 필요하다면, 메인 앱을 두고 하나(또는 그 이상)의 하위 응용프로그램을 "마운트"할 수 있습니다.
## **FastAPI** 응용프로그램 마운트
## **FastAPI** 애플리케이션 마운트 { #mounting-a-fastapi-application }
마운트"란 완전히 독립적인" 응용프로그램을 특정 경로에 추가하여 해당 하위 응용프로그램에 선언된 *경로 동작*을 통해 해당 경로 아래에 있는 모든 작업들을 처리할 수 있도록 하는 것을 의미합니다.
"마운트"란 완전히 "독립적인" 애플리케이션을 특정 경로에 추가하고, 그 하위 응용프로그램에 선언된 _경로 처리_로 해당 경로 아래 모든 을 처리도록 하는 것을 의미합니다.
### 최상단 응용프로그램
### 최상위 애플리케이션 { #top-level-application }
먼저, 메인, 최상단의 **FastAPI** 응용프로그램과 이것의 *경로 동작*을 생성합니다:
먼저, 메인 최상 **FastAPI** 애플리케이션과 그 *경로 처리*를 생성합니다:
{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
### 하위 응용프로그램
### 하위 응용프로그램 { #sub-application }
다음으로, 하위 응용프로그램과 이것의 *경로 동작*을 생성합니다:
다음, 하위 응용프로그램과 *경로 처리*를 생성합니다.
이 하위 응용프로그램은 또 다른 표준 FastAPI 응용프로그램입니다. 다만 이것은 “마운트입니다:
이 하위 응용프로그램은 또 다른 표준 FastAPI 애플리케이션이지만, "마운트"애플리케이션입니다:
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
### 하위 응용프로그램 마운트
### 하위 응용프로그램 마운트 { #mount-the-sub-application }
최상단 응용프로그램, `app`에 하위 응용프로그램, `subapi`를 마운트합니다.
최상위 애플리케이션 `app` 하위 응용프로그램 `subapi`를 마운트합니다.
예시에서, 하위 응용프로그램션은 `/subapi` 경로에 마운트 될 것입니다:
경우 `/subapi` 경로에 마운트니다:
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
### 자동으로 생성된 API 문서 확인
### 자동 API 문서 확인 { #check-the-automatic-api-docs }
이제, `uvicorn`으로 메인 응용프로그램을 실행하십시오. 당신의 파일이 `main.py`라면, 이렇게 실행합니다:
이제 파일과 함께 `fastapi` 명령을 실행하세요:
<div class="termy">
```console
$ uvicorn main:app --reload
$ 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>
그리고 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>에서 문서를 여십시오.
그리고 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>에서 문서를 여세요.
메인 응용프로그램의 *경로 동작*만을 포함하는, 메인 응용프로그램에 대한 자동 API 문서를 확인할 수 있습니다:
메인 앱의 자동 API 문서를 보게 될 것이며, 메인 앱 자체의 _경로 처리_만 포함됩니다:
<img src="https://fastapi.tiangolo.com//img/tutorial/sub-applications/image01.png">
<img src="/img/tutorial/sub-applications/image01.png">
다음으로, <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>에서 하위 응용프로그램의 문서를 여십시오.
다음, <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>에서 하위 응용프로그램의 문서를 여세요.
하위 경로 접두사 `/subapi` 아래에 선언된 *경로 동작* 을 포함하는, 하위 응용프로그램에 대한 자동 API 문서를 확인할 수 있습니다:
하위 응용프로그램의 자동 API 문서를 보게 될 것이며, 하위 경로 접두사 `/subapi` 아래에 올바르게 포함된 하위 응용프로그램 자체의 _경로 처리_만 포함됩니다:
<img src="https://fastapi.tiangolo.com//img/tutorial/sub-applications/image02.png">
<img src="/img/tutorial/sub-applications/image02.png">
두 사용자 인터페이스 중 어느 하나를 사용해야하는 경우, 브라우저 특정 응용프로그램 또는 하위 응용프로그램과 각각 통신할 수 있기 때문에 올바르게 동작할 것입니다.
두 사용자 인터페이스 중 어느 것과 상호작용을 시도하더라도 올바르게 동작할 것입니다. 브라우저가 각 특정 또는 하위 앱과 통신할 수 있기 때문입니다.
### 기술적 세부사항: `root_path`
### 기술적 세부사항: `root_path` { #technical-details-root-path }
위에 설명된 것과 같이 하위 응용프로그램을 마운트하는 경우, FastAPI는 `root_path`라고 하는 ASGI 명세의 매커니즘을 사용하여 하위 응용프로그램에 대한 마운트 경로 통신을 처리합니다.
위에 설명한 대로 하위 응용프로그램을 마운트하, FastAPI는 ASGI 명세의 메커니즘인 `root_path` 사용 하위 응용프로그램에 대한 마운트 경로를 전달하는 작업을 처리합니다.
를 통해, 하위 응용프로그램은 문서 UI를 위해 경로 접두사를 사용해야 한다는 사실을 인지합니다.
렇게 하면 하위 응용프로그램은 문서 UI를 위해 해당 경로 접두사를 사용해야 한다는 것을 알게 됩니다.
하위 응용프로그램역시 다른 하위 응용프로그램을 마운트하는 것이 가능하며 FastAPI가 모든 `root_path` 들을 자동으로 처리하기 때문에 모든 것 올바르게 동작할 것입니다.
또한 하위 응용프로그램도 자체적으로 하위 앱을 마운트할 수 있으며, FastAPI가 모든 `root_path` 자동으로 처리하기 때문에 모든 것 올바르게 동작니다.
`root_path`와 이것을 사용하는 방법에 대해서는 [프록시의 뒷단](./behind-a-proxy.md){.internal-link target=_blank} 섹션에서 배울 수 있습니다.
`root_path`와 이를 명시적으로 사용하는 방법에 대해서는 [프록시](behind-a-proxy.md){.internal-link target=_blank} 섹션에서 더 알아볼 수 있습니다.

View File

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

View File

@@ -1,14 +1,14 @@
# 테스트 의존성 오버라이드
# 오버라이드로 의존성 테스트하기 { #testing-dependencies-with-overrides }
## 테스트 중 의존성 오버라이드하기
## 테스트 중 의존성 오버라이드하기 { #overriding-dependencies-during-testing }
테스트를 진행하다 보면 의존성을 오버라이드해야 하는 경우가 있습니다.
테스트를 진행하다 보면 테스트 중에 의존성을 오버라이드해야 하는 경우가 있습니다.
원래 의존성을 실행하고 싶지 않을 수도 있습니다(또는 그 의존성이 가지고 있는 하위 의존성까지도 실행되지 않길 원할 수 있습니다).
대신, 테스트 동안(특정 테스트에서만) 사용될 다른 의존성을 제공하고, 원래 의존성이 사용되던 곳에서 사용할 수 있는 값을 제공하기를 원할 수 있습니다.
### 사용 사례: 외부 서비스
### 사용 사례: 외부 서비스 { #use-cases-external-service }
예를 들어, 외부 인증 제공자를 호출해야 하는 경우를 생각해봅시다.
@@ -18,11 +18,11 @@
외부 제공자를 한 번만 테스트하고 싶을 수도 있지만 테스트를 실행할 때마다 반드시 호출할 필요는 없습니다.
이 경우 해당 공급자를 호출하는 종속성을 오버라이드하고 테스트에 대해서만 모의 사용자를 반환하는 사용자 지정 종속성을 사용할 수 있습니다.
이 경우 해당 공급자를 호출하는 의존성을 오버라이드하고 테스트에 대해서만 모의 사용자를 반환하는 사용자 지정 의존성을 사용할 수 있습니다.
### `app.dependency_overrides` 속성 사용하기
### `app.dependency_overrides` 속성 사용하기 { #use-the-app-dependency-overrides-attribute }
이런 경우를 위해 **FastAPI** 응용 프로그램에는 `app.dependency_overrides`라는 속성이 있습니다. 이는 간단한 `dict`입니다.
이런 경우를 위해 **FastAPI** 애플리케이션에는 `app.dependency_overrides`라는 속성이 있습니다. 이는 간단한 `dict`입니다.
테스트를 위해 의존성을 오버라이드하려면, 원래 의존성(함수)을 키로 설정하고 오버라이드할 의존성(다른 함수)을 값으로 설정합니다.
@@ -34,7 +34,7 @@
**FastAPI** 애플리케이션 어디에서든 사용된 의존성에 대해 오버라이드를 설정할 수 있습니다.
원래 의존성은 *경로 동작 함수*, *경로 동작 데코레이터*(반환값을 사용하지 않는 경우), `.include_router()` 호출 등에서 사용될 수 있습니다.
원래 의존성은 *경로 처리 함수*, *경로 처리 데코레이터*(반환값을 사용하지 않는 경우), `.include_router()` 호출 등에서 사용될 수 있습니다.
FastAPI는 여전히 이를 오버라이드할 수 있습니다.
@@ -42,7 +42,7 @@ FastAPI는 여전히 이를 오버라이드할 수 있습니다.
그런 다음, `app.dependency_overrides`를 빈 `dict`로 설정하여 오버라이드를 재설정(제거)할 수 있습니다:
```python
```Python
app.dependency_overrides = {}
```

View File

@@ -1,5 +1,12 @@
# 이벤트 테스트: 시작 - 종료
# 이벤트 테스트: 라이프스팬 및 시작 - 종료 { #testing-events-lifespan-and-startup-shutdown }
테스트에서 이벤트 핸들러(`startup``shutdown`)를 실행해야 하는 경우, `with` 문과 함께 `TestClient`를 사용할 수 있습니다.
테스트에서 `lifespan` 실행해야 하는 경우, `with` 문과 함께 `TestClient`를 사용할 수 있습니다:
{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
["공식 Starlette 문서 사이트에서 테스트에서 라이프스팬 실행하기."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)에 대한 자세한 내용을 더 읽을 수 있습니다.
더 이상 권장되지 않는 `startup``shutdown` 이벤트의 경우, 다음과 같이 `TestClient`를 사용할 수 있습니다:
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}

View File

@@ -1,13 +1,13 @@
# WebSocket 테스트하기
# WebSocket 테스트하기 { #testing-websockets }
`TestClient`를 사용하여 WebSocket을 테스트할 수 있습니다.
같은 `TestClient`를 사용하여 WebSocket을 테스트할 수 있습니다.
이를 위해 `with` 문에서 `TestClient`를 사용하여 WebSocket에 연결합니다:
{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
/// note | 참고
자세한 내용은 Starlette의 <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank"> WebSocket 테스트</a>에 관한 설명서를 참고하시길 바랍니다.
자세한 내용은 Starlette의 <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank">testing WebSockets</a> 문서를 확인하세요.
///

View File

@@ -1,10 +1,10 @@
# `Request` 직접 사용하기
# `Request` 직접 사용하기 { #using-the-request-directly }
지금까지 요청에서 필요한 부분을 각 타입으로 선언하여 사용해 왔습니다.
다음과 같은 곳에서 데이터를 가져왔습니다:
* 경로의 파라미터로부터.
* 경로를 매개변수로.
* 헤더.
* 쿠키.
* 기타 등등.
@@ -13,29 +13,29 @@
하지만 `Request` 객체에 직접 접근해야 하는 상황이 있을 수 있습니다.
## `Request` 객체에 대한 세부 사항
## `Request` 객체에 대한 세부 사항 { #details-about-the-request-object }
**FastAPI**는 실제로 내부에 **Starlette**을 사용하며, 그 위에 여러 도구를 덧붙인 구조입니다. 따라서 여러분이 필요할 때 Starlette의 <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">`Request`</a> 객체를 직접 사용할 수 있습니다.
`Request` 객체에서 데이터를 직접 가져오는 경우(예: 본문을 읽기)에는 FastAPI가 해당 데이터를 검증하거나 변환하지 않으며, 문서화(OpenAPI를 통한 문서 자동화(로 생성된) API 사용자 인터페이스)도 되지 않니다.
또한 이는 `Request` 객체에서 데이터를 직접 가져오는 경우(예: 본문을 읽기) FastAPI가 해당 데이터를 검증하거나 변환하지 않으며, 문서화(OpenAPI를 통한 자동 API 사용자 인터페이스)도 되지 않는다는 의미이기도 합니다.
그러나 다른 매개변수(예: Pydantic 모델을 사용한 본문)는 여전히 검증, 변환, 주석 추가 등이 이루어집니다.
하지만 특정한 경우에는 `Request` 객체에 직접 접근하는 것이 유용할 수 있습니다.
하지만 특정한 경우에는 `Request` 객체를 가져오는 것이 유용할 수 있습니다.
## `Request` 객체를 직접 사용하기
## `Request` 객체를 직접 사용하기 { #use-the-request-object-directly }
여러분이 클라이언트의 IP 주소/호스트 정보를 *경로 작동 함수* 내부에서 가져와야 한다고 가정해 보겠습니다.
여러분이 클라이언트의 IP 주소/호스트 정보를 *경로 처리 함수* 내부에서 가져와야 한다고 가정해 보겠습니다.
이를 위해서는 요청에 직접 접근해야 합니다.
{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
*경로 작동 함수* 매개변수를 `Request` 타입으로 선언하면 **FastAPI**가 해당 매개변수에 `Request` 객체를 전달하는 것을 알게 됩니다.
*경로 처리 함수* 매개변수를 `Request` 타입으로 선언하면 **FastAPI**가 해당 매개변수에 `Request`를 전달하는 것을 알게 됩니다.
/// tip | 팁
이 경우, 요청 매개변수와 함께 경로 매개변수를 선언한 것을 볼 수 있습니다.
이 경우, 요청 매개변수 옆에 경로 매개변수를 선언하고 있다는 점을 참고하세요.
따라서, 경로 매개변수는 추출되고 검증되며 지정된 타입으로 변환되고 OpenAPI로 주석이 추가됩니다.
@@ -43,14 +43,14 @@
///
## `Request` 설명서
## `Request` 설명서 { #request-documentation }
여러분은 `Request` 객체에 대한 더 자세한 내용을 <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">공식 Starlette 설명서 사이트</a>에서 읽어볼 수 있습니다.
여러분은 <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">공식 Starlette 설명서 사이트`Request` 객체</a>에 대한 더 자세한 내용을 읽어볼 수 있습니다.
/// note | 기술 세부사항
`from starlette.requests import Request`를 사용할 수도 있습니다.
**FastAPI**는 여러분(개발자)를 위한 편의를 위해 이를 직접 제공하지만, 실제로는 Starlette에서 가져온 것입니다.
**FastAPI**는 여러분(개발자)를 위한 편의를 위해 이를 직접 제공하지만, Starlette에서 직접 가져온 것입니다.
///

View File

@@ -1,10 +1,10 @@
# WebSockets
# WebSockets { #websockets }
여러분은 **FastAPI**에서 <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSockets</a>를 사용할 수 있습니다.
## `WebSockets` 설치
## `websockets` 설치 { #install-websockets }
[가상 환경](../virtual-environments.md){.internal-link target=_blank)를 생성하고 활성화한 다음, `websockets`를 설치하세요:
[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 생성하고 활성화한 다음, `websockets`("WebSocket" 프로토콜을 쉽게 사용할 수 있게 해주는 Python 라이브러리)를 설치하세요:
<div class="termy">
@@ -16,13 +16,13 @@ $ pip install websockets
</div>
## WebSockets 클라이언트
## WebSockets 클라이언트 { #websockets-client }
### 프로덕션 환경에서
### 프로덕션 환경에서 { #in-production }
여러분의 프로덕션 시스템에서는 React, Vue.js 또는 Angular와 같은 최신 프레임워크로 생성된 프런트엔드를 사용하고 있을 가능성이 높습니다.
백엔드와 WebSockets을 사용해 통신하려면 아마도 프런트엔드의 유틸리티를 사용할 것입니다.
그리고 백엔드와 WebSockets을 사용해 통신하려면 아마도 프런트엔드의 유틸리티를 사용할 것입니다.
또는 네이티브 코드로 WebSocket 백엔드와 직접 통신하는 네이티브 모바일 응용 프로그램을 가질 수도 있습니다.
@@ -30,23 +30,23 @@ $ pip install websockets
---
하지만 이번 예제에서는 일부 자바스크립트를 포함한 간단한 HTML 문서를 사용하겠습니다. 모든 것을 긴 문자열 안에 넣습니다.
하지만 이번 예제에서는 일부 자바스크립트를 포함한 매우 간단한 HTML 문서를 사용하겠습니다. 모든 것을 긴 문자열 안에 넣습니다.
물론, 이는 최적의 방법이 아니며 프로덕션 환경에서는 사용하지 않을 것입니다.
프로덕션 환경에서는 위에서 설명한 옵션 중 하나를 사용하는 것이 좋습니다.
프로덕션 환경에서는 위에서 설명한 옵션 중 하나를 사용할 것입니다.
그러나 이는 WebSockets의 서버 측에 집중하고 동작하는 예제를 제공하는 가장 간단한 방법입니다:
{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
## `websocket` 생성하기
## `websocket` 생성하기 { #create-a-websocket }
**FastAPI** 응용 프로그램에서 `websocket`을 생성합니다:
{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
/// note | 기술 세부사항
/// note | 기술 세부사항
`from starlette.websockets import WebSocket`을 사용할 수도 있습니다.
@@ -54,17 +54,17 @@ $ pip install websockets
///
## 메시지를 대기하고 전송하기
## 메시지를 대기하고 전송하기 { #await-for-messages-and-send-messages }
WebSocket 경로에서 메시지를 대기(`await`)하고 전송할 수 있습니다.
{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
여러분은 이진 데이터, 텍스트, JSON 데이터를 받을 수 있고 전송할 수 있습니다.
## 시도해보기
## 시도해보기 { #try-it }
파일 이름이 `main.py`라고 가정하고 응용 프로그램을 실행합니다:
파일 이름이 `main.py`라고 가정하고 다음으로 응용 프로그램을 실행합니다:
<div class="termy">
@@ -76,7 +76,7 @@ $ fastapi dev main.py
</div>
브라우저에서 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>을 열어보세요.
브라우저에서 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>을 세요.
간단한 페이지가 나타날 것입니다:
@@ -86,7 +86,7 @@ $ fastapi dev main.py
<img src="/img/tutorial/websockets/image02.png">
**FastAPI** WebSocket 응용 프로그램이 응답을 돌려줄 것입니다:
그리고 WebSockets가 포함된 **FastAPI** 응용 프로그램이 응답을 돌려줄 것입니다:
<img src="/img/tutorial/websockets/image03.png">
@@ -94,9 +94,9 @@ $ fastapi dev main.py
<img src="/img/tutorial/websockets/image04.png">
모든 메시지는 동일한 WebSocket 연결을 사용합니다.
그리고 모든 메시지는 동일한 WebSocket 연결을 사용합니다.
## `Depends` 및 기타 사용하기
## `Depends` 및 기타 사용하기 { #using-depends-and-others }
WebSocket 엔드포인트에서 `fastapi`에서 다음을 가져와 사용할 수 있습니다:
@@ -107,21 +107,21 @@ WebSocket 엔드포인트에서 `fastapi`에서 다음을 가져와 사용할
* `Path`
* `Query`
이들은 다른 FastAPI 엔드포인트/*경로 작동*과 동일하게 동작합니다:
이들은 다른 FastAPI 엔드포인트/*경로 처리*와 동일하게 동작합니다:
{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *}
/// info | 정보
WebSocket에서는 `HTTPException`을 발생시키는 것하지 않습니다. 대신 `WebSocketException`을 발생시킵니다.
WebSocket이기 때문`HTTPException`을 발생시키는 것하지 않습니다. 대신 `WebSocketException`을 발생시킵니다.
명세서에 정의된 <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">유효한 코드</a>를 사용하여 종료 코드를 설정할 수 있습니다.
///
### 종속성을 가진 WebSockets 테스트
### 종속성을 가진 WebSockets 시도해보기 { #try-the-websockets-with-dependencies }
파일 이름이 `main.py`라고 가정하고 응용 프로그램을 실행합니다:
파일 이름이 `main.py`라고 가정하고 다음으로 응용 프로그램을 실행합니다:
<div class="termy">
@@ -133,9 +133,9 @@ $ fastapi dev main.py
</div>
브라우저에서 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>을 열어보세요.
브라우저에서 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>을 세요.
다음과 같은 값을 설정할 수 있습니다:
여기에서 다음을 설정할 수 있습니다:
* 경로에 사용된 "Item ID".
* 쿼리 매개변수로 사용된 "Token".
@@ -146,13 +146,13 @@ $ fastapi dev main.py
///
WebSocket에 연결하고 메시지를 전송 및 수신할 수 있습니다:
렇게 하면 WebSocket에 연결하고 메시지를 전송 및 수신할 수 있습니다:
<img src="/img/tutorial/websockets/image05.png">
## 연결 해제 및 다중 클라이언트 처리
## 연결 해제 및 다중 클라이언트 처리 { #handling-disconnections-and-multiple-clients }
WebSocket 연결이 닫히면, `await websocket.receive_text()``WebSocketDisconnect` 예외를 발생시킵니다. 이를 잡아 처리할 수 있습니다:
WebSocket 연결이 닫히면, `await websocket.receive_text()``WebSocketDisconnect` 예외를 발생시킵니다. 그러면 이 예제처럼 이를 잡아 처리할 수 있습니다.
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
@@ -160,7 +160,7 @@ WebSocket 연결이 닫히면, `await websocket.receive_text()`가 `WebSocketDis
* 여러 브라우저 탭에서 앱을 엽니다.
* 각 탭에서 메시지를 작성합니다.
* 한 탭을 닫아보세요.
* 그런 다음 탭 중 하나를 닫아보세요.
`WebSocketDisconnect` 예외가 발생하며, 다른 모든 클라이언트가 다음과 같은 메시지를 수신합니다:
@@ -170,17 +170,17 @@ Client #1596980209979 left the chat
/// tip | 팁
응용 프로그램은 여러 WebSocket 연결에 메시지를 브로드캐스트하는 방법을 보여주는 간단한 예제입니다.
은 여러 WebSocket 연결에 메시지를 처리하고 브로드캐스트하는 방법을 보여주는 최소한의 간단한 예제입니다.
그러나 모든 것을 메모리의 단일 리스트로 처리하므로, 프로세스가 실행 중인 동안만 동작하며 단일 프로세스에서만 작동합니다.
하지만 모든 것을 메모리의 단일 리스트로 처리하므로, 프로세스가 실행 중인 동안만 동작하며 단일 프로세스에서만 작동한다는 점을 기억하세요.
FastAPI와 쉽게 통합할 수 있으면서 더 견고하고 Redis, PostgreSQL 등을 지원하는 도구를 찾고 있다면, <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a>를 확인하세요.
FastAPI와 쉽게 통합할 수 있으면서 더 견고하고 Redis, PostgreSQL 등을 지원하는 도구가 필요하다면, <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a>를 확인하세요.
///
## 추가 정보
## 추가 정보 { #more-info }
다음 옵션에 대한 자세한 내용을 보려면 Starlette의 문서를 확인하세요:
다음 옵션에 대해 더 알아보려면 Starlette의 문서를 확인하세요:
* <a href="https://www.starlette.dev/websockets/" class="external-link" target="_blank">`WebSocket` 클래스</a>.
* <a href="https://www.starlette.dev/endpoints/#websocketendpoint" class="external-link" target="_blank">클래스 기반 WebSocket 처리</a>.

View File

@@ -1,10 +1,10 @@
# WSGI 포함하기 - Flask, Django 그 외
# WSGI 포함하기 - Flask, Django 그 외 { #including-wsgi-flask-django-others }
[서브 응용 프로그램 - 마운트](sub-applications.md){.internal-link target=_blank}, [프록시 뒤편에서](behind-a-proxy.md){.internal-link target=_blank}에서 보았듯이 WSGI 응용 프로그램들을 다음과 같이 마운트 할 수 있습니다.
[서브 응용 프로그램 - 마운트](sub-applications.md){.internal-link target=_blank}, [프록시 뒤편에서](behind-a-proxy.md){.internal-link target=_blank}에서 보았듯이 WSGI 응용 프로그램들을 마운트 할 수 있습니다.
`WSGIMiddleware`를 사용하여 WSGI 응용 프로그램(예: Flask, Django 등)을 감쌀 수 있습니다.
이를 위해 `WSGIMiddleware`를 사용 WSGI 응용 프로그램(예: Flask, Django 등)을 감쌀 수 있습니다.
## `WSGIMiddleware` 사용하기
## `WSGIMiddleware` 사용하기 { #using-wsgimiddleware }
`WSGIMiddleware`를 불러와야 합니다.
@@ -12,9 +12,9 @@
그 후, 해당 경로에 마운트합니다.
{* ../../docs_src/wsgi/tutorial001.py hl[2:3,23] *}
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
## 확인하기
## 확인하기 { #check-it }
이제 `/v1/` 경로에 있는 모든 요청은 Flask 응용 프로그램에서 처리됩니다.
@@ -26,7 +26,7 @@
Hello, World from Flask!
```
그리고 다음으로 이동하면 <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a> Flask의 응답을 볼 수 있습니다:
그리고 다음으로 이동하면 <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a> **FastAPI**의 응답을 볼 수 있습니다:
```JSON
{

View File

@@ -1,10 +1,10 @@
# 벤치마크
# 벤치마크 { #benchmarks }
독립적인 TechEmpower 벤치마크에 따르면 **FastAPI** 애플리케이션이 Uvicorn을 사용하여 <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">가장 빠른 Python 프레임워크 중 하나</a>로 실행되며, Starlette와 Uvicorn 자체(내부적으로 FastAPI가 사용하는 도구)보다 조금 아래에 위치합니다.
독립적인 TechEmpower 벤치마크에 따르면 **FastAPI** 애플리케이션이 Uvicorn을 사용하여 <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">사용 가능한 가장 빠른 Python 프레임워크 중 하나</a>로 실행되며, Starlette와 Uvicorn 자체(내부적으로 FastAPI가 사용하는 도구)보다 조금 아래에 위치합니다.
그러나 벤치마크와 비교를 확인할 때 다음 사항을 염두에 두어야 합니다.
## 벤치마크와 속도
## 벤치마크와 속도 { #benchmarks-and-speed }
벤치마크를 확인할 때, 일반적으로 여러 가지 유형의 도구가 동등한 것으로 비교되는 것을 볼 수 있습니다.
@@ -16,7 +16,7 @@
* **Uvicorn**: ASGI 서버
* **Starlette**: (Uvicorn 사용) 웹 마이크로 프레임워크
* **FastAPI**: (Starlette 사용) API 구축 위한 데이터 검증 등 여러 추가 기능이 포함된 API 마이크로 프레임워크
* **FastAPI**: (Starlette 사용) 데이터 검증 등 API 구축하기 위한 여러 추가 기능이 포함된 API 마이크로 프레임워크
* **Uvicorn**:
* 서버 자체 외에는 많은 추가 코드가 없기 때문에 최고의 성능을 발휘합니다.
@@ -29,6 +29,6 @@
* **FastAPI**:
* Starlette가 Uvicorn을 사용하므로 Uvicorn보다 빨라질 수 없는 것과 마찬가지로, **FastAPI**는 Starlette를 사용하므로 더 빠를 수 없습니다.
* FastAPI는 Starlette에 추가적으로 더 많은 기능을 제공합니다. API를 구축할 때 거의 항상 필요한 데이터 검증 및 직렬화와 같은 기능들이 포함되어 있습니다. 그리고 이를 사용하면 문서 자동화 기능도 제공됩니다(문서 자동화는 응용 프로그램 실행 시 오버헤드를 추가하지 않고 시작 시 생성됩니다).
* FastAPI를 사용하지 않고 직접 Starlette(또는 Sanic, Flask, Responder 등)를 사용했다면 데이터 검증 및 직렬화를 직접 구현해야 합니다. 따라서 최종 응용 프로그램은 FastAPI를 사용한 것과 동일한 오버헤드를 가지게 될 것입니다. 많은 경우 데이터 검증 및 직렬화가 응용 프로그램에서 작성된 코드 중 가장 많은 부분을 차지합니다.
* 따라서 FastAPI를 사용함으로써 개발 시간, 버그, 코드 라인을 줄일 수 있으며, FastAPI를 사용하지 않았을 때와 동일하거나 더 나은 성능을 얻을 수 있니다(코드에서 모두 구현해야 하기 때문에).
* FastAPI를 비교할 때는 Flask-apispec, NestJS, Molten 등 데이터 검증, 직렬화 및 문서화가 통합된 자동 데이터 검증, 직렬화 및 문서화를 제공하는 웹 응용 프로그램 프레임워크(또는 도구 집합)와 비교하세요.
* FastAPI를 사용하지 않고 직접 Starlette(또는 다른 도구, 예: Sanic, Flask, Responder 등)를 사용했다면 데이터 검증 및 직렬화를 직접 구현해야 합니다. 따라서 최종 응용 프로그램은 FastAPI를 사용한 것과 동일한 오버헤드를 가지게 될 것입니다. 많은 경우 데이터 검증 및 직렬화가 응용 프로그램에서 작성된 코드 중 가장 많은 부분을 차지합니다.
* 따라서 FastAPI를 사용함으로써 개발 시간, 버그, 코드 라인을 줄일 수 있으며, FastAPI를 사용하지 않았을 때와 동일한 성능(또는 더 나은 성능)을 얻을 수 있을 것입니다(코드에서 모두 구현해야 하기 때문에).
* FastAPI를 비교할 때는 Flask-apispec, NestJS, Molten 등 데이터 검증, 직렬화 및 문서화를 제공하는 웹 애플리케이션 프레임워크(또는 도구 집합)와 비교하세요. 통합된 자동 데이터 검증, 직렬화 및 문서화를 제공하는 프레임워크입니다.

View File

@@ -1,13 +1,24 @@
# FastAPI를 클라우드 제공업체에서 배포하기
# 클라우드 제공업체에서 FastAPI 배포하기 { #deploy-fastapi-on-cloud-providers }
사실상 거의 **모든 클라우드 제공업체**를 사용하여 여러분의 FastAPI 애플리케이션을 배포할 수 있습니다.
대부분의 경우, 주요 클라우드 제공업체에서는 FastAPI를 배포할 수 있도록 가이드를 제공합니다.
## 클라우드 제공업체 - 후원자들
## FastAPI Cloud { #fastapi-cloud }
몇몇 클라우드 제공업체들은 [**FastAPI를 후원하며**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, 이를 통해 FastAPI와 FastAPI **생태계**가 지속적이고 건전한 **발전**을 할 수 있습니다.
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>**는 **FastAPI**를 만든 동일한 작성자와 팀이 구축했습니다.
이는 FastAPI **커뮤니티** (여러분)에 대한 진정한 헌신을 보여줍니다. 그들은 여러분에게 **좋은 서비스**를 제공할 뿐 만이 아니라 여러분이 **훌륭하고 건강한 프레임워크인** FastAPI 를 사용하길 원하기 때문입니다. 🙇
최소한의 노력으로 API **구축**, **배포**, **접근**하는 과정을 간소화합니다.
아래와 같은 서비스를 사용해보고 각 서비스의 가이드를 따를 수도 있습니다.
FastAPI로 앱을 빌드할 때의 동일한 **개발자 경험**을 클라우드에 **배포**하는 데에도 제공합니다. 🎉
FastAPI Cloud는 *FastAPI and friends* 오픈 소스 프로젝트의 주요 후원자이자 자금 제공자입니다. ✨
## 클라우드 제공업체 - 후원자들 { #cloud-providers-sponsors }
다른 몇몇 클라우드 제공업체들도 ✨ [**FastAPI를 후원합니다**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨. 🙇
가이드를 따라 하고 서비스를 사용해보기 위해 이들도 고려해볼 수 있습니다:
* <a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" class="external-link" target="_blank">Render</a>
* <a href="https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi" class="external-link" target="_blank">Railway</a>

View File

@@ -1,17 +1,17 @@
# 컨테이너의 FastAPI - 도커
# 컨테이너의 FastAPI - 도커 { #fastapi-in-containers-docker }
FastAPI 플리케이션을 배포할 때 일반적인 접근 방법은 **리눅스 컨테이너 이미지**를 생성하는 것입니다. 이 방법은 주로 <a href="https://www.docker.com/" class="external-link" target="_blank">**도커**</a>를 사용해 이루어집니다. 그런 다음 해당 컨테이너 이미지를 몇가지 방법으로 배포할 수 있습니다.
FastAPI 플리케이션을 배포할 때 일반적인 접근 방법은 **리눅스 컨테이너 이미지**를 빌드하는 것입니다. 보통 <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>를 사용해 수행합니다. 그런 다음 해당 컨테이너 이미지를 몇 가지 가능한 방법 중 하나로 배포할 수 있습니다.
리눅스 컨테이너를 사용하는 데에는 **보안**, **반복 가능성**, **단순함** 장점이 있습니다.
리눅스 컨테이너를 사용하 **보안**, **재현 가능성**, **단순함** 여러 장점이 있습니다.
/// tip | 팁
시간에 쫓기고 있고 이미 이런들을 알고 있다면 [`Dockerfile`👇](#build-a-docker-image-for-fastapi)로 점프할 수 있습니다.
시간이 없고 이미 이런 내용들을 알고 계신가요? 아래의 [`Dockerfile` 👇](#build-a-docker-image-for-fastapi)로 이동하세요.
///
<details>
<summary>도커파일 미리보기 👀</summary>
<summary>Dockerfile Preview 👀</summary>
```Dockerfile
FROM python:3.9
@@ -24,128 +24,125 @@ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
# If running behind a proxy like Nginx or Traefik add --proxy-headers
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"]
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
```
</details>
## 컨테이너란
## 컨테이너란 { #what-is-a-container }
컨테이너(주로 리눅스 컨테이너)는 어플리케이션의 의존성과 필요한 파일모두 패키징하는 매우 **가벼운** 방법입니다. 컨테이너는 같은 시스템에 있는 다른 컨테이너(다른 플리케이션이나 요소들)와 독립적으로 유지됩니다.
컨테이너(주로 리눅스 컨테이너)는 모든 의존성과 필요한 파일을 포함해 애플리케이션을 패키징하면서, 같은 시스템 다른 컨테이너(다른 플리케이션이나 컴포넌트)와는 분리된 상태로 유지할 수 있는 매우 **가벼운** 방법입니다.
리눅스 컨테이너는 호스트(머신, 가상 머신, 클라우드 서버 등)와 같은 리눅스 커널을 사용해 실행됩니다. 이말은 리눅스 컨테이너가 (전체 운영체제를 모방하는 다른 가상 머신교했을 때) 매우 가볍다는 것을 의미합니다.
리눅스 컨테이너는 호스트(머신, 가상 머신, 클라우드 서버 등)와 같은 리눅스 커널을 사용해 실행됩니다. 즉, 전체 운영체제를 에뮬레이션하는 완전한 가상 머신해 매우 가볍습니다.
이 방법을 통해, 컨테이너는 직접 프로세스를 실행하는 것과 비슷한 정도의 **적은 자원**을 소비합니다 (가상 머신은 훨씬 많은 자원을 소비할 것입니다).
이 방식으로 컨테이너는 프로세스를 직접 실행하는 것과 비슷한 수준의 **적은 자원**을 소비합니다(가상 머신은 훨씬 많은 자원을 소비니다).
컨테이너는 또한 그들만의 **독립** 실행 프로세스 (일반적으로 하나의 프로세스로 충분합니다), 파일 시스템, 그리고 네트워크를 가지므로 배포, 보안, 개발 및 기타 과정을 단순화 합니다.
또한 컨테이너는 자체적인 **격리** 실행 프로세스(보통 하나의 프로세스), 파일 시스템, 네트워크를 가지므로 배포, 보안, 개발 을 단순화합니다.
## 컨테이너 이미지란
## 컨테이너 이미지란 { #what-is-a-container-image }
**컨테이너**는 **컨테이너 이미지** 실행한 것 입니다.
**컨테이너**는 **컨테이너 이미지**에서 실행니다.
컨테이너 이미지 컨테이너에 필요한 모든 파일, 환경 변수 그리고 디폴트 명령/프로그램의 **정적** 버전입니다. 여기서 **정적**이란 말은 컨테이너 **이미지**가 작동되거나 행되지 않으며, 단지 패키 파일과 메타 데이터라는 것을 의미합니다.
컨테이너 이미지 컨테이너에 있어야 하는 모든 파일, 환경 변수, 기본 명령/프로그램의 **정적** 버전입니다. 여기서 **정적**이라는 것은 컨테이너 **이미지**가 실행 중이거나 행되는 것이 아니라, 패키징된 파일과 메타데이터일 뿐이라는 뜻입니다.
저장된 정적 텐츠인 **컨테이너 이미지**와 대조되게, **컨테이너** 보통 실행될 수 있는 작동 인스턴스를 의미합니다.
저장된 정적 텐츠인 "**컨테이너 이미지**"달리, "**컨테이너**"는 보통 실행 중인 인스턴스, 즉 **실행되는** 대상을 의미합니다.
**컨테이너**가 (**컨테이너 이미지**로 부터) 시작되고 실행되면, 컨테이너는 파일이나 환경 변수 생성하거나 변경할 수 있습니다. 이러한 변화는 오직 컨테이너에만 존재하며, 기반이 되는 컨테이너 이미지에는 지속되지 않습니다 (즉 디스크에 저장되지 않습니다).
**컨테이너**가 시작되어 실행 중이면(**컨테이너 이미지**로부터 시작됨) 파일, 환경 변수 등을 생성하거나 변경할 수 있습니다. 이러한 변경은 해당 컨테이너에만 존재하며, 기반이 되는 컨테이너 이미지에는 지속되지 않습니다(디스크에 저장되지 않습니다).
컨테이너 이미지는 **프로그램** 파일과 텐츠, `python`과 어떤 파일 `main.py`에 비할 수 있습니다.
컨테이너 이미지는 **프로그램** 파일과 그 콘텐츠, 예를 들어 `python`과 어떤 파일 `main.py`에 비할 수 있습니다.
그리고 (**컨테이너 이미지**와 대비해서) **컨테이너**는 이미지의 실제 실행 인스턴스로 **프로세스**에 비할 수 있습니다. 사실, 컨테이너는 **프로세스 러닝** 있을 때만 실행됩니다 (그리고 보통 하나의 프로세스 입니다). 컨테이너 내부에 실행되는 프로세스가 없으면 종료됩니다.
그리고 **컨테이너** 자체는(**컨테이너 이미지**와 달리) 이미지의 실제 실행 인스턴스로 **프로세스**에 비할 수 있습니다. 실제로 컨테이너는 **실행 중인 프로세스** 있을 때만 실행됩니다(보통 단일 프로세스입니다). 컨테이너 내부에 실행 중인 프로세스가 없으면 컨테이너는 중지됩니다.
## 컨테이너 이미지
## 컨테이너 이미지 { #container-images }
도커는 **컨테이너 이미지**와 **컨테이너**를 생성하고 관리하는 주요 도구 중 하나가 되어왔습니다.
Docker는 **컨테이너 이미지**와 **컨테이너**를 생성하고 관리하는 주요 도구 중 하나니다.
그리고 <a href="https://hub.docker.com/" class="external-link" target="_blank">도커 허브</a>에 다양한 도구, 환경, 데이터베이스, 그리고 어플리케이션에 대해 미리 만들어진 **공식 컨테이너 이미지**가 공개되어 있습니다.
또한 <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a>에 다양한 도구, 환경, 데이터베이스, 플리케이션을 위한 미리 만들어진 **공식 컨테이너 이미지**가 공개되어 있습니다.
예를 들어, 공식 <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">파이썬 이미지</a>가 있습니다.
예를 들어, 공식 <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">Python Image</a>가 있습니다.
또한 다른 대상, 예를 들면 데이터베이스를 위한 이미지도 있습니다:
그리고 데이터베이스 등 다양한 용도의 다른 이미지도 많이 있습니다. 예를 들면:
* <a href="https://hub.docker.com/_/postgres" class="external-link" target="_blank">PostgreSQL</a>
* <a href="https://hub.docker.com/_/mysql" class="external-link" target="_blank">MySQL</a>
* <a href="https://hub.docker.com/_/mongo" class="external-link" target="_blank">MongoDB</a>
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a> 등
미리 만들어진 컨테이너 이미지를 사용하면 서로 다른 도구들을 **결합**하기 쉽습니다. 대부분의 경우에, **공식 이미지** 사용하고 환경 변수를 통해 설정할 수 있습니다.
미리 만들어진 컨테이너 이미지를 사용하면 서로 다른 도구 **결합**하고 사용하기가 매우 쉽습니다. 예를 들어 새로운 데이터베이스를 시험해 볼 때도 그렇습니다. 대부분의 경우 **공식 이미지** 사용하고, 환경 변수로 설정만 하면 됩니다.
런 방법으로 대부분의 경우 컨테이너와 도커에 대해 배울 수 있으며 다양한 도구와 요소들에 대한 지식을 재사용할 수 있습니다.
렇게 하면 많은 경우 컨테이너와 Docker를 학습하고, 그 지식을 여러 다른 도구와 컴포넌트에 재사용할 수 있습니다.
따라서, 서로 다른 **다중 컨테이너**를 생성한 다음 이들을 연결할 수 있습니다. 예를 들어 데이터베이스, 파이썬 어플리케이션, 리액트 프론트엔드 플리케이션을 사용하는 웹 서버에 대한 컨테이너를 만들어 이들의 내부 네트워크로 각 컨테이너를 연결할 수 있습니다.
따라서 데이터베이스, Python 애플리케이션, React 프론트엔드 플리케이션이 있는 웹 서버 등 서로 다른 것들을 담은 **여러 컨테이너**를 실행하고 내부 네트워크를 통해 연결할 수 있습니다.
모든 컨테이너 관리 시스템(도커나 쿠버네티스)은 이러한 네트워킹 특성을 포함하고 있습니다.
Docker나 Kubernetes 같은 모든 컨테이너 관리 시스템에는 이러한 네트워킹 기능이 통합되어 있습니다.
## 컨테이너와 프로세스
## 컨테이너와 프로세스 { #containers-and-processes }
**컨테이너 이미지**는 보통 **컨테이너** 시작하기 위해 필요한 메타데이터와 디폴트 커맨드/프로그램과 그 프로그램에 전달하기 위한 파라미터들을 포함합니다. 이는 커맨드 라인에서 프로그램을 실행할 때 필요한 값들과 유사합니다.
**컨테이너 이미지**는 보통 **컨테이너** 시작될 때 실행되어야 하는 기본 프로그램/명령과 해당 프로그램에 전달할 매개변수를 메타데이터에 포함합니다. 커맨드 라인에서 실행할 때와 매우 유사합니다.
**컨테이너**가 시작되면, 해당 커맨드/프로그램 실행니다 (그러나 다른 커맨드/프로그램을 실행하도록 오버라이드 할 수 있습니다).
**컨테이너**가 시작되면 해당 명령/프로그램 실행니다(다만 오버라이드하여 다른 명령/프로그램을 실행하 할 수 있습니다).
컨테이너는 **메인 프로세스**(커맨드 또는 프로그램) 실행되는 동안 실행됩니다.
컨테이너는 **메인 프로세스**(명령 또는 프로그램) 실행되는 동안 실행됩니다.
컨테이너는 일반적으로 **단일 프로세스**를 가지고 있지만, 메인 프로세스 서브 프로세스를 시작하는 것도 가능하며, 이 방법으로 하나의 컨테이너에 **다중 프로세스**를 가질 수 있습니다.
컨테이너는 보통 **단일 프로세스**를 가지지만, 메인 프로세스에서 서브프로세스를 시작할 수도 있으며, 그러면 같은 컨테이너에 **여러 프로세스**가 존재하게 됩니다.
그러나 **최소 하나의 실행중인 프로세스**를 가지지 않고서는 실행중인 컨테이너를 가질 수 없습니다. 만약 메인 프로세스가 중되면, 컨테이너도 중됩니다.
하지만 **최소 하나의 실행 중인 프로세스** 없이 실행 중인 컨테이너를 가질 수 없습니다. 메인 프로세스가 중되면 컨테이너도 중됩니다.
## FastAPI를 위한 도커 이미지 빌드하기
## FastAPI를 위한 도커 이미지 빌드하기 { #build-a-docker-image-for-fastapi }
이제 무언가를 만들어 봅시다! 🚀
좋습니다, 이제 무언가를 만들어 봅시다! 🚀
**공식 파이썬** 이미지에 기반하여, FastAPI를 위한 **도커 이미지**를 **처음부터** 생성하는 방법을 보겠습니다.
**공식 Python** 이미지에 기반하여 FastAPI**Docker 이미지**를 **처음부터** 빌드하는 방법을 보여드리겠습니다.
**대부분의 경우**에 다음과 같은 것들을 하게 됩니다. 예를 들면:
이는 **대부분의 경우**에 하고 싶은 방식입니다. 예를 들면:
* **쿠버네티스** 또는 유사한 도구 사용하기
* **라즈베리 파이**로 실행하기
* 컨테이너 이미지를 실행할 클라우드 서비스 사용하기
* **Kubernetes** 또는 유사한 도구 사용할 때
* **Raspberry Pi**에서 실행할 때
* 컨테이너 이미지를 대신 실행해주는 클라우드 서비스 사용할 때
### 요구 패키지
### 패키지 요구사항 { #package-requirements }
일반적으로는 어플리케이션의 특정 파일을 위한 **패키지 요구 조건**이 있을 것입니다.
보통 애플리케이션의 **패키지 요구사항**을 어떤 파일에 적어 둡니다.
그 요구 조건을 **설치**하는 방법은 여러분이 사용하는 도구에 따라 다를 것입니다.
이는 주로 그 요구사항을 **설치**하는 사용하는 도구에 따라 달라집니다.
가장 일반적인 방법은 패키지 이름과 버전이 줄 별로 기록된 `requirements.txt` 파일을 만드는 것입니다.
가장 일반적인 방법은 패키지 이름과 버전을 한 줄에 하나씩 적어 둔 `requirements.txt` 파일을 사용하는 것입니다.
버전 범위를 설정하기 위해서는 [FastAPI 버전들에 대하여](versions.md){.internal-link target=_blank}에 쓰여진 것과 같은 아이디어를 사용니다.
버전 범위를 설정할 때는 [FastAPI 버전들에 대하여](versions.md){.internal-link target=_blank}에서 읽은 것과 같은 아이디어를 사용하면 됩니다.
예를 들어, `requirements.txt` 파일은 다음과 같을 수 있습니다:
예를 들어 `requirements.txt` 다음과 같을 수 있습니다:
```
fastapi>=0.68.0,<0.69.0
pydantic>=1.8.0,<2.0.0
uvicorn>=0.15.0,<0.16.0
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
```
그리고 일반적으로 패키지 종속성은 `pip`로 설치합니다. 예를 들:
그리고 보통 `pip` 패키지 의존성을 설치합니다. 예를 들:
<div class="termy">
```console
$ pip install -r requirements.txt
---> 100%
Successfully installed fastapi pydantic uvicorn
Successfully installed fastapi pydantic
```
</div>
/// info | 정보
패키지 종속성을 정의하고 설치하기 위한 방법과 도구는 다양합니다.
나중에 아래 세션에서 Poetry를 사용한 예시를 보이겠습니다. 👇
패키지 의존성을 정의하고 설치하는 다른 형식과 도구도 있습니다.
///
### **FastAPI** 코드 생성하기
### **FastAPI** 코드 생성하기 { #create-the-fastapi-code }
* `app` 디렉터리를 생성하고 이동합니다.
* 빈 파일 `__init__.py`을 생성합니다.
* 다음과 같은 `main.py`을 생성합니다:
* `app` 디렉터리를 만들고 들어갑니다.
* 빈 파일 `__init__.py`를 만듭니다.
* 다음 내용으로 `main.py` 파일을 만듭니다:
```Python
from typing import Union
@@ -165,79 +162,109 @@ def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
### 도커파일
### Dockerfile { #dockerfile }
이제 같은 프로젝트 디렉터리에 다음과 같은 파일 `Dockerfile`을 생성합니다:
이제 같은 프로젝트 디렉터리에 다음 내용으로 `Dockerfile` 파일을 만듭니다:
```{ .dockerfile .annotate }
# (1)
# (1)!
FROM python:3.9
# (2)
# (2)!
WORKDIR /code
# (3)
# (3)!
COPY ./requirements.txt /code/requirements.txt
# (4)
# (4)!
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (5)
# (5)!
COPY ./app /code/app
# (6)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
# (6)!
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. 공식 파이썬 베이스 이미지에서 시작합니다.
1. 공식 Python 베이스 이미지에서 시작합니다.
2. 현재 워킹 디렉터리를 `/code`로 설정합니다.
2. 현재 작업 디렉터리를 `/code`로 설정합니다.
여기에 `requirements.txt` 파일과 `app` 디렉터리를 위치시킬 것입니다.
여기에 `requirements.txt` 파일과 `app` 디렉터리를 것입니다.
3. 요구 조건과 파일을 `/code` 디렉터리로 복사합니다.
3. 요구사항 파일을 `/code` 디렉터리로 복사합니다.
처음에는 **오직** 요구 조건이 필요한 파일만 복사하고, 이외의 코드는 그대로 둡니다.
처음에는 요구사항 파일만 **단독으로** 복사하고, 나머지 코드는 복사하지 않습니다.
이 파일 **자주 바뀌지 않기 때문에**, 도커는 파일을 탐지하여 이 단계 **캐시**를 사용하 다음 단계에서도 캐시를 사용할 수 있도록 합니다.
이 파일 **자주 바뀌지 않기** 때문에 Docker는 이를 감지하여 이 단계에서 **캐시**를 사용하고, 다음 단계에서도 캐시를 사용할 수 있게 해줍니다.
4. 요구 조건 파일에 있는 패키지 종속성을 설치합니다.
4. 요구사항 파일에 있는 패키지 의존성을 설치합니다.
`--no-cache-dir` 옵션은 `pip`에게 다운로드한 패키지들을 로컬 환경에 저장하지 않도록 전달합니다. 이는 마치 같은 패키지를 설치하기 위해 오직 `pip`만 다시 실행하면 될 것 같지만, 컨테이너 작업하는 경우 그렇지니다.
`--no-cache-dir` 옵션은 `pip` 다운로드한 패키지 로컬에 저장하지 않도록 합니다. 이는 `pip`가 같은 패키지를 설치하기 위해 다시 실행될 때만 의미가 있지만, 컨테이너 작업에서는 그렇지 않기 때문입니다.
/// note | 노트
/// note | 참고
`--no-cache-dir` 는 오직 `pip` 관련되어 있으며, 도커나 컨테이너와는 무관합니다.
`--no-cache-dir` `pip`에만 관련되어 있으며 Docker나 컨테이너와는 관련이 없습니다.
///
`--upgrade` 옵션은 `pip`에게 설치된 패키지들을 업데이트하도록 합니다.
`--upgrade` 옵션은 이미 설치된 패키지가 있다면 `pip`가 이를 업그레이드하도록 합니다.
이전 단계에서 파일을 복사한 것이 **도커 캐시**에 의해 탐지되기 때문에, 이 단계에서도 가능한 한 **도커 캐시**를 사용하게 됩니다.
이전 단계에서 파일을 복사한 것이 **Docker 캐시**에 의해 감지될 수 있으므로, 이 단계에서도 가능하면 **Docker 캐시를 사용**합니다.
이 단계에서 캐시를 사용하면 **매번** 모든 종속성을 다운로드고 설치할 필요가 없어, 개발 과정에서 이미지를 지속적으로 생성하는 데에 드는 **시간**을 많이 **절약**할 수 있습니다.
이 단계에서 캐시를 사용하면 개발 중에 이미지를 반복해서 빌드할 때, 의존성을 **매번 다운로드고 설치하는** 대신 많은 **시간**을 **절약**할 수 있습니다.
5. `/code` 디렉터리 `./app` 디렉터리 복사합니다.
5. `./app` 디렉터리 `/code` 디렉터리 안으로 복사합니다.
**자주 변경되는** 모든 코드를 포함하고 있기 때문에, 도커 **캐시**는 이 단계나 **이후 단계에서** 잘 사용되지 않습니다.
이 디렉터리에는 **가장 자주 변경되는** 코드가 모두 포함되어 있으므로, Docker **캐시**는 이 단계나 **이후 단계들**에서는 쉽게 사용되지 않습니다.
그러므로 컨테이너 이미지 빌드 시간을 최적화하기 위해 `Dockerfile`의 **거의 끝 부분**에 입력하는 것이 중요합니다.
따라서 컨테이너 이미지 빌드 시간을 최적화하려면 `Dockerfile`의 **끝부분 근처**에 는 것이 중요합니다.
6. `uvicorn` 서버를 실행하기 위해 **커맨드** 설정합니다.
6. 내부적으로 Uvicorn을 사용하는 `fastapi run`을 사용하도록 **명령** 설정합니다.
`CMD`는 문자열 리스트를 입력받고, 각 문자열은 커맨드 라인의 각 줄에 입력할 문자열입니다.
`CMD`는 문자열 리스트를 받으며, 각 문자열은 커맨드 라인에서 공백으로 구분해 입력하는 항목들입니다.
커맨드는 **현재 워킹 디렉터리**에서 실행되며, 이는 위에서 `WORKDIR /code`로 설정한 `/code` 디렉터리와 같습니다.
프로그램이 `/code`에서 시작하고 그 속에 `./app` 디렉터리가 여러분의 코드와 함께 들어있기 때문에, **Uvicorn**은 이를 보고 `app`을 `app.main`으로부터 **불러 올** 것입니다.
명령은 **현재 작업 디렉터리**에서 실행되며, 이는 위에서 `WORKDIR /code`로 설정한 `/code` 디렉터리와 같습니다.
/// tip | 팁
각 코드 라인을 코드의 숫자 버블을 클릭하여 리뷰할 수 있습니다. 👆
코드의 숫자 버블을 클릭해 각 줄이 하는 일을 확인하세요. 👆
///
이제 여러분은 다음과 같은 디렉터리 구조를 가지고 있을 것입니다:
/// warning | 경고
아래에서 설명하는 것처럼 `CMD` 지시어는 **항상** **exec form**을 사용해야 합니다.
///
#### `CMD` 사용하기 - Exec Form { #use-cmd-exec-form }
Docker 지시어 <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a>는 두 가지 형식으로 작성할 수 있습니다:
✅ **Exec** form:
```Dockerfile
# ✅ Do this
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ **Shell** form:
```Dockerfile
# ⛔️ Don't do this
CMD fastapi run app/main.py --port 80
```
FastAPI가 정상적으로 종료(graceful shutdown)되고 [lifespan 이벤트](../advanced/events.md){.internal-link target=_blank}가 트리거되도록 하려면, 항상 **exec** form을 사용하세요.
자세한 내용은 <a href="https://docs.docker.com/reference/dockerfile/#shell-and-exec-form" class="external-link" target="_blank">shell and exec form에 대한 Docker 문서</a>를 참고하세요.
이는 `docker compose`를 사용할 때 꽤 눈에 띌 수 있습니다. 좀 더 기술적인 상세 내용은 Docker Compose FAQ 섹션을 참고하세요: <a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">Why do my services take 10 seconds to recreate or stop?</a>.
#### 디렉터리 구조 { #directory-structure }
이제 다음과 같은 디렉터리 구조가 되어야 합니다:
```
.
@@ -248,51 +275,51 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
└── requirements.txt
```
#### TLS 종료 프록시의 배후
#### TLS 종료 프록시의 배후 { #behind-a-tls-termination-proxy }
만약 여러분이 컨테이너를 Nginx 또는 Traefik 같은 TLS 종료 프록시 (로드 밸런서) 뒤에서 실행하고 있다면, `--proxy-headers` 옵션을 더하는 것이 좋습니다. 이 옵션은 Uvicorn에게 어플리케이션이 HTTPS 등의 뒤에서 실행되고 있으므로 프록시에서 전송된 헤더를 신뢰할 수 있다고 알립니다.
Nginx Traefik 같은 TLS 종료 프록시(로드 밸런서) 뒤에서 컨테이너를 실행하고 있다면 `--proxy-headers` 옵션을 추가하세요. 이 옵션은 (FastAPI CLI를 통해) Uvicorn에게 해당 프록시가 보낸 헤더를 신뢰하도록 하여, 애플리케이션이 HTTPS 뒤에서 실행 중임을 알게 합니다.
```Dockerfile
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### 도커 캐시
#### 도커 캐시 { #docker-cache }
이 `Dockerfile`에는 중요한 트릭이 있는데, 처음에는 **의존성이 있는 파일만** 복사하고, 나머지 코드는 그대로 둡니다. 왜 이런 방법을 써야하는지 설명하겠습니다.
이 `Dockerfile`에는 중요한 트릭이 있습니다. 먼저 **의존성 파일만** 복사하고, 나머지 코드는 복사하지 않는 것입니다. 왜 그런지 설명하겠습니다.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
도커와 다른 도구들은 컨테이너 이미지를 **증가하는 방식으로 빌드**합니다. `Dockerfile`의 맨 윗 부분부터 시작해, 레이어 위에 새로운 레이어를 더하는 방식으로, `Dockerfile`의 각 지시 사항으로 부터 생성된 어떤 파일이든 더해갑니다.
Docker와 다른 도구들은 `Dockerfile`의 위에서부터 시작해, 각 지시어가 만든 파일을 포함하며 **레이어를 하나씩 위에 쌓는 방식으로** 컨테이너 이미지를 **점진적으로** 빌드합니다.
도커 그리고 이와 유사한 도구들은 이미지 생성 시에 **내부 캐시** 사용합니다. 만약 어떤 파일이 마지막으로 컨테이너 이미지를 빌드부터 바뀌지 않았다면, 파일을 다시 복사하로운 레이어를 처음부터 생성하는 것이 아니라, 마지막에 생성했던 **같은 레이어를 재사용**합니다.
Docker와 유사한 도구들은 이미지를 빌드할 때 **내부 캐시** 사용합니다. 어떤 파일이 마지막으로 컨테이너 이미지를 빌드했을 때부터 바뀌지 않았다면, 파일을 다시 복사하 새 레이어를 처음부터 만드는 대신, 이전에 만든 **같은 레이어를 재사용**합니다.
단지 파일 복사를 지양하는 것으로 효율이 많이 향상되는 것은 아니지만, 단계에서 캐시를 사용했기 때문에, **다음 단계에서도 마찬가지로 캐시를 사용**할 수 있습니다. 예를 들어, 다음과 같 의존성을 설치하는 지시 사항을 위한 캐시를 사용할 수 있습니다:
파일 복사를 하는 것으로 큰 개선이 생기지는 않을 수 있지만, 해당 단계에서 캐시를 사용했기 때문에 **다음 단계에서도 캐시를 사용할 수** 있습니다. 예를 들어 다음과 같 의존성을 설치하는 지시어에서 캐시를 사용할 수 있습니다:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
패키지를 포함하는 파일은 **자주 변경되지 않습니다**. 따라서 해당 파일만 복사하므로서, 도커는 그 단계 **캐시를 사용**할 수 있습니다.
패키지 요구사항 파일은 **자주 변경되지 않습니다**. 따라서 파일만 복사하면 Docker는 그 단계에서 **캐시를 사용할 수** 있습니다.
다음으로, 도커는 **다음 단계에서** 의존성을 다운로드하고 설치하는 **캐시를 사용**할 수 있게 됩니다. 바로 이 과정에서 우리는 **많은 시간을 절약**하게 됩니다. ✨ ...그리고 기다리 지루도 피할 수 있습니다. 😪😆
리고 Docker는 그 다음 단계에서 의존성을 다운로드하고 설치할 때도 **캐시를 사용할 수** 있습니다. 바로 여기에서 **많은 시간을 절약**하게 됩니다. ✨ ...그리고 기다리 지루해지는 것도 피할 수 있습니다. 😪😆
패키지 의존성을 다운로드고 설치하는 데는 ** 분이 걸릴 수 있지만**, **캐시**를 사용하면 최대 **만에** 끝낼 수 있습니다.
패키지 의존성을 다운로드고 설치하는 데는 ****이 걸릴 수 있지만, **캐시**를 사용하면 많아야 ** 초**니다.
또한 여러분이 개발 과정에서 코드 변경 사항이 반영되었는지 확인하기 위해 컨테이너 이미지를 계속해서 빌드하면, 절약된 시간은 적되어 더욱 커질 것입니다.
또한 개발 중에 코드 변경 사항이 동작하는지 확인하기 위해 컨테이너 이미지를 계속 빌드하게 되므로, 이렇게 절약되는 시간은 적되어 상당히 커집니다.
리고 나서 `Dockerfile`의 거의 끝 부분에서, 모든 코드를 복사합니다. 이것이 **가장 빈번하게 변경**되는 부분이며, 대부분의 경우에 이 다음 단계에서는 캐시를 사용할 수 없기 때문에 가장 마지막에 둡니다.
다음 `Dockerfile`의 끝부분 근처에서 모든 코드를 복사합니다. 이 부분은 **가장 자주 변경되는** 부분이므로, 거의 항상 이 단계 이후에는 캐시를 사용할 수 없기 때문에 끝부분에 둡니다.
```Dockerfile
COPY ./app /code/app
```
### 도커 이미지 생성하기
### 도커 이미지 생성하기 { #build-the-docker-image }
이제 모든 파일이 제자리에 있으니, 컨테이너 이미지를 빌드합니다.
이제 모든 파일이 제자리에 있으니 컨테이너 이미지를 빌드해봅시다.
* (여러분의 `Dockerfile` `app` 디렉터리가 위치한) 프로젝트 디렉터리로 이동합니다.
* 프로젝트 디렉터리로 이동합니다(`Dockerfile`이 있고 `app` 디렉터리를 포함하는 위치).
* FastAPI 이미지를 빌드합니다:
<div class="termy">
@@ -307,13 +334,13 @@ $ docker build -t myimage .
/// tip | 팁
끝에 있는 `.` 에 주목합시다. 이는 `./`와 동하며, 도커에게 컨테이너 이미지를 빌드하기 위한 디렉터리를 알려줍니다.
끝에 있는 `.`에 주목하세요. 이는 `./`와 동하며, Docker에게 컨테이너 이미지를 빌드할 때 사용할 디렉터리를 알려줍니다.
이 경우에는 현재 디렉터리(`.`)와 같습니다.
이 경우 현재 디렉터리(`.`)니다.
///
### 도커 컨테이너 시작하기
### 도커 컨테이너 시작하기 { #start-the-docker-container }
* 여러분의 이미지에 기반하여 컨테이너를 실행합니다:
@@ -325,35 +352,35 @@ $ docker run -d --name mycontainer -p 80:80 myimage
</div>
## 체크하기
## 확인하기 { #check-it }
여러분의 도커 컨테이너 URL에서 실행 사항을 체크할 수 있니다. 예를 들어: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> 또는 <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (또는 동일하게, 여러분의 도커 호스트를 용해서 체크할 수 있습니다).
Docker 컨테이너 URL에서 확인할 수 있어야 합니다. 예를 들어: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> 또는 <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a>(또는 Docker 호스트를 용해 동등하게 확인할 수 있습니다).
아래와 비슷한 것을 보게 될 것입니다:
아래와 같은 것을 보게 될 것입니다:
```JSON
{"item_id": 5, "q": "somequery"}
```
## 인터랙티브 API 문서
## 인터랙티브 API 문서 { #interactive-api-docs }
이제 여러분은 <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> 또는 <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a>로 이동할 수 있습니다(또는, 여러분의 도커 호스트를 이용할 수 있습니다).
이제 <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> 또는 <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a>(또는 Docker 호스트를 사용해 동등하게 접근)로 이동할 수 있습니다.
여러분은 자동으로 생성된 인터랙티브 API(<a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</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 문서
## 대안 API 문서 { #alternative-api-docs }
또한 여러분은 <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> 또는 <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a>으로 이동할 수 있습니다(또는, 여러분의 도커 호스트를 이용할 수 있습니다).
또한 <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> 또는 <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a>(또는 Docker 호스트를 사용해 동등하게 접근)로 이동할 수 있습니다.
여러분은 자동으로 생성된 대안 문서(<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">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)
## 단일 파일 FastAPI로 도커 이미지 생성하기
## 단일 파일 FastAPI로 도커 이미지 빌드하기 { #build-a-docker-image-with-a-single-file-fastapi }
만약 여러분의 FastAPI가 하나의 파일이라면, 예를 들어 `./app` 디렉터리 없이 `main.py` 파일만으로 이루어져 있다면, 파일 구조는 다음과 유사할 것입니다:
FastAPI가 단일 파일(예: `./app` 디렉터리 없이 `main.py`만 있는 경우)이라면, 파일 구조는 다음과 같을 수 있습니다:
```
.
@@ -362,7 +389,7 @@ $ docker run -d --name mycontainer -p 80:80 myimage
└── requirements.txt
```
러면 여러분들은 `Dockerfile` 내에 있는 파일을 복사하기 위해 그저 상응하는 경로를 바꾸기만 하면 됩니다:
런 다음 `Dockerfile`에서 해당 파일을 복사하도록 경로만 맞게 변경하면 됩니다:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.9
@@ -373,359 +400,221 @@ COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (1)
# (1)!
COPY ./main.py /code/
# (2)
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
# (2)!
CMD ["fastapi", "run", "main.py", "--port", "80"]
```
1. `main.py` 파일을 `/code` 디렉터리로 곧바로 복사합니다(`./app` 디렉터리는 고려하지 않습니다).
1. `main.py` 파일을 `/code` 디렉터리로 직접 복사합니다(`./app` 디렉터리 없이).
2. Uvicorn을 실행해 `app` 객체를 (`app.main` 대신) `main`으로 부터 불러오도록 합니다.
2. 단일 파일 `main.py`에 있는 애플리케이션을 제공(serve)하기 위해 `fastapi run`을 사용합니다.
그 다음 Uvicorn 커맨드를 조정해서 FastAPI 객체를 불러오는데 `app.main` 대신에 새로운 모듈 `main`을 사용하도록 합니다.
`fastapi run`에 파일을 전달하면, 이것이 패키지의 일부가 아닌 단일 파일이라는 것을 자동으로 감지하고, 어떻게 임포트해서 FastAPI 앱을 제공할지 알아냅니다. 😎
## 배포 개념
## 배포 개념 { #deployment-concepts }
이제 컨테이너의 측면에서 [배포 개념](concepts.md){.internal-link target=_blank}에서 다루었던 것과 같은 배포 개념에 대해 이야기해 보겠습니다.
컨테이너 관점에서 같은 [배포 개념](concepts.md){.internal-link target=_blank}들을 다시 이야기해 봅시다.
컨테이너는 주로 플리케이션을 빌드하고 배포하기 위한 과정을 단순화하는 도구이지만, **배포 개념**에 대한 특정 접근을 강하지 않기 때문에 가능한 배포 전략에는 여러가지가 있습니다.
컨테이너는 주로 플리케이션의 **빌드 및 배포** 과정을 단순화하는 도구이지만, 이러한 **배포 개념**을 처리하는 특정 접근 방식을 강하지으며, 가능한 전략은 여러 가지니다.
**좋은 소식**은 서로 다른 전략들을 포괄하는 배포 개념이 있다는 점입니다. 🎉
**좋은 소식**은 각 전략마다 모든 배포 개념을 다룰 수 있는 방법이 있다는 점입니다. 🎉
컨테이너 측면에서 **배포 개념**을 리뷰해 보겠습니다:
컨테이너 관점에서 **배포 개념**살펴봅시다:
* HTTPS
* 구동하기
* 시작 시 자동 실행
* 재시작
* 복제 (실행 중인 프로세스 수)
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작하기 전 단계
* 시작 전 사전 단계
## HTTPS
## HTTPS { #https }
만약 우리가 FastAPI 플리케이션을 위한 **컨테이너 이미지**에만 집중한다면 (그리고 나중에 실행 **컨테이너**), HTTPS는 일반적으로 다른 도구에 의해 **외부적으로** 다루어질 것 입니다.
FastAPI 플리케이션 **컨테이너 이미지**(그리고 나중에 실행 중인 **컨테이너**)에만 집중한다면, HTTPS는 보통 다른 도구에 의해 **외부적으로** 처리됩니다.
**HTTPS**와 **인증서**의 **자동** 취득을 다루는 것은 다른 컨테이너가 될 수 있는데, 예를 들어 <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>을 사용하는 것입니다.
예를 들어 <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>을 사용하는 다른 컨테이너가 **HTTPS**와 **인증서**의 **자동** 획득을 처리할 수 있습니다.
/// tip | 팁
Traefik은 도커, 쿠버네티스, 그리고 다른 도구와 통합되어 있어 여러분의 컨테이너를 포함하는 HTTPS를 셋업하고 설정하는 것이 매우 쉽습니다.
Traefik은 Docker, Kubernetes 등과 통합되어 있어, 이를 사용해 컨테이너에 HTTPS를 설정하고 구성하기가 매우 쉽습니다.
///
대안적으로, HTTPS 클라우드 제공자에 의해 서비스의 일환으로 다루어질 수도 있습니다 (이때도 어플리케이션은 여전히 컨테이너에서 실행될 것입니다).
또는 HTTPS 클라우드 제공자 서비스의 일부로 처리할 수도 있습니다(애플리케이션은 여전히 컨테이너에서 실행니다).
## 구동과 재시작
## 시작 시 자동 실행과 재시작 { #running-on-startup-and-restarts }
여러분의 컨테이너를 **시작하고 실행하는** 데에 일반적으로 사용되는 도구는 따로 있습니다.
보통 컨테이너를 **시작하고 실행**하는 역할을 담당하는 다른 도구가 있습니다.
이는 **도커** 자체일 수도 있고, **도커 컴포즈**, **쿠버네티스**, **클라우드 서비스** 등이 될 수 있습니다.
직접 **Docker**일 수도 있고, **Docker Compose**, **Kubernetes**, **클라우드 서비스** 등 있습니다.
대부분 (또는 전체) 경우, 컨테이너를 구동하거나 고장시에 재시작하도록 하는 간단한 옵션이 있습니다. 예를 들어, 도커에서는, 커맨드 라인 옵션 `--restart` 입니다.
대부분(또는 전부)의 경우, 시작 시 컨테이너를 실행하고 실패 시 재시작을 활성화하는 간단한 옵션이 있습니다. 예를 들어 Docker에서는 커맨드 라인 옵션 `--restart`입니다.
컨테이너를 사용하지 않고서는, 어플리케이션을 구동하고 재시작하는 것이 매우 번거롭고 어려울 수 있습니다. 하지만 **컨테이너를 사용한다면** 대부분의 경우에 이런 기능 기본으로 포함되어 있습니다. ✨
컨테이너를 사용하지 않으면 애플리케이션을 시작 시 자동 실행하고 재시작까지 구성하는 것이 번거롭고 어습니다. 하지만 **컨테이너로 작업할 때** 대부분의 경우 기능 기본으로 포함되어 있습니다. ✨
## 복제 - 프로세스 개수
## 복제 - 프로세스 개수 { #replication-number-of-processes }
만약 여러분이 **쿠버네티스**와 머신 <abbr title="A group of machines that are configured to be connected and work together in some way.">클러스터</abbr>, 도커 스왐 모드, 노마드, 또는 다른 여러 머신 위에 분산 컨테이너를 관리하는 복잡한 시스템을 다루고 있다면, 여러분은 각 컨테이너에서 (워커와 함께 사용하는 Gunicorn 같은) **프로세스 매니저** 대신 **클러스터 레벨**에서 **복제를 다루**고 싶을 것입니다.
**Kubernetes**, Docker Swarm Mode, Nomad 등의 복잡한 시스템으로 여러 머신에 분산된 컨테이너를 관리하는 <abbr title="A group of machines that are configured to be connected and work together in some way.">cluster</abbr>를 사용한다면, 각 컨테이너에서(**워커를 사용하는 Uvicorn** 같은) **프로세스 매니저**를 쓰는 대신, **클러스터 레벨**에서 **복제를 처리**고 싶을 가능성이 큽니다.
쿠버네티스와 같은 분산 컨테이너 관리 시스템 중 일부는 일반적으로 들어오는 요청에 대한 **로드 밸런싱**을 지원하면서 **컨테이너 복제**를 다루는 통합된 방법을 가지고 있습니다. 모두 **클러스터 레벨**에서 말이죠.
Kubernetes 같은 분산 컨테이너 관리 시스템은 보통 들어오는 요청에 대한 **로드 밸런싱**을 지원하면서도, **컨테이너 복제**를 처리하는 통합된 방법을 가지고 있습니다. 모두 **클러스터 레벨**에서.
런 경우에, 여러분은 [위에서 묘사된 것](#dockerfile)처럼 **처음부터 도커 이미지를** 빌드해서, 의존성을 설치하고, Uvicorn 워커를 관리하는 Gunicorn 대신 **단일 Uvicorn 프로세스**를 실행하고 싶을 것입니다.
런 경우에는 [위에서 설명한 대로](#dockerfile) 의존성을 설치하고, 여러 Uvicorn 워커를 사용하는 대신 **단일 Uvicorn 프로세스**를 실행하는 **처음부터 만든 Docker 이미지**를 사용하는 것이 좋을 것입니다.
### 로드 밸런서
### 로드 밸런서 { #load-balancer }
컨테이너로 작업할 때, 여러분은 일반적으로 **메인 포트의 상황을 감지하는** 요소를 가지고 있을 것입니다. 이는 **HTTPS**를 다루는 **TLS 종료 프록시**와 같은 다른 컨테이너일 수도 있고, 유사한 다른 도구일 수도 있습니다.
컨테이너를 사용할 때는 보통 **메인 포트에서 대기(listening)하는** 컴포넌트가 있습니다. **HTTPS**를 처리하기 위한 **TLS 종료 프록시** 역할을 하는 다른 컨테이너일 수도 있고, 유사한 도구일 수도 있습니다.
요소가 요청의 **로드**를 읽어들이고 각 워커에게 (바라건대) **균형적으로** 분한다면, 이 요소는 일반적으로 **로드 밸런서**라고 불립니다.
컴포넌트가 요청의 **부하(load)**를 받아 워커들에 (가능하면) **균형 있게** 분한다면, 보통 **로드 밸런서**라고 부릅니다.
/// tip | 팁
HTTPS를 위해 사용된 **TLS 종료 프록시** 요소 또한 **로드 밸런서**가 될 수 있습니다.
HTTPS에 사용되는 동일한 **TLS 종료 프록시** 컴포넌트가 **로드 밸런서**이기도 한 경우가 많습니다.
///
또한 컨테이너로 작업할 때, 컨테이너를 시작하고 관리하기 위해 사용한 것과 동일한 시스템은 이미 해당 **로드 밸런서**로 부터 여러분의 앱에 해당하는 컨테이너로 **네트워크 통신**(예를 들어, HTTP 요청)을 전하는 내부적인 도구를 가지고 있을 것입니다 (여기서도 로드 밸런서는 **TLS 종료 프록시**일 수 있습니다).
또한 컨테이너로 작업할 때, 를 시작하고 관리하 시스템은 이미 해당 **로드 밸런서**(또는 **TLS 종료 프록시**)에서 여러분의 앱이 있는 컨테이너로 **네트워크 통신**(예: HTTP 요청)을 전하는 내부 도구를 가지고 있습니다.
### 하나의 로드 밸런서 - 다중 워커 컨테이너
### 하나의 로드 밸런서 - 여러 워커 컨테이너 { #one-load-balancer-multiple-worker-containers }
**쿠버네티스**나 또는 다른 분산 컨테이너 관리 시스템으로 작업할 때, 시스템 내부 네트워킹 메커니즘을 이용함으로써 메인 **포트**를 감지하고 있는 단일 **로드 밸런서** 여러분의 앱에서 실행되고 있는 **여러개의 컨테이너** 통신(요청)을 전할 수 있게 됩니다.
**Kubernetes** 같은 분산 컨테이너 관리 시스템에서는 내부 네트워킹 메커니즘을 통해, 메인 **포트**에서 대기하는 단일 **로드 밸런서** 여러분의 앱 실행는 **여러 컨테이너** 통신(요청)을 전할 수 있니다.
여러분의 앱에서 실행되고 있는 각각의 컨테이너는 일반적으로 **하나의 프로세스**질 것입니다 (예를 들어, FastAPI 플리케이션에서 실행되는 하나의 Uvicorn 프로세스처럼). 이 컨테이너들은 모두 같은 것을 실행하는 점에서 **동일한 컨테이너**이지만, 프로세스, 메모리 등은 공유하지 않습니다. 이 방식으로 여러분은 CPU의 **서로 다른 코어** 또는 **서로 다른 머신** **병렬화**하는 이점을 얻을 수 있습니다.
앱을 실행하는 각 컨테이너는 보통 **프로세스 하나만** 가니다(예: FastAPI 플리케이션 실행하는 Uvicorn 프로세스). 모두 같은 것을 실행하는 **동일한 컨테이너**이지만, 각자 고유한 프로세스, 메모리 등을 가집니다. 이렇게 하면 CPU의 **서로 다른 코어** 또는 **서로 다른 머신**에서 **병렬화** 이점을 얻을 수 있습니다.
또한 **로드 밸런서**가 있는 분산 컨테이너 시스템은 여러분의 앱에 있는 컨테이너 각각에 **차례대로 요청을 분산**시킬 것 입니다. 따라서 각 요청은 여러분의 앱에서 실행는 여러개의 **복제된 컨테이너** 중 하나에 의해 다루어질 것 입니다.
그리고 **로드 밸런서**가 있는 분산 컨테이너 시스템은 여러분의 앱을 실행하는 각 컨테이너에 **번갈아가며** 요청을 **분산**니다. 따라서 각 요청은 여러분의 앱 실행는 여러 **복제된 컨테이너** 중 하나에서 처리될 수 있습니다.
그리고 일반적으로 **로드 밸런서**는 여러분의 클러스터에 있는 *다른* 앱으로 가는 요청들도 다룰 수 있으며 (예를 들어, 다른 도메인으로 가거나 다른 URL 경로 접두사경우), 통신을 클러스터에 있는 *바로 그 다른* 플리케이션으로 제대로 전할 수 있습니다.
또한 보통 이 **로드 밸런서**는 클러스터 내 *다른* 앱으로 가는 요청(예: 다른 도메인, 또는 다른 URL 경로 접두사 아래로 가는 요청)도 처리할 수 있으며, 통신을 클러스터에서 실행 중인 *그 다른* 플리케이션의 올바른 컨테이너로 전할 수 있습니다.
### 단일 프로세스를 가지는 컨테이너
### 컨테이너당 하나의 프로세스 { #one-process-per-container }
이 시나리오의 경우, 여러분은 이미 클러스터 레벨에서 복제를 다루고 있을 것이므로 **컨테이너 당 단일 (Uvicorn) 프로세스**를 가지고자 할 것입니다.
이 시나리오에서는 이미 클러스터 레벨에서 복제를 처리하고 있으므로, **컨테이너당 단일 (Uvicorn) 프로세스**를 두는 것이 좋을 가능성이 큽니다.
따라서, 여러분은 Gunicorn 이나 Uvicorn 워커, 또는 Uvicorn 워커를 사용하는 Uvicorn 매니저와 같은 프로세스 매니저를 가지고 싶어하지 **않을** 것입니다. 여러분은 컨테이너 당 **단일 Uvicorn 프로세스**를 가지고 싶어할 것입니다 (그러나 아마도 다중 컨테이너를 가질 것입니다).
따라서 이 경우 컨테이너에서 `--workers` 커맨드 라인 옵션 같은 방식으로 여러 워커를 두고 싶지는 **않을** 것입니다. 컨테이너당 **단일 Uvicorn 프로세스**만 두고(하지만 컨테이너는 여러 개일 수 있습니다) 싶을 것입니다.
이미 여러분이 클러스터 시스템을 관리하고 있으므로, (Uvicorn 워커를 관리하는 Gunicorn 이나 Uvicorn 처럼) 컨테이너 내에 다른 프로세스 매니저를 가지는 것은 **불필요한 복잡성**만 더하게 될 것입니다.
컨테이너 내부에 (여러 워커를 위한) 또 다른 프로세스 매니저를 는 것은, 이미 클러스터 시스템에서 처리하고 있는 **불필요한 복잡성**만 추가할 가능성이 큽니다.
### 다중 프로세스를 가지는 컨테이너와 특수한 경우
### 여러 프로세스를 가 컨테이너와 특수한 경우 { #containers-with-multiple-processes-and-special-cases }
당연한 말이지만, 여러분이 내부적으로 **Uvicorn 워커 프로세스**를 시작하는 **Gunicorn 프로세스 매니저**를 가지는 단일 컨테이너를 원하는 **특수한 경우**도 있을 것입니다.
물론 컨테이너 하나에 여러 **Uvicorn 워커 프로세스**를 두고 싶을 수 있는 **특수한 경우**도 있니다.
그런 경우에, 여러분들은 **Gunicorn**을 프로세스 매니저로 포함하는 **공식 도커 이미지**를 사용할 수 있습니다. 이 프로세스 매니저는 다중 **Uvicorn 워커 프로세스들**을 실행하며, 디폴트 세팅으로 현재 CPU 코어에 기반하여 자동으로 워커 수를 조정합니다. 이 사항에 대해서는 아래의 [Gunicorn과 함께하는 공식 도커 이미지 - Uvicorn](#official-docker-image-with-gunicorn-uvicorn)에서 더 다루겠습니다.
그런 경우에는 `--workers` 커맨드 라인 옵션을 사용해 실행할 워커 수를 설정할 수 있습니다:
이런 경우에 해당하는 몇가지 예시가 있습니다:
```{ .dockerfile .annotate }
FROM python:3.9
#### 단순한 앱
WORKDIR /code
만약 여러분의 어플리케이션이 **충분히 단순**해서 (적어도 아직은) 프로세스 개수를 파인-튠 할 필요가 없거나 클러스터가 아닌 **단일 서버**에서 실행하고 있다면, 여러분은 컨테이너 내에 프로세스 매니저를 사용하거나 (공식 도커 이미지에서) 자동으로 설정되는 디폴트 값을 사용할 수 있습니다.
COPY ./requirements.txt /code/requirements.txt
#### 도커 구성
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
여러분은 **도커 컴포즈**로 (클러스터가 아닌) **단일 서버로** 배포할 수 있으며, 이 경우에 공유된 네트워크와 **로드 밸런싱**을 포함하는 (도커 컴포즈로) 컨테이너의 복제를 관리하는 단순한 방법이 없을 수도 있습니다.
COPY ./app /code/app
그렇다면 여러분은 **프로세스 매니저**와 함께 내부에 **몇개의 워커 프로세스들**을 시작하는 **단일 컨테이너**를 필요로 할 수 있습니다.
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
#### Prometheus와 다른 이유들
1. 여기서는 `--workers` 커맨드 라인 옵션으로 워커 수를 4로 설정합니다.
여러분은 **단일 프로세스**를 가지는 **다중 컨테이너** 대신 **다중 프로세스**를 가지는 **단일 컨테이너**를 채택하는 **다른 이유**가 있을 수 있습니다.
이런 방식이 의미가 있을 수 있는 예시는 다음과 같습니다:
예를 들어 (여러분의 장치 설정에 따라) Prometheus 익스포터와 같이 같은 컨테이너에 들어오는 **각 요청에 대해** 접근권한을 가지는 도구를 사용할 수 있습니다.
#### 단순한 앱 { #a-simple-app }
이 경우에 여러분이 **여러개의 컨테이너들**을 가지고 있다면, Prometheus가 **메트릭을 읽어 들일 때**, 디폴트로 **매번 하나의 컨테이너**(특정 리퀘스트를 관리하는 바로 그 컨테이너)로 부터 읽어들일 것입니다. 이는 모든 복제된 컨테이너에 대해 **축적된 메트릭들**을 읽어들이는 것과 대비됩니다.
애플리케이션이 **충분히 단순**해서 클러스터가 아닌 **단일 서버**에서 실행할 수 있다면, 컨테이너에 프로세스 매니저를 두고 싶을 수 있습니다.
그렇다면 이 경우에는 **다중 프로세스**를 가지는 **하나의 컨테이너**를 두어서 같은 컨테이너에서 모든 내부 프로세스에 대한 Prometheus 메트릭을 수집하는 로컬 도구(예를 들어 Prometheus 익스포터 같은)를 두어서 이 메그릭들을 하나의 컨테이너에 내에서 공유하는 방법이 더 단순할 것입니다.
#### Docker Compose { #docker-compose }
**Docker Compose**로 클러스터가 아닌 **단일 서버**에 배포하는 경우, 공유 네트워크와 **로드 밸런싱**을 유지하면서(Docker Compose로) 컨테이너 복제를 관리하는 쉬운 방법이 없을 수 있습니다.
그렇다면 **프로세스 매니저**가 컨테이너 내부에서 **여러 워커 프로세스**를 시작하는 **단일 컨테이너**를 원할 수 있습니다.
---
요점은, 이 중 **어느것도** 여러분들이 반드시 따라야하는 **확정된 사실** 아니라는 것입니다. 여러분은 이 아이디어들을 **여러분의 고유한 이용 사례를 평가**하는데 사용하고, 여러분의 시스템에 가장 적합한 접근법이 어떤 것인지 결정하며, 다음 개념관리하는 방법을 확인할 수 있습니다:
핵심은, 이것들 중 **어느 것도** 무조건 따라야 하는 **절대적인 규칙** 아니라는 것입니다. 이 아이디어들을 사용해 **여러분의 용 사례를 평가**하고, 여러분의 시스템에 가장 적합한 접근 방식을 결정하면서 다음 개념을 어떻게 관리할지 확인할 수 있습니다:
* 보안 - HTTPS
* 구동하기
* 시작 시 자동 실행
* 재시작
* 복제 (실행 중인 프로세스 수)
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작하기 전 단계
* 시작 전 사전 단계
## 메모리
## 메모리 { #memory }
만약 여러분이 **컨테이너 당 단일 프로세스**를 실행한다면, 여러분은 각 컨테이너(복제된 경우에는 여러개의 컨테이너들)에 대해 잘 정의되고, 안정적이며, 제한된 용량의 메모리 소비량을 가지고 있을 것입니다.
**컨테이너당 단일 프로세스**를 실행하면, 각 컨테이너(복제된 경우 여러 개)마다 소비하는 메모리 양이 대체로 잘 정의되고 안정적이며 제한된 값이 됩니다.
러면 여러분의 컨테이너 관리 시스템(예를 들어 **쿠버네티스**) 설정에서 앞서 정의된 것과 같은 메모리 제한과 요구사항을 설정할 수 있습니다. 이런 방법으로 **가용 머신**이 필요로하는 메모리와 클러스터에 있는 가용 머신들을 염두에 두고 **컨테이너를 복제**할 수 있습니다.
런 다음 컨테이너 관리 시스템(예: **Kubernetes**) 설정에서 동일하게 메모리 제한과 요구사항을 설정할 수 있습니다. 그러면 클러스터에서 사용 가능한 머신에 있는 메모리와 컨테이너가 필요로 하는 메모리 양을 고려해 **컨테이너를 복제**할 수 있습니다.
만약 여러분의 어플리케이션이 **단순**하다면,것은 **문제가 되지 않을** 것이고, 고정된 메모리 제한을 구체화할 필요 없을 것입니다. 하지만 여러분의 어플리케이션이 (예를 들어 **머신 러닝** 모델같이) **많은 메모리를 소요한다면**, 어플리케이션이 얼마나 많은 양의 메모리를 사용하는지 확인하고 **각 머신에서** 사용하는 **컨테이너 수**를 조정할 필요가 있습니다 (그리고 필요에 따라 여러분의 클러스터에 머신을 추가할 수 있습니다).
플리케이션이 **단순**하다면 이는 아마도 **문제가 되지 않을** 것이고, 엄격한 메모리 제한을 지정할 필요 없을 수도 있습니다. 하지만 **많은 메모리를 사용한다면**(예: **머신 러닝** 모델), 얼마나 많은 메모리를 소비하는지 확인하고, **각 머신**에서 실행되는 **컨테이너 수**를 조정해야 합니다(필요하다면 클러스터에 머신을 추가할 수 있습니다).
만약 여러분이 **컨테이너 당 여러개의 프로세스**를 실행한다면 (예를 들어 공식 도커 이미지 처럼), 여러분은 시작된 프로세스 수가 가용한 것 보다 **더 많은 메모리를 소비**하지 않는지 확인해야 합니다.
**컨테이너당 여러 프로세스**를 실행한다면, 시작되는 프로세스 수가 사용 가능한 것보다 **더 많은 메모리를 소비하지** 않는지 확인해야 합니다.
## 시작하기 전 단계들과 컨테이너
## 시작 전 단계 컨테이너 { #previous-steps-before-starting-and-containers }
만약 여러분이 컨테이너(예를 들어 도커, 쿠버네티스)를 사용한다면, 여러분이 접근할 수 있는 주요 방법은 크게 두가지가 있습니다.
컨테이너(예: Docker, Kubernetes)를 사용한다면, 사용할 수 있는 주요 접근 방식은 두 가지입니다.
### 다중 컨테이너
### 여러 컨테이너 { #multiple-containers }
만약 여러분이 **여러개의 컨테이너**를 가지고 있다면, 아마도 각각의 컨테이너는 **하나의 프로세스**를 가지고 있을 것입니다(예를 들어, **쿠버네티스** 클러스터에서). 그러면 여러분은 복제된 워커 컨테이너를 실행하기 **전에**, 하나의 컨테이너에 있는 **이전의 단계들을** 수행하는 단일 프로세스를 가지는 **별도의 컨테이너**을 가지고 싶을 것입니다.
**여러 컨테이너**가 있고 각 컨테이너가 보통 **단일 프로세스**를 실행한다면(예: **Kubernetes** 클러스터), 복제된 워커 컨테이너를 실행하기 **전에**, 단일 컨테이너에서 단일 프로세스로 **시작 전 사전 단계** 수행하는 **별도의 컨테이너**를 두고 싶을 가능성이 큽니다.
/// info | 정보
만약 여러분이 쿠버네티스를 사용하고 있다면, 아마도 이는 <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>일 것입니다.
Kubernetes를 사용한다면, 이는 아마도 <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>일 것입니다.
///
만약 여러분의 이용 사례에서 전 단계들을 **병렬적으로 여러번** 수행하는데에 문제가 없다면 (예를 들어 데이터베이스 이전을 실행하지 않고 데이터베이스가 준비되었는지 확인만 하는 경우), 메인 프로세스를 시작하기 전에 이 단계들을 각 컨테이너에 넣을 수 있습니다.
용 사례에서 시작 전 사전 단계 **여러 번 병렬로 실행**해도 문제가 없다면(예: 데이터베이스 마이그레이션을 실행하는 것이 아니라, 데이터베이스가 준비되었는지 확인만 하는 경우), 메인 프로세스를 시작하기 전에 각 컨테이너에 그 단계를 넣을 수 있습니다.
### 단일 컨테이너
### 단일 컨테이너 { #single-container }
만약 여러분의 셋업이 **다중 프로세스**(또는 하나의 프로세스)를 시작하는 **하나의 컨테이너**를 가지는 단순한 셋업이라면, 사전 단계들을 앱을 포함하는 프로세스를 시작하기 직전에 같은 컨테이너에서 실행할 수 있습니다. 공식 도커 이미지는 이를 내부적으로 지원합니다.
**단일 컨테이너**에서 여러 **워커 프로세스**(또는 단일 프로세스)를 시작하는 단순한 셋업이라면, 앱이 있는 프로세스를 시작하기 직전에 같은 컨테이너에서 시작 전 사전 단계를 실행할 수 있습니다.
## Gunicorn과 함께하는 공식 도커 이미지 - Uvicorn
### 베이스 도커 이미지 { #base-docker-image }
앞 챕터에서 자세하게 설명된 것 처럼, Uvicorn 워커와 같이 실행되는 Gunicorn을 포함하는 공식 도커 이미지가 있습니다: [서버 워커 - Uvicorn과 함께하는 Gunicorn](server-workers.md){.internal-link target=_blank}.
과거에는 공식 FastAPI Docker 이미지가 있습니다: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. 하지만 이제는 deprecated되었습니다. ⛔️
이 이미지는 주로 위에서 설명된 상황에서 유용할 것입니다: [다중 프로세스를 가지는 컨테이너와 특수한 경우들](#containers-with-multiple-processes-and-special-cases).
아마도 이 베이스 도커 이미지(또는 유사한 다른 이미지)는 **사용하지 않는** 것이 좋습니다.
* <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
**Kubernetes**(또는 다른 도구)를 사용하고, 클러스터 레벨에서 여러 **컨테이너**로 **복제**를 이미 설정해 둔 경우라면, 위에서 설명한 대로 **처음부터 이미지를 빌드하는 것**이 더 낫습니다: [FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi).
/// warning | 경고
그리고 여러 워커가 필요하다면, `--workers` 커맨드 라인 옵션을 간단히 사용하면 됩니다.
여러분이 이 베이스 이미지 또는 다른 유사한 이미지를 필요로 하지 **않을** 높은 가능성이 있으며, [위에서 설명된 것처럼: FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi) 처음부터 이미지를 빌드하는 것이 더 나을 수 있습니다.
/// note Technical Details | 기술 세부사항
이 Docker 이미지는 Uvicorn이 죽은 워커를 관리하고 재시작하는 기능을 지원하지 않던 시기에 만들어졌습니다. 그래서 Gunicorn과 Uvicorn을 함께 사용해야 했고, Gunicorn이 Uvicorn 워커 프로세스를 관리하고 재시작하도록 하기 위해 상당한 복잡성이 추가되었습니다.
하지만 이제 Uvicorn(그리고 `fastapi` 명령)은 `--workers`를 지원하므로, 베이스 도커 이미지를 사용하는 대신 직접 이미지를 빌드하지 않을 이유가 없습니다(코드 양도 사실상 거의 같습니다 😅).
///
이 이미지는 가능한 CPU 코어에 기반한 **몇개의 워커 프로세스**를 설정하는 **자동-튜닝** 메커니즘을 포함하고 있습니다.
## 컨테이너 이미지 배포하기 { #deploy-the-container-image }
이 이미지는 **민감한 디폴트** 값을 가지고 있지만, 여러분들은 여전히 **환경 변수** 또는 설정 파일을 통해 설정값을 수정하고 업데이트 할 수 있습니다.
또한 스크립트를 통해 <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#pre_start_path" class="external-link" target="_blank">**시작하기 전 사전 단계**</a>를 실행하는 것을 지원합니다.
/// tip | 팁
모든 설정과 옵션을 보려면, 도커 이미지 페이지로 이동합니다: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
///
### 공식 도커 이미지에 있는 프로세스 개수
이 이미지에 있는 **프로세스 개수**는 가용한 CPU **코어들**로 부터 **자동으로 계산**됩니다.
이것이 의미하는 바는 이미지가 CPU로부터 **최대한의 성능**을 **쥐어짜낸다**는 것입니다.
여러분은 이 설정 값을 **환경 변수**나 기타 방법들로 조정할 수 있습니다.
그러나 프로세스의 개수가 컨테이너가 실행되고 있는 CPU에 의존한다는 것은 또한 **소요되는 메모리의 크기** 또한 이에 의존한다는 것을 의미합니다.
그렇기 때문에, 만약 여러분의 어플리케이션이 많은 메모리를 요구하고 (예를 들어 머신러닝 모델처럼), 여러분의 서버가 CPU 코어 수는 많지만 **적은 메모리**를 가지고 있다면, 여러분의 컨테이너는 가용한 메모리보다 많은 메모리를 사용하려고 시도할 수 있으며, 결국 퍼포먼스를 크게 떨어뜨릴 수 있습니다(심지어 고장이 날 수도 있습니다). 🚨
### `Dockerfile` 생성하기
이 이미지에 기반해 `Dockerfile`을 생성하는 방법은 다음과 같습니다:
```Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
```
### 더 큰 어플리케이션
만약 여러분이 [다중 파일을 가지는 더 큰 어플리케이션](../tutorial/bigger-applications.md){.internal-link target=_blank}을 생성하는 섹션을 따랐다면, 여러분의 `Dockerfile`은 대신 이렇게 생겼을 것입니다:
```Dockerfile hl_lines="7"
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app/app
```
### 언제 사용할까
여러분들이 **쿠버네티스**(또는 유사한 다른 도구) 사용하거나 클러스터 레벨에서 다중 컨테이너를 이용해 이미 **사본**을 설정하고 있다면, 공식 베이스 이미지(또는 유사한 다른 이미지)를 사용하지 **않는** 것 좋습니다. 그런 경우에 여러분은 다음에 설명된 것 처럼 **처음부터 이미지를 빌드하는 것**이 더 낫습니다: [FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi).
이 이미지는 위의 [다중 프로세스를 가지는 컨테이너와 특수한 경우들](#containers-with-multiple-processes-and-special-cases)에서 설명된 특수한 경우에 대해서만 주로 유용할 것입니다. 예를 들어, 만약 여러분의 어플리케이션이 **충분히 단순**해서 CPU에 기반한 디폴트 프로세스 개수를 설정하는 것이 잘 작동한다면, 클러스터 레벨에서 수동으로 사본을 설정할 필요가 없을 것이고, 여러분의 앱에서 하나 이상의 컨테이너를 실행하지도 않을 것입니다. 또는 만약에 여러분이 **도커 컴포즈**로 배포하거나, 단일 서버에서 실행하거나 하는 경우에도 마찬가지입니다.
## 컨테이너 이미지 배포하기
컨테이너 (도커) 이미지를 완성한 뒤에 이를 배포하는 방법에는 여러가지 방법이 있습니다.
컨테이너(Docker) 이미지를 만든 후에는 이를 배포하는 여러 방법이 있습니다.
예를 들어:
* 단일 서버에서 **도커 컴포즈**로 배포하기
* **쿠버네티스** 클러스터로 배포하기
* 도커 스왐 모드 클러스터로 배포하기
* 노마드 같은 다른 도구로 배포하기
* 여러분의 컨테이너 이미지를 배포해주는 클라우드 서비스로 배포하기
* 단일 서버에서 **Docker Compose**로
* **Kubernetes** 클러스터로
* Docker Swarm Mode 클러스터로
* Nomad 같은 다른 도구로
* 컨테이너 이미지를 받아 배포해주는 클라우드 서비스로
## Poetry의 도커 이미지
## `uv`를 사용하는 도커 이미지 { #docker-image-with-uv }
만약 여러분들이 프로젝트 의존성을 관리하기 위해 <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a>를 사용한다면, 도커의 멀티-스테이지 빌딩을 사용할 수 있습니다:
프로젝트를 설치하고 관리하기 위해 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>를 사용한다면, <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">uv Docker guide</a>를 따를 수 있습니다.
```{ .dockerfile .annotate }
# (1)
FROM python:3.9 as requirements-stage
## 요약 { #recap }
# (2)
WORKDIR /tmp
# (3)
RUN pip install poetry
# (4)
COPY ./pyproject.toml ./poetry.lock* /tmp/
# (5)
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
# (6)
FROM python:3.9
# (7)
WORKDIR /code
# (8)
COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt
# (9)
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (10)
COPY ./app /code/app
# (11)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
```
1. 첫 스테이지로, `requirements-stage`라고 이름 붙였습니다.
2. `/tmp`를 현재의 워킹 디렉터리로 설정합니다.
이 위치에 우리는 `requirements.txt` 파일을 생성할 것입니다.
3. 이 도커 스테이지에서 Poetry를 설치합니다.
4. 파일 `pyproject.toml`와 `poetry.lock`를 `/tmp` 디렉터리로 복사합니다.
`./poetry.lock*` (`*`로 끝나는) 파일을 사용하기 때문에, 파일이 아직 사용가능하지 않더라도 고장나지 않을 것입니다.
5. `requirements.txt` 파일을 생성합니다.
6. 이것이 마지막 스테이지로, 여기에 위치한 모든 것이 마지막 컨테이너 이미지에 포함될 것입니다.
7. 현재의 워킹 디렉터리를 `/code`로 설정합니다.
8. 파일 `requirements.txt`를 `/code` 디렉터리로 복사합니다.
이 파일은 오직 이전의 도커 스테이지에만 존재하며, 때문에 복사하기 위해서 `--from-requirements-stage` 옵션이 필요합니다.
9. 생성된 `requirements.txt` 파일에 패키지 의존성을 설치합니다.
10. `app` 디렉터리를 `/code` 디렉터리로 복사합니다.
11. `uvicorn` 커맨드를 실행하여, `app.main`에서 불러온 `app` 객체를 사용하도록 합니다.
/// tip | 팁
버블 숫자를 클릭해 각 줄이 하는 일을 알아볼 수 있습니다.
///
**도커 스테이지**란 `Dockefile`의 일부로서 나중에 사용하기 위한 파일들을 생성하기 위한 **일시적인 컨테이너 이미지**로 작동합니다.
첫 스테이지는 오직 **Poetry를 설치**하고 Poetry의 `pyproject.toml` 파일로부터 프로젝트 의존성을 위한 **`requirements.txt`를 생성**하기 위해 사용됩니다.
이 `requirements.txt` 파일은 **다음 스테이지**에서 `pip`로 사용될 것입니다.
마지막 컨테이너 이미지에는 **오직 마지막 스테이지만** 보존됩니다. 이전 스테이지(들)은 버려집니다.
Poetry를 사용할 때 **도커 멀티-스테이지 빌드**를 사용하는 것이 좋은데, 여러분들의 프로젝트 의존성을 설치하기 위해 마지막 컨테이너 이미지에 **오직** `requirements.txt` 파일만 필요하지, Poetry와 그 의존성은 있을 필요가 없기 때문입니다.
이 다음 (또한 마지막) 스테이지에서 여러분들은 이전에 설명된 것과 비슷한 방식으로 방식으로 이미지를 빌드할 수 있습니다.
### TLS 종료 프록시의 배후 - Poetry
이전에 언급한 것과 같이, 만약 여러분이 컨테이너를 Nginx 또는 Traefik과 같은 TLS 종료 프록시 (로드 밸런서) 뒤에서 실행하고 있다면, 커맨드에 `--proxy-headers` 옵션을 추가합니다:
```Dockerfile
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
```
## 요약
컨테이너 시스템(예를 들어 **도커**나 **쿠버네티스**)을 사용하여 모든 **배포 개념**을 다루는 것은 꽤 간단합니다:
컨테이너 시스템(예: **Docker**, **Kubernetes**)을 사용하면 모든 **배포 개념**을 다루는 것이 상당히 단순해집니다:
* HTTPS
* 구동하기
* 시작 시 자동 실행
* 재시작
* 복제 (실행 중인 프로세스 수)
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작하기 전 단계
* 시작 전 사전 단계
대부분의 경우에서 여러분은 어떤 베이스 이미지 사용하지 않고 공식 파이썬 도커 이미지에 기반해 **처음부터 컨테이너 이미지를 빌드**할 것입니다.
대부분의 경우 베이스 이미지 사용하지 않고, 공식 Python Docker 이미지에 기반해 **처음부터 컨테이너 이미지를 빌드**하는 것이 좋습니다.
`Dockerfile`에 있는 지시 사항을 **순서대로** 다루고 **도커 캐시**를 사용하는 것으로 여러분은 **빌드 시간을 최소화**할 수 있으며, 이로써 생산성을 최대화할 수 있습니다 (그리고 지루함 피할 수 있죠) 😎
특별한 경우에는, FastAPI를 위한 공식 도커 이미지를 사용할 수도 있습니다. 🤓
`Dockerfile`에서 지시어의 **순서**와 **Docker 캐시**를 신경 쓰면 **빌드 시간을 최소화** 생산성을 최대화할 수 있습니다(그리고 지루함 피할 수 있습니다). 😎

View File

@@ -1,21 +1,23 @@
# 배포하기 - 들어가면서
# 배포 { #deployment }
**FastAPI**을 배포하는 것은 비교적 쉽습니다.
**FastAPI** 애플리케이션을 배포하는 것은 비교적 쉽습니다.
## 배포의 의미
## 배포의 의미 { #what-does-deployment-mean }
**배포**란 애플리케이션을 **사용자가 사용**할 수 있도록 하는 데 필요한 단계를 수행하는 것을 의미합니다.
애플리케이션을 **배포**한다는 것은 **사용자가 사용**할 수 있도록 하는 데 필요한 단계를 수행하는 것을 의미합니다.
**웹 API**의 경우, 일반적으로 **사용자**가 중단이나 오류 없이 애플리케이션에 효율적으로 **접근**할 수 있도록 좋은 성능, 안정성 등을 제공하는 **서버 프로그램** 함께 **원격 시스템**에 이를 설치하는 작업을 의미합니다.
**웹 API**의 경우, 일반적으로 **원격 머신**에 이를 설치하고, 좋은 성능, 안정성 등을 제공하는 **서버 프로그램** 함께 구성하여 **사용자**가 중단이나 문제 없이 애플리케이션에 효율적으로 **접근**할 수 있게 하는 것을 포함합니다.
이는 지속적으로 코드를 변경하고, 지우고, 수정하고, 개발 서버를 중지했다가 다시 시작하는 등의 **개발** 단계와 대조됩니다.
이는 지속적으로 코드를 변경하고, 망가뜨리고 고치고, 개발 서버를 중지했다가 다시 시작하는 등의 **개발** 단계와 대조됩니다.
## 배포 전략
## 배포 전략 { #deployment-strategies }
사용하는 도구나 특정 사례에 따라 여러 가지 방법이 있습니다.
구체적인 사용 사례와 사용하는 도구에 따라 여러 가지 방법이 있습니다.
배포도구들을 사용하여 직접 **서버 배포**하거나, 배포작업의 일부를 수행하는 **클라우드 서비스** 또는 다른 방법을 사용할 수도 있습니다.
여러 도구를 조합해 직접 **서버 배포**할 수도 있고, 작업의 일부를 대신해 주는 **클라우드 서비스**를 사용할 수도 있으며, 다른 가능한 선택지도 있습니다.
**FastAPI** 애플리케이션을 배포할 때 선택할 수 있는 몇 가지 주요 방법을 보여 드리겠습니다 (대부분 다른 유형의 웹 애플리케이션에도 적용됩니다).
예를 들어, FastAPI 뒤에 있는 저희 팀은 FastAPI로 작업하는 것과 같은 개발자 경험을 유지하면서, FastAPI 앱을 클라우드에 가능한 한 간소화된 방식으로 배포할 수 있도록 <a href="https://fastapicloud.com" class="external-link" target="_blank">**FastAPI Cloud**</a>를 만들었습니다.
다음 차례에 자세한 내용과 이를 위한 몇 가지 기술을 볼 수 있습니다. ✨
**FastAPI** 애플리케이션을 배포할 때 아마 염두에 두어야 할 몇 가지 주요 개념을 보여드리겠습니다(대부분은 다른 유형의 웹 애플리케이션에도 적용됩니다).
다음 섹션에서 염두에 둘 더 많은 세부사항과 이를 위한 몇 가지 기술을 볼 수 있습니다. ✨

View File

@@ -1,130 +1,87 @@
# 서버 워커 - 구니콘과 유비콘
# 서버 워커 - 워커와 함께 사용하는 Uvicorn { #server-workers-uvicorn-with-workers }
단계에서의 배포 개념들을 다시 확인해보겠습니다:
전의 배포 개념들을 다시 확인해보겠습니다:
* 보안 - HTTPS
* 서버 시작과 동시에 실행하기
* 서버 시작 실행
* 재시작
* **복제(실행 중인 프로세스의 숫자)**
* **복제(실행 중인 프로세스)**
* 메모리
* 시작하기 전의 여러 단계
* 시작하기 전의 이전 단계
지금까지 문서의 모든 튜토리얼을 참고하여 **단일 프로세스**로 Uvicorn과 같은 **서버 프로그램**을 실행했을 것입니다.
지금까지 문서의 모든 튜토리얼을 참고하면서, `fastapi` 명령처럼 Uvicorn을 실행하는 **서버 프로그램**을 사용해 **단일 프로세스**로 실행해 왔을 가능성이 큽니다.
애플리케이션을 배포할 때 **다중 코어**를 활용하고 더 많은 요청을 처리할 수 있도록 **프로세스 복제**이 필요합니다.
애플리케이션을 배포할 때 **다중 코어**를 활용하고 더 많은 요청을 처리할 수 있도록 **프로세스 복제**를 하고 싶을 가능성이 큽니다.
과정이었던 [배포 개념들](concepts.md){.internal-link target=_blank}에서 본 것처럼 여러가지 방법이 존재합니다.
장의 [배포 개념들](concepts.md){.internal-link target=_blank}에서 본 것처럼, 사용할 수 있는 전략이 여러 가지 있습니다.
지금부터 <a href="https://gunicorn.org/" class="external-link" target="_blank">**구니콘**</a>을 **유비콘 워커 프로세스**와 함께 사용하는 방법을 알려드리겠습니다.
여기서는 `fastapi` 명령을 사용하거나 `uvicorn` 명령을 직접 사용해서, **워커 프로세스**와 함께 **Uvicorn**을 사용하는 방법을 보여드리겠습니다.
/// info | 정보
만약 도커와 쿠버네티스 같은 컨테이너를 사용하고 있다면 다음 챕터 [FastAPI와 컨테이너 - 도커](docker.md){.internal-link target=_blank}에서 더 많은 정보를 얻을 수 있습니다.
Docker나 Kubernetes 같은 컨테이너를 사용하고 있다면, 다음 장인 [컨테이너에서의 FastAPI - 도커](docker.md){.internal-link target=_blank}에서 더 자세히 설명하겠습니다.
특히, 쿠버네티스에서 실행할 때는 구니콘을 사용하지 않고 대신 컨테이너당 하나의 유비콘 프로세스를 실행하는 것이 좋습니다. 이 장의 뒷부분에서 설명하겠습니다.
특히 **Kubernetes**에서 실행할 때는 워커를 사용하기보다는, 대신 **컨테이너당 단일 Uvicorn 프로세스 하나**를 실행하고 싶을 가능성이 크지만, 해당 내용은 그 장의 에서 설명하겠습니다.
///
## 구니콘과 유비콘 워커
## 여러 워커 { #multiple-workers }
**Gunicorn**은 **WSGI 표준**을 주로 사용하는 애플리케이션 서버입니다. 이것은 구니콘이 플라스크와 쟝고와 같은 애플리케이션을 제공할 수 있다는 것을 의미합니다. 구니콘 자체는 최신 **<a href="https://asgi.readthedocs.io/en/latest/" class="external-link" target="_blank">ASGI 표준</a>**을 사용하기 때문에 FastAPI와 호환되지 않습니다.
`--workers` 커맨드라인 옵션으로 여러 워커를 시작할 수 있습니다:
하지만 구니콘은 **프로세스 관리자**역할을 하고 사용자에게 특정 **워커 프로세스 클래스**를 알려줍니다. 그런 다음 구니콘은 해당 클래스를 사용하여 하나 이상의 **워커 프로세스**를 시작합니다.
//// tab | `fastapi`
그리고 **유비콘**은 **구니콘과 호환되는 워커 클래스**가 있습니다.
이 조합을 사용하여 구니콘은 **프로세스 관리자** 역할을 하며 **포트**와 **IP**를 관찰하고, **유비콘 클래스**를 실행하는 워커 프로세스로 통신 정보를 **전송**합니다.
그리고 나서 구니콘과 호환되는 **유비콘 워커** 클래스는 구니콘이 보낸 데이터를 FastAPI에서 사용하기 위한 ASGI 표준으로 변환하는 일을 담당합니다.
## 구니콘과 유비콘 설치하기
`fastapi` 명령을 사용한다면:
<div class="termy">
```console
$ pip install "uvicorn[standard]" gunicorn
$ <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:solid">main.py</u>
---> 100%
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production 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://0.0.0.0: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://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started parent process <b>[</b><font color="#34E2E2"><b>27365</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27368</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27369</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27370</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27367</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> Waiting for application startup.
<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> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
이 명령어는 유비콘 `standard` 추가 패키지(좋은 성능을 위한)와 구니콘을 설치할 것입니다.
////
## 구니콘을 유비콘 워커와 함께 실행하기
//// tab | `uvicorn`
설치 후 구니콘 실행하기:
<div class="termy">
```console
$ gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
[19499] [INFO] Starting gunicorn 20.1.0
[19499] [INFO] Listening at: http://0.0.0.0:80 (19499)
[19499] [INFO] Using worker: uvicorn.workers.UvicornWorker
[19511] [INFO] Booting worker with pid: 19511
[19513] [INFO] Booting worker with pid: 19513
[19514] [INFO] Booting worker with pid: 19514
[19515] [INFO] Booting worker with pid: 19515
[19511] [INFO] Started server process [19511]
[19511] [INFO] Waiting for application startup.
[19511] [INFO] Application startup complete.
[19513] [INFO] Started server process [19513]
[19513] [INFO] Waiting for application startup.
[19513] [INFO] Application startup complete.
[19514] [INFO] Started server process [19514]
[19514] [INFO] Waiting for application startup.
[19514] [INFO] Application startup complete.
[19515] [INFO] Started server process [19515]
[19515] [INFO] Waiting for application startup.
[19515] [INFO] Application startup complete.
```
</div>
각 옵션이 무엇을 의미하는지 살펴봅시다:
* 이것은 유비콘과 똑같은 문법입니다. `main`은 파이썬 모듈 네임 "`main`"을 의미하므로 `main.py`파일을 뜻합니다. 그리고 `app`**FastAPI** 어플리케이션이 들어 있는 변수의 이름입니다.
* `main:app`이 파이썬의 `import` 문법과 흡사한 면이 있다는 걸 알 수 있습니다:
```Python
from main import app
```
* 곧, `main:app`안에 있는 콜론의 의미는 파이썬에서 `from main import app`에서의 `import`와 같습니다.
* `--workers`: 사용할 워커 프로세스의 개수이며 숫자만큼의 유비콘 워커를 실행합니다. 이 예제에서는 4개의 워커를 실행합니다.
* `--worker-class`: 워커 프로세스에서 사용하기 위한 구니콘과 호환되는 워커클래스.
* 이런식으로 구니콘이 import하여 사용할 수 있는 클래스를 전달해줍니다:
```Python
import uvicorn.workers.UvicornWorker
```
* `--bind`: 구니콘이 관찰할 IP와 포트를 의미합니다. 콜론 (`:`)을 사용하여 IP와 포트를 구분합니다.
* 만약에 `--bind 0.0.0.0:80` (구니콘 옵션) 대신 유비콘을 직접 실행하고 싶다면 `--host 0.0.0.0`과 `--port 80`을 사용해야 합니다.
출력에서 각 프로세스에 대한 **PID** (process ID)를 확인할 수 있습니다. (단순한 숫자입니다)
출력 내용:
* 구니콘 **프로세스 매니저**는 PID `19499`로 실행됩니다. (직접 실행할 경우 숫자가 다를 수 있습니다)
* 다음으로 `Listening at: http://0.0.0.0:80`을 시작합니다.
* 그런 다음 사용해야할 `uvicorn.workers.UvicornWorker`의 워커클래스를 탐지합니다.
* 그리고 PID `19511`, `19513`, `19514`, 그리고 `19515`를 가진 **4개의 워커**를 실행합니다.
또한 구니콘은 워커의 수를 유지하기 위해 **죽은 프로세스**를 관리하고 **재시작**하는 작업을 책임집니다. 이것은 이번 장 상단 목록의 **재시작** 개념을 부분적으로 도와주는 것입니다.
그럼에도 불구하고 필요할 경우 외부에서 **구니콘을 재시작**하고, 혹은 **서버를 시작할 때 실행**할 수 있도록 하고 싶어할 것입니다.
## 유비콘과 워커
유비콘은 몇 개의 **워커 프로세스**와 함께 실행할 수 있는 선택지가 있습니다.
그럼에도 불구하고, 유비콘은 워커 프로세스를 다루는 데에 있어서 구니콘보다 더 제한적입니다. 따라서 이 수준(파이썬 수준)의 프로세스 관리자를 사용하려면 구니콘을 프로세스 관리자로 사용하는 것이 좋습니다.
보통 이렇게 실행할 수 있습니다:
`uvicorn` 명령을 직접 사용하는 편이 좋다면:
<div class="termy">
@@ -148,36 +105,35 @@ $ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
</div>
새로운 옵션인 `--workers`은 유비콘에게 4개의 워커 프로세스를 사용한다고 알려줍니다.
////
각 프로세스의 **PID**를 확인할 수 있습니다. `27365`는 상위 프로세스(**프로세스 매니저**), 그리고 각각의 워커프로세스는 `27368`, `27369`, `27370`, 그리고 `27367`입니다.
여기서 새로운 옵션은 `--workers`뿐이며, Uvicorn에게 워커 프로세스 4개를 시작하라고 알려줍니다.
## 배포 개념들
또한 각 프로세스의 **PID**도 확인할 수 있는데, 상위 프로세스(이것이 **프로세스 관리자**)의 PID는 `27365`이고, 각 워커 프로세스의 PID는 `27368`, `27369`, `27370`, `27367`입니다.
여기에서는 **유비콘 워커 프로세스**를 관리하는 **구니콘**(또는 유비콘)을 사용하여 애플리케이션을 **병렬화**하고, CPU **멀티 코어**의 장점을 활용하고, **더 많은 요청**을 처리할 수 있는 방법을 살펴보았습니다.
## 배포 개념들 { #deployment-concepts }
워커를 사용하는 것은 배포 개념 목록에서 주로 **복제본** 부분과 **재시작**에 약간 도움이 되지만 다른 배포 개념들도 다루어야 합니다:
여기서는 여러 **워커**를 사용해 애플리케이션 실행을 **병렬화**하고, CPU의 **다중 코어**를 활용하며, **더 많은 요청**을 제공할 수 있는 방법을 살펴봤습니다.
위의 배포 개념 목록에서 워커를 사용하는 것은 주로 **복제** 부분에 도움이 되고, **재시작**에도 약간 도움이 되지만, 나머지 항목들도 여전히 신경 써야 합니다:
* **보안 - HTTPS**
* **서버 시작과 동시에 실행하기**
* **서버 시작 실행**
* ***재시작***
* 복제(실행 중인 프로세스의 숫자)
* 복제(실행 중인 프로세스)
* **메모리**
* **시작하기 전의 여러 단계**
* **시작하기 전의 이전 단계**
## 컨테이너와 도커 { #containers-and-docker }
## 컨테이너와 도커
다음 장인 [컨테이너에서의 FastAPI - 도커](docker.md){.internal-link target=_blank}에서는 다른 **배포 개념들**을 처리하기 위해 사용할 수 있는 몇 가지 전략을 설명하겠습니다.
다음 장인 [FastAPI와 컨테이너 - 도커](docker.md){.internal-link target=_blank}에서 다른 **배포 개념들**을 다루는 전략들을 알려드리겠습니다.
단일 Uvicorn 프로세스를 실행하기 위해, **처음부터 여러분만의 이미지를 직접 빌드**하는 방법을 보여드리겠습니다. 이는 간단한 과정이며, **Kubernetes** 같은 분산 컨테이너 관리 시스템을 사용할 때 아마도 이렇게 하고 싶을 것입니다.
또한 간단한 케이스에서 사용할 수 있는, **구니콘과 유비콘 워커**가 포함돼 있는 **공식 도커 이미지**와 함께 몇 가지 기본 구성을 보여드리겠습니다.
## 요약 { #recap }
그리고 단일 유비콘 프로세스(구니콘 없이)를 실행할 수 있도록 **사용자 자신의 이미지를 처음부터 구축**하는 방법도 보여드리겠습니다. 이는 간단한 과정이며, **쿠버네티스**와 같은 분산 컨테이너 관리 시스템을 사용할 때 수행할 작업입니다.
`fastapi` 또는 `uvicorn` 명령에서 `--workers` CLI 옵션을 사용해 여러 워커 프로세스를 실행하면, **멀티 코어 CPU**를 활용해 **여러 프로세스를 병렬로 실행**할 수 있습니다.
## 요약
다른 배포 개념들을 직접 처리하면서 **자체 배포 시스템**을 구축하는 경우, 이러한 도구와 아이디어를 활용할 수 있습니다.
당신은 **구니콘**(또는 유비콘)을 유비콘 워커와 함께 프로세스 관리자로 사용하여 **멀티-코어 CPU**를 활용하는 **멀티 프로세스를 병렬로 실행**할 수 있습니다.
다른 배포 개념을 직접 다루면서 **자신만의 배포 시스템**을 구성하는 경우 이러한 도구와 개념들을 활용할 수 있습니다.
다음 장에서 컨테이너(예: 도커 및 쿠버네티스)와 함께하는 **FastAPI**에 대해 배워보세요. 이러한 툴에는 다른 **배포 개념**들을 간단히 해결할 수 있는 방법이 있습니다. ✨
다음 장에서 컨테이너(예: Docker 및 Kubernetes)와 함께 사용하는 **FastAPI**에 대해 알아보세요. 해당 도구들이 다른 **배포 개념들**도 간단히 해결하는 방법이 있다는 것을 확인할 수 있습니다.

View File

@@ -1,94 +1,93 @@
# FastAPI 버전들에 대하여
# FastAPI 버전들에 대하여 { #about-fastapi-versions }
**FastAPI** 는 이미 많은 응용 프로그램과 시스템들을 만드는데 사용되고 있습니다. 그리고 100%의 테스트 정확성을 가지고 있습니다. 하지만 이것은 아직까지도 빠르게 발전하고 있습니다.
**FastAPI**는 이미 많은 애플리케이션과 시스템에서 프로덕션으로 사용되고 있습니다. 그리고 테스트 커버리지는 100%로 유지됩니다. 하지만 개발은 여전히 빠르게 진행되고 있습니다.
새로운 특징들이 빈번하게 추가되고, 오류들이 지속적으로 수정되고 있습니다. 그리고 코드 계속적으로 향상되고 있습니다.
새로운 기능이 자주 추가되고, 버그가 규칙적으로 수정되며, 코드 계속해서 지속적으로 개선되고 있습니다.
이것이 아직도 최신 버전이 `0.x.x`이유입니다. 이것은 각각의 버전이 잠재적으로 변할 수 있다는 것을 보여줍니다. 이는 <a href="https://semver.org/" class="external-link" target="_blank">유의적 버전</a> 관습을 따릅니다.
그래서 현재 버전이 아직 `0.x.x`입니다. 이는 각 버전이 잠재적으로 하위 호환성이 깨지는 변경을 포함할 수 있음을 반영합니다. 이는 <a href="https://semver.org/" class="external-link" target="_blank">Semantic Versioning</a> 관례를 따릅니다.
지금 바로 **FastAPI**로 응용 프로그램을 만들 수 있습니다. 이때 (아마 지금까지 그래 왔던 것처럼), 사용하는 버전이 코드와 잘 맞는지 확인해야합니다.
지금 바로 **FastAPI**로 프로덕션 애플리케이션을 만들 수 있습니다(그리고 아마도 한동안 그렇게 해오셨을 것입니다). 다만 나머지 코드와 함께 올바르게 동작하는 버전을 사용하고 있는지 확인하기만 하면 됩니다.
## `fastapi` 버전을 표시
## `fastapi` 버전을 고정하기 { #pin-your-fastapi-version }
가장 먼저 해야할 응용 프로그램이 잘 작동하는 가장 최신의 구체적인 **FastAPI** 버전을 표시하는 것입니다.
가장 먼저 해야 여러분의 애플리케이션에서 올바르게 동작하는 것으로 알고 있는 **FastAPI**의 최신 구체 버전에 맞춰 사용 중인 버전을 "고정(pin)"하는 것입니다.
예를 들어, 응용 프로그램에 `0.45.0` 버전을 사용다고 가정니다.
예를 들어, 앱에서 `0.112.0` 버전을 사용하고 있다고 가정해 보겠습니다.
만약에 `requirements.txt` 파일을 사용다면, 다음과 같이 버전을 명세할 수 있습니다:
`requirements.txt` 파일을 사용다면 다음과 같이 버전을 지정할 수 있습니다:
```txt
fastapi==0.45.0
fastapi[standard]==0.112.0
```
것은 `0.45.0` 버전을 사용다는 것을 의미니다.
는 정확히 `0.112.0` 버전을 사용다는 의미니다.
또는 다음과 같이 표시할 수 있습니다:
또는 다음과 같이 고정할 수 있습니다:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
이는 `0.112.0` 이상이면서 `0.113.0` 미만의 버전을 사용한다는 의미입니다. 예를 들어 `0.112.2` 버전도 허용됩니다.
`uv`, Poetry, Pipenv 등 다른 도구로 설치를 관리한다면, 모두 패키지의 특정 버전을 정의할 수 있는 방법을 제공합니다.
## 이용 가능한 버전들 { #available-versions }
사용 가능한 버전(예: 현재 최신 버전이 무엇인지 확인하기 위해)은 [Release Notes](../release-notes.md){.internal-link target=_blank}에서 확인할 수 있습니다.
## 버전들에 대해 { #about-versions }
Semantic Versioning 관례에 따르면, `1.0.0` 미만의 어떤 버전이든 잠재적으로 하위 호환성이 깨지는 변경을 추가할 수 있습니다.
FastAPI는 또한 "PATCH" 버전 변경은 버그 수정과 하위 호환성이 깨지지 않는 변경을 위한 것이라는 관례를 따릅니다.
/// tip | 팁
"PATCH"는 마지막 숫자입니다. 예를 들어 `0.2.3`에서 PATCH 버전은 `3`입니다.
///
따라서 다음과 같이 버전을 고정할 수 있어야 합니다:
```txt
fastapi>=0.45.0,<0.46.0
```
이것은 `0.45.0` 버전과 같거나 높으면서 `0.46.0` 버전 보다는 낮은 버전을 사용했다는 것을 의미합니다. 예를 들어, `0.45.2` 버전과 같은 경우는 해당 조건을 만족합니다.
만약에 Poetry, Pipenv, 또는 그밖의 다양한 설치 도구를 사용한다면, 패키지에 구체적인 버전을 정의할 수 있는 방법을 가지고 있을 것입니다.
## 이용가능한 버전들
[Release Notes](../release-notes.md){.internal-link target=_blank}를 통해 사용할 수 있는 버전들을 확인할 수 있습니다.(예를 들어, 가장 최신의 버전을 확인할 수 있습니다.)
## 버전들에 대해
유의적 버전 관습을 따라서, `1.0.0` 이하의 모든 버전들은 잠재적으로 급변할 수 있습니다.
FastAPI는 오류를 수정하고, 일반적인 변경사항을 위해 "패치"버전의 관습을 따릅니다.
하위 호환성이 깨지는 변경과 새로운 기능은 "MINOR" 버전에 추가됩니다.
/// tip | 팁
여기서 말하는 "패치"란 버전의 마지막 숫자로, 예를 들어 `0.2.3` 버전에서 "패치"는 `3`을 의미합니다.
"MINOR"는 가운데 숫자입니다. 예를 들어 `0.2.3`에서 MINOR 버전은 `2`니다.
///
따라서 다음과 같이 버전을 표시할 수 있습니다:
## FastAPI 버전 업그레이드하기 { #upgrading-the-fastapi-versions }
앱에 테스트를 추가해야 합니다.
**FastAPI**에서는 매우 쉽습니다(Starlette 덕분에). 문서를 확인해 보세요: [Testing](../tutorial/testing.md){.internal-link target=_blank}
테스트를 갖춘 뒤에는 **FastAPI** 버전을 더 최신 버전으로 업그레이드하고, 테스트를 실행하여 모든 코드가 올바르게 동작하는지 확인하세요.
모든 것이 동작하거나 필요한 변경을 한 뒤 모든 테스트가 통과한다면, `fastapi`를 그 새로운 최신 버전으로 고정할 수 있습니다.
## Starlette에 대해 { #about-starlette }
`starlette`의 버전은 고정하지 않는 것이 좋습니다.
서로 다른 **FastAPI** 버전은 Starlette의 특정한 더 새로운 버전을 사용하게 됩니다.
따라서 **FastAPI**가 올바른 Starlette 버전을 사용하도록 그냥 두면 됩니다.
## Pydantic에 대해 { #about-pydantic }
Pydantic은 자체 테스트에 **FastAPI**에 대한 테스트도 포함하고 있으므로, Pydantic의 새 버전(`1.0.0` 초과)은 항상 FastAPI와 호환됩니다.
여러분에게 맞는 `1.0.0` 초과의 어떤 Pydantic 버전으로든 고정할 수 있습니다.
예를 들어:
```txt
fastapi>=0.45.0,<0.46.0
```
수정된 사항과 새로운 요소들이 "마이너" 버전에 추가되었습니다.
/// tip | 팁
"마이너"란 버전 넘버의 가운데 숫자로, 예를 들어서 `0.2.3`의 "마이너" 버전은 `2`입니다.
///
## FastAPI 버전의 업그레이드
응용 프로그램을 검사해야합니다.
(Starlette 덕분에), **FastAPI** 를 이용하여 굉장히 쉽게 할 수 있습니다. [Testing](../tutorial/testing.md){.internal-link target=_blank}문서를 확인해 보십시오:
검사를 해보고 난 후에, **FastAPI** 버전을 더 최신으로 업그레이드 할 수 있습니다. 그리고 코드들이 테스트에 정상적으로 작동하는지 확인을 해야합니다.
만약에 모든 것이 정상 작동하거나 필요한 부분을 변경하고, 모든 검사를 통과한다면, 새로운 버전의 `fastapi`를 표시할 수 있습니다.
## Starlette에 대해
`starlette`의 버전은 표시할 수 없습니다.
서로다른 버전의 **FastAPI**가 구체적이고 새로운 버전의 Starlette을 사용할 것입니다.
그러므로 **FastAPI**가 알맞은 Starlette 버전을 사용하도록 하십시오.
## Pydantic에 대해
Pydantic은 **FastAPI** 를 위한 검사를 포함하고 있습니다. 따라서, 새로운 버전의 Pydantic(`1.0.0`이상)은 항상 FastAPI와 호환됩니다.
작업을 하고 있는 `1.0.0` 이상의 모든 버전과 `2.0.0` 이하의 Pydantic 버전을 표시할 수 있습니다.
예를 들어 다음과 같습니다:
```txt
pydantic>=1.2.0,<2.0.0
pydantic>=2.7.0,<3.0.0
```

View File

@@ -1,4 +1,4 @@
# 환경 변수
# 환경 변수 { #environment-variables }
/// tip | 팁
@@ -6,11 +6,11 @@
///
환경 변수는 파이썬 코드의 **바깥**인, **운영 체제**에 존재하는 변수입니다. 파이썬 코드 다른 프로그램에서 읽을 수 있습니다.
환경 변수(또는 "**env var**"라고도 합니다)는 파이썬 코드의 **바깥**인, **운영 체제**에 존재하는 변수이며, 파이썬 코드(또는 다른 프로그램에서도)에서 읽을 수 있습니다.
환경 변수는 애플리케이션 **설정**을 처리하거나, 파이썬의 **설치** 과정의 일부로 유용니다.
환경 변수는 애플리케이션 **설정**을 처리하거나, 파이썬의 **설치** 과정의 일부로 유용할 수 있습니다.
## 환경 변수를 만들고 사용하기
## 환경 변수를 만들고 사용하기 { #create-and-use-env-vars }
파이썬 없이도, **셸 (터미널)** 에서 환경 변수를 **생성** 하고 사용할 수 있습니다.
@@ -50,9 +50,9 @@ Hello Wade Wilson
////
## 파이썬에서 환경 변수 읽기
## 파이썬에서 env var 읽기 { #read-env-vars-in-python }
파이썬 **바깥**인 터미널에서(다른 도구로도 가능) 환경 변수를 생성도 할 수도 있고, 이를 **파이썬에서 읽을 수 있습니다.**
파이썬 **바깥**인 터미널에서(또는 다른 어떤 방법으로든) 환경 변수를 만들고, 그런 다음 **파이썬에서 읽을 수 있습니다**.
예를 들어 다음과 같은 `main.py` 파일이 있다고 합시다:
@@ -67,7 +67,7 @@ print(f"Hello {name} from Python")
<a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> 의 두 번째 인자는 반환할 기본값입니다.
여기서는 `"World"`를 넣었기에 기본값으로써 사용됩니다. 넣지 않으면 `None` 이 기본값으로 사용됩니다.
제공하지 않으면 기본값은 `None`이며, 여기서는 사용할 기본값으로 `"World"`를 제공합니다.
///
@@ -129,7 +129,7 @@ Hello Wade Wilson from Python
환경변수는 코드 바깥에서 설정될 수 있지만, 코드에서 읽을 수 있고, 나머지 파일과 함께 저장(`git`에 커밋)할 필요가 없으므로, 구성이나 **설정** 에 사용하는 것이 일반적입니다.
**특정 프로그램 호출**에 대해서만 사용할 수 있는 환경 변수를 만들 수도 있습니다. 해당 프로그램에서만 사용할 수 있고, 해당 프로그램이 실행되는 동안만 사용할 수 있습니다.
또한 **특정 프로그램 호출**에 대해서만 사용할 수 있는 환경 변수를 만들 수도 있는데, 해당 프로그램에서만 사용할 수 있고, 해당 프로그램이 실행되는 동안만 사용할 수 있습니다.
그렇게 하려면 프로그램 바로 앞, 같은 줄에 환경 변수를 만들어야 합니다:
@@ -157,17 +157,17 @@ Hello World from Python
///
## 타입과 검증
## 타입과 검증 { #types-and-validation }
이 환경변수들은 오직 **텍스트 문자열**로만 처리할 수 있습니다. 텍스트 문자열은 파이썬 외부에 있으며 다른 프로그램 및 나머지 시스템(Linux, Windows, macOS 다른 운영 체제)과 호환되어야 합니다.
이 환경변수들은 오직 **텍스트 문자열**로만 처리할 수 있습니다. 텍스트 문자열은 파이썬 외부에 있으며 다른 프로그램 및 나머지 시스템(그리고 Linux, Windows, macOS 같은 서로 다른 운영 체제에서도)과 호환되어야 합니다.
즉, 파이썬에서 환경 변수로부터 읽은 **모든 값**은 **`str`**이 되고, 다른 타입으로의 변환이나 검증은 코드에서 수행해야 합니다.
**애플리케이션 설정**을 처리하기 위한 환경 변수 사용에 대한 자세한 내용은 [고급 사용자 가이드 - 설정 및 환경 변수](./advanced/settings.md){.internal-link target=\_blank} 에서 확인할 수 있습니다.
**애플리케이션 설정**을 처리하기 위한 환경 변수 사용에 대한 자세한 내용은 [고급 사용자 가이드 - 설정 및 환경 변수](./advanced/settings.md){.internal-link target=_blank} 에서 확인할 수 있습니다.
## `PATH` 환경 변수
## `PATH` 환경 변수 { #path-environment-variable }
**`PATH`**라고 불리는, **특별한** 환경변수가 있습니다. 운영체제(Linux, Windows, macOS 등)에서 실행할 프로그램을 찾기위해 사용됩니다.
**`PATH`**라고 불리는, **특별한** 환경변수가 있습니다. 운영체제(Linux, macOS, Windows)에서 실행할 프로그램을 찾기위해 사용됩니다.
변수 `PATH`의 값은 Linux와 macOS에서는 콜론 `:`, Windows에서는 세미콜론 `;`으로 구분된 디렉토리로 구성된 긴 문자열입니다.
@@ -181,11 +181,11 @@ Hello World from Python
이는 시스템이 다음 디렉토리에서 프로그램을 찾아야 함을 의미합니다:
- `/usr/local/bin`
- `/usr/bin`
- `/bin`
- `/usr/sbin`
- `/sbin`
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
@@ -197,9 +197,9 @@ C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System3
이는 시스템이 다음 디렉토리에서 프로그램을 찾아야 함을 의미합니다:
- `C:\Program Files\Python312\Scripts`
- `C:\Program Files\Python312`
- `C:\Windows\System32`
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
@@ -209,7 +209,7 @@ C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System3
찾으면 **사용합니다**. 그렇지 않으면 **다른 디렉토리**에서 계속 찾습니다.
### 파이썬 설치와 `PATH` 업데이트
### 파이썬 설치와 `PATH` 업데이트 { #installing-python-and-updating-the-path }
파이썬을 설치할 때, 아마 `PATH` 환경 변수를 업데이트 할 것이냐고 물어봤을 겁니다.
@@ -285,13 +285,13 @@ $ C:\opt\custompython\bin\python
////
이 정보는 [가상 환경](virtual-environments.md){.internal-link target=\_blank} 에 대해 알아볼 때 유용할 것입니다.
이 정보는 [가상 환경](virtual-environments.md){.internal-link target=_blank} 에 대해 알아볼 때 유용할 것입니다.
## 결론
## 결론 { #conclusion }
이 문서를 읽고 **환경 변수**가 무엇이고 파이썬에서 어떻게 사용하는지 기본적으로 이해하셨을 겁니다.
이 문서를 통해 **환경 변수**가 무엇이고 파이썬에서 어떻게 사용하는지 기본적으로 이해하셨을 겁니다.
또한 <a href="https://ko.wikipedia.org/wiki/환경_변수" class="external-link" target="_blank">환경 변수에 대한 위키피디아(한국어)</a>에서 이에 대해 자세히 알아볼 수 있습니다.
또한 <a href="https://en.wikipedia.org/wiki/Environment_variable" class="external-link" target="_blank">환경 변수에 대한 위키피디아</a>에서 이에 대해 자세히 알아볼 수 있습니다.
많은 경우에서, 환경 변수가 어떻게 유용하고 적용 가능한지 바로 명확하게 알 수는 없습니다. 하지만 개발할 때 다양한 시나리오에서 계속 나타나므로 이에 대해 아는 것이 좋습니다.

View File

@@ -1,46 +1,41 @@
# 조건부적인 OpenAPI
# 조건부 OpenAPI { #conditional-openapi }
필요한 경우, 설정 및 환경 변수를 사용하여 환경에 따라 조건부로 OpenAPI를 구성하고 완전히 비활성화할 수도 있습니다.
필요한 경우, 설정 및 환경 변수를 사용하여 환경에 따라 OpenAPI를 조건부로 구성하고 완전히 비활성화할 수도 있습니다.
## 보안, API 및 docs에 대해서
## 보안, API 및 docs에 대해서 { #about-security-apis-and-docs }
프로덕션에서, 문서화된 사용자 인터페이스(UI)를 숨기는 것이 API를 보호하는 방법이 *되어서는 안 됩니다*.
이는 API에 추가적인 보안을 제공하지 않으며, *경로 작업*은 여전히 동일한 위치에서 사용 할 수 있습니다.
이는 API에 추가적인 보안을 제공하지 않으며, *경로 처리*는 여전히 동일한 위치에서 사용 할 수 있습니다.
코드에 보안 결함이 있다면, 그 결함은 여전히 존재할 것입니다.
문서를 숨기는 것은 API와 상호작용하는 방법을 이해하기 어렵게 만들며, 프로덕션에서 디버깅을 더 어렵게 만들 수 있습니다. 이는 단순히 <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">'모호성에 의한 보안'</a>의 한 형태로 간주될 수 있습니다.
문서를 숨기는 것은 API와 상호작용하는 방법을 이해하기 어렵게 만들며, 프로덕션에서 디버깅을 더 어렵게 만들 수 있습니다. 이는 단순히 <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">Security through obscurity</a>의 한 형태로 간주될 수 있습니다.
API를 보호하고 싶다면, 예를 들어 다음과 같은 더 나은 방법들이 있습니다:
* 요청 본문과 응답에 대해 잘 정의된 Pydantic 모델을 사용하도록 하세요.
* 요청 본문과 응답에 대해 잘 정의된 Pydantic 모델이 있는지 확인하세요.
* 종속성을 사용하여 필요한 권한과 역할을 구성하세요.
* 평문 비밀번호를 절대 저장하지 말고, 오직 암호화된 비밀번호만 저장하세요.
* Passlib과 JWT 토큰과 같은 잘 알려진 암호화 도구들을 구현하고 사용하세요.
* 평문 비밀번호를 절대 저장하지 말고, 비밀번호 해시만 저장하세요.
* pwdlib와 JWT 토큰 등과 같은 잘 알려진 암호화 도구들을 구현하고 사용하세요.
* 필요한 곳에 OAuth2 범위를 사용하여 더 세분화된 권한 제어를 추가하세요.
* ...등등.
* 등등....
그럼에도 불구하고, 특정 환경(예: 프로덕션)에서 또는 환경 변수의 설정에 따라 API docs를 비활성화해야 하는 매우 특정한 사용 사례가 있을 수 있습니다.
그럼에도 불구하고, 특정 환경(예: 프로덕션)에서 또는 환경 변수의 설정에 따라 API 문서를 비활성화해야 하는 매우 특정한 사용 사례가 있을 수 있습니다.
## 설정 및 환경변수의 조건부 OpenAPI { #conditional-openapi-from-settings-and-env-vars }
## 설정 및 환경변수의 조건부 OpenAPI
동일한 Pydantic 설정을 사용하여 생성된 OpenAPI 및 문서 UI를 쉽게 구성할 수 있습니다.
동일한 Pydantic 설정을 사용하여 생성된 OpenAPI 및 docs UI를 쉽게 구성할 수 있습니다.
예를 들어:
{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
여기서 `openapi_url` 설정을 기본값인 `"/openapi.json"`으로 선언합니다.
그런 뒤, 우리는 `FastAPI` 앱을 만들 때 그것을 사용합니다.
환경 변수 `OPENAPI_URL`을 빈 문자열로 설정하여 OpenAPI(문서 UI 포함)를 비활성화할 수도 있습니다. 예를 들어:
그런 다음 환경 변수 `OPENAPI_URL`을 빈 문자열로 설정하여 OpenAPI(UI docs 포함)를 비활성화할 수도 있습니다. 예를 들어:
<div class="termy">

View File

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

View File

@@ -1,11 +1,11 @@
# FastAPI
# FastAPI { #fastapi }
<style>
.md-content .md-typeset h1 { display: none; }
</style>
<p align="center">
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
<a href="https://fastapi.tiangolo.com/ko"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>FastAPI 프레임워크, 고성능, 간편한 학습, 빠른 코드 작성, 준비된 프로덕션</em>
@@ -27,7 +27,7 @@
---
**문서**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**문서**: <a href="https://fastapi.tiangolo.com/ko" target="_blank">https://fastapi.tiangolo.com</a>
**소스 코드**: <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
@@ -37,36 +37,41 @@ FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트
주요 특징으로:
* **빠름**: (Starlette과 Pydantic 덕분에) **NodeJS** 및 **Go**와 대등할 정도로 매우 높은 성능. [사용 가능한 가장 빠른 파이썬 프레임워크 중 하나](#_11).
* **빠름**: (Starlette과 Pydantic 덕분에) **NodeJS** 및 **Go**와 대등할 정도로 매우 높은 성능. [사용 가능한 가장 빠른 파이썬 프레임워크 중 하나](#performance).
* **빠른 코드 작성**: 약 200%에서 300%까지 기능 개발 속도 증가. *
* **적은 버그**: 사람(개발자)에 의한 에러 약 40% 감소. *
* **직관적**: 훌륭한 편집기 지원. 모든 곳에서 <abbr title="also known as auto-complete, autocompletion, IntelliSense">자동완성</abbr>. 적은 디버깅 시간.
* **쉬움**: 쉽게 사용하고 배우도록 설계. 적은 문서 읽기 시간.
* **짧음**: 코드 중복 최소화. 각 매개변수 선언의 여러 기능. 적은 버그.
* **견고함**: 준비된 프로덕션 용 코드를 얻으십시오. 자동 대화형 문서와 함께.
* **표준 기반**: API에 대한 (완전히 호환되는) 개방형 표준 기반: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (이전에 Swagger로 알려졌던) 및 <a href="http://json-schema.org/" class="external-link" target="_blank">JSON 스키마</a>.
* **표준 기반**: API에 대한 (완전히 호환되는) 개방형 표준 기반: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (이전에 Swagger로 알려졌던) 및 <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* 내부 개발팀의 프로덕션 애플리케이션을 빌드한 테스트에 근거한 측정</small>
## 골드 스폰서
## 스폰서 { #sponsors }
<!-- sponsors -->
{% if sponsors %}
### 키스톤 스폰서 { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
### 골드 및 실버 스폰서 { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
<!-- /sponsors -->
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">다른 스폰서</a>
<a href="https://fastapi.tiangolo.com/ko/fastapi-people/#sponsors" class="external-link" target="_blank">다른 스폰서</a>
## 의견들
## 의견들 { #opinions }
"_[...] 저는 요즘 **FastAPI**를 많이 사용하고 있습니다. [...] 사실 우리 팀의 **마이크로소프트 ML 서비스** 전부를 바꿀 계획입니다. 그중 일부는 핵심 **Windows**와 몇몇의 **Office** 제품들이 통합되고 있습니다._"
@@ -94,7 +99,7 @@ FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트
"_솔직히, 당신이 만든 것은 매우 견고하고 세련되어 보입니다. 여러 면에서 **Hug**가 이렇게 되었으면 합니다 - 그걸 만든 누군가를 보는 것은 많은 영감을 줍니다._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> 제작자</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://github.com/hugapi/hug" target="_blank">Hug</a> 제작자</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
@@ -106,50 +111,54 @@ FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트
---
## **Typer**, FastAPI의 CLI
"_프로덕션 Python API를 만들고자 한다면, 저는 **FastAPI**를 강력히 추천합니다. **아름답게 설계**되었고, **사용이 간단**하며, **확장성이 매우 뛰어나**고, 우리의 API 우선 개발 전략에서 **핵심 구성 요소**가 되었으며 Virtual TAC Engineer 같은 많은 자동화와 서비스를 이끌고 있습니다._"
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
---
## FastAPI 미니 다큐멘터리 { #fastapi-mini-documentary }
2025년 말에 공개된 <a href="https://www.youtube.com/watch?v=mpR8ngthqiE" class="external-link" target="_blank">FastAPI 미니 다큐멘터리</a>가 있습니다. 온라인에서 시청할 수 있습니다:
<a href="https://www.youtube.com/watch?v=mpR8ngthqiE" target="_blank"><img src="https://fastapi.tiangolo.com/img/fastapi-documentary.jpg" alt="FastAPI Mini Documentary"></a>
## **Typer**, CLI를 위한 FastAPI { #typer-the-fastapi-of-clis }
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
웹 API 대신 터미널에서 사용할 <abbr title="Command Line Interface">CLI</abbr> 앱을 만들고 있다면, <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>를 확인해 보십시오.
**Typer**는 FastAPI의 동생입니다. 그리고 **FastAPI의 CLI**가 되기 위해 생겼습니다. ⌨️ 🚀
**Typer**는 FastAPI의 동생입니다. 그리고 **CLI를 위한 FastAPI**가 되기 위해 생겼습니다. ⌨️ 🚀
## 요구사항
## 요구사항 { #requirements }
FastAPI는 거인들의 어깨 위에 서 있습니다:
* 웹 부분을 위한 <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a>.
* 데이터 부분을 위한 <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>.
## 설치
## 설치 { #installation }
<a href="https://fastapi.tiangolo.com/ko/virtual-environments/" class="external-link" target="_blank">가상 환경</a>을 생성하고 활성화한 다음 FastAPI를 설치하세요:
<div class="termy">
```console
$ pip install fastapi
$ pip install "fastapi[standard]"
---> 100%
```
</div>
프로덕션을 위해 <a href="http://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a> 또는 <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>과 같은 ASGI 서버도 필요할 겁니다.
**Note**: 모든 터미널에서 동작하도록 `"fastapi[standard]"`를 따옴표로 감싸 넣었는지 확인하세요.
<div class="termy">
## 예제 { #example }
```console
$ pip install "uvicorn[standard]"
### 만들기 { #create-it }
---> 100%
```
</div>
## 예제
### 만들기
* `main.py` 파일을 만드십시오:
다음 내용으로 `main.py` 파일을 만드십시오:
```Python
from typing import Union
@@ -172,9 +181,9 @@ def read_item(item_id: int, q: Union[str, None] = None):
<details markdown="1">
<summary>또는 <code>async def</code> 사용하기...</summary>
여러분의 코드가 `async` / `await`을 사용한다면, `async def`를 사용하십시오.
여러분의 코드가 `async` / `await`을 사용한다면, `async def`를 사용하십시오:
```Python hl_lines="9 14"
```Python hl_lines="9 14"
from typing import Union
from fastapi import FastAPI
@@ -194,22 +203,35 @@ async def read_item(item_id: int, q: Union[str, None] = None):
**Note**:
잘 모르겠다면, <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">문서에서 `async`와 `await`</a>에 관한 _"급하세요?"_ 섹션을 확인해 보십시오.
잘 모르겠다면, <a href="https://fastapi.tiangolo.com/ko/async/#in-a-hurry" target="_blank">문서에서 `async`와 `await`</a>에 관한 _"급하세요?"_ 섹션을 확인해 보십시오.
</details>
### 실행하기
### 실행하기 { #run-it }
서버를 실행하십시오:
다음 명령으로 서버를 실행하십시오:
<div class="termy">
```console
$ uvicorn main:app --reload
$ fastapi dev main.py
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
@@ -217,17 +239,17 @@ INFO: Application startup complete.
</div>
<details markdown="1">
<summary><code>uvicorn main:app --reload</code> 명령에 관하여...</summary>
<summary><code>fastapi dev main.py</code> 명령에 관하여...</summary>
명령 `uvicorn main:app`은 다음을 나타냅니다:
`fastapi dev` 명령 `main.py` 파일을 읽고, 그 안의 **FastAPI** 앱을 감지한 다음, <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>을 사용해 서버를 시작합니다.
* `main`: `main.py` 파일 (파이썬 "모듈").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
* `--reload`: 코드가 변경된 후 서버 재시작하기. 개발환경에서만 사용하십시오.
기본적으로 `fastapi dev`는 로컬 개발을 위해 auto-reload가 활성화된 상태로 시작됩니다.
자세한 내용은 <a href="https://fastapi.tiangolo.com/ko/fastapi-cli/" target="_blank">FastAPI CLI 문서</a>에서 확인할 수 있습니다.
</details>
### 확인하기
### 확인하기 { #check-it }
브라우저로 <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>를 열어보십시오.
@@ -241,10 +263,10 @@ INFO: Application startup complete.
* _경로_ `/` 및 `/items/{item_id}`에서 HTTP 요청 받기.
* 두 _경로_ 모두 `GET` <em>연산</em>(HTTP _메소드_ 로 알려진)을 받습니다.
* _경로_ `/items/{item_id}`는 _경로 매개변수_ `int`형 이어야 하는 `item_id`를 가지고 있습니다.
* _경로_ `/items/{item_id}`는 선택적인 `str`형 이어야 하는 _경로 매개변수_ `q`를 가지고 있습니다.
* _경로_ `/items/{item_id}`는 `int`형 이어야 하는 _경로 매개변수_ `item_id`를 가지고 있습니다.
* _경로_ `/items/{item_id}`는 선택적인 `str`형 _쿼리 매개변수_ `q`를 가지고 있습니다.
### 대화형 API 문서
### 대화형 API 문서 { #interactive-api-docs }
이제 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>로 가보십시오.
@@ -252,7 +274,7 @@ INFO: Application startup complete.
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### 대안 API 문서
### 대안 API 문서 { #alternative-api-docs }
그리고 이제 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>로 가봅시다.
@@ -260,13 +282,13 @@ INFO: Application startup complete.
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## 예제 심화
## 예제 업그레이드 { #example-upgrade }
이제 `PUT` 요청에 있는 본문(Body)을 받기 위해 `main.py` 수정해봅시다.
이제 `PUT` 요청에서 본문을 받기 위해 `main.py` 파일을 수정해봅시다.
Pydantic을 이용해 파이썬 표준 타입으로 본문을 선언합니다.
Pydantic 덕분에 표준 Python 타입을 사용해 본문을 선언합니다.
```Python hl_lines="4 9 10 11 12 25 26 27"
```Python hl_lines="4 9-12 25-27"
from typing import Union
from fastapi import FastAPI
@@ -296,25 +318,25 @@ def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
서버 자동으로 리로딩 할 수 있어야 합니다 (위에서 `uvicorn` 명령에 `--reload`을 추가 했기 때문입니다).
`fastapi dev` 서버 자동으로 리로딩되어야 합니다.
### 대화형 API 문서 업그레이드
### 대화형 API 문서 업그레이드 { #interactive-api-docs-upgrade }
이제 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>로 이동합니다.
* 대화형 API 문서 새 본문과 함께 자동으로 업데이트니다:
* 대화형 API 문서 새 본문을 포함해 자동으로 업데이트니다:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* "Try it out" 버튼을 클릭하면, 매개변수를 채울 수 있게 해주고 직접 API와 상호작용 할 수 있습니다:
* "Try it out" 버튼을 클릭하면, 매개변수를 채우고 API와 직접 상호작용할 수 있습니다:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* 그러고 나서 "Execute" 버튼을 누르면, 사용자 인터페이스 API와 통신하고 매개변수를 전송하며 그 결과를 가져와서 화면에 표시합니다:
* 그런 다음 "Execute" 버튼을 클릭하면, 사용자 인터페이스 API와 통신하고 매개변수를 전송한 뒤 결과를 받아 화면에 표시합니다:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### 대안 API 문서 업그레이드
### 대안 API 문서 업그레이드 { #alternative-api-docs-upgrade }
그리고 이제, <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>로 이동합니다.
@@ -322,7 +344,7 @@ def update_item(item_id: int, item: Item):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### 요약
### 요약 { #recap }
요약하면, 여러분은 매개변수의 타입, 본문 등을 함수 매개변수로서 **한번에** 선언했습니다.
@@ -351,8 +373,8 @@ item: Item
* 타입 검사.
* 데이터 검증:
* 데이터가 유효하지 않을 때 자동으로 생성하는 명확한 에러.
* 중첩된 JSON 객체에 대한 유효성 검사.
* 입력 데이터 <abbr title="다음으로 알려진: 직렬화, 파싱, 마샬링">변환</abbr>: 네트워크에서 파이썬 데이터 및 타입으로 전송. 읽을 수 있는 것들:
* 깊이 중첩된 JSON 객체에 대한 유효성 검사.
* 입력 데이터 <abbr title="also known as: serialization, parsing, marshalling">변환</abbr>: 네트워크에서 파이썬 데이터 및 타입으로 전송. 읽을 수 있는 것들:
* JSON.
* 경로 매개변수.
* 쿼리 매개변수.
@@ -360,7 +382,7 @@ item: Item
* 헤더.
* 폼(Forms).
* 파일.
* 출력 데이터 <abbr title="다음으로 알려진: 직렬화, 파싱, 마샬링">변환</abbr>: 파이썬 데이터 및 타입을 네트워크 데이터로 전환(JSON 형식으로):
* 출력 데이터 <abbr title="also known as: serialization, parsing, marshalling">변환</abbr>: 파이썬 데이터 및 타입을 네트워크 데이터로 전환(JSON 형식으로):
* 파이썬 타입 변환 (`str`, `int`, `float`, `bool`, `list`, 등).
* `datetime` 객체.
* `UUID` 객체.
@@ -377,13 +399,13 @@ item: Item
* `GET` 및 `PUT` 요청에 `item_id`가 경로에 있는지 검증.
* `GET` 및 `PUT` 요청에 `item_id`가 `int` 타입인지 검증.
* 그렇지 않다면 클라이언트는 유용하고 명확한 에러를 볼 수 있습니다.
* `GET` 요청에 `q`라는 선택적인 쿼리 매개변수가 검사(`http://127.0.0.1:8000/items/foo?q=somequery`처럼).
* `GET` 요청에 `q`라는 선택적인 쿼리 매개변수가 있는지 검사(`http://127.0.0.1:8000/items/foo?q=somequery`처럼).
* `q` 매개변수는 `= None`으로 선언되었기 때문에 선택사항입니다.
* `None`이 없다면 필수사항입니다(`PUT`의 경우와 마찬가지로).
* `/items/{item_id}`으로의 `PUT` 요청은 본문을 JSON으로 읽음:
* `name`을 필수 속성으로 갖고 `str` 형인지 검사.
* `price` 필수 속성으로 갖고 `float` 형지 검사.
* 만약 주어진다면, `is_offer`를 선택 속성으로 갖고 `bool` 형지 검사.
* `price` 필수 속성으로 갖고 `float` 형이어야 하는지 검사.
* 만약 주어진다면, `is_offer`를 선택 속성으로 갖고 `bool` 형이어야 하는지 검사.
* 이 모든 것은 깊이 중첩된 JSON 객체에도 적용됩니다.
* JSON을 변환하거나 JSON으로 변환하는 것을 자동화.
* 다음에서 사용할 수 있는 모든 것을 OpenAPI로 문서화:
@@ -417,30 +439,88 @@ item: Item
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
더 많은 기능을 포함한 보다 완전한 예제의 경우, <a href="https://fastapi.tiangolo.com/tutorial/">튜토리얼 - 사용자 가이드</a>를 보십시오.
더 많은 기능을 포함한 보다 완전한 예제의 경우, <a href="https://fastapi.tiangolo.com/ko/tutorial/">튜토리얼 - 사용자 가이드</a>를 보십시오.
**스포일러 주의**: 튜토리얼 - 사용자 가이드는:
* 서로 다른 장소에서 **매개변수** 선언: **헤더**, **쿠키**, **폼 필드** 그리고 **파일**.
* `maximum_length` 또는 `regex`처럼 **유효성 제약**하는 방법.
* 강력하고 사용하기 쉬운 **<abbr title="컴포넌트, 리소스, 제공자, 서비스, injectables라 알려진">의존성 주입</abbr>** 시스템.
* 강력하고 사용하기 쉬운 **<abbr title="also known as components, resources, providers, services, injectables">의존성 주입</abbr>** 시스템.
* **OAuth2** 지원을 포함한 **JWT tokens** 및 **HTTP Basic**을 갖는 보안과 인증.
* (Pydantic 덕분에) **깊은 중첩 JSON 모델**을 선언하는데 더 진보한 (하지만 마찬가지로 쉬운) 기술.
* <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> 및 기타 라이브러리와의 **GraphQL** 통합.
* (Starlette 덕분에) 많은 추가 기능:
* **웹 소켓**
* **GraphQL**
* HTTPX 및 `pytest`에 기반한 극히 쉬운 테스트
* **CORS**
* **쿠키 세션**
* ...기타 등등.
## 성능
### 앱 배포하기(선택 사항) { #deploy-your-app-optional }
독립된 TechEmpower 벤치마크에서 Uvicorn에서 작동하는 FastAPI 어플리케이션이 <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">사용 가능한 가장 빠른 프레임워크 중 하나</a>로 Starlette와 Uvicorn(FastAPI에서 내부적으로 사용)에만 밑돌고 있습니다. (*)
선택적으로 FastAPI 앱을 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>에 배포할 수 있습니다. 아직이라면 대기자 명단에 등록해 보세요. 🚀
자세한 내용은 <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">벤치마크</a> 섹션을 보십시오.
이미 **FastAPI Cloud** 계정이 있다면(대기자 명단에서 초대해 드렸습니다 😉), 한 번의 명령으로 애플리케이션을 배포할 수 있습니다.
## 선택가능한 의존성
배포하기 전에, 로그인되어 있는지 확인하세요:
<div class="termy">
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
</div>
그런 다음 앱을 배포하세요:
<div class="termy">
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
</div>
이게 전부입니다! 이제 해당 URL에서 앱에 접근할 수 있습니다. ✨
#### FastAPI Cloud 소개 { #about-fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>**는 **FastAPI** 뒤에 있는 동일한 작성자와 팀이 만들었습니다.
최소한의 노력으로 API를 **빌드**, **배포**, **접근**하는 과정을 간소화합니다.
FastAPI로 앱을 빌드할 때의 동일한 **개발자 경험**을 클라우드에 **배포**하는 데까지 확장해 줍니다. 🎉
FastAPI Cloud는 *FastAPI and friends* 오픈 소스 프로젝트의 주요 스폰서이자 자금 제공자입니다. ✨
#### 다른 클라우드 제공자에 배포하기 { #deploy-to-other-cloud-providers }
FastAPI는 오픈 소스이며 표준을 기반으로 합니다. 선택한 어떤 클라우드 제공자에도 FastAPI 앱을 배포할 수 있습니다.
클라우드 제공자의 가이드를 따라 FastAPI 앱을 배포하세요. 🤓
## 성능 { #performance }
독립된 TechEmpower 벤치마크에서 Uvicorn에서 작동하는 FastAPI 애플리케이션이 <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">사용 가능한 가장 빠른 Python 프레임워크 중 하나</a>로 Starlette와 Uvicorn(FastAPI에서 내부적으로 사용)에만 밑돌고 있습니다. (*)
자세한 내용은 <a href="https://fastapi.tiangolo.com/ko/benchmarks/" class="internal-link" target="_blank">벤치마크</a> 섹션을 보십시오.
## 의존성 { #dependencies }
FastAPI는 Pydantic과 Starlette에 의존합니다.
### `standard` 의존성 { #standard-dependencies }
FastAPI를 `pip install "fastapi[standard]"`로 설치하면 `standard` 그룹의 선택적 의존성이 함께 설치됩니다.
Pydantic이 사용하는:
@@ -448,21 +528,38 @@ Pydantic이 사용하는:
Starlette이 사용하는:
* <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - `TestClient`를 사용하려면 필요.
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - 기본 템플릿 설정을 사용하려면 필요.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - `request.form()`과 함께 <abbr title="HTTP 요청에서 파이썬 데이터로 가는 문자열 변환">"parsing"</abbr> 지원을 원하면 필요.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` 지원을 위해 필요.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Starlette의 `SchemaGenerator` 지원을 위해 필요 (FastAPI와 쓸때는 필요 없을 것입니다).
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - `GraphQLApp` 지원을 위해 필요.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - `TestClient`를 사용하려면 필요.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - 기본 템플릿 설정을 사용하려면 필요.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - `request.form()`과 함께 form <abbr title="HTTP 요청에서 파이썬 데이터로 가는 문자열 변환">"parsing"</abbr> 지원을 원하면 필요.
FastAPI / Starlette이 사용하는:
FastAPI 사용하는:
* <a href="http://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - 애플리케이션을 로드하고 제공하는 서버.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - `ORJSONResponse`을 사용하려면 필요.
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - 애플리케이션을 로드하고 제공하는 서버를 위한 것입니다. 여기에는 고성능 서빙에 필요한 일부 의존성(예: `uvloop`)이 포함된 `uvicorn[standard]`가 포함됩니다.
* `fastapi-cli[standard]` - `fastapi` 명령을 제공하기 위한 것입니다.
* 여기에는 FastAPI 애플리케이션을 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>에 배포할 수 있게 해주는 `fastapi-cloud-cli`가 포함됩니다.
### `standard` 의존성 없이 { #without-standard-dependencies }
`standard` 선택적 의존성을 포함하고 싶지 않다면, `pip install "fastapi[standard]"` 대신 `pip install fastapi`로 설치할 수 있습니다.
### `fastapi-cloud-cli` 없이 { #without-fastapi-cloud-cli }
표준 의존성과 함께 FastAPI를 설치하되 `fastapi-cloud-cli` 없이 설치하고 싶다면, `pip install "fastapi[standard-no-fastapi-cloud-cli]"`로 설치할 수 있습니다.
### 추가 선택적 의존성 { #additional-optional-dependencies }
추가로 설치하고 싶을 수 있는 의존성도 있습니다.
추가 선택적 Pydantic 의존성:
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - 설정 관리를 위한 것입니다.
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - Pydantic에서 사용할 추가 타입을 위한 것입니다.
추가 선택적 FastAPI 의존성:
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - `ORJSONResponse`를 사용하려면 필요.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - `UJSONResponse`를 사용하려면 필요.
`pip install fastapi[all]`를 통해 이 모두를 설치 할 수 있습니다.
## 라이센스
## 라이센스 { #license }
이 프로젝트는 MIT 라이센스 조약에 따라 라이센스가 부여됩니다.

View File

@@ -1,5 +1,5 @@
# 배우기
# 배우기 { #learn }
여기 **FastAPI**를 배우기 위한 입문 자료와 자습서가 있습니다.
여기 **FastAPI**를 배우기 위한 입문 섹션과 자습서가 있습니다.
여러분은 FastAPI를 배우기 위 **책**, **강의**, **공식 자료** 그리고 추천받은 방법을 고려할 수 있습니다. 😎
여러분은 이것을 FastAPI를 배우기 위 **책**, **강의**, **공식**이자 권장되는 방법으로 생각할 수 있습니다. 😎

View File

@@ -1,4 +1,4 @@
# Full Stack FastAPI 템플릿
# Full Stack FastAPI 템플릿 { #full-stack-fastapi-template }
템플릿은 일반적으로 특정 설정과 함께 제공되지만, 유연하고 커스터마이징이 가능하게 디자인 되었습니다. 이 특성들은 여러분이 프로젝트의 요구사항에 맞춰 수정, 적용을 할 수 있게 해주고, 템플릿이 완벽한 시작점이 되게 해줍니다. 🏁
@@ -6,23 +6,23 @@
GitHub 저장소: <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Full Stack FastAPI 템플릿</a>
## Full Stack FastAPI 템플릿 - 기술 스택과 기능들
## Full Stack FastAPI 템플릿 - 기술 스택과 기능들 { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com): Python 백엔드 API.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com): Python SQL 데이터 상호작용을 위한 (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev): FastAPI에 의해 사용되는, 데이터 검증과 설정관리.
- 💾 [PostgreSQL](https://www.postgresql.org): SQL 데이터베이스.
- 🚀 [React](https://react.dev): 프론트엔드.
- 💃 TypeScript, hooks, [Vite](https://vitejs.dev) 및 기타 현대적인 프론트엔드 스택을 사용.
- 🎨 [Chakra UI](https://chakra-ui.com): 프론트엔드 컴포넌트.
- Python 백엔드 API를 위한 [**FastAPI**](https://fastapi.tiangolo.com/ko).
- 🧰 Python SQL 데이터베이스 상호작용을 위한 [SQLModel](https://sqlmodel.tiangolo.com) (ORM).
- 🔍 FastAPI에 의해 사용되는, 데이터 검증과 설정 관리를 위한 [Pydantic](https://docs.pydantic.dev).
- 💾 SQL 데이터베이스로서의 [PostgreSQL](https://www.postgresql.org).
- 🚀 프론트엔드를 위한 [React](https://react.dev).
- 💃 TypeScript, hooks, Vite 및 기타 현대적인 프론트엔드 스택을 사용.
- 🎨 프론트엔드 컴포넌트를 위한 [Tailwind CSS](https://tailwindcss.com) 및 [shadcn/ui](https://ui.shadcn.com).
- 🤖 자동으로 생성된 프론트엔드 클라이언트.
- 🧪 E2E 테스트를 위한 [Playwright](https://playwright.dev).
- 🧪 End-to-End 테스트를 위한 [Playwright](https://playwright.dev).
- 🦇 다크 모드 지원.
- 🐋 [Docker Compose](https://www.docker.com): 개발 환경과 프로덕션(운영).
- 🐋 개발 환경과 프로덕션(운영)을 위한 [Docker Compose](https://www.docker.com).
- 🔒 기본으로 지원되는 안전한 비밀번호 해싱.
- 🔑 JWT 토큰 인증.
- 🔑 JWT (JSON Web Token) 인증.
- 📫 이메일 기반 비밀번호 복구.
- ✅ [Pytest]를 이용한 테스트(https://pytest.org).
- 📞 [Traefik](https://traefik.io): 리버스 프록시 / 로드 밸런서.
- ✅ [Pytest](https://pytest.org)를 이용한 테스트.
- 📞 리버스 프록시 / 로드 밸런서로서의 [Traefik](https://traefik.io).
- 🚢 Docker Compose를 이용한 배포 지침: 자동 HTTPS 인증서를 처리하기 위한 프론트엔드 Traefik 프록시 설정 방법을 포함.
- 🏭 GitHub Actions를 기반으로 CI (지속적인 통합) 및 CD (지속적인 배포).

View File

@@ -1,313 +1,466 @@
# 파이썬 타입 소개
# 파이썬 타입 소개 { #python-types-intro }
파이썬은 선택적으로 "타입 힌트(type hints)"를 지원합니다.
파이썬은 선택적으로 "타입 힌트(type hints)"(“type annotations”라고도 함)를 지원합니다.
이러한 **타입 힌트**은 변수의 <abbr title="예를 들면: str, int, float, bool">타입</abbr>을 선언할 수 있게 해주는 특수한 구문입니다.
이러한 **"타입 힌트"** 또는 애너테이션은 변수의 <abbr title="for example: str, int, float, bool">타입</abbr>을 선언할 수 있게 해주는 특수한 구문입니다.
변수의 타입을 지정하면 에디터와 툴이도움을 줄 수 있게 됩니다.
변수의 타입을 선언하면 에디터와 도구가지원을 제공할 수 있니다.
이 문서는 파이썬 타입 힌트에 대한 **빠른 자습서 / 내용환기** 수준의 문서입니다. 여기서는 **FastAPI**를 쓰기 위한 최소한의 내용만을 다룹니다.
이 문서는 파이썬 타입 힌트에 대한 **빠른 자습서 / 내용 환기**입니다. **FastAPI**와 함께 사용하기 위해 필요한 최소한만 다룹니다... 실제로는 아주 조금만 있으면 됩니다.
**FastAPI**는 타입 힌트에 기반을 두고 있으며, 이는 많은 장점과 이익이 있습니다.
**FastAPI**는 모두 이러한 타입 힌트에 기반을 두고 있으며, 이는 많은 장점과 이점을 제공합니다.
비록 **FastAPI**를 쓰지 않는다고 하더라도, 조금이라도 알아두면 도움이 될 것입니다.
하지만 **FastAPI**를 전혀 사용하지 않더라도, 타입 힌트를 조금만 배워도 도움이 니다.
/// note | 참고
파이썬에 능숙하셔서 타입 힌트에 대해 모두 아신다면, 다음 챕터로 건너뛰세요.
파이썬에 능숙하 타입 힌트에 대해 이미 모두 알고 있다면, 다음 장으로 건너뛰세요.
///
## 동기 부여
## 동기 부여 { #motivation }
간단한 예제부터 시작해봅시다:
간단한 예제 시작해봅시다:
{* ../../docs_src/python_types/tutorial001.py *}
{* ../../docs_src/python_types/tutorial001_py39.py *}
이 프로그램을 실행한 결과값:
이 프로그램을 호출하면 다음이 출력됩니다:
```
John Doe
```
함수는 아래와 같이 실행됩니다:
함수는 다음을 수행합니다:
* `first_name``last_name`를 받습니다.
* `title()`로 각 첫 자를 대문자로 변환시킵니다.
* 두 단어를 중간에 공백을 두고 <abbr title="두 개를 하나로 차례차례 이어지게 하다">연결</abbr>합니다.
* `title()`로 각각의자를 대문자로 변환니다.
* 가운데에 공백을 두고 <abbr title="Puts them together, as one. With the contents of one after the other.">연결</abbr>합니다.
{* ../../docs_src/python_types/tutorial001.py hl[2] *}
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
### 수정하기 { #edit-it }
### 코드 수정
매우 간단한 프로그램입니다.
이건 매우 간단한 프로그램입니다.
하지만 이제, 이것을 처음부터 작성한다고 상상해봅시다.
그런데 처음부터 작성한다고 생각을 해봅시다.
어느 시점엔 함수를 정의하기 시작했고, 매개변수도 준비해두었을 겁니다...
여러분은 매개변수를 준비했고, 함수를 정의하기 시작했을 겁니다.
그런데 "첫 글자를 대문자로 변환하는 그 메서드"를 호출해야 합니다.
이때 "첫 글자를 대문자로 바꾸는 함수"를 호출해야 합니다.
`upper`였나요? `uppercase`였나요? `first_uppercase`? `capitalize`?
`upper`였나? 아니면 `uppercase`? `first_uppercase`? `capitalize`?
그 다음, 개발자들의 오랜 친구인 에디터 자동완성을 시도합니다.
그때 개발자들의 오랜 친구, 에디터 자동완성을 시도해봅니다.
함수의 첫 번째 매개변수인 `first_name`을 입력하고, 점(`.`)을 찍은 다음, 완성을 트리거하기 위해 `Ctrl+Space`를 누릅니다.
당신은 `first_name`를 입력한 뒤 점(`.`)을 입력하고 자동완성을 켜기 위해서 `Ctrl+Space`를 눌렀습니다.
하지만 슬프게도 아무런 도움이 되지 않습니다:
하지만, 슬프게도 쓸만한 게 아무것도 없습니다:
<img src="/img/python-types/image01.png">
### 타입 추가하기
### 타입 추가하기 { #add-types }
이전 버전에서 한 줄만 수정해봅시다.
저희는 이 함수의 매개변수 부분:
함수의 매개변수인 정확히 이 부분:
```Python
first_name, last_name
```
을 아래와 같이 바꿀 겁니다:
에서:
```Python
first_name: str, last_name: str
```
로 바꾸겠습니다.
이게 다입니다.
"타입 힌트"입니다:
것들이 "타입 힌트"입니다:
{* ../../docs_src/python_types/tutorial002.py hl[1] *}
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
타입힌트는 다음과 같이 기본 값을 선언하는 것과는 다릅니다:
이것은 다음처럼 기본값을 선언하는 것과는 다릅니다:
```Python
first_name="john", last_name="doe"
```
이는 다른 것입니다.
다른 것입니다.
등호(`=`) 대신 콜론(`:`)을 쓰고 있습니다.
등호(`=`)가 아니라 콜론(`:`)을 사용합니다.
일반적으로 타입힌트를 추가한다고 해서 특별하게 어떤 일이 일어나지도 않습니다.
그리고 보통 타입 힌트를 추가해도, 타입 힌트 없이 일어나는 일과 비교해 특별히 달라지는 것은 없습니다.
그렇지만 이제, 다시 함수를 만드는 중이라고 생각해봅시다. 다만 이번엔 타입 힌트가 있습니다.
지만 이제, 타입 힌트를 포함해 그 함수를 다시 만드는 중이라고 상상해봅시다.
같은 상황에서 `Ctrl+Space`로 자동완성을 작동시키면,
같은 지점에서 `Ctrl+Space`로 자동완성을 트리거하면 다음이 보입니다:
<img src="/img/python-types/image02.png">
아래와 같이 "그렇지!"하는 옵션이 나올때까지 스크롤을 내려서 볼 수 있습니다:
그러면 스크롤하며 옵션을 보다가, "기억나는" 것을 찾을 수 있습니다:
<img src="/img/python-types/image03.png">
## 더 큰 동기부여
## 더 큰 동기부여 { #more-motivation }
아래 함수를 보면, 이미 타입 힌트가 적용되어 있는 걸 볼 수 있습니다:
함수를 확인해보세요. 이미 타입 힌트가 있습니다:
{* ../../docs_src/python_types/tutorial003.py hl[1] *}
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
편집기가 변수의 타입을 알고 있기 때문에, 자동완성 뿐 아니라 에러도 확인할 수 있습니다:
에디터가 변수의 타입을 알고 있기 때문에, 자동완성만 되는 게 아니라 오류 검사도 할 수 있습니다:
<img src="/img/python-types/image04.png">
이제 고쳐야하는 걸 알기 때문에, `age``str(age)`과 같이 문자열로 바꾸게 됩니다:
이제 고쳐야 한다는 것을 알고, `age``str(age)` 문자열로 바니다:
{* ../../docs_src/python_types/tutorial004.py hl[2] *}
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
## 타입 선언 { #declaring-types }
## 타입 선언
방금 타입 힌트를 선언하는 주요 위치를 보았습니다. 함수 매개변수입니다.
방금 함수의 매개변수로써 타입 힌트를 선언하는 주요 장소를 보았습니다.
이것은 **FastAPI**와 함께 사용할 때도 주요 위치입니다.
이 위치는 여러분이 **FastAPI**와 함께 이를 사용하는 주요 장소입니다.
### Simple 타입
### Simple 타입 { #simple-types }
`str`뿐 아니라 모든 파이썬 표준 타입을 선언할 수 있습니다.
예를 들:
예를 들어 다음을 사용할 수 있습니다:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005.py hl[1] *}
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
### 타입 매개변수가 있는 Generic(제네릭) 타입 { #generic-types-with-type-parameters }
### 타입 매개변수를 활용한 Generic(제네릭) 타입
`dict`, `list`, `set`, `tuple`처럼 다른 값을 담을 수 있는 데이터 구조가 있습니다. 그리고 내부 값에도 각자의 타입이 있을 수 있습니다.
`dict`, `list`, `set`, `tuple`과 같은 값을 저장할 수 있는 데이터 구조가 있고, 내부의 값은 각자의 타입을 가질 수도 있습니다.
이렇게 내부 타입을 가지는 타입을 "**generic**" 타입이라고 부릅니다. 그리고 내부 타입까지 포함해 선언할 수도 있습니다.
타입과 내부 타입을 선언하기 위해서는 파이썬 표준 모듈 `typing`이용해야 합니다.
이런 타입과 내부 타입을 선언하려면 표준 파이썬 모듈 `typing`사용할 수 있습니다. 이 모듈은 이러한 타입 힌트를 지원하기 위해 존재합니다.
구체적으로는 아래 타입 힌트를 지원합니다.
#### 더 최신 버전의 Python { #newer-versions-of-python }
#### `List`
`typing`을 사용하는 문법은 Python 3.6부터 최신 버전까지, Python 3.9, Python 3.10 등을 포함한 모든 버전과 **호환**됩니다.
예를 들면, `str``list`인 변수를 정의해봅시다.
파이썬이 발전함에 따라 **더 최신 버전**에서는 이러한 타입 애너테이션 지원이 개선되며, 많은 경우 타입 애너테이션을 선언하기 위해 `typing` 모듈을 import해서 사용할 필요조차 없게 됩니다.
`typing`에서 `List`(대문자 `L`)를 import 합니다.
프로젝트에서 더 최신 버전의 파이썬을 선택할 수 있다면, 그 추가적인 단순함을 활용할 수 있습니다.
{* ../../docs_src/python_types/tutorial006.py hl[1] *}
이 문서 전체에는 각 파이썬 버전과 호환되는 예제가 있습니다(차이가 있을 때).
예를 들어 "**Python 3.6+**"는 Python 3.6 이상(3.7, 3.8, 3.9, 3.10 등 포함)과 호환된다는 뜻입니다. 그리고 "**Python 3.9+**"는 Python 3.9 이상(3.10 등 포함)과 호환된다는 뜻입니다.
콜론(`:`) 문법을 이용하여 변수를 선언합니다.
**최신 버전의 Python**을 사용할 수 있다면, 최신 버전용 예제를 사용하세요. 예를 들어 "**Python 3.10+**"처럼, 가장 **좋고 가장 단순한 문법**을 갖게 됩니다.
타입으로는 `List`를 넣어줍니다.
#### List { #list }
이때 배열은 내부 타입을 포함하는 타입이기 때문에 대괄호 안에 넣어줍니다.
예를 들어, `str``list`인 변수를 정의해봅시다.
{* ../../docs_src/python_types/tutorial006.py hl[4] *}
같은 콜론(`:`) 문법으로 변수를 선언합니다.
타입으로 `list`를 넣습니다.
/// tip | 팁
대괄호 안의 내부 타입은 "타입 매개변수(type paramters)"라고 합니다.
이번 예제에서는 `str``List`에 들어간 타입 매개변수 입니다.
///
이는 "`items``list`인데, 배열에 들어있는 아이템 각각은 `str`이다"라는 뜻입니다.
이렇게 함으로써, 에디터는 배열에 들어있는 아이템을 처리할때도 도움을 줄 수 있게 됩니다:
<img src="/img/python-types/image05.png">
타입이 없으면 이건 거의 불가능이나 다름 없습니다.
변수 `item``items`의 개별 요소라는 사실을 알아두세요.
그리고 에디터는 계속 `str`라는 사실을 알고 도와줍니다.
#### `Tuple`과 `Set`
`tuple``set`도 동일하게 선언할 수 있습니다.
{* ../../docs_src/python_types/tutorial007.py hl[1,4] *}
이 뜻은 아래와 같습니다:
* 변수 `items_t`는, 차례대로 `int`, `int`, `str``tuple`이다.
* 변수 `items_s`는, 각 아이템이 `bytes``set`이다.
#### `Dict`
`dict`를 선언하려면 컴마로 구분된 2개의 파라미터가 필요합니다.
첫 번째 매개변수는 `dict`의 키(key)이고,
두 번째 매개변수는 `dict`의 값(value)입니다.
{* ../../docs_src/python_types/tutorial008.py hl[1,4] *}
이 뜻은 아래와 같습니다:
* 변수 `prices``dict`이다:
* `dict`의 키(key)는 `str`타입이다. (각 아이템의 이름(name))
* `dict`의 값(value)는 `float`타입이다. (각 아이템의 가격(price))
#### `Optional`
`str`과 같이 타입을 선언할 때 `Optional`을 쓸 수도 있는데, "선택적(Optional)"이기때문에 `None`도 될 수 있습니다:
```Python hl_lines="1 4"
{!../../docs_src/python_types/tutorial009.py!}
```
`Optional[str]`을 `str` 대신 쓰게 되면, 특정 값이 실제로는 `None`이 될 수도 있는데 항상 `str`이라고 가정하는 상황에서 에디터가 에러를 찾게 도와줄 수 있습니다.
#### Generic(제네릭) 타입
이 타입은 대괄호 안에 매개변수를 가지며, 종류는:
* `List`
* `Tuple`
* `Set`
* `Dict`
* `Optional`
* ...등등
위와 같은 타입은 **Generic(제네릭) 타입** 혹은 **Generics(제네릭스)**라고 불립니다.
### 타입으로서의 클래스
변수의 타입으로 클래스를 선언할 수도 있습니다.
이름(name)을 가진 `Person` 클래스가 있다고 해봅시다.
{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
그렇게 하면 변수를 `Person`이라고 선언할 수 있게 됩니다.
{* ../../docs_src/python_types/tutorial010.py hl[6] *}
그리고 역시나 모든 에디터 도움을 받게 되겠죠.
<img src="/img/python-types/image06.png">
## Pydantic 모델
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>은 데이터 검증(Validation)을 위한 파이썬 라이브러리입니다.
당신은 속성들을 포함한 클래스 형태로 "모양(shape)"을 선언할 수 있습니다.
그리고 각 속성은 타입을 가지고 있습니다.
이 클래스를 활용하여서 값을 가지고 있는 인스턴스를 만들게 되면, 필요한 경우에는 적당한 타입으로 변환까지 시키기도 하여 데이터가 포함된 객체를 반환합니다.
그리고 결과 객체에 대해서는 에디터의 도움을 받을 수 있게 됩니다.
Pydantic 공식 문서 예시:
{* ../../docs_src/python_types/tutorial011.py *}
`list`는 내부 타입을 포함하는 타입이므로, 그 타입들을 대괄호 안에 넣습니다:
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
/// info | 정보
Pydantic<에 대해 더 배우고 싶다면 <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">공식 문서</a>를 참고하세요.</a>
대괄호 안의 내부 타입은 "type parameters"라고 부릅니다.
이 경우 `str``list`에 전달된 타입 매개변수입니다.
///
**FastAPI**는 모두 Pydantic을 기반으로 되어 있습니다.
이는 "변수 `items``list`이고, 이 `list`의 각 아이템은 `str`이다"라는 뜻입니다.
이 모든 것이 실제로 어떻게 사용되는지에 대해서는 [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank} 에서 더 많이 확인하실 수 있습니다.
이렇게 하면, 에디터는 리스트의 아이템을 처리하는 동안에도 지원을 제공할 수 있습니다:
## **FastAPI**에서의 타입 힌트
<img src="/img/python-types/image05.png">
**FastAPI**는 여러 부분에서 타입 힌트의 장점을 취하고 있습니다.
타입이 없으면, 이는 거의 불가능합니다.
**FastAPI**에서 타입 힌트와 함께 매개변수를 선언하면 장점은:
변수 `item`이 리스트 `items`의 요소 중 하나라는 점에 주목하세요.
그리고 에디터는 여전히 이것이 `str`임을 알고, 그에 대한 지원을 제공합니다.
#### Tuple과 Set { #tuple-and-set }
`tuple``set`도 동일하게 선언할 수 있습니다:
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
이는 다음을 의미합니다:
* 변수 `items_t`는 3개의 아이템을 가진 `tuple`이며, `int`, 또 다른 `int`, 그리고 `str`입니다.
* 변수 `items_s``set`이며, 각 아이템의 타입은 `bytes`입니다.
#### Dict { #dict }
`dict`를 정의하려면, 쉼표로 구분된 2개의 타입 매개변수를 전달합니다.
첫 번째 타입 매개변수는 `dict`의 키를 위한 것입니다.
두 번째 타입 매개변수는 `dict`의 값을 위한 것입니다:
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
이는 다음을 의미합니다:
* 변수 `prices``dict`입니다:
*`dict`의 키는 `str` 타입입니다(예: 각 아이템의 이름).
*`dict`의 값은 `float` 타입입니다(예: 각 아이템의 가격).
#### Union { #union }
변수가 **여러 타입 중 어떤 것이든** 될 수 있다고 선언할 수 있습니다. 예를 들어 `int` 또는 `str`입니다.
Python 3.6 이상(3.10 포함)에서는 `typing``Union` 타입을 사용하고, 대괄호 안에 허용할 수 있는 타입들을 넣을 수 있습니다.
Python 3.10에는 가능한 타입들을 <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>세로 막대(`|`)</abbr>로 구분해 넣을 수 있는 **새 문법**도 있습니다.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
두 경우 모두 이는 `item`이 `int` 또는 `str`일 수 있다는 뜻입니다.
#### `None`일 수도 있음 { #possibly-none }
값이 `str` 같은 타입일 수도 있지만, `None`일 수도 있다고 선언할 수 있습니다.
Python 3.6 이상(3.10 포함)에서는 `typing` 모듈에서 `Optional`을 import해서 사용하여 선언할 수 있습니다.
```Python hl_lines="1 4"
{!../../docs_src/python_types/tutorial009_py39.py!}
```
그냥 `str` 대신 `Optional[str]`을 사용하면, 값이 항상 `str`이라고 가정하고 있지만 실제로는 `None`일 수도 있는 상황에서 에디터가 오류를 감지하도록 도와줍니다.
`Optional[Something]`은 사실 `Union[Something, None]`의 축약이며, 서로 동등합니다.
또한 이는 Python 3.10에서 `Something | None`을 사용할 수 있다는 의미이기도 합니다:
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
//// tab | Python 3.9+ alternative
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
#### `Union` 또는 `Optional` 사용하기 { #using-union-or-optional }
Python 3.10 미만 버전을 사용한다면, 아주 **주관적인** 관점에서의 팁입니다:
* 🚨 `Optional[SomeType]` 사용을 피하세요
* 대신 ✨ **`Union[SomeType, None]`을 사용하세요** ✨.
둘은 동등하고 내부적으로는 같은 것이지만, `Optional`이라는 단어가 값이 선택 사항인 것처럼 보일 수 있기 때문에 `Optional` 대신 `Union`을 권장합니다. 실제 의미는 값이 선택 사항이라는 뜻이 아니라, "값이 `None`일 수 있다"는 뜻이기 때문입니다. 선택 사항이 아니고 여전히 필수인 경우에도요.
`Union[SomeType, None]`이 의미를 더 명확하게 드러낸다고 생각합니다.
이건 단지 단어와 이름의 문제입니다. 하지만 그런 단어들이 여러분과 팀원이 코드에 대해 생각하는 방식에 영향을 줄 수 있습니다.
예로, 이 함수를 봅시다:
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
매개변수 `name`은 `Optional[str]`로 정의되어 있지만, **선택 사항이 아닙니다**. 매개변수 없이 함수를 호출할 수 없습니다:
```Python
say_hi() # Oh, no, this throws an error! 😱
```
기본값이 없기 때문에 `name` 매개변수는 **여전히 필수입니다**(*optional*이 아님). 그럼에도 `name`은 값으로 `None`을 허용합니다:
```Python
say_hi(name=None) # This works, None is valid 🎉
```
좋은 소식은 Python 3.10을 사용하면, 타입의 유니온을 정의하기 위해 간단히 `|`를 사용할 수 있어서 이런 걱정을 할 필요가 없다는 점입니다:
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
그러면 `Optional`이나 `Union` 같은 이름에 대해 걱정할 필요도 없습니다. 😎
#### Generic(제네릭) 타입 { #generic-types }
대괄호 안에 타입 매개변수를 받는 이러한 타입들은 **Generic types** 또는 **Generics**라고 부릅니다. 예를 들면:
//// tab | Python 3.10+
대괄호와 내부 타입을 사용해, 동일한 내장 타입들을 제네릭으로 사용할 수 있습니다:
* `list`
* `tuple`
* `set`
* `dict`
그리고 이전 파이썬 버전과 마찬가지로 `typing` 모듈의 다음도 사용할 수 있습니다:
* `Union`
* `Optional`
* ...그 밖의 것들.
Python 3.10에서는 제네릭 `Union`과 `Optional`을 사용하는 대안으로, 타입 유니온을 선언하기 위해 <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>세로 막대(`|`)</abbr>를 사용할 수 있는데, 훨씬 더 좋고 단순합니다.
////
//// tab | Python 3.9+
대괄호와 내부 타입을 사용해, 동일한 내장 타입들을 제네릭으로 사용할 수 있습니다:
* `list`
* `tuple`
* `set`
* `dict`
그리고 `typing` 모듈의 제네릭들:
* `Union`
* `Optional`
* ...그 밖의 것들.
////
### 타입으로서의 클래스 { #classes-as-types }
변수의 타입으로 클래스를 선언할 수도 있습니다.
이름을 가진 `Person` 클래스가 있다고 해봅시다:
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
그러면 `Person` 타입의 변수를 선언할 수 있습니다:
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
그리고 다시, 에디터의 모든 지원을 받을 수 있습니다:
<img src="/img/python-types/image06.png">
이는 "`one_person`은 `Person` 클래스의 **인스턴스**"라는 뜻입니다.
"`one_person`은 `Person`이라는 **클래스**다"라는 뜻이 아닙니다.
## Pydantic 모델 { #pydantic-models }
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>은 데이터 검증을 수행하는 파이썬 라이브러리입니다.
속성을 가진 클래스 형태로 데이터의 "모양(shape)"을 선언합니다.
그리고 각 속성은 타입을 가집니다.
그 다음 그 클래스의 인스턴스를 몇 가지 값으로 생성하면, 값들을 검증하고, (그런 경우라면) 적절한 타입으로 변환한 뒤, 모든 데이터를 가진 객체를 제공합니다.
그리고 그 결과 객체에 대해 에디터의 모든 지원을 받을 수 있습니다.
Pydantic 공식 문서의 예시:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | 정보
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic에 대해 더 알아보려면 문서를 확인하세요</a>.
///
**FastAPI**는 모두 Pydantic에 기반을 두고 있습니다.
이 모든 것은 [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank}에서 실제로 많이 보게 될 것입니다.
/// tip | 팁
Pydantic은 기본값 없이 `Optional` 또는 `Union[Something, None]`을 사용할 때 특별한 동작이 있습니다. 이에 대해서는 Pydantic 문서의 <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Required Optional fields</a>에서 더 읽을 수 있습니다.
///
## 메타데이터 애너테이션이 있는 타입 힌트 { #type-hints-with-metadata-annotations }
파이썬에는 `Annotated`를 사용해 이러한 타입 힌트에 **추가 <abbr title="Data about the data, in this case, information about the type, e.g. a description.">메타데이터</abbr>**를 넣을 수 있는 기능도 있습니다.
Python 3.9부터 `Annotated`는 표준 라이브러리의 일부이므로, `typing`에서 import할 수 있습니다.
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
파이썬 자체는 이 `Annotated`로 아무것도 하지 않습니다. 그리고 에디터와 다른 도구들에게는 타입이 여전히 `str`입니다.
하지만 `Annotated`의 이 공간을 사용해, 애플리케이션이 어떻게 동작하길 원하는지에 대한 추가 메타데이터를 **FastAPI**에 제공할 수 있습니다.
기억해야 할 중요한 점은 `Annotated`에 전달하는 **첫 번째 *타입 매개변수***가 **실제 타입**이라는 것입니다. 나머지는 다른 도구를 위한 메타데이터일 뿐입니다.
지금은 `Annotated`가 존재하며, 표준 파이썬이라는 것만 알면 됩니다. 😎
나중에 이것이 얼마나 **강력**할 수 있는지 보게 될 것입니다.
/// tip | 팁
이것이 **표준 파이썬**이라는 사실은, 에디터에서 가능한 **최고의 개발자 경험**을 계속 얻을 수 있다는 뜻이기도 합니다. 사용하는 도구로 코드를 분석하고 리팩터링하는 등에서도요. ✨
또한 코드가 많은 다른 파이썬 도구 및 라이브러리와 매우 호환된다는 뜻이기도 합니다. 🚀
///
## **FastAPI**에서의 타입 힌트 { #type-hints-in-fastapi }
**FastAPI**는 이러한 타입 힌트를 활용해 여러 가지를 합니다.
**FastAPI**에서는 타입 힌트로 매개변수를 선언하면 다음을 얻습니다:
* **에디터 도움**.
* **타입 확인**.
...그리고 **FastAPI**는 같은 정의를 아래에도 용합니다:
...그리고 **FastAPI**는 같은 선언을 다음에도 용합니다:
* **요구사항 정의**: 요청 경로 매개변수, 쿼리 매개변수, 헤더, 바디, 의존성 등.
* **데이터 변환**: 요청에서 요한 타입으로.
* **데이터 검증**: 각 요청마다:
* 데이터가 유효하지 않은 경우에는 **자동으로 에러**를 생합니다.
* OpenAPI를 활용한 **API 문서화**:
* 자동으로 상호작용하는 유저 인터페이스에 쓰이게 됩니다.
* **요구사항 정의**: 요청 경로 매개변수, 쿼리 매개변수, 헤더, 바디, 의존성 등에서.
* **데이터 변환**: 요청에서 요한 타입으로.
* **데이터 검증**: 각 요청에서:
* 데이터가 유효하지 않을 때 클라이언트에 반환되는 **자동 오류**를 생합니다.
* OpenAPI를 사용해 API를 **문서화**:
* 자동 상호작용 문서 UI에서 사용됩니다.
위 내용이 다소 추상적일 수도 있지만, 걱정마세요. [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank}에서 전부 확인 가능합니다.
이 모든 것이 다소 추상적으로 들릴 수도 있습니다. 걱정하지 마세요. [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank}에서 실제로 확인하게 될 것입니다.
가장 중요한 건, 표준 파이썬 타입을 한 곳에서(클래스를 더하거나, 데코레이터 사용하는 대신) 사용함으로써 **FastAPI**가 당신을 위해 많은 일을 해준다는 사실이죠.
가장 중요한 점은 표준 파이썬 타입을 한 곳에서 사용함으로써(더 많은 클래스, 데코레이터 등을 추가하는 대신) **FastAPI**가 여러분을 위해 많은 일을 해준다는 사실입니다.
/// info | 정보
만약 모든 자습서를 다 보았음에도 타입에 대해보고자 방문한 경우에는 <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">`mypy`에서 제공하는 "cheat sheet"</a>이 좋은 자료가 될 겁니다.
자습서를 모두 끝내고 타입에 대해 더 알아보기 위해 다시 돌아왔다면, 좋은 자료로 <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">`mypy` "cheat sheet"</a>가 있습니다.
///

View File

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

View File

@@ -1,84 +1,86 @@
# 백그라운드 작업
# 백그라운드 작업 { #background-tasks }
FastAPI에서는 응답을 반환한 후에 실행할 백그라운드 작업을 정의할 수 있습니다.
FastAPI에서는 응답을 반환한 *후에* 실행할 백그라운드 작업을 정의할 수 있습니다.
백그라운드 작업은 클라이언트가 응답을 받기 위해 작업이 완료될 때까지 기다릴 필요가 없기 때문에 요청 후에 발생해야하는 작업에 매우 유용합니다.
백그라운드 작업은 요청 후에 발생해야 하지만, 클라이언트가 응답을 받기 전에 작업이 완료될 때까지 기다릴 필요가 없는 작업에 유용합니다.
이러한 작업에는 다음이 포함됩니다.
예를 들면 다음과 같습니다.
* 작업을 수행한 후 전송되는 이메일 알림
* 이메일 서버에 연결하고 이메일을 전송하는 것은 (몇 초 정도) "느린" 경향이 있으므로, 응답은 즉시 반환하고 이메일 알림은 백그라운드에서 전송하는 게 가능합니다.
* 작업을 수행한 후 전송되는 이메일 알림:
* 이메일 서버에 연결하고 이메일을 전송하는 것은 (몇 초 정도) "느린" 경향이 있으므로, 응답은 즉시 반환하고 이메일 알림은 백그라운드에서 전송할 수 있습니다.
* 데이터 처리:
* 예를 들어 처리에 오랜 시간이 걸리는 데이터를 받았을 때 "Accepted" (HTTP 202)을 반환하고, 백그라운드에서 데이터를 처리할 수 있습니다.
* 예를 들어 처리에 오랜 시간이 걸리는 프로세스를 거쳐야 하는 파일을 받았다면, "Accepted"(HTTP 202) 응답을 반환하고 백그라운드에서 파일을 처리할 수 있습니다.
## `백그라운드 작업` 사용
## `BackgroundTasks` 사용 { #using-backgroundtasks }
먼저 아래와 같이 `BackgroundTasks`를 임포트하고, `BackgroundTasks`_경로 작동 함수_ 에서 매개변수로 가져오고 정의합니다.
먼저 `BackgroundTasks`를 임포트하고, `BackgroundTasks` 타입 선언으로 *경로 처리 함수*에 매개변수를 정의합니다:
{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
**FastAPI** `BackgroundTasks` 체를 생성하고, 매개 변수로 전달합니다.
**FastAPI**가 `BackgroundTasks` 타입의 객체를 생성하고 해당 매개변수로 전달합니다.
## 작업 함수 생성
## 작업 함수 생성 { #create-a-task-function }
백그라운드 작업으로 실행할 함수를 정의합니다.
백그라운드 작업으로 실행할 함수를 생성합니다.
것은 단순히 매개변수를 받을 수 있는 표준 함수일 뿐입니다.
매개변수를 받을 수 있는 표준 함수일 뿐입니다.
**FastAPI**는 이것이 `async def` 함수이든, 일반 `def` 함수이든 내부적으로 이를 올바르게 처리합니다.
`async def` 함수일 수도, 일반 `def` 함수일 수도 있으며, **FastAPI**가 이를 올바르게 처리하는 방법을 알고 있습니다.
이 경우, 아래 작업은 파일에 쓰는 함수입니다. (이메일 보내기레이션)
이 경우 작업 함수는 파일에 쓰기를 수행합니다(이메일 전송을레이션).
그리고 작업은 `async``await`를 사용하지 않으므로 일반 `def` 함수로 선언합니다.
그리고 쓰기 작업은 `async``await`를 사용하지 않으므로, 일반 `def` 함수를 정의합니다:
{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
## 백그라운드 작업 추가
## 백그라운드 작업 추가 { #add-the-background-task }
_경로 작동 함수_ 내에서 작업 함수를 `.add_task()` 함수 통해 _백그라운드 작업_ 체에 전달합니다.
*경로 처리 함수*에서 `.add_task()` 메서드로 작업 함수를 *백그라운드 작업* 체에 전달합니다:
{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
`.add_task()` 함수는 다음과 같은 인자를 받습니다 :
`.add_task()`는 다음 인자를 받습니다:
- 백그라운드에서 실행되는 작업 함수 (`write_notification`).
- 작업 함수에 순서대로 전달되어야 하는 일련의 인자 (`email`).
- 작업 함수에 전달되어야하는 모든 키워드 인자 (`message="some notification"`).
* 백그라운드에서 실행 작업 함수(`write_notification`).
* 작업 함수에 순서대로 전달되어야 하는 인자 시퀀스(`email`).
* 작업 함수에 전달되어야 하는 키워드 인자(`message="some notification"`).
## 의존성 주입
## 의존성 주입 { #dependency-injection }
`BackgroundTasks` 의존성 주입 시스템과 함께 사용하면 _경로 작동 함수_, 종속성, 하위 종속성 등 여러 수준에서 BackgroundTasks 유형의 매개변수를 선언할 수 있습니다.
`BackgroundTasks` 의존성 주입 시스템에서도 동작하며, *경로 처리 함수*, 의존성(dependable), 하위 의존성 등 여러 수준에서 `BackgroundTasks` 타입의 매개변수를 선언할 수 있습니다.
**FastAPI**는 각 경우에 수행할 작업과 동일한 체를 내부적으로 재사용하기에, 모든 백그라운드 작업이 함께 병합되고 나중에 백그라운드에서 실행됩니다.
**FastAPI**는 각 경우에 무엇을 해야 하는지와 동일한 체를 어떻게 재사용해야 하는지를 알고 있으므로, 모든 백그라운드 작업이 함께 병합되어 이후 백그라운드에서 실행됩니다:
{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *}
이 예제에서는 응답이 반환된 후에 `log.txt` 파일에 메시지가 기록됩니다.
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
요청에 쿼리가 있는 경우 백그라운드 작업의 로그에 기록됩니다.
그리고 _경로 작동 함수_ 에서 생성된 또 다른 백그라운드 작업은 경로 매개 변수를 활용하여 사용하여 메시지를 작성니다.
이 예제에서는 응답이 전송된 *후에* 메시지가 `log.txt` 파일에 작성니다.
## 기술적 세부사항
요청에 쿼리가 있었다면, 백그라운드 작업으로 로그에 작성됩니다.
그 다음 *경로 처리 함수*에서 생성된 또 다른 백그라운드 작업이 `email` 경로 매개변수를 사용해 메시지를 작성합니다.
## 기술적 세부사항 { #technical-details }
`BackgroundTasks` 클래스는 <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`starlette.background`</a>에서 직접 가져옵니다.
`BackgroundTasks` 클래스는 FastAPI에 직접 임포트하거나 포함하기 때문에 실수로 `BackgroundTask` (끝에 `s`가 없음)을 임포트하더라도 starlette.background에서 `BackgroundTask`를 가져오는 것을 방지할 수 있습니다.
FastAPI에 직접 임포트/포함되어 있으므로 `fastapi`에서 임포트할 수 있고, 실수로 `starlette.background`에서 대안인 `BackgroundTask`(끝에 `s`가 없음)를 임포트하는 것을 할 수 있습니다.
(`BackgroundTask`가 아닌) `BackgroundTasks` 사용하면, _경로 작동 함수_ 매개변수로 사용할 수 있게 되고 나머지는 **FastAPI**가 대신 처리하도록 할 수 있습니다. 이것은 `Request` 객체를 직접 사용하는 것과 같은 방식입니다.
`BackgroundTask`가 아닌 `BackgroundTasks` 사용하면, 이를 *경로 처리 함수*의 매개변수로 사용할 수 있고 나머지는 **FastAPI**가 `Request` 객체를 직접 사용할 때처럼 대신 처리해 줍니다.
FastAPI에서 `BackgroundTask` 단독으로 사용하는 것은 여전히 가능합니다. 하지만 객체를 코드에서 생성하고, 객체를 포함하는 Starlette `Response`를 반환해야 합니다.
FastAPI에서 `BackgroundTask` 단독으로 사용하는 것도 가능하지만, 코드에서 객체를 생성하고 이를 포함하는 Starlette `Response`를 반환해야 합니다.
<a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`Starlette의 공식 문서`</a>에서 백그라운드 작업에 대한 자세한 내용을 확인할 수 있습니다.
더 자세한 내용은 <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">Starlette의 Background Tasks 공식 문서</a>에서 확인할 수 있습니다.
## 경고
## 주의사항 { #caveat }
만약 무거운 백그라운드 작업을 수행해야하고 동일한 프로세스에서 실행할 필요가 없는 경우 (예: 메모리, 변수 등을 공유할 필요가 없음) <a href="https://docs.celeryq.dev" class="external-link" target="_blank">`Celery`</a> 같은 큰 도구를 사용하 도움이 될 수 있습니다.
무거운 백그라운드 계산을 수행해야 하고, 반드시 동일한 프로세스에서 실행할 필요가 없다면(예: 메모리, 변수 등을 공유할 필요가 없음) <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a> 같은 큰 도구를 사용하는 것이 도움이 될 수 있습니다.
RabbitMQ 또는 Redis 같은 메시지/작업 큐 시스템 보다 복잡한 구성이 필요한 경향이 있지만, 여러 작업 프로세스 특히 여러 서버 백그라운드에서 실행할 수 있습니다.
이들은 RabbitMQ Redis 같은 메시지/작업 큐 관리자 등 더 복잡한 설정을 필요로 하는 경향이 있지만, 여러 프로세스에서, 특히 여러 서버에서 백그라운드 작업을 실행할 수 있습니다.
그러나 동일한 FastAPI 앱에서 변수 및 개체에 접근해야햐는 작은 백그라운드 수행이 필요한 경우 (예 : 알림 이메일 보내기) 간단하게 `BackgroundTasks`사용해보세요.
하지만 동일한 **FastAPI** 앱의 변수와 객체에 접근해야 하거나(또는 이메일 알림 전송처럼) 작은 백그라운드 작업을 수행해야 한다면, `BackgroundTasks`간단히 사용하면 됩니다.
## 요약
## 요약 { #recap }
백그라운드 작업을 추가하기 위해 _경로 작동 함수_ 매개변수로 `BackgroundTasks`가져오고 사용합니다.
*경로 처리 함수*와 의존성에서 매개변수로 `BackgroundTasks`임포트해 사용하여 백그라운드 작업을 추가합니다.

View File

@@ -1,8 +1,8 @@
# 본문 - 필드
# 본문 - 필드 { #body-fields }
`Query`, `Path``Body`를 사용해 *경로 작동 함수* 매개변수 내에서 추가적인 검증이나 메타데이터를 선언한 것처럼 Pydantic의 `Field`를 사용하여 모델 내에서 검증과 메타데이터를 선언할 수 있습니다.
`Query`, `Path``Body`를 사용해 *경로 처리 함수* 매개변수 내에서 추가적인 검증이나 메타데이터를 선언한 것처럼 Pydantic의 `Field`를 사용하여 모델 내에서 검증과 메타데이터를 선언할 수 있습니다.
## `Field` 임포트
## `Field` 임포트 { #import-field }
먼저 이를 임포트해야 합니다:
@@ -14,7 +14,7 @@
///
## 모델 어트리뷰트 선언
## 모델 어트리뷰트 선언 { #declare-model-attributes }
그 다음 모델 어트리뷰트와 함께 `Field`를 사용할 수 있습니다:
@@ -22,7 +22,7 @@
`Field``Query`, `Path``Body`와 같은 방식으로 동작하며, 모두 같은 매개변수들 등을 가집니다.
/// note | 기술 세부사항
/// note | 기술 세부사항
실제로 `Query`, `Path`등, 여러분이 앞으로 볼 다른 것들은 공통 클래스인 `Param` 클래스의 서브클래스 객체를 만드는데, 그 자체로 Pydantic의 `FieldInfo` 클래스의 서브클래스입니다.
@@ -36,11 +36,11 @@
/// tip | 팁
주목할 점은 타입, 기본 값 및 `Field`로 이루어진 각 모델 어트리뷰트가 `Path`, `Query``Body`대신 `Field`를 사용하는 *경로 작동 함수*의 매개변수와 같은 구조를 가진다는 점 입니다.
주목할 점은 타입, 기본 값 및 `Field`로 이루어진 각 모델 어트리뷰트가 `Path`, `Query``Body`대신 `Field`를 사용하는 *경로 처리 함수*의 매개변수와 같은 구조를 가진다는 점 입니다.
///
## 별도 정보 추가
## 별도 정보 추가 { #add-extra-information }
`Field`, `Query`, `Body`, 그 외 안에 별도 정보를 선언할 수 있습니다. 이는 생성된 JSON 스키마에 포함됩니다.
@@ -53,7 +53,7 @@
///
## 요약
## 요약 { #recap }
모델 어트리뷰트를 위한 추가 검증과 메타데이터 선언하기 위해 Pydantic의 `Field` 를 사용할 수 있습니다.

View File

@@ -1,26 +1,24 @@
# 본문 - 다중 매개변수
# 본문 - 다중 매개변수 { #body-multiple-parameters }
지금부터 `Path``Query`를 어떻게 사용하는지 확인겠습니다.
이제 `Path``Query`를 어떻게 사용하는지 확인했으니, 요청 본문 선언에 대한 더 고급 사용법을 살펴보겠습니다.
요청 본문 선언에 대한 심화 사용법을 알아보겠습니다.
## `Path`, `Query` 및 본문 매개변수 혼합 { #mix-path-query-and-body-parameters }
## `Path`, `Query` 및 본문 매개변수 혼합
먼저, 물론 `Path`, `Query` 요청 본문 매개변수 선언을 자유롭게 혼합해서 사용할 수 있고, **FastAPI**는 어떤 동작을 할지 압니다.
당연하게 `Path`, `Query` 및 요청 본문 매개변수 선언을 자유롭게 혼합해서 사용할 수 있고, **FastAPI**는 어떤 동작을 할지 압니다.
또한 기본 값을 `None`으로 설정해 본문 매개변수택사항으로 선언할 수 있습니다:
또한, 기본 값을 `None`으로 설정해 본문 매개변수를 선택사항으로 선언할 수 있습니다.
{* ../../docs_src/body_multiple_params/tutorial001.py hl[19:21] *}
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | 참고
이 경우에는 본문으로 부터 가져 ` item`은 기본값이 `None`이기 때문에, 선택사항이라는 점을 유의해야 합니다.
이 경우에는 본문에서 가져 `item`이 선택사항이라는 점을 유의하세요. 기본값이 `None`이기 때문입니다.
///
## 다중 본문 매개변수
## 다중 본문 매개변수 { #multiple-body-parameters }
이전 예제에서 보듯이, *경로 작동*은 아래와 같이 `Item` 속성을 가진 JSON 본문을 예상합니다:
이전 예제에서, *경로 처리*는 아래처럼 `Item` 속성을 가진 JSON 본문을 예상합니다:
```JSON
{
@@ -33,11 +31,12 @@
하지만, 다중 본문 매개변수 역시 선언할 수 있습니다. 예. `item``user`:
{* ../../docs_src/body_multiple_params/tutorial002.py hl[22] *}
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
이 경우에, **FastAPI**는 이 함수 안에 한 개 이상의 본문 매개변수(Pydantic 모델인 두 매개변수)가 있다고 알 것입니다.
그래서, 본문 매개변수 이름을 키(필드 명)로 사용할 수 있고, 다음과 같은 본문을 예측합니다:
이 경우에, **FastAPI**는 이 함수에 본문 매개변수가 1개보다 많다는 것을 알아챌 것입니다(두 매개변수가 Pydantic 모델입니다).
그래서, 본문에서 매개변수 이름을 키(필드 이름)로 사용하고, 다음과 같은 본문을 예상합니다:
```JSON
{
@@ -56,29 +55,28 @@
/// note | 참고
이전과 같이 `item`이 선언 되었더라도, 본문 내의 `item` 있을 것이라고 예측합니다.
`item` 이전과 같은 방식으로 선언되었더라도, 이제는 본문에서 `item` 안에 있을 것으로 예상된다는 점을 유의하세요.
///
FastAPI는 요청 자동으로 변환, 매개변수 `item``user`를 특별한 내용으로 받도록 할 것입니다.
**FastAPI**는 요청에서 자동으로 변환을 수행하여, 매개변수 `item`이 해당하는 내용을 받고 `user`도 마찬가지로 받도록 니다.
복합 데이터의 검증을 수행하고 OpenAPI 스키마 및 자동 문서 문서화합니다.
복합 데이터의 검증을 수행하고, OpenAPI 스키마 및 자동 문서에도 그에 맞게 문서화합니다.
## 본문 내의 단일 값
## 본문 내의 단일 값 { #singular-values-in-body }
쿼리 및 경로 매개변수에 대한 추가 데이터를 정의하는 `Query``Path`와 같이, **FastAPI**는 동등한 `Body`를 제공합니다.
쿼리 및 경로 매개변수에 대한 추가 데이터를 정의하는 `Query``Path`가 있는 것과 같은 방식으로, **FastAPI**는 동등한 `Body`를 제공합니다.
예를 들어 이전 모델을 확장하면, `item``user`와 동일한 본문에 또 다른 `importance`라는 키를 갖도록 할 수있습니다.
예를 들어 이전 모델을 확장해서, `item``user` 외에도 같은 본문에 `importance`라는 다른 키를 두고 싶을 있습니다.
단일 값 그대로 선언한다면, **FastAPI**는 쿼리 매개변수 가정할 것입니다.
단일 값이므로 그대로 선언면, **FastAPI**는 이를 쿼리 매개변수라고 가정할 것입니다.
하지만, **FastAPI**의 `Body`를 사용 다른 본문 키로 처리하도록 제어할 수 있습니다:
하지만 `Body`를 사용하여 다른 본문 키로 처리하도록 **FastAPI**에 지시할 수 있습니다:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
{* ../../docs_src/body_multiple_params/tutorial003.py hl[23] *}
이 경우에는 **FastAPI**는 본문을 이와 같이 예측할 것입니다:
이 경우에는 **FastAPI**가 다음과 같은 본문을 예상할 것입니다:
```JSON
{
@@ -96,58 +94,55 @@ FastAPI는 요청을 자동으로 변환해, 매개변수의 `item`과 `user`를
}
```
다시 말해, 데이터 타입, 검증, 문서 등을 변환합니다.
다시 말해, 데이터 타입을 변환하고, 검증하고, 문서화하는 등의 작업을 수행합니다.
## 다중 본문 매개변수와 쿼리
## 다중 본문 매개변수와 쿼리 { #multiple-body-params-and-query }
당연히, 필요할 때마다 추가적인 쿼리 매개변수 선언할 수 있고, 이는 본문 매개변수에 추가됩니다.
물론, 필요할 때마다 어떤 본문 매개변수에 추가로 쿼리 매개변수 선언할 수 있니다.
기본적으로 단일 값은 쿼리 매개변수로 해석되므로, 명시적으로 `Query`를 추가할 필요고, 아래처럼 할 수 있습니다:
{* ../../docs_src/body_multiple_params/tutorial004.py hl[27] *}
이렇게:
기본적으로 단일 값은 쿼리 매개변수로 해석되므로, 명시적으로 `Query`를 추가할 필요 없이 이렇게 하면 됩니다:
```Python
q: Optional[str] = None
q: Union[str, None] = None
```
또는 Python 3.10 이상에서는:
```Python
q: str | None = None
```
예를 들어:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | 정보
`Body` 또한 `Query`, `Path` 그리고 이후에 볼 다른 것들처럼 동일한 추가 검증과 메타데이터 매개변수를 갖고 있습니다.
`Body` 또한 `Query`, `Path` 그리고 이후에 볼 다른 것들과 마찬가지로 동일한 추가 검증과 메타데이터 매개변수를 모두 갖고 있습니다.
///
## 단일 본문 매개변수 삽입하기
## 단일 본문 매개변수 삽입하기 { #embed-a-single-body-parameter }
Pydantic 모델 `Item` `item` 본문 매개변수로 오직 한개만 갖고있다고 하겠습니다.
Pydantic 모델 `Item`에서 가져온 단일 `item` 본문 매개변수있다고 하겠습니다.
기본적으로 **FastAPI**는 직접 본문으로 예측할 것입니다.
기본적으로 **FastAPI**는 본문을 직접 예상합니다.
하지만, 만약 모델 내용에 `item `키를 가진 JSON으로 예측하길 원한다면, 추가적인 본문 매개변수를 선언한 것처럼 `Body`의 특별한 매개변수 `embed`를 사용할 수 있습니다:
{* ../../docs_src/body_multiple_params/tutorial005.py hl[17] *}
아래 처럼:
하지만 추가 본문 매개변수를 선언할 때처럼, `item` 키를 가지고 그 안에 모델 내용이 들어 있는 JSON을 예상하게 하려면, `Body`의 특별한 매개변수 `embed`를 사용할 수 있습니다:
```Python
item: Item = Body(..., embed=True)
item: Item = Body(embed=True)
```
이 경우에 **FastAPI**는 본문을 아래 대신에:
다음과 같이요:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
이 경우 **FastAPI**는 다음과 같은 본문을 예상합니다:
```JSON hl_lines="2"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
아래 처럼 예측할 것 입니다:
```JSON
{
"item": {
"name": "Foo",
@@ -158,12 +153,23 @@ item: Item = Body(..., embed=True)
}
```
## 정리
다음 대신에:
요청이 단 한개의 본문을 가지고 있더라도, *경로 작동 함수*로 다중 본문 매개변수를 추가할 수 있습니다.
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
하지만, **FastAPI**는 이를 처리하고, 함수에 올바른 데이터를 제공하며, *경로 작동*으로 올바른 스키마를 검증하고 문서화 합니다.
## 정리 { #recap }
또한, 단일 값을 본문의 일부로 받도록 선언할 수 있습니다.
요청은 본문을 하나만 가질 수 있지만, *경로 처리 함수*에 다중 본문 매개변수를 추가할 수 있습니다.
그리고 **FastAPI**는 단 한개의 매개변수가 선언 되더라도, 본문 내의 키로 삽입 시킬 수 있습니다.
하지만 **FastAPI**는 이를 처리하고, 함수에 올바른 데이터를 제공하며, *경로 처리*에서 올바른 스키마를 검증하고 문서화합니다.
또한 단일 값을 본문의 일부로 받도록 선언할 수 있습니다.
그리고 단 하나의 매개변수만 선언되어 있더라도, **FastAPI**에 본문을 키 안에 삽입하도록 지시할 수 있습니다.

View File

@@ -1,35 +1,26 @@
# 본문 - 중첩 모델
# 본문 - 중첩 모델 { #body-nested-models }
**FastAPI**를 용하면 (Pydantic 덕분에) 단독으로 깊 중첩된 모델을 정의, 검증, 문서화하 사용할 수 있습니다.
## 리스트 필드
**FastAPI**를 용하면 (Pydantic 덕분에) 임의로 깊 중첩된 모델을 정의, 검증, 문서화하 사용할 수 있습니다.
## 리스트 필드 { #list-fields }
어트리뷰트를 서브타입으로 정의할 수 있습니다. 예를 들어 파이썬 `list`는:
{* ../../docs_src/body_nested_models/tutorial001.py hl[14] *}
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
이는 `tags` 항목 리스트로 만듭니다. 각 항목의 타입을 선언하지 않더라도요.
이는 `tags`를 리스트로 만들지만, 리스트 요소의 타입을 선언하지습니다.
## 타입 매개변수가 있는 리스트 필드
## 타입 매개변수가 있는 리스트 필드 { #list-fields-with-type-parameter }
하지만 파이썬 내부 타입이나 "타입 매개변수"를 선언할 수 있는 특정 방법이 있습니다:
하지만 파이썬에는 내부 타입, 즉 "타입 매개변수"를 사용해 리스트를 선언하는 특정 방법이 있습니다:
### typing의 `List` 임포트
### 타입 매개변수로 `list` 선언 { #declare-a-list-with-a-type-parameter }
먼저, 파이썬 표준 `typing` 모듈에서 `List`를 임포트합니다:
{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
### 타입 매개변수로 `List` 선언
`list`, `dict`, `tuple`과 같은 타입 매개변수(내부 타입)를 갖는 타입을 선언하려면:
* `typing` 모듈에서 임포트
* 대괄호를 사용하여 "타입 매개변수"로 내부 타입 전달: `[``]`
`list`, `dict`, `tuple`처럼 타입 매개변수(내부 타입)를 갖는 타입을 선언하려면,
대괄호 `[``]`를 사용해 내부 타입(들)을 "타입 매개변수"로 전달하세요.
```Python
from typing import List
my_list: List[str]
my_list: list[str]
```
이 모든 것은 타입 선언을 위한 표준 파이썬 문법입니다.
@@ -38,45 +29,45 @@ my_list: List[str]
마찬가지로 예제에서 `tags`를 구체적으로 "문자열의 리스트"로 만들 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial002.py hl[14] *}
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## 집합 타입
## 집합 타입 { #set-types }
그런데 생각해보니 태그는 반복되면 안 되고, 고유한(Unique) 문자열이어야 할 것 같습니다.
그런데 생각해보니 태그는 반복되면 안 되고, 아마 고유한 문자열이어야 할 것니다.
그리고 파이썬 집합을 위한 특별한 데이터 타입 `set`이 있습니다.
그리고 파이썬에는 고유한 항목들의 집합을 위한 특별한 데이터 타입 `set`이 있습니다.
그렇다면 `Set`을 임포트 하고 `tags``str``set`으로 선언할 수 있습니다:
그렇다면 `tags`를 문자열의 집합으로 선언할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial003.py hl[1,14] *}
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
덕분에 중복 데이터가 있는 요청을 수신하더라도 고유한 항목들의 집합으로 변환됩니다.
이렇게 하면 중복 데이터가 있는 요청을 더라도 고유한 항목들의 집합으로 변환됩니다.
그리고 해당 데이터를 출력 할 때마다 소스에 중복이 있더라도 고유한 항목들의 집합으로 출력됩니다.
그리고 해당 데이터를 출력할 때마다, 소스에 중복이 있더라도 고유한 항목들의 집합으로 출력됩니다.
또한 그에 따라 주석이 생기고 문서화됩니다.
## 중첩 모델
## 중첩 모델 { #nested-models }
Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
그런데 해당 타입 자체 또다른 Pydantic 모델의 타입이 될 수 있습니다.
그런데 타입 자체 다른 Pydantic 모델 수 있습니다.
그러므로 특정한 어트리뷰트 이름, 타입, 검증을 사용하여 깊게 중첩된 JSON "객체"를 선언할 수 있습니다.
따라서 특정한 어트리뷰트 이름, 타입, 검증을 사용하여 깊게 중첩된 JSON "객체"를 선언할 수 있습니다.
모든 것이 단독으로 중첩됩니다.
모든 것이 임의의 깊이로 중첩됩니다.
### 서브모델 정의
### 서브모델 정의 { #define-a-submodel }
예를 들어, `Image` 모델을 선언할 수 있습니다:
예를 들어, `Image` 모델을 정의할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial004.py hl[9:11] *}
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### 서브모을 타입으로 사용
### 서브모을 타입으로 사용 { #use-the-submodel-as-a-type }
그리고 어트리뷰트의 타입으로 사용할 수 있습니다:
그리고 이를 어트리뷰트의 타입으로 사용할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial004.py hl[20] *}
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
이는 **FastAPI**가 다음과 유사한 본문을 기대한다는 것을 의미합니다:
@@ -94,32 +85,32 @@ Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
}
```
다시 한번, **FastAPI**를 사용하여 해당 선언을 함으로써 얻는 것은:
다시 한번, **FastAPI**로 그 선언만 해도 얻는 것은:
* 중첩 모델도 편집기 지원(자동완성 등)
* 데이터 변환
* 데이터 검증
* 자동 문서화
## 특별한 타입과 검증
## 특별한 타입과 검증 { #special-types-and-validation }
`str`, `int`, `float` 등과 같은 단일 타입과는 별개로, `str`을 상속하는 더 복잡한 단일 타입을 사용할 수 있습니다.
`str`, `int`, `float` 등과 같은 일반적인 단일 타입과는 별개로, `str`을 상속하는 더 복잡한 단일 타입을 사용할 수 있습니다.
모든 옵션을 보려면, <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">Pydantic's exotic types</a> 문서를 확인하세요. 다음 장에서 몇가지 예제를 볼 수 있습니다.
사용할 수 있는 모든 옵션을 보려면 <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">Pydantic의 Type Overview</a>를 확인하세요. 다음 장에서 몇 가지 예제를 볼 수 있습니다.
예를 들어 `Image` 모델`url` 필드를 `str` 대신 Pydantic의 `HttpUrl`로 선언할 수 있습니다:
예를 들어 `Image` 모델에 `url` 필드가 있으므로, 이`str` 대신 Pydantic의 `HttpUrl` 인스턴스로 선언할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial005.py hl[4,10] *}
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
이 문자열 유효한 URL인지 검사하고 JSON 스키마/OpenAPI로 문서화 됩니다.
이 문자열 유효한 URL인지 검사되며, JSON Schema / OpenAPI에도 그에 맞게 문서화됩니다.
## 서브모델 리스트를 갖는 어트리뷰트
## 서브모델 리스트를 갖는 어트리뷰트 { #attributes-with-lists-of-submodels }
`list`, `set` 등의 서브타입으로 Pydantic 모델을 사용할 수도 있습니다:
{* ../../docs_src/body_nested_models/tutorial006.py hl[20] *}
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
아래와 같은 JSON 본문으로 예상(변환, 검증, 문서화 등)합니다:
아래와 같은 JSON 본문 예상(변환, 검증, 문서화 등)합니다:
```JSON hl_lines="11"
{
@@ -147,84 +138,84 @@ Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
/// info | 정보
`images` 키가 어떻게 이미지 객체 리스트를 갖는지 주목하세요.
`images` 키가 이제 이미지 객체 리스트를 갖는지 주목하세요.
///
## 깊게 중첩된 모델
## 깊게 중첩된 모델 { #deeply-nested-models }
단독으로 깊게 중첩된 모델을 정의할 수 있습니다:
임의로 깊게 중첩된 모델을 정의할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial007.py hl[9,14,20,23,27] *}
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | 정보
`Offer`가 선택사항 `Image` 리스트를 차례로 갖는 `Item` 리스트를 어떻게 가지고 있는지 주목하세요
`Offer`가 `Item` 리스트를 가지고, 그 `Item`이 다시 선택 사항인 `Image` 리스트를 갖는지 주목하세요
///
## 순수 리스트의 본문
## 순수 리스트의 본문 { #bodies-of-pure-lists }
예상되는 JSON 본문의 최상위 값이 JSON `array`(파이썬 `list`)면, Pydantic 모델에서와 마찬가지로 함수의 매개변수에서 타입을 선언할 수 있습니다:
예상되는 JSON 본문의 최상위 값이 JSON `array`(파이썬 `list`)면, Pydantic 모델에서와 마찬가지로 함수의 매개변수에서 타입을 선언할 수 있습니다:
```Python
images: List[Image]
images: list[Image]
```
이를 아래처럼:
{* ../../docs_src/body_nested_models/tutorial008.py hl[15] *}
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
## 어디서나 편집기 지원
## 어디서나 편집기 지원 { #editor-support-everywhere }
그리고 어디서나 편집기 지원을 받을수 있습니다.
그리고 어디서나 편집기 지원을 받을 수 있습니다.
리스트 내부 항목의 경우에도:
<img src="/img/tutorial/body-nested-models/image01.png">
Pydantic 모델 대신 `dict` 직접 사용하여 작업할 경우, 이러한 편집기 지원을 받을수 없습니다.
Pydantic 모델 대신 `dict` 직접 작업한다면 이런 종류의 편집기 지원을 받을 수 없습니다.
하지만 수신한 딕셔너리가 자동으로 변환되고 출력도 자동으로 JSON으로 변환되므로 걱정할 필요는 없습니다.
하지만 그 부분에 대해서도 걱정할 필요는 없습니다. 들어오는 dict는 자동으로 변환되고, 출력도 자동으로 JSON으로 변환니다.
## 단독 `dict` 본문
## 임의의 `dict` 본문 { #bodies-of-arbitrary-dicts }
일부 타입의 키와 다른 타입의 값을 사용하여 `dict`로 본문을 선언할 수 있습니다.
또한 키는 어떤 타입이고 값은 다른 타입인 `dict`로 본문을 선언할 수 있습니다.
(Pydantic을 사용 경우처럼) 유효한 필드/어트리뷰트 이름이 무엇인지 알 필요가 없습니다.
이렇게 하면 (Pydantic 모델을 사용하는 경우처럼) 유효한 필드/어트리뷰트 이름이 무엇인지 미리 알 필요가 없습니다.
아직 모르는 키를 받으려는 경우 유용합니다.
아직 모르는 키를 받으려는 경우 유용합니다.
---
다른 유용한 경우는 다른 타입의 키를 가질 때입니다. 예. `int`.
다른 유용한 경우는 다른 타입(예: `int`)의 키를 갖고 싶을 때입니다.
여기서 그 경우를 볼 것입니다.
이 경우, `float` 값을 가진 `int` 키가 있는 모든 `dict` 받아들입니다:
이 경우, `int` 키와 `float` 값을 가진 한 어떤 `dict` 받아들입니다:
{* ../../docs_src/body_nested_models/tutorial009.py hl[15] *}
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
/// tip | 팁
JSON은 오직 `str`형 키만 지원한다는 것을 염두에 두세요.
JSON은 키로 `str`만 지원한다는 것을 염두에 두세요.
하지만 Pydantic은 자동 데이터 변환이 있습니다.
하지만 Pydantic은 자동 데이터 변환 기능이 있습니다.
즉, API 클라이언트가 문자열을 키로 보내더라도 해당 문자열이 순수한 정수를 포함하는한 Pydantic 이를 변환하고 검증합니다.
즉, API 클라이언트는 키로 문자열만 보낼 수 있더라도, 해당 문자열이 순수한 정수를 포함하기만 하면 Pydantic 이를 변환하고 검증합니다.
러므로 `weights`로 받 `dict`는 실제로 `int` 키와 `float` 값을 가집니다.
리고 `weights`로 받 `dict`는 실제로 `int` 키와 `float` 값을 갖게 됩니다.
///
## 요약
## 요약 { #recap }
**FastAPI**를 사용하면 Pydantic 모델이 제공하는 최대 유연성을 확보하면서 코드를 간단하고 짧게, 그리고 우아하게 유지할 수 있습니다.
**FastAPI**를 사용하면 Pydantic 모델이 제공하는 최대 유연성을 확보하면서 코드를 간단하고 짧고 우아하게 유지할 수 있습니다.
물론 아래의 이점도 있습니다:
하지만 아래의 모든 이점도 있습니다:
* 편집기 지원 (자동완성이 어디서나!)
* 데이터 변환 (일명 파싱/직렬화)
* 편집기 지원(어디서나 자동완성!)
* 데이터 변환(일명 파싱/직렬화)
* 데이터 검증
* 스키마 문서화
* 자동 문서

View File

@@ -1,10 +1,10 @@
# 요청 본문
# 요청 본문 { #request-body }
클라이언트(브라우저라고 해봅시다)로부터 여러분의 API로 데이터를 보내야 할 때, **요청 본문**으로 보냅니다.
**요청** 본문은 클라이언트에서 API로 보내지는 데이터입니다. **응답** 본문은 API가 클라이언트로 보내는 데이터입니다.
여러분의 API는 대부분의 경우 **응답** 본문을 보내야 합니다. 하지만 클라이언트는 **요청** 본문을 매 번 보낼 필요 없습니다.
여러분의 API는 대부분의 경우 **응답** 본문을 보내야 합니다. 하지만 클라이언트는 항상 **요청 본문**을 보낼 필요고, 때로는 (쿼리 매개변수와 함께) 어떤 경로만 요청하고 본문은 보내지 않을 수도 있습니다.
**요청** 본문을 선언하기 위해서 모든 강력함과 이점을 갖춘 <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> 모델을 사용합니다.
@@ -18,13 +18,13 @@
///
## Pydantic의 `BaseModel` 임포트
## Pydantic의 `BaseModel` 임포트 { #import-pydantics-basemodel }
먼저 `pydantic`에서 `BaseModel`를 임포트해야 합니다:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## 여러분의 데이터 모델 만들기
## 여러분의 데이터 모델 만들기 { #create-your-data-model }
`BaseModel`를 상속받은 클래스로 여러분의 데이터 모델을 선언합니다.
@@ -32,6 +32,7 @@
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
쿼리 매개변수를 선언할 때와 같이, 모델 어트리뷰트가 기본 값을 가지고 있어도 이는 필수가 아닙니다. 그외에는 필수입니다. 그저 `None`을 사용하여 선택적으로 만들 수 있습니다.
예를 들면, 위의 이 모델은 JSON "`object`" (혹은 파이썬 `dict`)을 다음과 같이 선언합니다:
@@ -39,7 +40,7 @@
```JSON
{
"name": "Foo",
"description": "선택적인 설명란",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
@@ -54,15 +55,15 @@
}
```
## 매개변수로서 선언하기
## 매개변수로서 선언하기 { #declare-it-as-a-parameter }
여러분의 *경로 작동*에 추가하기 위해, 경로 매개변수 그리고 쿼리 매개변수에서 선언했던 것과 같은 방식으로 선언하면 됩니다.
여러분의 *경로 처리*에 추가하기 위해, 경로 매개변수 그리고 쿼리 매개변수에서 선언했던 것과 같은 방식으로 선언하면 됩니다.
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...그리고 만들어낸 모델인 `Item`으로 타입을 선언합니다.
## 결과
## 결과 { #results }
위에서의 단순한 파이썬 타입 선언으로, **FastAPI**는 다음과 같이 동작합니다:
@@ -72,20 +73,20 @@
* 만약 데이터가 유효하지 않다면, 정확히 어떤 것이 그리고 어디에서 데이터가 잘 못 되었는지 지시하는 친절하고 명료한 에러를 반환할 것입니다.
* 매개변수 `item`에 포함된 수신 데이터를 제공합니다.
* 함수 내에서 매개변수를 `Item` 타입으로 선언했기 때문에, 모든 어트리뷰트와 그에 대한 타입에 대한 편집기 지원(완성 등)을 또한 받을 수 있습니다.
* 여러분의 모델을 위한 <a href="https://json-schema.org" class="external-link" target="_blank">JSON 스키마</a> 정의를 생성합니다. 여러분의 프로젝트에 적합하다면 여러분이 사용하고 싶은 곳 어디에서나 사용할 수 있습니다.
* 이러한 스키마는, 생성된 OpenAPI 스키마 일부가 될 것이며, 자동 문서화 <abbr title="사용자 인터페이스">UI</abbr>에 사용됩니다.
* 여러분의 모델을 위한 <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> 정의를 생성합니다. 여러분의 프로젝트에 적합하다면 여러분이 사용하고 싶은 곳 어디에서나 사용할 수 있습니다.
* 이러한 스키마는, 생성된 OpenAPI 스키마 일부가 될 것이며, 자동 문서화 <abbr title="User Interfaces 사용자 인터페이스">UIs</abbr>에 사용됩니다.
## 자동 문서화
## 자동 문서화 { #automatic-docs }
모델의 JSON 스키마는 생성된 OpenAPI 스키마에 포함되며 대화형 API 문서에 표시됩니다:
<img src="/img/tutorial/body/image01.png">
이를 필요로 하는 각각의 *경로 작동*내부의 API 문서에도 사용됩니다:
이를 필요로 하는 각각의 *경로 처리* 내부의 API 문서에도 사용됩니다:
<img src="/img/tutorial/body/image02.png">
## 편집기 지원
## 편집기 지원 { #editor-support }
편집기에서, 함수 내에서 타입 힌트와 완성을 어디서나 (만약 Pydantic model 대신에 `dict`을 받을 경우 나타나지 않을 수 있습니다) 받을 수 있습니다:
@@ -97,13 +98,13 @@
단순한 우연이 아닙니다. 프레임워크 전체가 이러한 디자인을 중심으로 설계되었습니다.
그 어떤 실행 전에, 모든 편집기에서 작동할 수 있도록 보장하기 위해 설계 단계에서 혹독하게 테스트되었습니다.
그 어떤 구현 전에, 모든 편집기에서 작동할 수 있도록 보장하기 위해 설계 단계에서 혹독하게 테스트되었습니다.
이를 지원하기 위해 Pydantic 자체에서 몇몇 변경점이 있었습니다.
이전 스크린샷은 <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>를 찍은 것입니다.
하지만 똑같은 편집기 지원을 <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>에서 받을 수 있거나, 대부분의 다른 편집기에서도 받을 수 있습니다:
하지만 똑같은 편집기 지원을 <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> 대부분의 다른 파이썬 편집기에서도 받을 수 있습니다:
<img src="/img/tutorial/body/image05.png">
@@ -113,21 +114,21 @@
다음 사항을 포함해 Pydantic 모델에 대한 편집기 지원을 향상시킵니다:
* 자동 완성
* 타입 확인
* 리팩토링
* 검색
* 점검
* auto-completion
* type checks
* refactoring
* searching
* inspections
///
## 모델 사용하기
## 모델 사용하기 { #use-the-model }
함수 안에서 모델 객체의 모든 어트리뷰트에 직접 접근 가능합니다:
{* ../../docs_src/body/tutorial002_py310.py hl[19] *}
{* ../../docs_src/body/tutorial002_py310.py *}
## 요청 본문 + 경로 매개변수
## 요청 본문 + 경로 매개변수 { #request-body-path-parameters }
경로 매개변수와 요청 본문을 동시에 선언할 수 있습니다.
@@ -135,7 +136,8 @@
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## 요청 본문 + 경로 + 쿼리 매개변수
## 요청 본문 + 경로 + 쿼리 매개변수 { #request-body-path-query-parameters }
**본문**, **경로** 그리고 **쿼리** 매개변수 모두 동시에 선언할 수도 있습니다.
@@ -153,10 +155,12 @@
FastAPI는 `q`의 값이 필요없음을 알게 될 것입니다. 기본 값이 `= None`이기 때문입니다.
`Union[str, None]`에 있는 `Union`은 FastAPI에 의해 사용된 것이 아니지만, 편집기로 하여금 더 나은 지원과 에러 탐지를 지원할 것입니다.
Python 3.10+의 `str | None` 또는 Python 3.9+의 `Union[str, None]`에 있는 `Union`은 FastAPI가 `q` 값이 필수가 아님을 판단하기 위해 사용하지 않습니다. 기본 값이 `= None`이기 때문에 필수가 아님을 알게 됩니다.
하지만 타입 어노테이션을 추가하면 편집기가 더 나은 지원을 제공하고 오류를 감지할 수 있습니다.
///
## Pydantic없이
## Pydantic없이 { #without-pydantic }
만약 Pydantic 모델을 사용하고 싶지 않다면, **Body** 매개변수를 사용할 수도 있습니다. [Body - 다중 매개변수: 본문에 있는 유일한 값](body-multiple-params.md#_2){.internal-link target=_blank} 문서를 확인하세요.
만약 Pydantic 모델을 사용하고 싶지 않다면, **Body** 매개변수를 사용할 수도 있습니다. [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank} 문서를 확인하세요.

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