diff --git a/.github/labeler.yml b/.github/labeler.yml index cdaefbf2d8..57c5e1120f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -31,6 +31,7 @@ internal: - .pre-commit-config.yaml - pdm_build.py - requirements*.txt + - uv.lock - docs/en/data/sponsors.yml - docs/en/overrides/main.html - all-globs-to-all-files: diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index f40ec4dc47..2ae588da13 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -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 there’s 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." } } diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 891f767175..5cbbde61fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,35 @@ env: UV_NO_SYNC: true jobs: + changes: + runs-on: ubuntu-latest + # Required permissions + permissions: + pull-requests: read + # Set job outputs to values from filter step + outputs: + src: ${{ steps.filter.outputs.src }} + steps: + - uses: actions/checkout@v6 + # For pull requests it's not necessary to checkout the code but for the main branch it is + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + src: + - .github/workflows/test.yml + - docs_src/** + - fastapi/** + - scripts/** + - tests/** + - .python-version + - pyproject.toml + - uv.lock + test: + needs: + - changes + if: needs.changes.outputs.src == 'true' strategy: matrix: os: [ windows-latest, macos-latest ] @@ -91,7 +119,8 @@ jobs: include-hidden-files: true coverage-combine: - needs: [test] + needs: + - test runs-on: ubuntu-latest steps: - name: Dump GitHub context @@ -143,3 +172,4 @@ jobs: uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} + allowed-skips: coverage-combine,test diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d88b70b7b6..64b84bfbd2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,6 +30,13 @@ repos: language: unsupported types: [python] + - id: local-mypy + name: mypy check + entry: uv run mypy fastapi + require_serial: true + language: unsupported + pass_filenames: false + - id: add-permalinks-pages language: unsupported name: add-permalinks-pages diff --git a/README.md b/README.md index 1057b86942..963de51edf 100644 --- a/README.md +++ b/README.md @@ -164,8 +164,6 @@ $ pip install "fastapi[standard]" Create a file `main.py` with: ```Python -from typing import Union - from fastapi import FastAPI app = FastAPI() @@ -177,7 +175,7 @@ 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: str | None = None): return {"item_id": item_id, "q": q} ``` @@ -186,9 +184,7 @@ def read_item(item_id: int, q: Union[str, None] = None): If your code uses `async` / `await`, use `async def`: -```Python hl_lines="9 14" -from typing import Union - +```Python hl_lines="7 12" from fastapi import FastAPI app = FastAPI() @@ -200,7 +196,7 @@ async def read_root(): @app.get("/items/{item_id}") -async def read_item(item_id: int, q: Union[str, None] = None): +async def read_item(item_id: int, q: str | None = None): return {"item_id": item_id, "q": q} ``` @@ -291,9 +287,7 @@ Now modify the file `main.py` to receive a body from a `PUT` request. Declare the body using standard Python types, thanks to Pydantic. -```Python hl_lines="4 9-12 25-27" -from typing import Union - +```Python hl_lines="2 7-10 23-25" from fastapi import FastAPI from pydantic import BaseModel @@ -303,7 +297,7 @@ app = FastAPI() class Item(BaseModel): name: str price: float - is_offer: Union[bool, None] = None + is_offer: bool | None = None @app.get("/") @@ -312,7 +306,7 @@ 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: str | None = None): return {"item_id": item_id, "q": q} diff --git a/docs/de/docs/_llm-test.md b/docs/de/docs/_llm-test.md index bc7ce363c7..0b95fe3a8d 100644 --- a/docs/de/docs/_llm-test.md +++ b/docs/de/docs/_llm-test.md @@ -189,7 +189,7 @@ Siehe Abschnitt `### Links` im allgemeinen Prompt in `scripts/translate.py`. //// -## HTML „abbr“-Elemente { #html-abbr-elements } +## HTML-„abbr“-Elemente { #html-abbr-elements } //// tab | Test diff --git a/docs/de/docs/tutorial/bigger-applications.md b/docs/de/docs/tutorial/bigger-applications.md index 963baf44d5..d478d77c27 100644 --- a/docs/de/docs/tutorial/bigger-applications.md +++ b/docs/de/docs/tutorial/bigger-applications.md @@ -56,19 +56,19 @@ from app.routers import items Die gleiche Dateistruktur mit Kommentaren: -``` +```bash . -├── app # „app“ ist ein Python-Package -│   ├── __init__.py # diese Datei macht „app“ zu einem „Python-Package“ -│   ├── main.py # „main“-Modul, z. B. import app.main -│   ├── dependencies.py # „dependencies“-Modul, z. B. import app.dependencies -│   └── routers # „routers“ ist ein „Python-Subpackage“ -│   │ ├── __init__.py # macht „routers“ zu einem „Python-Subpackage“ -│   │ ├── items.py # „items“-Submodul, z. B. import app.routers.items -│   │ └── users.py # „users“-Submodul, z. B. import app.routers.users -│   └── internal # „internal“ ist ein „Python-Subpackage“ -│   ├── __init__.py # macht „internal“ zu einem „Python-Subpackage“ -│   └── admin.py # „admin“-Submodul, z. B. import app.internal.admin +├── app # "app" ist ein Python-Package +│   ├── __init__.py # diese Datei macht "app" zu einem "Python-Package" +│   ├── main.py # "main"-Modul, z. B. import app.main +│   ├── dependencies.py # "dependencies"-Modul, z. B. import app.dependencies +│   └── routers # "routers" ist ein "Python-Subpackage" +│   │ ├── __init__.py # macht "routers" zu einem "Python-Subpackage" +│   │ ├── items.py # "items"-Submodul, z. B. import app.routers.items +│   │ └── users.py # "users"-Submodul, z. B. import app.routers.users +│   └── internal # "internal" ist ein "Python-Subpackage" +│   ├── __init__.py # macht "internal" zu einem "Python-Subpackage" +│   └── admin.py # "admin"-Submodul, z. B. import app.internal.admin ``` ## `APIRouter` { #apirouter } @@ -479,7 +479,7 @@ $ fastapi dev app/main.py -und öffnen Sie die Dokumentation unter http://127.0.0.1:8000/docs. +Und öffnen Sie die Dokumentation unter http://127.0.0.1:8000/docs. Sie sehen die automatische API-Dokumentation, einschließlich der Pfade aller Submodule, mit den richtigen Pfaden (und Präfixen) und den richtigen Tags: diff --git a/docs/en/data/contributors.yml b/docs/en/data/contributors.yml index 0c144cd4ce..41115ccbd4 100644 --- a/docs/en/data/contributors.yml +++ b/docs/en/data/contributors.yml @@ -1,17 +1,17 @@ tiangolo: login: tiangolo - count: 857 + count: 871 avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4 url: https://github.com/tiangolo dependabot: login: dependabot - count: 130 + count: 133 avatarUrl: https://avatars.githubusercontent.com/in/29110?v=4 url: https://github.com/apps/dependabot alejsdev: login: alejsdev count: 53 - avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=85ceac49fb87138aebe8d663912e359447329090&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=0facffe3abf87f57a1f05fa773d1119cc5c2f6a5&v=4 url: https://github.com/alejsdev pre-commit-ci: login: pre-commit-ci @@ -20,8 +20,8 @@ pre-commit-ci: url: https://github.com/apps/pre-commit-ci YuriiMotov: login: YuriiMotov - count: 36 - avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4 + count: 38 + avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4 url: https://github.com/YuriiMotov github-actions: login: github-actions @@ -40,7 +40,7 @@ dmontagu: url: https://github.com/dmontagu svlandeg: login: svlandeg - count: 16 + count: 17 avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4 url: https://github.com/svlandeg nilslindemann: @@ -126,7 +126,7 @@ hitrust: ShahriyarR: login: ShahriyarR count: 4 - avatarUrl: https://avatars.githubusercontent.com/u/3852029?u=631b2ae59360ab380c524b32bc3d245aff1165af&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/3852029?u=2dc6402d9053ee53f7afc407089cbab21c68f21d&v=4 url: https://github.com/ShahriyarR adriangb: login: adriangb @@ -266,7 +266,7 @@ Nimitha-jagadeesha: lucaromagnoli: login: lucaromagnoli count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/38782977?u=15df02e806a2293af40ac619fba11dbe3c0c4fd4&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/38782977?u=a09a2e916625fa035f9dfa25ebc58e07aac8ec36&v=4 url: https://github.com/lucaromagnoli salmantec: login: salmantec @@ -521,7 +521,7 @@ s111d: estebanx64: login: estebanx64 count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=1900887aeed268699e5ea6f3fb7db614f7b77cd3&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=812422ae5d6a4bc5ff331c901fc54f9ab3cecf5c&v=4 url: https://github.com/estebanx64 ndimares: login: ndimares diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 50b1145301..f8085b4523 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -65,9 +65,6 @@ bronze: # - url: https://testdriven.io/courses/tdd-fastapi/ # title: Learn to build high-quality web apps with best practices # img: https://fastapi.tiangolo.com/img/sponsors/testdriven.svg - - url: https://lambdatest.com/?utm_source=fastapi&utm_medium=partner&utm_campaign=sponsor&utm_term=opensource&utm_content=webpage - title: LambdaTest, AI-Powered Cloud-based Test Orchestration Platform - img: https://fastapi.tiangolo.com/img/sponsors/lambdatest.png - - url: https://requestly.com/fastapi - title: All-in-one platform to Test, Mock and Intercept APIs. Built for speed, privacy and offline support. - img: https://fastapi.tiangolo.com/img/sponsors/requestly.png + - url: https://www.testmu.ai/?utm_source=fastapi&utm_medium=partner&utm_campaign=sponsor&utm_term=opensource&utm_content=webpage + title: TestMu AI. The Native AI-Agentic Cloud Platform to Supercharge Quality Engineering. + img: https://fastapi.tiangolo.com/img/sponsors/testmu.png diff --git a/docs/en/data/topic_repos.yml b/docs/en/data/topic_repos.yml index d089c7e5a7..a37cb6dcfd 100644 --- a/docs/en/data/topic_repos.yml +++ b/docs/en/data/topic_repos.yml @@ -1,176 +1,181 @@ - name: full-stack-fastapi-template html_url: https://github.com/fastapi/full-stack-fastapi-template - stars: 40334 + stars: 41312 owner_login: fastapi owner_html_url: https://github.com/fastapi - name: Hello-Python html_url: https://github.com/mouredev/Hello-Python - stars: 33628 + stars: 34206 owner_login: mouredev owner_html_url: https://github.com/mouredev - name: serve html_url: https://github.com/jina-ai/serve - stars: 21817 + stars: 21832 owner_login: jina-ai owner_html_url: https://github.com/jina-ai - name: HivisionIDPhotos html_url: https://github.com/Zeyi-Lin/HivisionIDPhotos - stars: 20409 + stars: 20661 owner_login: Zeyi-Lin owner_html_url: https://github.com/Zeyi-Lin - name: sqlmodel html_url: https://github.com/fastapi/sqlmodel - stars: 17415 + stars: 17567 owner_login: fastapi owner_html_url: https://github.com/fastapi - name: fastapi-best-practices html_url: https://github.com/zhanymkanov/fastapi-best-practices - stars: 15776 + stars: 16291 owner_login: zhanymkanov owner_html_url: https://github.com/zhanymkanov - name: Douyin_TikTok_Download_API html_url: https://github.com/Evil0ctal/Douyin_TikTok_Download_API - stars: 15588 + stars: 16132 owner_login: Evil0ctal owner_html_url: https://github.com/Evil0ctal -- name: machine-learning-zoomcamp - html_url: https://github.com/DataTalksClub/machine-learning-zoomcamp - stars: 12447 - owner_login: DataTalksClub - owner_html_url: https://github.com/DataTalksClub - name: SurfSense html_url: https://github.com/MODSetter/SurfSense - stars: 12128 + stars: 12723 owner_login: MODSetter owner_html_url: https://github.com/MODSetter +- name: machine-learning-zoomcamp + html_url: https://github.com/DataTalksClub/machine-learning-zoomcamp + stars: 12575 + owner_login: DataTalksClub + owner_html_url: https://github.com/DataTalksClub - name: fastapi_mcp html_url: https://github.com/tadata-org/fastapi_mcp - stars: 11326 + stars: 11478 owner_login: tadata-org owner_html_url: https://github.com/tadata-org - name: awesome-fastapi html_url: https://github.com/mjhea0/awesome-fastapi - stars: 10901 + stars: 11018 owner_login: mjhea0 owner_html_url: https://github.com/mjhea0 - name: XHS-Downloader html_url: https://github.com/JoeanAmier/XHS-Downloader - stars: 9584 + stars: 9938 owner_login: JoeanAmier owner_html_url: https://github.com/JoeanAmier - name: polar html_url: https://github.com/polarsource/polar - stars: 8951 + stars: 9348 owner_login: polarsource owner_html_url: https://github.com/polarsource - name: FastUI html_url: https://github.com/pydantic/FastUI - stars: 8934 + stars: 8949 owner_login: pydantic owner_html_url: https://github.com/pydantic - name: FileCodeBox html_url: https://github.com/vastsa/FileCodeBox - stars: 7934 + stars: 8060 owner_login: vastsa owner_html_url: https://github.com/vastsa - name: nonebot2 html_url: https://github.com/nonebot/nonebot2 - stars: 7248 + stars: 7311 owner_login: nonebot owner_html_url: https://github.com/nonebot - name: hatchet html_url: https://github.com/hatchet-dev/hatchet - stars: 6392 + stars: 6479 owner_login: hatchet-dev owner_html_url: https://github.com/hatchet-dev - name: fastapi-users html_url: https://github.com/fastapi-users/fastapi-users - stars: 5899 + stars: 5970 owner_login: fastapi-users owner_html_url: https://github.com/fastapi-users - name: serge html_url: https://github.com/serge-chat/serge - stars: 5754 + stars: 5751 owner_login: serge-chat owner_html_url: https://github.com/serge-chat - name: strawberry html_url: https://github.com/strawberry-graphql/strawberry - stars: 4577 + stars: 4598 owner_login: strawberry-graphql owner_html_url: https://github.com/strawberry-graphql +- name: devpush + html_url: https://github.com/hunvreus/devpush + stars: 4407 + owner_login: hunvreus + owner_html_url: https://github.com/hunvreus +- name: Kokoro-FastAPI + html_url: https://github.com/remsky/Kokoro-FastAPI + stars: 4359 + owner_login: remsky + owner_html_url: https://github.com/remsky - name: poem html_url: https://github.com/poem-web/poem - stars: 4303 + stars: 4337 owner_login: poem-web owner_html_url: https://github.com/poem-web - name: chatgpt-web-share html_url: https://github.com/chatpire/chatgpt-web-share - stars: 4287 + stars: 4279 owner_login: chatpire owner_html_url: https://github.com/chatpire - name: dynaconf html_url: https://github.com/dynaconf/dynaconf - stars: 4221 + stars: 4244 owner_login: dynaconf owner_html_url: https://github.com/dynaconf -- name: Kokoro-FastAPI - html_url: https://github.com/remsky/Kokoro-FastAPI - stars: 4181 - owner_login: remsky - owner_html_url: https://github.com/remsky +- name: Yuxi-Know + html_url: https://github.com/xerrors/Yuxi-Know + stars: 4154 + owner_login: xerrors + owner_html_url: https://github.com/xerrors - name: atrilabs-engine html_url: https://github.com/Atri-Labs/atrilabs-engine - stars: 4090 + stars: 4086 owner_login: Atri-Labs owner_html_url: https://github.com/Atri-Labs -- name: devpush - html_url: https://github.com/hunvreus/devpush - stars: 4037 - owner_login: hunvreus - owner_html_url: https://github.com/hunvreus - name: logfire html_url: https://github.com/pydantic/logfire - stars: 3896 + stars: 3975 owner_login: pydantic owner_html_url: https://github.com/pydantic - name: LitServe html_url: https://github.com/Lightning-AI/LitServe - stars: 3756 + stars: 3797 owner_login: Lightning-AI owner_html_url: https://github.com/Lightning-AI - name: huma html_url: https://github.com/danielgtaylor/huma - stars: 3702 + stars: 3785 owner_login: danielgtaylor owner_html_url: https://github.com/danielgtaylor -- name: Yuxi-Know - html_url: https://github.com/xerrors/Yuxi-Know - stars: 3680 - owner_login: xerrors - owner_html_url: https://github.com/xerrors - name: datamodel-code-generator html_url: https://github.com/koxudaxi/datamodel-code-generator - stars: 3675 + stars: 3731 owner_login: koxudaxi owner_html_url: https://github.com/koxudaxi - name: fastapi-admin html_url: https://github.com/fastapi-admin/fastapi-admin - stars: 3659 + stars: 3697 owner_login: fastapi-admin owner_html_url: https://github.com/fastapi-admin - name: farfalle html_url: https://github.com/rashadphz/farfalle - stars: 3497 + stars: 3506 owner_login: rashadphz owner_html_url: https://github.com/rashadphz - name: tracecat html_url: https://github.com/TracecatHQ/tracecat - stars: 3421 + stars: 3458 owner_login: TracecatHQ owner_html_url: https://github.com/TracecatHQ +- name: mcp-context-forge + html_url: https://github.com/IBM/mcp-context-forge + stars: 3216 + owner_login: IBM + owner_html_url: https://github.com/IBM - name: opyrator html_url: https://github.com/ml-tooling/opyrator - stars: 3136 + stars: 3134 owner_login: ml-tooling owner_html_url: https://github.com/ml-tooling - name: docarray @@ -180,316 +185,311 @@ owner_html_url: https://github.com/docarray - name: fastapi-realworld-example-app html_url: https://github.com/nsidnev/fastapi-realworld-example-app - stars: 3051 + stars: 3072 owner_login: nsidnev owner_html_url: https://github.com/nsidnev -- name: mcp-context-forge - html_url: https://github.com/IBM/mcp-context-forge - stars: 3034 - owner_login: IBM - owner_html_url: https://github.com/IBM - name: uvicorn-gunicorn-fastapi-docker html_url: https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker - stars: 2904 + stars: 2908 owner_login: tiangolo owner_html_url: https://github.com/tiangolo - name: FastAPI-template html_url: https://github.com/s3rius/FastAPI-template - stars: 2680 + stars: 2728 owner_login: s3rius owner_html_url: https://github.com/s3rius - name: best-of-web-python html_url: https://github.com/ml-tooling/best-of-web-python - stars: 2662 + stars: 2686 owner_login: ml-tooling owner_html_url: https://github.com/ml-tooling - name: YC-Killer html_url: https://github.com/sahibzada-allahyar/YC-Killer - stars: 2614 + stars: 2648 owner_login: sahibzada-allahyar owner_html_url: https://github.com/sahibzada-allahyar - name: sqladmin html_url: https://github.com/aminalaee/sqladmin - stars: 2587 + stars: 2637 owner_login: aminalaee owner_html_url: https://github.com/aminalaee - name: fastapi-react html_url: https://github.com/Buuntu/fastapi-react - stars: 2566 + stars: 2573 owner_login: Buuntu owner_html_url: https://github.com/Buuntu - name: RasaGPT html_url: https://github.com/paulpierre/RasaGPT - stars: 2456 + stars: 2460 owner_login: paulpierre owner_html_url: https://github.com/paulpierre - name: supabase-py html_url: https://github.com/supabase/supabase-py - stars: 2394 + stars: 2428 owner_login: supabase owner_html_url: https://github.com/supabase +- name: 30-Days-of-Python + html_url: https://github.com/codingforentrepreneurs/30-Days-of-Python + stars: 2347 + owner_login: codingforentrepreneurs + owner_html_url: https://github.com/codingforentrepreneurs - name: nextpy html_url: https://github.com/dot-agent/nextpy - stars: 2338 + stars: 2337 owner_login: dot-agent owner_html_url: https://github.com/dot-agent - name: fastapi-utils html_url: https://github.com/fastapiutils/fastapi-utils - stars: 2289 + stars: 2299 owner_login: fastapiutils owner_html_url: https://github.com/fastapiutils - name: langserve html_url: https://github.com/langchain-ai/langserve - stars: 2234 + stars: 2255 owner_login: langchain-ai owner_html_url: https://github.com/langchain-ai -- name: 30-Days-of-Python - html_url: https://github.com/codingforentrepreneurs/30-Days-of-Python - stars: 2232 - owner_login: codingforentrepreneurs - owner_html_url: https://github.com/codingforentrepreneurs +- name: NoteDiscovery + html_url: https://github.com/gamosoft/NoteDiscovery + stars: 2182 + owner_login: gamosoft + owner_html_url: https://github.com/gamosoft - name: solara html_url: https://github.com/widgetti/solara - stars: 2141 + stars: 2154 owner_login: widgetti owner_html_url: https://github.com/widgetti - name: mangum html_url: https://github.com/Kludex/mangum - stars: 2046 + stars: 2071 owner_login: Kludex owner_html_url: https://github.com/Kludex - name: fastapi_best_architecture html_url: https://github.com/fastapi-practices/fastapi_best_architecture - stars: 1963 + stars: 2036 owner_login: fastapi-practices owner_html_url: https://github.com/fastapi-practices -- name: NoteDiscovery - html_url: https://github.com/gamosoft/NoteDiscovery - stars: 1943 - owner_login: gamosoft - owner_html_url: https://github.com/gamosoft -- name: agentkit - html_url: https://github.com/BCG-X-Official/agentkit - stars: 1936 - owner_login: BCG-X-Official - owner_html_url: https://github.com/BCG-X-Official - name: vue-fastapi-admin html_url: https://github.com/mizhexiaoxiao/vue-fastapi-admin - stars: 1909 + stars: 1983 owner_login: mizhexiaoxiao owner_html_url: https://github.com/mizhexiaoxiao -- name: manage-fastapi - html_url: https://github.com/ycd/manage-fastapi - stars: 1887 - owner_login: ycd - owner_html_url: https://github.com/ycd +- name: agentkit + html_url: https://github.com/BCG-X-Official/agentkit + stars: 1941 + owner_login: BCG-X-Official + owner_html_url: https://github.com/BCG-X-Official +- name: fastapi-langgraph-agent-production-ready-template + html_url: https://github.com/wassim249/fastapi-langgraph-agent-production-ready-template + stars: 1920 + owner_login: wassim249 + owner_html_url: https://github.com/wassim249 - name: openapi-python-client html_url: https://github.com/openapi-generators/openapi-python-client - stars: 1879 + stars: 1900 owner_login: openapi-generators owner_html_url: https://github.com/openapi-generators +- name: manage-fastapi + html_url: https://github.com/ycd/manage-fastapi + stars: 1894 + owner_login: ycd + owner_html_url: https://github.com/ycd - name: slowapi html_url: https://github.com/laurentS/slowapi - stars: 1845 + stars: 1891 owner_login: laurentS owner_html_url: https://github.com/laurentS - name: piccolo html_url: https://github.com/piccolo-orm/piccolo - stars: 1843 + stars: 1854 owner_login: piccolo-orm owner_html_url: https://github.com/piccolo-orm +- name: fastapi-cache + html_url: https://github.com/long2ice/fastapi-cache + stars: 1816 + owner_login: long2ice + owner_html_url: https://github.com/long2ice - name: python-week-2022 html_url: https://github.com/rochacbruno/python-week-2022 stars: 1813 owner_login: rochacbruno owner_html_url: https://github.com/rochacbruno -- name: fastapi-cache - html_url: https://github.com/long2ice/fastapi-cache - stars: 1805 - owner_login: long2ice - owner_html_url: https://github.com/long2ice - name: ormar html_url: https://github.com/collerek/ormar - stars: 1785 + stars: 1797 owner_login: collerek owner_html_url: https://github.com/collerek -- name: fastapi-langgraph-agent-production-ready-template - html_url: https://github.com/wassim249/fastapi-langgraph-agent-production-ready-template - stars: 1780 - owner_login: wassim249 - owner_html_url: https://github.com/wassim249 - name: FastAPI-boilerplate html_url: https://github.com/benavlabs/FastAPI-boilerplate - stars: 1734 + stars: 1792 owner_login: benavlabs owner_html_url: https://github.com/benavlabs - name: termpair html_url: https://github.com/cs01/termpair - stars: 1724 + stars: 1727 owner_login: cs01 owner_html_url: https://github.com/cs01 - name: fastapi-crudrouter html_url: https://github.com/awtkns/fastapi-crudrouter - stars: 1671 + stars: 1677 owner_login: awtkns owner_html_url: https://github.com/awtkns - name: langchain-serve html_url: https://github.com/jina-ai/langchain-serve - stars: 1633 + stars: 1634 owner_login: jina-ai owner_html_url: https://github.com/jina-ai - name: fastapi-pagination html_url: https://github.com/uriyyo/fastapi-pagination - stars: 1588 + stars: 1607 owner_login: uriyyo owner_html_url: https://github.com/uriyyo - name: awesome-fastapi-projects html_url: https://github.com/Kludex/awesome-fastapi-projects - stars: 1583 + stars: 1592 owner_login: Kludex owner_html_url: https://github.com/Kludex -- name: coronavirus-tracker-api - html_url: https://github.com/ExpDev07/coronavirus-tracker-api - stars: 1571 - owner_login: ExpDev07 - owner_html_url: https://github.com/ExpDev07 - name: bracket html_url: https://github.com/evroon/bracket - stars: 1549 + stars: 1580 owner_login: evroon owner_html_url: https://github.com/evroon +- name: coronavirus-tracker-api + html_url: https://github.com/ExpDev07/coronavirus-tracker-api + stars: 1570 + owner_login: ExpDev07 + owner_html_url: https://github.com/ExpDev07 - name: fastapi-amis-admin html_url: https://github.com/amisadmin/fastapi-amis-admin - stars: 1491 + stars: 1512 owner_login: amisadmin owner_html_url: https://github.com/amisadmin -- name: fastapi-boilerplate - html_url: https://github.com/teamhide/fastapi-boilerplate - stars: 1452 - owner_login: teamhide - owner_html_url: https://github.com/teamhide - name: fastcrud html_url: https://github.com/benavlabs/fastcrud - stars: 1452 + stars: 1471 owner_login: benavlabs owner_html_url: https://github.com/benavlabs +- name: fastapi-boilerplate + html_url: https://github.com/teamhide/fastapi-boilerplate + stars: 1461 + owner_login: teamhide + owner_html_url: https://github.com/teamhide - name: awesome-python-resources html_url: https://github.com/DjangoEx/awesome-python-resources - stars: 1430 + stars: 1435 owner_login: DjangoEx owner_html_url: https://github.com/DjangoEx - name: prometheus-fastapi-instrumentator html_url: https://github.com/trallnag/prometheus-fastapi-instrumentator - stars: 1399 + stars: 1417 owner_login: trallnag owner_html_url: https://github.com/trallnag - name: fastapi-code-generator html_url: https://github.com/koxudaxi/fastapi-code-generator - stars: 1371 + stars: 1382 owner_login: koxudaxi owner_html_url: https://github.com/koxudaxi +- name: fastapi-scaff + html_url: https://github.com/atpuxiner/fastapi-scaff + stars: 1367 + owner_login: atpuxiner + owner_html_url: https://github.com/atpuxiner - name: fastapi-tutorial html_url: https://github.com/liaogx/fastapi-tutorial - stars: 1346 + stars: 1360 owner_login: liaogx owner_html_url: https://github.com/liaogx - name: budgetml html_url: https://github.com/ebhy/budgetml - stars: 1345 + stars: 1343 owner_login: ebhy owner_html_url: https://github.com/ebhy -- name: fastapi-scaff - html_url: https://github.com/atpuxiner/fastapi-scaff - stars: 1331 - owner_login: atpuxiner - owner_html_url: https://github.com/atpuxiner - name: bolt-python html_url: https://github.com/slackapi/bolt-python - stars: 1266 + stars: 1276 owner_login: slackapi owner_html_url: https://github.com/slackapi - name: bedrock-chat html_url: https://github.com/aws-samples/bedrock-chat - stars: 1266 + stars: 1268 owner_login: aws-samples owner_html_url: https://github.com/aws-samples - name: fastapi-alembic-sqlmodel-async - html_url: https://github.com/jonra1993/fastapi-alembic-sqlmodel-async - stars: 1260 - owner_login: jonra1993 - owner_html_url: https://github.com/jonra1993 + html_url: https://github.com/vargasjona/fastapi-alembic-sqlmodel-async + stars: 1265 + owner_login: vargasjona + owner_html_url: https://github.com/vargasjona - name: fastapi_production_template html_url: https://github.com/zhanymkanov/fastapi_production_template - stars: 1222 + stars: 1227 owner_login: zhanymkanov owner_html_url: https://github.com/zhanymkanov -- name: langchain-extract - html_url: https://github.com/langchain-ai/langchain-extract - stars: 1179 - owner_login: langchain-ai - owner_html_url: https://github.com/langchain-ai - name: restish html_url: https://github.com/rest-sh/restish - stars: 1152 + stars: 1200 owner_login: rest-sh owner_html_url: https://github.com/rest-sh +- name: langchain-extract + html_url: https://github.com/langchain-ai/langchain-extract + stars: 1183 + owner_login: langchain-ai + owner_html_url: https://github.com/langchain-ai - name: odmantic html_url: https://github.com/art049/odmantic - stars: 1143 + stars: 1162 owner_login: art049 owner_html_url: https://github.com/art049 -- name: authx - html_url: https://github.com/yezz123/authx - stars: 1128 - owner_login: yezz123 - owner_html_url: https://github.com/yezz123 -- name: SAG - html_url: https://github.com/Zleap-AI/SAG - stars: 1104 - owner_login: Zleap-AI - owner_html_url: https://github.com/Zleap-AI - name: aktools html_url: https://github.com/akfamily/aktools - stars: 1072 + stars: 1155 owner_login: akfamily owner_html_url: https://github.com/akfamily - name: RuoYi-Vue3-FastAPI html_url: https://github.com/insistence/RuoYi-Vue3-FastAPI - stars: 1063 + stars: 1155 owner_login: insistence owner_html_url: https://github.com/insistence +- name: authx + html_url: https://github.com/yezz123/authx + stars: 1142 + owner_login: yezz123 + owner_html_url: https://github.com/yezz123 +- name: SAG + html_url: https://github.com/Zleap-AI/SAG + stars: 1110 + owner_login: Zleap-AI + owner_html_url: https://github.com/Zleap-AI - name: flock html_url: https://github.com/Onelevenvy/flock - stars: 1059 + stars: 1069 owner_login: Onelevenvy owner_html_url: https://github.com/Onelevenvy - name: fastapi-observability html_url: https://github.com/blueswen/fastapi-observability - stars: 1046 + stars: 1063 owner_login: blueswen owner_html_url: https://github.com/blueswen - name: enterprise-deep-research html_url: https://github.com/SalesforceAIResearch/enterprise-deep-research - stars: 1019 + stars: 1061 owner_login: SalesforceAIResearch owner_html_url: https://github.com/SalesforceAIResearch - name: titiler html_url: https://github.com/developmentseed/titiler - stars: 1016 + stars: 1039 owner_login: developmentseed owner_html_url: https://github.com/developmentseed - name: every-pdf html_url: https://github.com/DDULDDUCK/every-pdf - stars: 1004 + stars: 1017 owner_login: DDULDDUCK owner_html_url: https://github.com/DDULDDUCK - name: autollm html_url: https://github.com/viddexa/autollm - stars: 1003 + stars: 1005 owner_login: viddexa owner_html_url: https://github.com/viddexa - name: lanarky html_url: https://github.com/ajndkr/lanarky - stars: 996 + stars: 995 owner_login: ajndkr owner_html_url: https://github.com/ajndkr diff --git a/docs/en/data/translation_reviewers.yml b/docs/en/data/translation_reviewers.yml index 62db8e805d..3d321818fc 100644 --- a/docs/en/data/translation_reviewers.yml +++ b/docs/en/data/translation_reviewers.yml @@ -10,12 +10,12 @@ Xewus: url: https://github.com/Xewus sodaMelon: login: sodaMelon - count: 127 + count: 128 avatarUrl: https://avatars.githubusercontent.com/u/66295123?u=be939db90f1119efee9e6110cc05066ff1f40f00&v=4 url: https://github.com/sodaMelon ceb10n: login: ceb10n - count: 117 + count: 119 avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4 url: https://github.com/ceb10n tokusumi: @@ -25,7 +25,7 @@ tokusumi: url: https://github.com/tokusumi hard-coders: login: hard-coders - count: 96 + count: 102 avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=78d12d1acdf853c817700145e73de7fd9e5d068b&v=4 url: https://github.com/hard-coders hasansezertasan: @@ -50,7 +50,7 @@ AlertRED: url: https://github.com/AlertRED tiangolo: login: tiangolo - count: 73 + count: 78 avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4 url: https://github.com/tiangolo Alexandrhub: @@ -58,26 +58,31 @@ Alexandrhub: count: 68 avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4 url: https://github.com/Alexandrhub +cassiobotaro: + login: cassiobotaro + count: 64 + avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4 + url: https://github.com/cassiobotaro waynerv: login: waynerv count: 63 avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4 url: https://github.com/waynerv -cassiobotaro: - login: cassiobotaro - count: 62 - avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4 - url: https://github.com/cassiobotaro +nilslindemann: + login: nilslindemann + count: 61 + avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4 + url: https://github.com/nilslindemann mattwang44: login: mattwang44 count: 61 avatarUrl: https://avatars.githubusercontent.com/u/24987826?u=58e37fb3927b9124b458945ac4c97aa0f1062d85&v=4 url: https://github.com/mattwang44 -nilslindemann: - login: nilslindemann - count: 59 - avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4 - url: https://github.com/nilslindemann +YuriiMotov: + login: YuriiMotov + count: 56 + avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4 + url: https://github.com/YuriiMotov Laineyzhang55: login: Laineyzhang55 count: 48 @@ -88,26 +93,21 @@ Kludex: count: 47 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4 url: https://github.com/Kludex -YuriiMotov: - login: YuriiMotov - count: 46 - avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4 - url: https://github.com/YuriiMotov komtaki: login: komtaki count: 45 avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4 url: https://github.com/komtaki +svlandeg: + login: svlandeg + count: 43 + avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4 + url: https://github.com/svlandeg rostik1410: login: rostik1410 count: 42 avatarUrl: https://avatars.githubusercontent.com/u/11443899?u=e26a635c2ba220467b308a326a579b8ccf4a8701&v=4 url: https://github.com/rostik1410 -svlandeg: - login: svlandeg - count: 42 - avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4 - url: https://github.com/svlandeg alperiox: login: alperiox count: 42 @@ -136,7 +136,7 @@ JavierSanchezCastro: alejsdev: login: alejsdev count: 37 - avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=85ceac49fb87138aebe8d663912e359447329090&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=0facffe3abf87f57a1f05fa773d1119cc5c2f6a5&v=4 url: https://github.com/alejsdev mezgoodle: login: mezgoodle @@ -383,6 +383,11 @@ Joao-Pedro-P-Holanda: count: 16 avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4 url: https://github.com/Joao-Pedro-P-Holanda +maru0123-2004: + login: maru0123-2004 + count: 16 + avatarUrl: https://avatars.githubusercontent.com/u/43961566?u=16ed8603a4d6a4665cb6c53a7aece6f31379b769&v=4 + url: https://github.com/maru0123-2004 JaeHyuckSa: login: JaeHyuckSa count: 16 @@ -418,11 +423,6 @@ mattkoehne: count: 14 avatarUrl: https://avatars.githubusercontent.com/u/80362153?v=4 url: https://github.com/mattkoehne -maru0123-2004: - login: maru0123-2004 - count: 14 - avatarUrl: https://avatars.githubusercontent.com/u/43961566?u=16ed8603a4d6a4665cb6c53a7aece6f31379b769&v=4 - url: https://github.com/maru0123-2004 jovicon: login: jovicon count: 13 @@ -458,6 +458,11 @@ wesinalves: count: 13 avatarUrl: https://avatars.githubusercontent.com/u/13563128?u=9eb17ed50645dd684bfec47e75dba4e9772ec9c1&v=4 url: https://github.com/wesinalves +andersonrocha0: + login: andersonrocha0 + count: 13 + avatarUrl: https://avatars.githubusercontent.com/u/22346169?u=93a1359c8c5461d894802c0cc65bcd09217e7a02&v=4 + url: https://github.com/andersonrocha0 NastasiaSaby: login: NastasiaSaby count: 12 @@ -471,7 +476,7 @@ oandersonmagalhaes: mkdir700: login: mkdir700 count: 12 - avatarUrl: https://avatars.githubusercontent.com/u/56359329?u=3d6ea8714f5000829b60dcf7b13a75b1e73aaf47&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/56359329?u=818e5f4b4dcc1a6ffb3e5aaa08fd827e5a726dfd&v=4 url: https://github.com/mkdir700 batlopes: login: batlopes @@ -493,11 +498,6 @@ KaniKim: count: 12 avatarUrl: https://avatars.githubusercontent.com/u/19832624?u=296dbdd490e0eb96e3d45a2608c065603b17dc31&v=4 url: https://github.com/KaniKim -andersonrocha0: - login: andersonrocha0 - count: 12 - avatarUrl: https://avatars.githubusercontent.com/u/22346169?u=93a1359c8c5461d894802c0cc65bcd09217e7a02&v=4 - url: https://github.com/andersonrocha0 gitgernit: login: gitgernit count: 12 @@ -558,6 +558,11 @@ Zhongheng-Cheng: count: 11 avatarUrl: https://avatars.githubusercontent.com/u/95612344?u=a0f7730a3cc7486827965e01a119ad610bda4b0a&v=4 url: https://github.com/Zhongheng-Cheng +Pyth3rEx: + login: Pyth3rEx + count: 11 + avatarUrl: https://avatars.githubusercontent.com/u/26427764?u=087724f74d813c95925d51e354554bd4b6d6bb60&v=4 + url: https://github.com/Pyth3rEx mariacamilagl: login: mariacamilagl count: 10 @@ -611,7 +616,7 @@ socket-socket: nick-cjyx9: login: nick-cjyx9 count: 10 - avatarUrl: https://avatars.githubusercontent.com/u/119087246?u=7227a2de948c68fb8396d5beff1ee5b0e057c42e&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/119087246?u=3d51dcbd79222ecb6538642f31dc7c8bb708d191&v=4 url: https://github.com/nick-cjyx9 marcelomarkus: login: marcelomarkus @@ -683,11 +688,6 @@ Yarous: count: 9 avatarUrl: https://avatars.githubusercontent.com/u/61277193?u=5b462347458a373b2d599c6f416d2b75eddbffad&v=4 url: https://github.com/Yarous -Pyth3rEx: - login: Pyth3rEx - count: 9 - avatarUrl: https://avatars.githubusercontent.com/u/26427764?u=087724f74d813c95925d51e354554bd4b6d6bb60&v=4 - url: https://github.com/Pyth3rEx dimaqq: login: dimaqq count: 8 @@ -736,7 +736,7 @@ minaton-ru: sungchan1: login: sungchan1 count: 8 - avatarUrl: https://avatars.githubusercontent.com/u/28076127?u=a816d86ef3e60450a7225f128caf9a394c9320f9&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/28076127?u=fadbf24840186aca639d344bb3e0ecf7ff3441cf&v=4 url: https://github.com/sungchan1 Serrones: login: Serrones @@ -761,7 +761,7 @@ anthonycepeda: fabioueno: login: fabioueno count: 7 - avatarUrl: https://avatars.githubusercontent.com/u/14273852?u=edd700982b16317ac6ebfd24c47bc0029b21d360&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/14273852?u=a3d546449cdc96621c32bcc26cf74be6e4390209&v=4 url: https://github.com/fabioueno cfraboulet: login: cfraboulet @@ -793,6 +793,11 @@ Zerohertz: count: 7 avatarUrl: https://avatars.githubusercontent.com/u/42334717?u=5ebf4d33e73b1ad373154f6cdee44f7cab4d05ba&v=4 url: https://github.com/Zerohertz +EdmilsonRodrigues: + login: EdmilsonRodrigues + count: 7 + avatarUrl: https://avatars.githubusercontent.com/u/62777025?u=217d6f3cd6cc750bb8818a3af7726c8d74eb7c2d&v=4 + url: https://github.com/EdmilsonRodrigues deniscapeto: login: deniscapeto count: 6 @@ -1028,11 +1033,6 @@ devluisrodrigues: count: 5 avatarUrl: https://avatars.githubusercontent.com/u/21125286?v=4 url: https://github.com/11kkw -EdmilsonRodrigues: - login: EdmilsonRodrigues - count: 5 - avatarUrl: https://avatars.githubusercontent.com/u/62777025?u=217d6f3cd6cc750bb8818a3af7726c8d74eb7c2d&v=4 - url: https://github.com/EdmilsonRodrigues lpdswing: login: lpdswing count: 4 @@ -1178,6 +1178,11 @@ SBillion: count: 4 avatarUrl: https://avatars.githubusercontent.com/u/1070649?u=3ab493dfc88b39da0eb1600e3b8e7df1c90a5dee&v=4 url: https://github.com/SBillion +seuthootDev: + login: seuthootDev + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/175179350?u=7c2cbc48ab43b52e0c86592111d92e013d72ea4d&v=4 + url: https://github.com/seuthootDev tyronedamasceno: login: tyronedamasceno count: 3 @@ -1266,7 +1271,7 @@ rafsaf: frnsimoes: login: frnsimoes count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/66239468?u=fd8d408946633acc4bea057c207e6c0833871527&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/66239468?u=cba345870d8d6b25dd6d56ee18f7120581e3c573&v=4 url: https://github.com/frnsimoes lieryan: login: lieryan @@ -1593,6 +1598,11 @@ ayr-ton: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/1090517?u=5cf70a0e0f0dbf084e074e494aa94d7c91a46ba6&v=4 url: https://github.com/ayr-ton +Kadermiyanyedi: + login: Kadermiyanyedi + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/48386782?u=e34f31bf50a8ed8d37fbfa4f301b0c190b1b4b86&v=4 + url: https://github.com/Kadermiyanyedi raphaelauv: login: raphaelauv count: 2 @@ -1831,7 +1841,7 @@ EgorOnishchuk: iamantonreznik: login: iamantonreznik count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/112612414?u=bf6de9a1ab17326fe14de0709719fff3826526d0&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/112612414?u=b9ba8d9b4d3940198bc3a4353dfce70c044a39b1&v=4 url: https://github.com/iamantonreznik Azazul123: login: Azazul123 @@ -1851,7 +1861,7 @@ NavesSapnis: isgin01: login: isgin01 count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/157279130?u=ddffde10876b50f35dc90d1337f507a630530a6a&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/157279130?u=16d6466476cf7dbc55a4cd575b6ea920ebdd81e1&v=4 url: https://github.com/isgin01 syedasamina56: login: syedasamina56 diff --git a/docs/en/data/translators.yml b/docs/en/data/translators.yml index 940b128da2..dd5900a417 100644 --- a/docs/en/data/translators.yml +++ b/docs/en/data/translators.yml @@ -8,9 +8,14 @@ jaystone776: count: 46 avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4 url: https://github.com/jaystone776 +tiangolo: + login: tiangolo + count: 31 + avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4 + url: https://github.com/tiangolo ceb10n: login: ceb10n - count: 29 + count: 30 avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4 url: https://github.com/ceb10n valentinDruzhinin: @@ -28,11 +33,6 @@ SwftAlpc: count: 23 avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4 url: https://github.com/SwftAlpc -tiangolo: - login: tiangolo - count: 22 - avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4 - url: https://github.com/tiangolo hasansezertasan: login: hasansezertasan count: 22 @@ -43,16 +43,16 @@ waynerv: count: 20 avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4 url: https://github.com/waynerv +hard-coders: + login: hard-coders + count: 16 + avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=78d12d1acdf853c817700145e73de7fd9e5d068b&v=4 + url: https://github.com/hard-coders AlertRED: login: AlertRED count: 16 avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4 url: https://github.com/AlertRED -hard-coders: - login: hard-coders - count: 15 - avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=78d12d1acdf853c817700145e73de7fd9e5d068b&v=4 - url: https://github.com/hard-coders Joao-Pedro-P-Holanda: login: Joao-Pedro-P-Holanda count: 14 @@ -108,6 +108,11 @@ pablocm83: count: 8 avatarUrl: https://avatars.githubusercontent.com/u/28315068?u=3310fbb05bb8bfc50d2c48b6cb64ac9ee4a14549&v=4 url: https://github.com/pablocm83 +YuriiMotov: + login: YuriiMotov + count: 8 + avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4 + url: https://github.com/YuriiMotov ptt3199: login: ptt3199 count: 7 @@ -133,11 +138,6 @@ Alexandrhub: count: 6 avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4 url: https://github.com/Alexandrhub -YuriiMotov: - login: YuriiMotov - count: 6 - avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=b9b13d598dddfab529a52d264df80a900bfe7060&v=4 - url: https://github.com/YuriiMotov Serrones: login: Serrones count: 5 @@ -291,7 +291,7 @@ hsuanchi: alejsdev: login: alejsdev count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=85ceac49fb87138aebe8d663912e359447329090&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=0facffe3abf87f57a1f05fa773d1119cc5c2f6a5&v=4 url: https://github.com/alejsdev riroan: login: riroan @@ -361,7 +361,7 @@ Rishat-F: ruzia: login: ruzia count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/24503?v=4 + avatarUrl: https://avatars.githubusercontent.com/u/24503?u=abce66d26c9611818720f11e6ae6773a6e0928f8&v=4 url: https://github.com/ruzia izaguerreiro: login: izaguerreiro @@ -413,6 +413,11 @@ ayr-ton: count: 2 avatarUrl: https://avatars.githubusercontent.com/u/1090517?u=5cf70a0e0f0dbf084e074e494aa94d7c91a46ba6&v=4 url: https://github.com/ayr-ton +Kadermiyanyedi: + login: Kadermiyanyedi + count: 2 + avatarUrl: https://avatars.githubusercontent.com/u/48386782?u=e34f31bf50a8ed8d37fbfa4f301b0c190b1b4b86&v=4 + url: https://github.com/Kadermiyanyedi KdHyeon0661: login: KdHyeon0661 count: 2 @@ -461,7 +466,7 @@ ArtemKhymenko: hasnatsajid: login: hasnatsajid count: 2 - avatarUrl: https://avatars.githubusercontent.com/u/86589885?u=6668823c3b029bfecf10a8918ed3af1aafb8b15e&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/86589885?v=4 url: https://github.com/hasnatsajid alperiox: login: alperiox diff --git a/docs/en/docs/advanced/wsgi.md b/docs/en/docs/advanced/wsgi.md index 07147df0a8..aa68617cf4 100644 --- a/docs/en/docs/advanced/wsgi.md +++ b/docs/en/docs/advanced/wsgi.md @@ -6,13 +6,29 @@ For that, you can use the `WSGIMiddleware` and use it to wrap your WSGI applicat ## Using `WSGIMiddleware` { #using-wsgimiddleware } -You need to import `WSGIMiddleware`. +/// info + +This requires installing `a2wsgi` for example with `pip install a2wsgi`. + +/// + +You need to import `WSGIMiddleware` from `a2wsgi`. Then wrap the WSGI (e.g. Flask) app with the middleware. And then mount that under a path. -{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *} +{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *} + +/// note + +Previously, it was recommended to use `WSGIMiddleware` from `fastapi.middleware.wsgi`, but it is now deprecated. + +It’s advised to use the `a2wsgi` package instead. The usage remains the same. + +Just ensure that you have the `a2wsgi` package installed and import `WSGIMiddleware` correctly from `a2wsgi`. + +/// ## Check it { #check-it } diff --git a/docs/en/docs/contributing.md b/docs/en/docs/contributing.md index 18b7f60f72..5e6d821b31 100644 --- a/docs/en/docs/contributing.md +++ b/docs/en/docs/contributing.md @@ -13,7 +13,7 @@ Create a virtual environment and install the required packages with ```console -$ uv sync +$ uv sync --extra all ---> 100% ``` @@ -32,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 `uv sync` instead of running `pip install fastapi` directly. +This only happens when you install using `uv sync --extra all` instead of running `pip install fastapi` directly. -That is because `uv sync` will install the local version of FastAPI in "editable" mode by default. +That is because `uv sync --extra all` will install the local version of FastAPI in "editable" mode by default. /// @@ -177,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, we’ve 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, we’re 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. - -What’s changing now: - -* 🚫 We’re 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 +#### 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 existing pull requests 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 `lang-es`. -* 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: `docs/es/llm-prompt.md`. /// tip -You can add comments with change suggestions to existing pull requests. - Check the docs about adding a pull request review to approve it or request changes. /// +#### Subscribe to Notifications for Your Language + * Check if there's a GitHub Discussion 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 List of ISO 639-1 codes. -#### 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: - -
- -```console -// Use the command "live" and pass the language code as a CLI argument -$ python ./scripts/docs.py live es - -[INFO] Serving on http://127.0.0.1:8008 -[INFO] Start watching changes -[INFO] Start detecting changes -``` - -
- -/// 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 http://127.0.0.1:8008 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. -
+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 Denial-of-service attack on our human effort. -
+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. +
+With great power tools comes great responsibility. +
-/// +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: - -
- -```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 -``` - -
- -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`: - -
- -```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 -``` - -
- -#### 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. diff --git a/docs/en/docs/deployment/docker.md b/docs/en/docs/deployment/docker.md index 6b71f7360d..7219f3afca 100644 --- a/docs/en/docs/deployment/docker.md +++ b/docs/en/docs/deployment/docker.md @@ -145,8 +145,6 @@ There are other formats and tools to define and install package dependencies. * Create a `main.py` file with: ```Python -from typing import Union - from fastapi import FastAPI app = FastAPI() @@ -158,7 +156,7 @@ 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: str | None = None): return {"item_id": item_id, "q": q} ``` diff --git a/docs/en/docs/fastapi-people.md b/docs/en/docs/fastapi-people.md index f2ca26013c..2c07af7647 100644 --- a/docs/en/docs/fastapi-people.md +++ b/docs/en/docs/fastapi-people.md @@ -196,31 +196,11 @@ They have contributed source code, documentation, etc. 📦 There are hundreds of other contributors, you can see them all in the FastAPI GitHub Contributors page. 👷 -## 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*. - -
- -{% for user in (translators.values() | list)[:50] %} - -{% if user.login not in skip_users %} - -
@{{ user.login }}
Translations: {{ user.count }}
- -{% endif %} - -{% endfor %} - -
- ## 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.
{% for user in (translation_reviewers.values() | list)[:50] %} diff --git a/docs/en/docs/img/sponsors/testmu.png b/docs/en/docs/img/sponsors/testmu.png new file mode 100644 index 0000000000..5603b04fae Binary files /dev/null and b/docs/en/docs/img/sponsors/testmu.png differ diff --git a/docs/en/docs/index.md b/docs/en/docs/index.md index 5eb47c7b32..ff0f4e9d9d 100644 --- a/docs/en/docs/index.md +++ b/docs/en/docs/index.md @@ -161,8 +161,6 @@ $ pip install "fastapi[standard]" Create a file `main.py` with: ```Python -from typing import Union - from fastapi import FastAPI app = FastAPI() @@ -174,7 +172,7 @@ 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: str | None = None): return {"item_id": item_id, "q": q} ``` @@ -183,9 +181,7 @@ def read_item(item_id: int, q: Union[str, None] = None): If your code uses `async` / `await`, use `async def`: -```Python hl_lines="9 14" -from typing import Union - +```Python hl_lines="7 12" from fastapi import FastAPI app = FastAPI() @@ -197,7 +193,7 @@ async def read_root(): @app.get("/items/{item_id}") -async def read_item(item_id: int, q: Union[str, None] = None): +async def read_item(item_id: int, q: str | None = None): return {"item_id": item_id, "q": q} ``` @@ -288,9 +284,7 @@ Now modify the file `main.py` to receive a body from a `PUT` request. Declare the body using standard Python types, thanks to Pydantic. -```Python hl_lines="4 9-12 25-27" -from typing import Union - +```Python hl_lines="2 7-10 23-25" from fastapi import FastAPI from pydantic import BaseModel @@ -300,7 +294,7 @@ app = FastAPI() class Item(BaseModel): name: str price: float - is_offer: Union[bool, None] = None + is_offer: bool | None = None @app.get("/") @@ -309,7 +303,7 @@ 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: str | None = None): return {"item_id": item_id, "q": q} diff --git a/docs/en/docs/js/custom.js b/docs/en/docs/js/custom.js index 48e95901d5..be326d3029 100644 --- a/docs/en/docs/js/custom.js +++ b/docs/en/docs/js/custom.js @@ -81,8 +81,14 @@ function setupTermynal() { } } saveBuffer(); + const inputCommands = useLines + .filter(line => line.type === "input") + .map(line => line.value) + .join("\n"); + node.textContent = inputCommands; const div = document.createElement("div"); - node.replaceWith(div); + node.style.display = "none"; + node.after(div); const termynal = new Termynal(div, { lineData: useLines, noInit: true, diff --git a/docs/en/docs/management-tasks.md b/docs/en/docs/management-tasks.md index aac4d6fe40..fc6e1eb280 100644 --- a/docs/en/docs/management-tasks.md +++ b/docs/en/docs/management-tasks.md @@ -74,7 +74,7 @@ Make sure you use a supported label from the `translate.yml`. -* 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 ISO 639-1 list of language codes. - -In any case, the language code should be in the file docs/language_names.yml. - -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 Translations GitHub Discussions -* 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 external_links.yml. - -* 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 diff --git a/docs/en/docs/reference/middleware.md b/docs/en/docs/reference/middleware.md index 3c666ccdaa..48ff85158d 100644 --- a/docs/en/docs/reference/middleware.md +++ b/docs/en/docs/reference/middleware.md @@ -35,11 +35,3 @@ It can be imported from `fastapi`: ```python from fastapi.middleware.trustedhost import TrustedHostMiddleware ``` - -::: fastapi.middleware.wsgi.WSGIMiddleware - -It can be imported from `fastapi`: - -```python -from fastapi.middleware.wsgi import WSGIMiddleware -``` diff --git a/docs/en/docs/reference/request.md b/docs/en/docs/reference/request.md index f1de216424..7994bf8a88 100644 --- a/docs/en/docs/reference/request.md +++ b/docs/en/docs/reference/request.md @@ -2,6 +2,8 @@ You can declare a parameter in a *path operation function* or dependency to be of type `Request` and then you can access the raw request object directly, without any validation, etc. +Read more about it in the [FastAPI docs about using Request directly](https://fastapi.tiangolo.com/advanced/using-request-directly/) + You can import it directly from `fastapi`: ```python diff --git a/docs/en/docs/reference/response.md b/docs/en/docs/reference/response.md index 00cf2c499c..c9085766c9 100644 --- a/docs/en/docs/reference/response.md +++ b/docs/en/docs/reference/response.md @@ -4,6 +4,8 @@ You can declare a parameter in a *path operation function* or dependency to be o You can also use it directly to create an instance of it and return it from your *path operations*. +Read more about it in the [FastAPI docs about returning a custom Response](https://fastapi.tiangolo.com/advanced/response-directly/#returning-a-custom-response) + You can import it directly from `fastapi`: ```python diff --git a/docs/en/docs/reference/responses.md b/docs/en/docs/reference/responses.md index 46f014fcc8..bd57861294 100644 --- a/docs/en/docs/reference/responses.md +++ b/docs/en/docs/reference/responses.md @@ -56,6 +56,8 @@ There are a couple of custom FastAPI response classes, you can use them to optim ## Starlette Responses +You can read more about all of them in the [FastAPI docs for Custom Response](https://fastapi.tiangolo.com/advanced/custom-response/) and in the [Starlette docs about Responses](https://starlette.dev/responses/). + ::: fastapi.responses.FileResponse options: members: diff --git a/docs/en/docs/reference/security/index.md b/docs/en/docs/reference/security/index.md index 9a5c5e15fb..8163aa2df2 100644 --- a/docs/en/docs/reference/security/index.md +++ b/docs/en/docs/reference/security/index.md @@ -28,6 +28,8 @@ from fastapi.security import ( ) ``` +Read more about them in the [FastAPI docs about Security](https://fastapi.tiangolo.com/tutorial/security/). + ## API Key Security Schemes ::: fastapi.security.APIKeyCookie diff --git a/docs/en/docs/reference/websockets.md b/docs/en/docs/reference/websockets.md index 4b7244e080..bd9f438be6 100644 --- a/docs/en/docs/reference/websockets.md +++ b/docs/en/docs/reference/websockets.md @@ -2,6 +2,8 @@ When defining WebSockets, you normally declare a parameter of type `WebSocket` and with it you can read data from the client and send data to it. +Read more about it in the [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/) + It is provided directly by Starlette, but you can import it from `fastapi`: ```python @@ -44,16 +46,6 @@ When you want to define dependencies that should be compatible with both HTTP an - send_json - close -When a client disconnects, a `WebSocketDisconnect` exception is raised, you can catch it. - -You can import it directly form `fastapi`: - -```python -from fastapi import WebSocketDisconnect -``` - -::: fastapi.WebSocketDisconnect - ## WebSockets - additional classes Additional classes for handling WebSockets. @@ -66,4 +58,16 @@ from fastapi.websockets import WebSocketDisconnect, WebSocketState ::: fastapi.websockets.WebSocketDisconnect +When a client disconnects, a `WebSocketDisconnect` exception is raised, you can catch it. + +You can import it directly form `fastapi`: + +```python +from fastapi import WebSocketDisconnect +``` + +Read more about it in the [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/#handling-disconnections-and-multiple-clients) + ::: fastapi.websockets.WebSocketState + +`WebSocketState` is an enumeration of the possible states of a WebSocket connection. diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 7727fa699c..50dedf7885 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,12 +7,60 @@ hide: ## Latest Changes +### Features + +* ✨ Add `viewport` meta tag to improve Swagger UI on mobile devices. PR [#14777](https://github.com/fastapi/fastapi/pull/14777) by [@Joab0](https://github.com/Joab0). +* 🚸 Improve error message for invalid query parameter type annotations. PR [#14479](https://github.com/fastapi/fastapi/pull/14479) by [@retwish](https://github.com/retwish). + +### Fixes + +* 🐛 Update `ValidationError` schema to include `input` and `ctx`. PR [#14791](https://github.com/fastapi/fastapi/pull/14791) by [@jonathan-fulton](https://github.com/jonathan-fulton). +* 🐛 Fix TYPE_CHECKING annotations for Python 3.14 (PEP 649). PR [#14789](https://github.com/fastapi/fastapi/pull/14789) by [@mgu](https://github.com/mgu). +* 🐛 Strip whitespaces from `Authorization` header credentials. PR [#14786](https://github.com/fastapi/fastapi/pull/14786) by [@WaveTheory1](https://github.com/WaveTheory1). +* 🐛 Fix OpenAPI duplication of `anyOf` refs for app-level responses with specified `content` and `model` as `Union`. PR [#14463](https://github.com/fastapi/fastapi/pull/14463) by [@DJMcoder](https://github.com/DJMcoder). + +### Refactors + +* 🎨 Tweak types for mypy. PR [#14816](https://github.com/fastapi/fastapi/pull/14816) by [@tiangolo](https://github.com/tiangolo). +* 🏷️ Re-export `IncEx` type from Pydantic instead of duplicating it. PR [#14641](https://github.com/fastapi/fastapi/pull/14641) by [@mvanderlee](https://github.com/mvanderlee). +* 💡 Update comment for Pydantic internals. PR [#14814](https://github.com/fastapi/fastapi/pull/14814) by [@tiangolo](https://github.com/tiangolo). + ### Docs +* 📝 Update docs for contributing translations, simplify title. PR [#14817](https://github.com/fastapi/fastapi/pull/14817) by [@tiangolo](https://github.com/tiangolo). +* 📝 Fix typing issue in `docs_src/app_testing/app_b` code example. PR [#14573](https://github.com/fastapi/fastapi/pull/14573) by [@timakaa](https://github.com/timakaa). +* 📝 Fix example of license identifier in documentation. PR [#14492](https://github.com/fastapi/fastapi/pull/14492) by [@johnson-earls](https://github.com/johnson-earls). +* 📝 Add banner to translated pages. PR [#14809](https://github.com/fastapi/fastapi/pull/14809) by [@YuriiMotov](https://github.com/YuriiMotov). +* 📝 Add links to related sections of docs to docstrings. PR [#14776](https://github.com/fastapi/fastapi/pull/14776) by [@YuriiMotov](https://github.com/YuriiMotov). +* 📝 Update embedded code examples to Python 3.10 syntax. PR [#14758](https://github.com/fastapi/fastapi/pull/14758) by [@YuriiMotov](https://github.com/YuriiMotov). +* 📝 Fix dependency installation command in `docs/en/docs/contributing.md`. PR [#14757](https://github.com/fastapi/fastapi/pull/14757) by [@YuriiMotov](https://github.com/YuriiMotov). +* 📝 Use return type annotation instead of `response_model` when possible. PR [#14753](https://github.com/fastapi/fastapi/pull/14753) by [@YuriiMotov](https://github.com/YuriiMotov). +* 📝 Use `WSGIMiddleware` from `a2wsgi` instead of deprecated `fastapi.middleware.wsgi.WSGIMiddleware`. PR [#14756](https://github.com/fastapi/fastapi/pull/14756) by [@YuriiMotov](https://github.com/YuriiMotov). +* 📝 Fix minor typos in release notes. PR [#14780](https://github.com/fastapi/fastapi/pull/14780) by [@whyvineet](https://github.com/whyvineet). +* 🐛 Fix copy button in custom.js. PR [#14722](https://github.com/fastapi/fastapi/pull/14722) by [@fcharrier](https://github.com/fcharrier). +* 📝 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 +* 🌐 Improve LLM prompt of `uk` documentation. PR [#14795](https://github.com/fastapi/fastapi/pull/14795) by [@roli2py](https://github.com/roli2py). +* 🌐 Update translations for ja (update-outdated). PR [#14588](https://github.com/fastapi/fastapi/pull/14588) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for uk (update outdated, found by fixer tool). PR [#14739](https://github.com/fastapi/fastapi/pull/14739) by [@YuriiMotov](https://github.com/YuriiMotov). +* 🌐 Update translations for tr (update-outdated). PR [#14745](https://github.com/fastapi/fastapi/pull/14745) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update `llm-prompt.md` for Korean language. PR [#14763](https://github.com/fastapi/fastapi/pull/14763) by [@seuthootDev](https://github.com/seuthootDev). +* 🌐 Update translations for ko (update outdated, found by fixer tool). PR [#14738](https://github.com/fastapi/fastapi/pull/14738) by [@YuriiMotov](https://github.com/YuriiMotov). +* 🌐 Update translations for de (update-outdated). PR [#14690](https://github.com/fastapi/fastapi/pull/14690) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update LLM prompt for Russian translations. PR [#14733](https://github.com/fastapi/fastapi/pull/14733) by [@YuriiMotov](https://github.com/YuriiMotov). +* 🌐 Update translations for ru (update-outdated). PR [#14693](https://github.com/fastapi/fastapi/pull/14693) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for pt (update-outdated). PR [#14724](https://github.com/fastapi/fastapi/pull/14724) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update Korean LLM prompt. PR [#14740](https://github.com/fastapi/fastapi/pull/14740) by [@hard-coders](https://github.com/hard-coders). +* 🌐 Improve LLM prompt for Turkish translations. PR [#14728](https://github.com/fastapi/fastapi/pull/14728) by [@Kadermiyanyedi](https://github.com/Kadermiyanyedi). +* 🌐 Update portuguese llm-prompt.md. PR [#14702](https://github.com/fastapi/fastapi/pull/14702) by [@ceb10n](https://github.com/ceb10n). +* 🌐 Update LLM prompt instructions file for French. PR [#14618](https://github.com/fastapi/fastapi/pull/14618) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for ko (add-missing). PR [#14699](https://github.com/fastapi/fastapi/pull/14699) by [@tiangolo](https://github.com/tiangolo). * 🌐 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). @@ -22,6 +70,21 @@ hide: ### Internal +* 🐛 Fix translation script commit in place. PR [#14818](https://github.com/fastapi/fastapi/pull/14818) by [@tiangolo](https://github.com/tiangolo). +* 🔨 Update translation script to retry if LLM-response doesn't pass validation with Translation Fixer tool. PR [#14749](https://github.com/fastapi/fastapi/pull/14749) by [@YuriiMotov](https://github.com/YuriiMotov). +* 👷 Run tests only on relevant code changes (not on docs). PR [#14813](https://github.com/fastapi/fastapi/pull/14813) by [@tiangolo](https://github.com/tiangolo). +* 👷 Run mypy by pre-commit. PR [#14806](https://github.com/fastapi/fastapi/pull/14806) by [@YuriiMotov](https://github.com/YuriiMotov). +* ⬆ Bump ruff from 0.14.3 to 0.14.14. PR [#14798](https://github.com/fastapi/fastapi/pull/14798) by [@dependabot[bot]](https://github.com/apps/dependabot). +* ⬆ Bump pyasn1 from 0.6.1 to 0.6.2. PR [#14804](https://github.com/fastapi/fastapi/pull/14804) by [@dependabot[bot]](https://github.com/apps/dependabot). +* ⬆ Bump sqlmodel from 0.0.27 to 0.0.31. PR [#14802](https://github.com/fastapi/fastapi/pull/14802) by [@dependabot[bot]](https://github.com/apps/dependabot). +* ⬆ Bump mkdocs-macros-plugin from 1.4.1 to 1.5.0. PR [#14801](https://github.com/fastapi/fastapi/pull/14801) by [@dependabot[bot]](https://github.com/apps/dependabot). +* ⬆ Bump gitpython from 3.1.45 to 3.1.46. PR [#14800](https://github.com/fastapi/fastapi/pull/14800) by [@dependabot[bot]](https://github.com/apps/dependabot). +* ⬆ Bump typer from 0.16.0 to 0.21.1. PR [#14799](https://github.com/fastapi/fastapi/pull/14799) by [@dependabot[bot]](https://github.com/apps/dependabot). +* 👥 Update FastAPI GitHub topic repositories. PR [#14803](https://github.com/fastapi/fastapi/pull/14803) by [@tiangolo](https://github.com/tiangolo). +* 👥 Update FastAPI People - Contributors and Translators. PR [#14796](https://github.com/fastapi/fastapi/pull/14796) by [@tiangolo](https://github.com/tiangolo). +* 🔧 Ensure that an edit to `uv.lock` gets the `internal` label. PR [#14759](https://github.com/fastapi/fastapi/pull/14759) by [@svlandeg](https://github.com/svlandeg). +* 🔧 Update sponsors: remove Requestly. PR [#14735](https://github.com/fastapi/fastapi/pull/14735) by [@tiangolo](https://github.com/tiangolo). +* 🔧 Update sponsors, LambdaTest changes to TestMu AI. PR [#14734](https://github.com/fastapi/fastapi/pull/14734) by [@tiangolo](https://github.com/tiangolo). * ⬆ 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). @@ -264,7 +327,7 @@ hide: ### Refactors -* 🔥 Remove dangling extra condiitonal no longer needed. PR [#14435](https://github.com/fastapi/fastapi/pull/14435) by [@tiangolo](https://github.com/tiangolo). +* 🔥 Remove dangling extra conditional no longer needed. PR [#14435](https://github.com/fastapi/fastapi/pull/14435) by [@tiangolo](https://github.com/tiangolo). * ♻️ Refactor internals, update `is_coroutine` check to reuse internal supported variants (unwrap, check class). PR [#14434](https://github.com/fastapi/fastapi/pull/14434) by [@tiangolo](https://github.com/tiangolo). ### Translations @@ -399,7 +462,7 @@ hide: ### Docs -* 📝 Upate docs for advanced dependencies with `yield`, noting the changes in 0.121.0, adding `scope`. PR [#14287](https://github.com/fastapi/fastapi/pull/14287) by [@tiangolo](https://github.com/tiangolo). +* 📝 Update docs for advanced dependencies with `yield`, noting the changes in 0.121.0, adding `scope`. PR [#14287](https://github.com/fastapi/fastapi/pull/14287) by [@tiangolo](https://github.com/tiangolo). ### Internal @@ -2627,7 +2690,7 @@ Read more in the [advisory: Content-Type Header ReDoS](https://github.com/tiango * 🌐 Add Japanese translation for `docs/ja/docs/tutorial/handling-errors.md`. PR [#1953](https://github.com/tiangolo/fastapi/pull/1953) by [@SwftAlpc](https://github.com/SwftAlpc). * 🌐 Add Japanese translation for `docs/ja/docs/tutorial/response-status-code.md`. PR [#1942](https://github.com/tiangolo/fastapi/pull/1942) by [@SwftAlpc](https://github.com/SwftAlpc). * 🌐 Add Japanese translation for `docs/ja/docs/tutorial/extra-models.md`. PR [#1941](https://github.com/tiangolo/fastapi/pull/1941) by [@SwftAlpc](https://github.com/SwftAlpc). -* 🌐 Add Japanese tranlsation for `docs/ja/docs/tutorial/schema-extra-example.md`. PR [#1931](https://github.com/tiangolo/fastapi/pull/1931) by [@SwftAlpc](https://github.com/SwftAlpc). +* 🌐 Add Japanese translation for `docs/ja/docs/tutorial/schema-extra-example.md`. PR [#1931](https://github.com/tiangolo/fastapi/pull/1931) by [@SwftAlpc](https://github.com/SwftAlpc). * 🌐 Add Japanese translation for `docs/ja/docs/tutorial/body-nested-models.md`. PR [#1930](https://github.com/tiangolo/fastapi/pull/1930) by [@SwftAlpc](https://github.com/SwftAlpc). * 🌐 Add Japanese translation for `docs/ja/docs/tutorial/body-fields.md`. PR [#1923](https://github.com/tiangolo/fastapi/pull/1923) by [@SwftAlpc](https://github.com/SwftAlpc). * 🌐 Add German translation for `docs/de/docs/tutorial/index.md`. PR [#9502](https://github.com/tiangolo/fastapi/pull/9502) by [@fhabers21](https://github.com/fhabers21). @@ -4003,7 +4066,7 @@ You hopefully updated to a supported version of Python a while ago. If you haven ### Fixes * 🐛 Fix `RuntimeError` raised when `HTTPException` has a status code with no content. PR [#5365](https://github.com/tiangolo/fastapi/pull/5365) by [@iudeen](https://github.com/iudeen). -* 🐛 Fix empty reponse body when default `status_code` is empty but the a `Response` parameter with `response.status_code` is set. PR [#5360](https://github.com/tiangolo/fastapi/pull/5360) by [@tmeckel](https://github.com/tmeckel). +* 🐛 Fix empty response body when default `status_code` is empty but the a `Response` parameter with `response.status_code` is set. PR [#5360](https://github.com/tiangolo/fastapi/pull/5360) by [@tmeckel](https://github.com/tmeckel). ### Docs diff --git a/docs/en/docs/translation-banner.md b/docs/en/docs/translation-banner.md new file mode 100644 index 0000000000..1422870744 --- /dev/null +++ b/docs/en/docs/translation-banner.md @@ -0,0 +1,11 @@ +/// details | 🌐 Translation by AI and humans + +This translation was made by AI guided by humans. 🤝 + +It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖 + +You can improve this translation by [helping us guide the AI LLM better](https://fastapi.tiangolo.com/contributing/#translations). + +[English version](ENGLISH_VERSION_URL) + +/// diff --git a/docs/en/docs/tutorial/body-multiple-params.md b/docs/en/docs/tutorial/body-multiple-params.md index ed23c81490..bb0c583685 100644 --- a/docs/en/docs/tutorial/body-multiple-params.md +++ b/docs/en/docs/tutorial/body-multiple-params.md @@ -102,15 +102,16 @@ Of course, you can also declare additional query parameters whenever you need, a As, by default, singular values are interpreted as query parameters, you don't have to explicitly add a `Query`, you can just do: +```Python +q: str | None = None +``` + +Or in Python 3.9: + ```Python q: Union[str, None] = None ``` -Or in Python 3.10 and above: - -```Python -q: str | None = None -``` For example: diff --git a/docs/en/docs/tutorial/path-operation-configuration.md b/docs/en/docs/tutorial/path-operation-configuration.md index 59be5ab376..1bb7ee5544 100644 --- a/docs/en/docs/tutorial/path-operation-configuration.md +++ b/docs/en/docs/tutorial/path-operation-configuration.md @@ -52,7 +52,7 @@ In these cases, it could make sense to store the tags in an `Enum`. You can add a `summary` and `description`: -{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *} +{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *} ## Description from docstring { #description-from-docstring } @@ -70,7 +70,7 @@ It will be used in the interactive docs: You can specify the response description with the parameter `response_description`: -{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[19] *} +{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *} /// info diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index 1c29546198..34d489d92a 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -317,6 +317,8 @@ extra: name: de - Deutsch - link: /es/ name: es - español + - link: /ja/ + name: ja - 日本語 - link: /ko/ name: ko - 한국어 - link: /pt/ diff --git a/docs/fr/llm-prompt.md b/docs/fr/llm-prompt.md index 5ff18c4e8b..228500fe23 100644 --- a/docs/fr/llm-prompt.md +++ b/docs/fr/llm-prompt.md @@ -6,123 +6,127 @@ Language code: fr. ### Grammar to use when talking to the reader -Use the formal grammar (use «vous» instead of «tu»). +Use the formal grammar (use `vous` instead of `tu`). + +Additionally, in instructional sentences, prefer the present tense for obligations: + +- Prefer `vous devez …` over `vous devrez …`, unless the English source explicitly refers to a future requirement. + +- When translating “make sure (that) … is …”, prefer the indicative after `vous assurer que` (e.g. `Vous devez vous assurer qu'il est …`) instead of the subjunctive (e.g. `qu'il soit …`). ### Quotes -1) Convert neutral double quotes («"») and English double typographic quotes («“» and «”») to French guillemets (««» and «»»). +- Convert neutral double quotes (`"`) to French guillemets (`«` and `»`). -2) In the French docs, guillemets are written without extra spaces: use «texte», not « texte ». - -3) Do not convert quotes inside code blocks, inline code, paths, URLs, or anything wrapped in backticks. +- Do not convert quotes inside code blocks, inline code, paths, URLs, or anything wrapped in backticks. Examples: - Source (English): +Source (English): - ««« - "Hello world" - “Hello Universe” - "He said: 'Hello'" - "The module is `__main__`" - »»» +``` +"Hello world" +“Hello Universe” +"He said: 'Hello'" +"The module is `__main__`" +``` - Result (French): +Result (French): - ««« - «Hello world» - «Hello Universe» - «He said: 'Hello'» - «The module is `__main__`» - »»» +``` +"Hello world" +“Hello Universe” +"He said: 'Hello'" +"The module is `__main__`" +``` ### Ellipsis -1) Make sure there is a space between an ellipsis and a word following or preceding the ellipsis. +- Make sure there is a space between an ellipsis and a word following or preceding the ellipsis. Examples: - Source (English): +Source (English): - ««« - ...as we intended. - ...this would work: - ...etc. - others... - More to come... - »»» +``` +...as we intended. +...this would work: +...etc. +others... +More to come... +``` - Result (French): +Result (French): - ««« - ... comme prévu. - ... cela fonctionnerait : - ... etc. - D'autres ... - La suite ... - »»» +``` +... comme prévu. +... cela fonctionnerait : +... etc. +D'autres ... +La suite ... +``` -2) This does not apply in URLs, code blocks, and code snippets. Do not remove or add spaces there. +- This does not apply in URLs, code blocks, and code snippets. Do not remove or add spaces there. ### Headings -1) Prefer translating headings using the infinitive form (as is common in the existing French docs): «Créer…», «Utiliser…», «Ajouter…». +- Prefer translating headings using the infinitive form (as is common in the existing French docs): `Créer…`, `Utiliser…`, `Ajouter…`. -2) For headings that are instructions written in imperative in English (e.g. “Go check …”), keep them in imperative in French, using the formal grammar (e.g. «Allez voir …»). - -3) Keep heading punctuation as in the source. In particular, keep occurrences of literal « - » (space-hyphen-space) as « - » (the existing French docs use a hyphen here). +- For headings that are instructions written in imperative in English (e.g. `Go check …`), keep them in imperative in French, using the formal grammar (e.g. `Allez voir …`). ### French instructions about technical terms -Do not try to translate everything. In particular, keep common programming terms when that is the established usage in the French docs (e.g. «framework», «endpoint», «plug-in», «payload»). Use French where the existing docs already consistently use French (e.g. «requête», «réponse»). +Do not try to translate everything. In particular, keep common programming terms (e.g. `framework`, `endpoint`, `plug-in`, `payload`). Keep class names, function names, modules, file names, and CLI commands unchanged. ### List of English terms and their preferred French translations -Below is a list of English terms and their preferred French translations, separated by a colon («:»). Use these translations, do not use your own. If an existing translation does not use these terms, update it to use them. +Below is a list of English terms and their preferred French translations, separated by a colon (:). Use these translations, do not use your own. If an existing translation does not use these terms, update it to use them. -* «/// note | Technical Details»: «/// note | Détails techniques» -* «/// note»: «/// note | Remarque» -* «/// tip»: «/// tip | Astuce» -* «/// warning»: «/// warning | Attention» -* «/// check»: «/// check | vérifier» -* «/// info»: «/// info» +- /// note | Technical Details»: /// note | Détails techniques +- /// note: /// note | Remarque +- /// tip: /// tip | Astuce +- /// warning: /// warning | Alertes +- /// check: /// check | Vérifications +- /// info: /// info -* «the docs»: «les documents» -* «the documentation»: «la documentation» +- the docs: les documents +- the documentation: la documentation -* «framework»: «framework» (do not translate to «cadre») -* «performance»: «performance» +- Exclude from OpenAPI: Exclusion d'OpenAPI -* «type hints»: «annotations de type» -* «type annotations»: «annotations de type» +- framework: framework (do not translate to cadre) +- performance: performance -* «autocomplete»: «autocomplétion» -* «autocompletion»: «autocomplétion» +- type hints: annotations de type +- type annotations: annotations de type -* «the request» (what the client sends to the server): «la requête» -* «the response» (what the server sends back to the client): «la réponse» +- autocomplete: autocomplétion +- autocompletion: autocomplétion -* «the request body»: «le corps de la requête» -* «the response body»: «le corps de la réponse» +- the request (what the client sends to the server): la requête +- the response (what the server sends back to the client): la réponse -* «path operation»: «opération de chemin» -* «path operations» (plural): «opérations de chemin» -* «path operation function»: «fonction de chemin» -* «path operation decorator»: «décorateur d'opération de chemin» +- the request body: le corps de la requête +- the response body: le corps de la réponse -* «path parameter»: «paramètre de chemin» -* «query parameter»: «paramètre de requête» +- path operation: chemin d'accès +- path operations (plural): chemins d'accès +- path operation function: fonction de chemin d'accès +- path operation decorator: décorateur de chemin d'accès -* «the `Request`»: «`Request`» (keep as code identifier) -* «the `Response`»: «`Response`» (keep as code identifier) +- path parameter: paramètre de chemin +- query parameter: paramètre de requête -* «deployment»: «déploiement» -* «to upgrade»: «mettre à niveau» +- the `Request`: `Request` (keep as code identifier) +- the `Response`: `Response` (keep as code identifier) -* «deprecated»: «déprécié» -* «to deprecate»: «déprécier» +- deployment: déploiement +- to upgrade: mettre à niveau -* «cheat sheet»: «aide-mémoire» -* «plug-in»: «plug-in» +- deprecated: déprécié +- to deprecate: déprécier + +- cheat sheet: aide-mémoire +- plug-in: plug-in diff --git a/docs/ja/docs/advanced/additional-status-codes.md b/docs/ja/docs/advanced/additional-status-codes.md index 33457f591c..14b7e8ba87 100644 --- a/docs/ja/docs/advanced/additional-status-codes.md +++ b/docs/ja/docs/advanced/additional-status-codes.md @@ -1,41 +1,41 @@ -# 追加のステータスコード +# 追加のステータスコード { #additional-status-codes } -デフォルトでは、 **FastAPI** は `JSONResponse` を使ってレスポンスを返します。その `JSONResponse` の中には、 *path operation* が返した内容が入ります。 +デフォルトでは、 **FastAPI** は `JSONResponse` を使ってレスポンスを返し、*path operation* から返した内容をその `JSONResponse` の中に入れます。 -それは、デフォルトのステータスコードか、 *path operation* でセットしたものを利用します。 +デフォルトのステータスコード、または *path operation* で設定したステータスコードが使用されます。 -## 追加のステータスコード +## 追加のステータスコード { #additional-status-codes_1 } -メインのステータスコードとは別に、他のステータスコードを返したい場合は、`Response` (`JSONResponse` など) に追加のステータスコードを設定して直接返します。 +メインのステータスコードとは別に追加のステータスコードを返したい場合は、`JSONResponse` のような `Response` を直接返し、追加のステータスコードを直接設定できます。 -例えば、itemを更新し、成功した場合は200 "OK"のHTTPステータスコードを返す *path operation* を作りたいとします。 +たとえば、item を更新でき、成功時に HTTP ステータスコード 200 "OK" を返す *path operation* を作りたいとします。 -しかし、新しいitemも許可したいです。itemが存在しない場合は、それらを作成して201 "Created"を返します。 +しかし、新しい item も受け付けたいとします。そして、item が以前存在しなかった場合には作成し、HTTP ステータスコード 201「Created」を返します。 -これを達成するには、 `JSONResponse` をインポートし、 `status_code` を設定して直接内容を返します。 +これを実現するには、`JSONResponse` をインポートし、望む `status_code` を設定して、そこで内容を直接返します。 -{* ../../docs_src/additional_status_codes/tutorial001.py hl[4,25] *} +{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *} /// warning | 注意 -上記の例のように `Response` を明示的に返す場合、それは直接返されます。 +上の例のように `Response` を直接返すと、それはそのまま返されます。 -モデルなどはシリアライズされません。 +モデルなどによってシリアライズされません。 -必要なデータが含まれていることや、値が有効なJSONであること (`JSONResponse` を使う場合) を確認してください。 +必要なデータが含まれていること、そして(`JSONResponse` を使用している場合)値が有効な JSON であることを確認してください。 /// /// note | 技術詳細 -`from starlette.responses import JSONResponse` を利用することもできます。 +`from starlette.responses import JSONResponse` を使うこともできます。 -**FastAPI** は `fastapi.responses` と同じ `starlette.responses` を、開発者の利便性のために提供しています。しかし有効なレスポンスはほとんどStarletteから来ています。 `status` についても同じです。 +**FastAPI** は開発者の利便性のために、`fastapi.responses` と同じ `starlette.responses` を提供しています。しかし、利用可能なレスポンスのほとんどは Starlette から直接提供されています。`status` も同様です。 /// -## OpenAPIとAPIドキュメント +## OpenAPI と API ドキュメント { #openapi-and-api-docs } -ステータスコードとレスポンスを直接返す場合、それらはOpenAPIスキーマ (APIドキュメント) には含まれません。なぜなら、FastAPIは何が返されるのか事前に知ることができないからです。 +追加のステータスコードとレスポンスを直接返す場合、それらは OpenAPI スキーマ(API ドキュメント)には含まれません。FastAPI には、事前に何が返されるかを知る方法がないからです。 -しかし、 [Additional Responses](additional-responses.md){.internal-link target=_blank} を使ってコードの中にドキュメントを書くことができます。 +しかし、[Additional Responses](additional-responses.md){.internal-link target=_blank} を使ってコード内にドキュメント化できます。 diff --git a/docs/ja/docs/advanced/custom-response.md b/docs/ja/docs/advanced/custom-response.md index 1b2cd914d4..9d881c013c 100644 --- a/docs/ja/docs/advanced/custom-response.md +++ b/docs/ja/docs/advanced/custom-response.md @@ -1,34 +1,40 @@ -# カスタムレスポンス - HTML、ストリーム、ファイル、その他のレスポンス +# カスタムレスポンス - HTML、ストリーム、ファイル、その他のレスポンス { #custom-response-html-stream-file-others } デフォルトでは、**FastAPI** は `JSONResponse` を使ってレスポンスを返します。 [レスポンスを直接返す](response-directly.md){.internal-link target=_blank}で見たように、 `Response` を直接返すことでこの挙動をオーバーライドできます。 -しかし、`Response` を直接返すと、データは自動的に変換されず、ドキュメントも自動生成されません (例えば、生成されるOpenAPIの一部としてHTTPヘッダー `Content-Type` に特定の「メディアタイプ」を含めるなど) 。 +しかし、`Response` を直接返すと(または `JSONResponse` のような任意のサブクラスを返すと)、データは自動的に変換されず(`response_model` を宣言していても)、ドキュメントも自動生成されません(例えば、生成されるOpenAPIの一部としてHTTPヘッダー `Content-Type` に特定の「メディアタイプ」を含めるなど)。 -しかし、*path operationデコレータ* に、使いたい `Response` を宣言することもできます。 +`response_class` パラメータを使用して、*path operation デコレータ* で使用したい `Response`(任意の `Response` サブクラス)を宣言することもできます。 -*path operation関数* から返されるコンテンツは、その `Response` に含まれます。 +*path operation 関数* から返されるコンテンツは、その `Response` に含まれます。 -そしてもし、`Response` が、`JSONResponse` や `UJSONResponse` の場合のようにJSONメディアタイプ (`application/json`) ならば、データは *path operationデコレータ* に宣言したPydantic `response_model` により自動的に変換 (もしくはフィルタ) されます。 +そしてその `Response` が、`JSONResponse` や `UJSONResponse` の場合のようにJSONメディアタイプ(`application/json`)なら、関数の返り値は *path operationデコレータ* に宣言した任意のPydantic `response_model` により自動的に変換(およびフィルタ)されます。 /// note | 備考 -メディアタイプを指定せずにレスポンスクラスを利用すると、FastAPIは何もコンテンツがないことを期待します。そのため、生成されるOpenAPIドキュメントにレスポンスフォーマットが記載されません。 +メディアタイプを指定せずにレスポンスクラスを利用すると、FastAPIはレスポンスにコンテンツがないことを期待します。そのため、生成されるOpenAPIドキュメントにレスポンスフォーマットが記載されません。 /// -## `ORJSONResponse` を使う +## `ORJSONResponse` を使う { #use-orjsonresponse } -例えば、パフォーマンスを出したい場合は、`orjson`をインストールし、`ORJSONResponse`をレスポンスとしてセットすることができます。 +例えば、パフォーマンスを絞り出したい場合は、`orjson`をインストールし、レスポンスとして `ORJSONResponse` をセットできます。 -使いたい `Response` クラス (サブクラス) をインポートし、 *path operationデコレータ* に宣言します。 +使いたい `Response` クラス(サブクラス)をインポートし、*path operationデコレータ* に宣言します。 -{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *} +大きなレスポンスの場合、`Response` を直接返すほうが、辞書を返すよりもはるかに高速です。 + +これは、デフォルトではFastAPIがチュートリアルで説明した同じ[JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}を使って、内部の各アイテムを検査し、JSONとしてシリアライズ可能であることを確認するためです。これにより、例えばデータベースモデルのような**任意のオブジェクト**を返せます。 + +しかし、返そうとしているコンテンツが **JSONでシリアライズ可能**であることが確実なら、それを直接レスポンスクラスに渡して、FastAPIがレスポンスクラスへ渡す前に返却コンテンツを `jsonable_encoder` に通すことで発生する追加のオーバーヘッドを回避できます。 + +{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *} /// info | 情報 -パラメータ `response_class` は、レスポンスの「メディアタイプ」を定義するために利用することもできます。 +パラメータ `response_class` は、レスポンスの「メディアタイプ」を定義するためにも利用されます。 この場合、HTTPヘッダー `Content-Type` には `application/json` がセットされます。 @@ -38,70 +44,70 @@ /// tip | 豆知識 -`ORJSONResponse` は、現在はFastAPIのみで利用可能で、Starletteでは利用できません。 +`ORJSONResponse` はFastAPIでのみ利用可能で、Starletteでは利用できません。 /// -## HTMLレスポンス +## HTMLレスポンス { #html-response } **FastAPI** からHTMLを直接返す場合は、`HTMLResponse` を使います。 * `HTMLResponse` をインポートする。 -* *path operation* のパラメータ `content_type` に `HTMLResponse` を渡す。 +* *path operation デコレータ* のパラメータ `response_class` に `HTMLResponse` を渡す。 -{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *} +{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *} /// info | 情報 -パラメータ `response_class` は、レスポンスの「メディアタイプ」を定義するために利用されます。 +パラメータ `response_class` は、レスポンスの「メディアタイプ」を定義するためにも利用されます。 この場合、HTTPヘッダー `Content-Type` には `text/html` がセットされます。 -そして、OpenAPIにはそのようにドキュメント化されます。 +そして、OpenAPIにはそのようにドキュメントされます。 /// -### `Response` を返す +### `Response` を返す { #return-a-response } -[レスポンスを直接返す](response-directly.md){.internal-link target=_blank}で見たように、レスポンスを直接返すことで、*path operation* の中でレスポンスをオーバーライドできます。 +[レスポンスを直接返す](response-directly.md){.internal-link target=_blank}で見たように、レスポンスを返すことで、*path operation* の中でレスポンスを直接オーバーライドすることもできます。 上記と同じ例において、 `HTMLResponse` を返すと、このようになります: -{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *} +{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *} /// warning | 注意 -*path operation関数* から直接返される `Response` は、OpenAPIにドキュメントされず (例えば、 `Content-Type` がドキュメントされない) 、自動的な対話的ドキュメントからも閲覧できません。 +*path operation関数* から直接返される `Response` は、OpenAPIにドキュメントされず(例えば、`Content-Type` がドキュメントされない)、自動的な対話的ドキュメントでも表示されません。 /// /// info | 情報 -もちろん、実際の `Content-Type` ヘッダーやステータスコードなどは、返された `Response` オブジェクトに由来しています。 +もちろん、実際の `Content-Type` ヘッダーやステータスコードなどは、返された `Response` オブジェクトに由来します。 /// -### OpenAPIドキュメントと `Response` のオーバーライド +### OpenAPIドキュメントと `Response` のオーバーライド { #document-in-openapi-and-override-response } -関数の中でレスポンスをオーバーライドしつつも、OpenAPI に「メディアタイプ」をドキュメント化したいなら、 `response_class` パラメータを使い、 `Response` オブジェクトを返します。 +関数の中でレスポンスをオーバーライドしつつも、OpenAPI に「メディアタイプ」をドキュメント化したいなら、`response_class` パラメータを使用し、かつ `Response` オブジェクトを返します。 -`response_class` はOpenAPIの *path operation* ドキュメントにのみ使用されますが、 `Response` はそのまま使用されます。 +`response_class` はOpenAPIの*path operation*のドキュメント化のためにのみ使用され、`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()` は、`str` のHTMLを返すのではなく `Response` を生成して返しています。 +この例では、関数 `generate_html_response()` は、`str` のHTMLを返すのではなく、`Response` を生成して返しています。 -`generate_html_response()` を呼び出した結果を返すことにより、**FastAPI** の振る舞いを上書きする `Response` が既に返されています。 +`generate_html_response()` を呼び出した結果を返すことにより、デフォルトの **FastAPI** の挙動をオーバーライドする `Response` をすでに返しています。 -しかし、一方では `response_class` に `HTMLResponse` を渡しているため、 **FastAPI** はOpenAPIや対話的ドキュメントでHTMLとして `text/html` でドキュメント化する方法を知っています。 +しかし、`response_class` にも `HTMLResponse` を渡しているため、**FastAPI** はOpenAPIと対話的ドキュメントで、`text/html` のHTMLとしてどのようにドキュメント化すればよいかを理解できます: -## 利用可能なレスポンス +## 利用可能なレスポンス { #available-responses } 以下が利用可能なレスポンスの一部です。 @@ -111,11 +117,11 @@ `from starlette.responses import HTMLResponse` も利用できます。 -**FastAPI** は開発者の利便性のために `fastapi.responses` として `starlette.responses` と同じものを提供しています。しかし、利用可能なレスポンスのほとんどはStarletteから直接提供されます。 +**FastAPI** は開発者の利便性のために、`starlette.responses` と同じものを `fastapi.responses` として提供しています。しかし、利用可能なレスポンスのほとんどはStarletteから直接提供されます。 /// -### `Response` +### `Response` { #response } メインの `Response` クラスで、他の全てのレスポンスはこれを継承しています。 @@ -128,41 +134,53 @@ * `headers` - 文字列の `dict` 。 * `media_type` - メディアタイプを示す `str` 。例えば `"text/html"` 。 -FastAPI (実際にはStarlette) は自動的にContent-Lengthヘッダーを含みます。また、media_typeに基づいたContent-Typeヘッダーを含み、テキストタイプのためにcharsetを追加します。 +FastAPI(実際にはStarlette)は自動的にContent-Lengthヘッダーを含みます。また、`media_type` に基づいたContent-Typeヘッダーを含み、テキストタイプのためにcharsetを追加します。 -{* ../../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` としてエンコードされたレスポンスを返します。 +データを受け取り、`application/json` としてエンコードされたレスポンスを返します。 上で読んだように、**FastAPI** のデフォルトのレスポンスとして利用されます。 -### `ORJSONResponse` +### `ORJSONResponse` { #orjsonresponse } 上で読んだように、`orjson`を使った、高速な代替のJSONレスポンスです。 -### `UJSONResponse` +/// info | 情報 -`ujson`を使った、代替のJSONレスポンスです。 - -/// warning | 注意 - -`ujson` は、いくつかのエッジケースの取り扱いについて、Pythonにビルトインされた実装よりも作りこまれていません。 +これは、例えば `pip install orjson` で `orjson` をインストールする必要があります。 /// -{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *} +### `UJSONResponse` { #ujsonresponse } + +`ujson`を使った、代替のJSONレスポンスです。 + +/// info | 情報 + +これは、例えば `pip install ujson` で `ujson` をインストールする必要があります。 + +/// + +/// warning | 注意 + +`ujson` は、いくつかのエッジケースの取り扱いについて、Pythonにビルトインされた実装ほど注意深くありません。 + +/// + +{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *} /// tip | 豆知識 @@ -170,33 +188,61 @@ FastAPI (実際にはStarlette) は自動的にContent-Lengthヘッダーを含 /// -### `RedirectResponse` +### `RedirectResponse` { #redirectresponse } -HTTPリダイレクトを返します。デフォルトでは307ステータスコード (Temporary Redirect) となります。 +HTTPリダイレクトを返します。デフォルトでは307ステータスコード(Temporary Redirect)となります。 -{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *} +`RedirectResponse` を直接返せます: -### `StreamingResponse` +{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *} -非同期なジェネレータか通常のジェネレータ・イテレータを受け取り、レスポンスボディをストリームします。 +--- -{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *} +または、`response_class` パラメータで使用できます: -#### `StreamingResponse` をファイルライクなオブジェクトとともに使う +{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *} -ファイルライクなオブジェクト (例えば、 `open()` で返されたオブジェクト) がある場合、 `StreamingResponse` に含めて返すことができます。 +その場合、*path operation*関数からURLを直接返せます。 -これにはクラウドストレージとの連携や映像処理など、多くのライブラリが含まれています。 +この場合に使用される `status_code` は `RedirectResponse` のデフォルトである `307` になります。 -{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *} +--- + +また、`status_code` パラメータを `response_class` パラメータと組み合わせて使うこともできます: + +{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *} + +### `StreamingResponse` { #streamingresponse } + +非同期ジェネレータ、または通常のジェネレータ/イテレータを受け取り、レスポンスボディをストリームします。 + +{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *} + +#### ファイルライクオブジェクトで `StreamingResponse` を使う { #using-streamingresponse-with-file-like-objects } + +file-like オブジェクト(例: `open()` で返されるオブジェクト)がある場合、そのfile-likeオブジェクトを反復処理するジェネレータ関数を作れます。 + +そうすれば、最初にすべてをメモリへ読み込む必要はなく、そのジェネレータ関数を `StreamingResponse` に渡して返せます。 + +これにはクラウドストレージとの連携、映像処理など、多くのライブラリが含まれます。 + +{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *} + +1. これはジェネレータ関数です。内部に `yield` 文を含むため「ジェネレータ関数」です。 +2. `with` ブロックを使うことで、ジェネレータ関数が終わった後(つまりレスポンスの送信が完了した後)にfile-likeオブジェクトが確実にクローズされるようにします。 +3. この `yield from` は、`file_like` という名前のものを反復処理するように関数へ指示します。そして反復された各パートについて、そのパートをこのジェネレータ関数(`iterfile`)から来たものとして `yield` します。 + + つまり、内部的に「生成」の作業を別のものへ移譲するジェネレータ関数です。 + + このようにすることで `with` ブロックに入れられ、完了後にfile-likeオブジェクトが確実にクローズされます。 /// tip | 豆知識 -ここでは `async` や `await` をサポートしていない標準の `open()` を使っているので、通常の `def` でpath operationを宣言していることに注意してください。 +ここでは `async` と `await` をサポートしていない標準の `open()` を使っているため、通常の `def` でpath operationを宣言している点に注意してください。 /// -### `FileResponse` +### `FileResponse` { #fileresponse } レスポンスとしてファイルを非同期的にストリームします。 @@ -204,29 +250,63 @@ HTTPリダイレクトを返します。デフォルトでは307ステータス * `path` - ストリームするファイルのファイルパス。 * `headers` - 含めたい任意のカスタムヘッダーの辞書。 -* `media_type` - メディアタイプを示す文字列。セットされなかった場合は、ファイル名やパスからメディアタイプが推察されます。 -* `filename` - セットされた場合、レスポンスの `Content-Disposition` に含まれます。 +* `media_type` - メディアタイプを示す文字列。未設定の場合、ファイル名やパスからメディアタイプが推測されます。 +* `filename` - 設定した場合、レスポンスの `Content-Disposition` に含まれます。 -ファイルレスポンスには、適切な `Content-Length` 、 `Last-Modified` 、 `ETag` ヘッダーが含まれます。 +ファイルレスポンスには、適切な `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` パラメータを使うこともできます: -**FastAPI** クラスのインスタンスか `APIRouter` を生成するときに、デフォルトのレスポンスクラスを指定できます。 +{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *} -定義するためのパラメータは、 `default_response_class` です。 +この場合、*path operation*関数からファイルパスを直接返せます。 -以下の例では、 **FastAPI** は、全ての *path operation* で `JSONResponse` の代わりに `ORJSONResponse` をデフォルトとして利用します。 +## カスタムレスポンスクラス { #custom-response-class } -{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *} +`Response` を継承した独自のカスタムレスポンスクラスを作成して利用できます。 + +例えば、`orjson`を使いたいが、同梱の `ORJSONResponse` クラスで使われていないカスタム設定も使いたいとします。 + +例えば、インデントされ整形されたJSONを返したいので、orjsonオプション `orjson.OPT_INDENT_2` を使いたいとします。 + +`CustomORJSONResponse` を作れます。主に必要なのは、コンテンツを `bytes` として返す `Response.render(content)` メソッドを作ることです: + +{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *} + +これまでは次のように返していたものが: + +```json +{"message": "Hello World"} +``` + +...このレスポンスでは次のように返されます: + +```json +{ + "message": "Hello World" +} +``` + +もちろん、JSONの整形よりも、これを活用するもっと良い方法が見つかるはずです。 😉 + +## デフォルトレスポンスクラス { #default-response-class } + +**FastAPI** クラスのインスタンス、または `APIRouter` を作成する際に、デフォルトで使用するレスポンスクラスを指定できます。 + +これを定義するパラメータは `default_response_class` です。 + +以下の例では、**FastAPI** はすべての*path operation*で、`JSONResponse` の代わりに `ORJSONResponse` をデフォルトとして使います。 + +{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *} /// tip | 豆知識 -前に見たように、 *path operation* の中で `response_class` をオーバーライドできます。 +これまでと同様に、*path operation*で `response_class` をオーバーライドできます。 /// -## その他のドキュメント +## その他のドキュメント { #additional-documentation } -また、OpenAPIでは `responses` を使ってメディアタイプやその他の詳細を宣言することもできます: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank} +OpenAPIでは `responses` を使ってメディアタイプやその他の詳細を宣言することもできます: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}。 diff --git a/docs/ja/docs/advanced/index.md b/docs/ja/docs/advanced/index.md index 22eaf6eb80..1d0f7566cd 100644 --- a/docs/ja/docs/advanced/index.md +++ b/docs/ja/docs/advanced/index.md @@ -1,27 +1,21 @@ -# 高度なユーザーガイド +# 高度なユーザーガイド { #advanced-user-guide } -## さらなる機能 +## さらなる機能 { #additional-features } -[チュートリアル - ユーザーガイド](../tutorial/index.md){.internal-link target=_blank}により、**FastAPI**の主要な機能は十分に理解できたことでしょう。 +メインの[チュートリアル - ユーザーガイド](../tutorial/index.md){.internal-link target=_blank}だけで、**FastAPI**の主要な機能を一通り把握するには十分なはずです。 -以降のセクションでは、チュートリアルでは説明しきれなかったオプションや設定、および機能について説明します。 +以降のセクションでは、その他のオプション、設定、追加機能を見ていきます。 /// tip | 豆知識 -以降のセクションは、 **必ずしも"応用編"ではありません**。 +以降のセクションは、**必ずしも「高度」ではありません**。 -ユースケースによっては、その中から解決策を見つけられるかもしれません。 +また、あなたのユースケースに対する解決策が、その中のどれかにある可能性もあります。 /// -## 先にチュートリアルを読む +## 先にチュートリアルを読む { #read-the-tutorial-first } -[チュートリアル - ユーザーガイド](../tutorial/index.md){.internal-link target=_blank}の知識があれば、**FastAPI**の主要な機能を利用することができます。 +メインの[チュートリアル - ユーザーガイド](../tutorial/index.md){.internal-link target=_blank}で得た知識があれば、**FastAPI**の機能の多くは引き続き利用できます。 -以降のセクションは、すでにチュートリアルを読んで、その主要なアイデアを理解できていることを前提としています。 - -## テスト駆動開発のコース - -このセクションの内容を補完するために脱初心者用コースを受けたい場合は、**TestDriven.io**による、Test-Driven Development with FastAPI and Dockerを確認するのがよいかもしれません。 - -現在、このコースで得られた利益の10%が**FastAPI**の開発のために寄付されています。🎉 😄 +また、以降のセクションでは、すでにそれを読んでいて、主要な考え方を理解していることを前提としています。 diff --git a/docs/ja/docs/advanced/path-operation-advanced-configuration.md b/docs/ja/docs/advanced/path-operation-advanced-configuration.md index 05188d5b25..a78c3cb026 100644 --- a/docs/ja/docs/advanced/path-operation-advanced-configuration.md +++ b/docs/ja/docs/advanced/path-operation-advanced-configuration.md @@ -1,30 +1,30 @@ -# Path Operationの高度な設定 +# Path Operationの高度な設定 { #path-operation-advanced-configuration } -## OpenAPI operationId +## OpenAPI operationId { #openapi-operationid } /// warning | 注意 -あなたがOpenAPIの「エキスパート」でなければ、これは必要ないかもしれません。 +OpenAPIの「エキスパート」でなければ、これはおそらく必要ありません。 /// *path operation* で `operation_id` パラメータを利用することで、OpenAPIの `operationId` を設定できます。 -`operation_id` は各オペレーションで一意にする必要があります。 +各オペレーションで一意になるようにする必要があります。 -{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *} +{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *} -### *path operation関数* の名前をoperationIdとして使用する +### *path operation関数* の名前をoperationIdとして使用する { #using-the-path-operation-function-name-as-the-operationid } -APIの関数名を `operationId` として利用したい場合、すべてのAPIの関数をイテレーションし、各 *path operation* の `operationId` を `APIRoute.name` で上書きすれば可能です。 +APIの関数名を `operationId` として利用したい場合、すべてのAPI関数をイテレーションし、各 *path operation* の `operation_id` を `APIRoute.name` で上書きすれば可能です。 -そうする場合は、すべての *path operation* を追加した後に行う必要があります。 +すべての *path operation* を追加した後に行うべきです。 -{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2,12:21,24] *} +{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *} /// tip | 豆知識 -`app.openapi()` を手動でコールする場合、その前に`operationId`を更新する必要があります。 +`app.openapi()` を手動で呼び出す場合、その前に `operationId` を更新するべきです。 /// @@ -32,22 +32,141 @@ APIの関数名を `operationId` として利用したい場合、すべてのAP この方法をとる場合、各 *path operation関数* が一意な名前である必要があります。 -それらが異なるモジュール (Pythonファイル) にあるとしてもです。 +異なるモジュール(Pythonファイル)にある場合でも同様です。 /// -## OpenAPIから除外する +## OpenAPIから除外する { #exclude-from-openapi } -生成されるOpenAPIスキーマ (つまり、自動ドキュメント生成の仕組み) から *path operation* を除外するには、 `include_in_schema` パラメータを `False` にします。 +生成されるOpenAPIスキーマ(つまり、自動ドキュメント生成の仕組み)から *path operation* を除外するには、`include_in_schema` パラメータを使用して `False` に設定します。 -{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *} +{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *} -## docstringによる説明の高度な設定 +## docstringによる説明の高度な設定 { #advanced-description-from-docstring } -*path operation関数* のdocstringからOpenAPIに使用する行を制限することができます。 +*path operation関数* のdocstringからOpenAPIに使用する行を制限できます。 -`\f` (「書式送り (Form Feed)」のエスケープ文字) を付与することで、**FastAPI** はOpenAPIに使用される出力をその箇所までに制限します。 +`\f`(エスケープされた「書式送り(form feed)」文字)を追加すると、**FastAPI** はその地点でOpenAPIに使用される出力を切り詰めます。 -ドキュメントには表示されませんが、他のツール (例えばSphinx) では残りの部分を利用できるでしょう。 +ドキュメントには表示されませんが、他のツール(Sphinxなど)は残りの部分を利用できます。 -{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *} +{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *} + +## 追加レスポンス { #additional-responses } + +*path operation* に対して `response_model` と `status_code` を宣言する方法はすでに見たことがあるでしょう。 + +それにより、*path operation* のメインのレスポンスに関するメタデータが定義されます。 + +追加のレスポンスについても、モデルやステータスコードなどとともに宣言できます。 + +これについてはドキュメントに章全体があります。 [OpenAPIの追加レスポンス](additional-responses.md){.internal-link target=_blank} で読めます。 + +## OpenAPI Extra { #openapi-extra } + +アプリケーションで *path operation* を宣言すると、**FastAPI** はOpenAPIスキーマに含めるために、その *path operation* に関連するメタデータを自動的に生成します。 + +/// note | 技術詳細 + +OpenAPI仕様では Operation Object と呼ばれています。 + +/// + +これには *path operation* に関するすべての情報が含まれ、自動ドキュメントを生成するために使われます。 + +`tags`、`parameters`、`requestBody`、`responses` などが含まれます。 + +この *path operation* 固有のOpenAPIスキーマは通常 **FastAPI** により自動生成されますが、拡張することもできます。 + +/// tip | 豆知識 + +これは低レベルな拡張ポイントです。 + +追加レスポンスを宣言するだけなら、より便利な方法として [OpenAPIの追加レスポンス](additional-responses.md){.internal-link target=_blank} を使うことができます。 + +/// + +`openapi_extra` パラメータを使って、*path operation* のOpenAPIスキーマを拡張できます。 + +### OpenAPI Extensions { #openapi-extensions } + +この `openapi_extra` は、例えば [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] *} + +自動APIドキュメントを開くと、その拡張は特定の *path operation* の下部に表示されます。 + + + +そして(APIの `/openapi.json` にある)生成されたOpenAPIを見ると、その拡張も特定の *path operation* の一部として確認できます。 + +```JSON hl_lines="22" +{ + "openapi": "3.1.0", + "info": { + "title": "FastAPI", + "version": "0.1.0" + }, + "paths": { + "/items/": { + "get": { + "summary": "Read Items", + "operationId": "read_items_items__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "x-aperture-labs-portal": "blue" + } + } + } +} +``` + +### カスタムOpenAPI *path operation* スキーマ { #custom-openapi-path-operation-schema } + +`openapi_extra` 内の辞書は、*path operation* 用に自動生成されたOpenAPIスキーマと深くマージされます。 + +そのため、自動生成されたスキーマに追加データを加えることができます。 + +例えば、Pydanticを使ったFastAPIの自動機能を使わずに独自のコードでリクエストを読み取り・検証することを選べますが、それでもOpenAPIスキーマでリクエストを定義したい場合があります。 + +それは `openapi_extra` で行えます。 + +{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *} + +この例では、Pydanticモデルを一切宣言していません。実際、リクエストボディはJSONとして parsed されず、直接 `bytes` として読み取られます。そして `magic_data_reader()` 関数が、何らかの方法でそれをパースする責務を担います。 + +それでも、リクエストボディに期待されるスキーマを宣言できます。 + +### カスタムOpenAPI content type { #custom-openapi-content-type } + +同じトリックを使って、PydanticモデルでJSON Schemaを定義し、それを *path operation* 用のカスタムOpenAPIスキーマセクションに含めることができます。 + +また、リクエスト内のデータ型がJSONでない場合でもこれを行えます。 + +例えばこのアプリケーションでは、PydanticモデルからJSON Schemaを抽出するFastAPIの統合機能や、JSONの自動バリデーションを使っていません。実際、リクエストのcontent typeをJSONではなくYAMLとして宣言しています。 + +{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *} + +それでも、デフォルトの統合機能を使っていないにもかかわらず、YAMLで受け取りたいデータのために、Pydanticモデルを使って手動でJSON Schemaを生成しています。 + +そしてリクエストを直接使い、ボディを `bytes` として抽出します。これは、FastAPIがリクエストペイロードをJSONとしてパースしようとすらしないことを意味します。 + +その後、コード内でそのYAMLコンテンツを直接パースし、さらに同じPydanticモデルを使ってYAMLコンテンツを検証しています。 + +{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *} + +/// tip | 豆知識 + +ここでは同じPydanticモデルを再利用しています。 + +ただし同様に、別の方法で検証することもできます。 + +/// diff --git a/docs/ja/docs/advanced/response-directly.md b/docs/ja/docs/advanced/response-directly.md index 42412d5070..7e83b9ffb2 100644 --- a/docs/ja/docs/advanced/response-directly.md +++ b/docs/ja/docs/advanced/response-directly.md @@ -1,4 +1,4 @@ -# レスポンスを直接返す +# レスポンスを直接返す { #return-a-response-directly } **FastAPI** の *path operation* では、通常は任意のデータを返すことができます: 例えば、 `dict`、`list`、Pydanticモデル、データベースモデルなどです。 @@ -10,7 +10,7 @@ これは例えば、カスタムヘッダーやcookieを返すときに便利です。 -## `Response` を返す +## `Response` を返す { #return-a-response } 実際は、`Response` やそのサブクラスを返すことができます。 @@ -26,7 +26,7 @@ これは多くの柔軟性を提供します。任意のデータ型を返したり、任意のデータ宣言やバリデーションをオーバーライドできます。 -## `jsonable_encoder` を `Response` の中で使う +## `jsonable_encoder` を `Response` の中で使う { #using-the-jsonable-encoder-in-a-response } **FastAPI** はあなたが返す `Response` に対して何も変更を加えないので、コンテンツが準備できていることを保証しなければなりません。 @@ -34,7 +34,7 @@ このようなケースでは、レスポンスにデータを含める前に `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 | 技術詳細 @@ -44,7 +44,7 @@ /// -## カスタム `Response` を返す +## カスタム `Response` を返す { #returning-a-custom-response } 上記の例では必要な部分を全て示していますが、あまり便利ではありません。`item` を直接返すことができるし、**FastAPI** はそれを `dict` に変換して `JSONResponse` に含めてくれるなど。すべて、デフォルトの動作です。 @@ -54,9 +54,9 @@ XMLを文字列にし、`Response` に含め、それを返します。 -{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *} +{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *} -## 備考 +## 備考 { #notes } `Response` を直接返す場合、バリデーションや、変換 (シリアライズ) や、自動ドキュメントは行われません。 diff --git a/docs/ja/docs/advanced/websockets.md b/docs/ja/docs/advanced/websockets.md index 2517530abe..6c68c9f0b1 100644 --- a/docs/ja/docs/advanced/websockets.md +++ b/docs/ja/docs/advanced/websockets.md @@ -1,10 +1,10 @@ -# WebSocket +# WebSockets { #websockets } -**FastAPI**でWebSocketが使用できます。 +**FastAPI**でWebSocketsが使用できます。 -## `WebSockets`のインストール +## `websockets`のインストール { #install-websockets } -まず `WebSockets`のインストールが必要です。 +[仮想環境](../virtual-environments.md){.internal-link target=_blank}を作成し、それを有効化してから、「WebSocket」プロトコルを簡単に使えるようにするPythonライブラリの`websockets`をインストールしてください。
@@ -16,13 +16,13 @@ $ pip install websockets
-## WebSocket クライアント +## WebSockets クライアント { #websockets-client } -### 本番環境 +### 本番環境 { #in-production } 本番環境では、React、Vue.js、Angularなどの最新のフレームワークで作成されたフロントエンドを使用しているでしょう。 -そして、バックエンドとWebSocketを使用して通信するために、おそらくフロントエンドのユーティリティを使用することになるでしょう。 +そして、バックエンドとWebSocketsを使用して通信するために、おそらくフロントエンドのユーティリティを使用することになるでしょう。 または、ネイティブコードでWebSocketバックエンドと直接通信するネイティブモバイルアプリケーションがあるかもしれません。 @@ -30,21 +30,21 @@ $ pip install websockets --- -ただし、この例では非常にシンプルなHTML文書といくつかのJavaScriptを、すべてソースコードの中に入れて使用することにします。 +ただし、この例では非常にシンプルなHTML文書といくつかのJavaScriptを、すべて長い文字列の中に入れて使用することにします。 もちろん、これは最適な方法ではありませんし、本番環境で使うことはないでしょう。 本番環境では、上記の方法のいずれかの選択肢を採用することになるでしょう。 -しかし、これはWebSocketのサーバーサイドに焦点を当て、実用的な例を示す最も簡単な方法です。 +しかし、これは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 | 技術詳細 @@ -54,22 +54,22 @@ $ pip install websockets /// -## メッセージの送受信 +## メッセージを待機して送信する { #await-for-messages-and-send-messages } -WebSocketルートでは、 `await` を使ってメッセージの送受信ができます。 +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` である場合、以下でアプリケーションを実行します。
```console -$ uvicorn main:app --reload +$ fastapi dev main.py INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` @@ -86,7 +86,7 @@ $ uvicorn main:app --reload -そして、 WebSocketを使用した**FastAPI**アプリケーションが応答します。 +そして、 WebSocketsを使用した**FastAPI**アプリケーションが応答します。 @@ -96,7 +96,7 @@ $ uvicorn main:app --reload そして、これらの通信はすべて同じWebSocket接続を使用します。 -## 依存関係 +## `Depends` などの使用 { #using-depends-and-others } WebSocketエンドポイントでは、`fastapi` から以下をインポートして使用できます。 @@ -107,28 +107,26 @@ WebSocketエンドポイントでは、`fastapi` から以下をインポート * `Path` * `Query` -これらは、他のFastAPI エンドポイント/*path operation* の場合と同じように機能します。 +これらは、他のFastAPI エンドポイント/*path operations* の場合と同じように機能します。 -{* ../../docs_src/websockets/tutorial002.py hl[58:65,68:83] *} +{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *} /// info | 情報 -WebSocket で `HTTPException` を発生させることはあまり意味がありません。したがって、WebSocketの接続を直接閉じる方がよいでしょう。 +これはWebSocketであるため、`HTTPException` を発生させることはあまり意味がありません。代わりに `WebSocketException` を発生させます。 クロージングコードは、仕様で定義された有効なコードの中から使用することができます。 -将来的には、どこからでも `raise` できる `WebSocketException` が用意され、専用の例外ハンドラを追加できるようになる予定です。これは、Starlette の PR #527 に依存するものです。 - /// -### 依存関係を用いてWebSocketsを試してみる +### 依存関係を用いてWebSocketsを試してみる { #try-the-websockets-with-dependencies } -ファイル名が `main.py` である場合、以下の方法でアプリケーションを実行します。 +ファイル名が `main.py` である場合、以下でアプリケーションを実行します。
```console -$ uvicorn main:app --reload +$ fastapi dev main.py INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` @@ -137,14 +135,14 @@ $ uvicorn main:app --reload ブラウザで http://127.0.0.1:8000 を開きます。 -クライアントが設定できる項目は以下の通りです。 +そこで、以下を設定できます。 * パスで使用される「Item ID」 * クエリパラメータとして使用される「Token」 /// tip | 豆知識 -クエリ `token` は依存パッケージによって処理されることに注意してください。 +クエリ `token` は依存関係によって処理されることに注意してください。 /// @@ -152,11 +150,11 @@ $ uvicorn main:app --reload -## 切断や複数クライアントへの対応 +## 切断や複数クライアントの処理 { #handling-disconnections-and-multiple-clients } WebSocket接続が閉じられると、 `await websocket.receive_text()` は例外 `WebSocketDisconnect` を発生させ、この例のようにキャッチして処理することができます。 -{* ../../docs_src/websockets/tutorial003.py hl[81:83] *} +{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *} 試してみるには、 @@ -174,15 +172,15 @@ Client #1596980209979 left the chat 上記のアプリは、複数の WebSocket 接続に対してメッセージを処理し、ブロードキャストする方法を示すための最小限のシンプルな例です。 -しかし、すべての接続がメモリ内の単一のリストで処理されるため、プロセスの実行中にのみ機能し、単一のプロセスでのみ機能することに注意してください。 +しかし、すべてがメモリ内の単一のリストで処理されるため、プロセスの実行中にのみ機能し、単一のプロセスでのみ機能することに注意してください。 -もしFastAPIと簡単に統合できて、RedisやPostgreSQLなどでサポートされている、より堅牢なものが必要なら、encode/broadcaster を確認してください。 +FastAPIと簡単に統合できて、RedisやPostgreSQLなどでサポートされている、より堅牢なものが必要なら、encode/broadcaster を確認してください。 /// -## その他のドキュメント +## 詳細情報 { #more-info } オプションの詳細については、Starletteのドキュメントを確認してください。 -* `WebSocket` クラス -* クラスベースのWebSocket処理 +* `WebSocket` クラス. +* クラスベースのWebSocket処理. diff --git a/docs/ja/docs/benchmarks.md b/docs/ja/docs/benchmarks.md index 966d199c5e..fbfba2e637 100644 --- a/docs/ja/docs/benchmarks.md +++ b/docs/ja/docs/benchmarks.md @@ -1,34 +1,34 @@ -# ベンチマーク +# ベンチマーク { #benchmarks } -TechEmpowerの独立したベンチマークでは、Uvicornの下で動作する**FastAPI**アプリケーションは、利用可能な最速のPythonフレームワークの1つであり、下回っているのはStarletteとUvicorn自体 (FastAPIによって内部で使用される) のみだと示されています。 +TechEmpowerの独立したベンチマークでは、Uvicornの下で動作する**FastAPI**アプリケーションは、利用可能な最速のPythonフレームワークの1つであり、下回っているのはStarletteとUvicorn自体(FastAPIによって内部で使用される)のみだと示されています。 ただし、ベンチマークを確認し、比較する際には下記の内容に気を付けてください。 -## ベンチマークと速度 +## ベンチマークと速度 { #benchmarks-and-speed } -ベンチマークを確認する時、異なるツールを同等なものと比較するのが一般的です。 +ベンチマークを確認する時、異なるタイプの複数のツールが同等のものとして比較されているのを目にするのが一般的です。 -具体的には、Uvicorn、Starlette、FastAPIを (他の多くのツールと) 比較しました。 +具体的には、Uvicorn、Starlette、FastAPIを(他の多くのツールの中で)まとめて比較しているのを目にすることがあります。 ツールで解決する問題がシンプルなほど、パフォーマンスが向上します。また、ほとんどのベンチマークは、ツールから提供される追加機能をテストしていません。 階層関係はこのようになります。 * **Uvicorn**: ASGIサーバー - * **Starlette**: (Uvicornを使用) WEBマイクロフレームワーク - * **FastAPI**: (Starletteを使用) データバリデーションなどの、APIを構築する追加機能を備えたAPIマイクロフレームワーク + * **Starlette**: (Uvicornを使用)webマイクロフレームワーク + * **FastAPI**: (Starletteを使用)データバリデーションなど、APIを構築するためのいくつかの追加機能を備えたAPIマイクロフレームワーク * **Uvicorn**: - * サーバー自体に余分なコードが少ないので、最高のパフォーマンスが得られます。 - * Uvicornにアプリケーションを直接書くことはできません。つまり、あなたのコードには、Starlette (または** FastAPI **) が提供するコードを、多かれ少なかれ含める必要があります。そうすると、最終的なアプリケーションは、フレームワークを使用してアプリのコードとバグを最小限に抑えた場合と同じオーバーヘッドになります。 - * もしUvicornを比較する場合は、Daphne、Hypercorn、uWSGIなどのアプリケーションサーバーと比較してください。 + * サーバー自体以外に余分なコードがあまりないため、最高のパフォーマンスになります。 + * Uvicornにアプリケーションを直接書くことはないでしょう。それは、あなたのコードに、Starlette(または**FastAPI**)が提供するコードを、少なくとも多かれ少なかれ含める必要があるということです。そして、もしそうした場合、最終的なアプリケーションは、フレームワークを使用してアプリのコードとバグを最小限に抑えた場合と同じオーバーヘッドになります。 + * Uvicornを比較する場合は、Daphne、Hypercorn、uWSGIなどのアプリケーションサーバーと比較してください。 * **Starlette**: - * Uvicornに次ぐ性能を持つでしょう。実際、StarletteはUvicornを使用しています。だから、より多くのコードを実行する必要があり、Uvicornよりも「遅く」なってしまうだけなのです。 - * しかし、パスベースのルーティングなどのシンプルなWEBアプリケーションを構築する機能を提供します。 - * もしStarletteを比較する場合は、Sanic、Flask、DjangoなどのWEBフレームワーク (もしくはマイクロフレームワーク) と比較してください。 + * Uvicornに次ぐ性能になるでしょう。実際、Starletteは実行にUvicornを使用しています。そのため、おそらく、より多くのコードを実行しなければならない分だけ、Uvicornより「遅く」なるだけです。 + * しかし、パスに基づくルーティングなどを使って、シンプルなwebアプリケーションを構築するためのツールを提供します。 + * Starletteを比較する場合は、Sanic、Flask、Djangoなどのwebフレームワーク(またはマイクロフレームワーク)と比較してください。 * **FastAPI**: - * StarletteがUvicornを使っているのと同じで、**FastAPI**はStarletteを使っており、それより速くできません。 - * FastAPIはStarletteの上にさらに多くの機能を提供します。データの検証やシリアライゼーションなど、APIを構築する際に常に必要な機能です。また、それを使用することで、自動ドキュメント化を無料で取得できます (ドキュメントは実行中のアプリケーションにオーバーヘッドを追加せず、起動時に生成されます) 。 - * FastAPIを使用せず、直接Starlette (またはSanic, Flask, Responderなど) を使用した場合、データの検証とシリアライズをすべて自分で実装する必要があります。そのため、最終的なアプリケーションはFastAPIを使用して構築した場合と同じオーバーヘッドが発生します。そして、多くの場合、このデータ検証とシリアライズは、アプリケーションのコードの中で最大の記述量になります。 - * FastAPIを使用することで、開発時間、バグ、コード行数を節約でき、使用しない場合 (あなたが全ての機能を実装し直した場合) と同じかそれ以上のパフォーマンスを得られます。 - * もしFastAPIを比較する場合は、Flask-apispec、NestJS、Moltenなどのデータ検証や、シリアライズの機能を提供するWEBフレームワーク (や機能のセット) と比較してください。これらはデータの自動検証や、シリアライズ、ドキュメント化が統合されたフレームワークです。 + * StarletteがUvicornを使用しており、それより速くできないのと同じように、**FastAPI**はStarletteを使用しているため、それより速くできません。 + * FastAPIはStarletteの上に、より多くの機能を提供します。データバリデーションやシリアライゼーションのように、APIを構築する際にほとんど常に必要な機能です。また、それを使用することで、自動ドキュメント化を無料で利用できます(自動ドキュメントは実行中のアプリケーションにオーバーヘッドを追加せず、起動時に生成されます)。 + * FastAPIを使用せず、Starletteを直接(またはSanic、Flask、Responderなど別のツールを)使用した場合、データバリデーションとシリアライゼーションをすべて自分で実装する必要があります。そのため、最終的なアプリケーションはFastAPIを使用して構築した場合と同じオーバーヘッドが発生します。そして多くの場合、このデータバリデーションとシリアライゼーションは、アプリケーションで書かれるコードの大部分になります。 + * そのため、FastAPIを使用することで、開発時間、バグ、コード行数を節約でき、使用しない場合(あなたがそれをすべて自分のコードで実装する必要があるため)と比べて、同じパフォーマンス(またはそれ以上)を得られる可能性があります。 + * FastAPIを比較する場合は、Flask-apispec、NestJS、Moltenなど、データバリデーション、シリアライゼーション、ドキュメント化を提供するwebアプリケーションフレームワーク(またはツール群)と比較してください。自動データバリデーション、シリアライゼーション、ドキュメント化が統合されたフレームワークです。 diff --git a/docs/ja/docs/deployment/concepts.md b/docs/ja/docs/deployment/concepts.md index a0d4fb35b1..787eb2e73f 100644 --- a/docs/ja/docs/deployment/concepts.md +++ b/docs/ja/docs/deployment/concepts.md @@ -1,4 +1,4 @@ -# デプロイメントのコンセプト +# デプロイメントのコンセプト { #deployments-concepts } **FastAPI**を用いたアプリケーションをデプロイするとき、もしくはどのようなタイプのWeb APIであっても、おそらく気になるコンセプトがいくつかあります。 @@ -10,12 +10,12 @@ * 起動時の実行 * 再起動 * レプリケーション(実行中のプロセス数) -* メモリー +* メモリ * 開始前の事前のステップ これらが**デプロイメント**にどのような影響を与えるかを見ていきましょう。 -最終的な目的は、**安全な方法で**APIクライアントに**サービスを提供**し、**中断を回避**するだけでなく、**計算リソース**(例えばリモートサーバー/仮想マシン)を可能な限り効率的に使用することです。🚀 +最終的な目的は、**安全な方法で**APIクライアントに**サービスを提供**し、**中断を回避**するだけでなく、**計算リソース**(例えばリモートサーバー/仮想マシン)を可能な限り効率的に使用することです。 🚀 この章では前述した**コンセプト**についてそれぞれ説明します。 @@ -27,16 +27,16 @@ しかし、今はこれらの重要な**コンセプトに基づくアイデア**を確認しましょう。これらのコンセプトは、他のどのタイプのWeb APIにも当てはまります。💡 -## セキュリティ - HTTPS +## セキュリティ - HTTPS { #security-https } [前チャプターのHTTPSについて](https.md){.internal-link target=_blank}では、HTTPSがどのようにAPIを暗号化するのかについて学びました。 通常、アプリケーションサーバにとって**外部の**コンポーネントである**TLS Termination Proxy**によって提供されることが一般的です。このプロキシは通信の暗号化を担当します。 -さらにセキュアな通信において、HTTPS証明書の定期的な更新を行いますが、これはTLS Termination Proxyと同じコンポーネントが担当することもあれば、別のコンポーネントが担当することもあります。 +さらに、HTTPS証明書の更新を担当するものが必要で、同じコンポーネントが担当することもあれば、別のコンポーネントが担当することもあります。 -### HTTPS 用ツールの例 +### HTTPS 用ツールの例 { #example-tools-for-https } TLS Termination Proxyとして使用できるツールには以下のようなものがあります: * Traefik @@ -59,11 +59,11 @@ TLS Termination Proxyとして使用できるツールには以下のような 次に考慮すべきコンセプトは、実際のAPIを実行するプログラム(例:Uvicorn)に関連するものすべてです。 -## プログラム と プロセス +## プログラム と プロセス { #program-and-process } 私たちは「**プロセス**」という言葉についてたくさん話すので、その意味や「**プログラム**」という言葉との違いを明確にしておくと便利です。 -### プログラムとは何か +### プログラムとは何か { #what-is-a-program } **プログラム**という言葉は、一般的にいろいろなものを表現するのに使われます: @@ -71,7 +71,7 @@ TLS Termination Proxyとして使用できるツールには以下のような * OSによって実行することができるファイル(例: `python`, `python.exe` or `uvicorn`) * OS上で**実行**している間、CPUを使用し、メモリ上に何かを保存する特定のプログラム(**プロセス**とも呼ばれる) -### プロセスとは何か +### プロセスとは何か { #what-is-a-process } **プロセス**という言葉は通常、より具体的な意味で使われ、OSで実行されているものだけを指します(先ほどの最後の説明のように): @@ -92,27 +92,29 @@ OSの「タスク・マネージャー」や「システム・モニター」( さて、**プロセス**と**プログラム**という用語の違いを確認したところで、デプロイメントについて話を続けます。 -## 起動時の実行 +## 起動時の実行 { #running-on-startup } ほとんどの場合、Web APIを作成するときは、クライアントがいつでもアクセスできるように、**常に**中断されることなく**実行される**ことを望みます。もちろん、特定の状況でのみ実行させたい特別な理由がある場合は別ですが、その時間のほとんどは、常に実行され、**利用可能**であることを望みます。 -### リモートサーバー上での実行 +### リモートサーバー上での実行 { #in-a-remote-server } -リモートサーバー(クラウドサーバー、仮想マシンなど)をセットアップするときにできる最も簡単なことは、ローカルで開発するときと同じように、Uvicorn(または同様のもの)を手動で実行することです。 この方法は**開発中**には役に立つと思われます。 +リモートサーバー(クラウドサーバー、仮想マシンなど)をセットアップするときにできる最も簡単なことは、ローカルで開発するときと同じように、`fastapi run`(Uvicornを使用します)や同様のものを手動で実行することです。 + +そしてこれは動作し、**開発中**には役に立つでしょう。 しかし、サーバーへの接続が切れた場合、**実行中のプロセス**はおそらくダウンしてしまうでしょう。 そしてサーバーが再起動された場合(アップデートやクラウドプロバイダーからのマイグレーションの後など)、おそらくあなたはそれに**気づかないでしょう**。そのため、プロセスを手動で再起動しなければならないことすら気づかないでしょう。つまり、APIはダウンしたままなのです。😱 -### 起動時に自動的に実行 +### 起動時に自動的に実行 { #run-automatically-on-startup } 一般的に、サーバープログラム(Uvicornなど)はサーバー起動時に自動的に開始され、**人の介入**を必要とせずに、APIと一緒にプロセスが常に実行されるようにしたいと思われます(UvicornがFastAPIアプリを実行するなど)。 -### 別のプログラムの用意 +### 別のプログラムの用意 { #separate-program } これを実現するために、通常は**別のプログラム**を用意し、起動時にアプリケーションが実行されるようにします。そして多くの場合、他のコンポーネントやアプリケーション、例えばデータベースも実行されるようにします。 -### 起動時に実行するツールの例 +### 起動時に実行するツールの例 { #example-tools-to-run-at-startup } 実行するツールの例をいくつか挙げます: @@ -127,31 +129,33 @@ OSの「タスク・マネージャー」や「システム・モニター」( 次の章で、より具体的な例を挙げていきます。 -## 再起動 +## 再起動 { #restarts } 起動時にアプリケーションが実行されることを確認するのと同様に、失敗後にアプリケーションが**再起動**されることも確認したいと思われます。 -### 我々は間違いを犯す +### 我々は間違いを犯す { #we-make-mistakes } 私たち人間は常に**間違い**を犯します。ソフトウェアには、ほとんど常に**バグ**があらゆる箇所に隠されています。🐛 -### 小さなエラーは自動的に処理される +そして私たち開発者は、それらのバグを見つけたり新しい機能を実装したりしながらコードを改善し続けます(新しいバグも追加してしまうかもしれません😅)。 + +### 小さなエラーは自動的に処理される { #small-errors-automatically-handled } FastAPIでWeb APIを構築する際に、コードにエラーがある場合、FastAPIは通常、エラーを引き起こした単一のリクエストにエラーを含めます。🛡 クライアントはそのリクエストに対して**500 Internal Server Error**を受け取りますが、アプリケーションは完全にクラッシュするのではなく、次のリクエストのために動作を続けます。 -### 重大なエラー - クラッシュ +### 重大なエラー - クラッシュ { #bigger-errors-crashes } しかしながら、**アプリケーション全体をクラッシュさせるようなコードを書いて**UvicornとPythonをクラッシュさせるようなケースもあるかもしれません。💥 -それでも、ある箇所でエラーが発生したからといって、アプリケーションを停止させたままにしたくないでしょう。 少なくとも壊れていない*パスオペレーション*については、**実行し続けたい**はずです。 +それでも、ある箇所でエラーが発生したからといって、アプリケーションを停止させたままにしたくないでしょう。 少なくとも壊れていない*path operation*については、**実行し続けたい**はずです。 -### クラッシュ後の再起動 +### クラッシュ後の再起動 { #restart-after-crash } しかし、実行中の**プロセス**をクラッシュさせるような本当にひどいエラーの場合、少なくとも2〜3回ほどプロセスを**再起動**させる外部コンポーネントが必要でしょう。 -/// tip +/// tip | 豆知識 ...とはいえ、アプリケーション全体が**すぐにクラッシュする**のであれば、いつまでも再起動し続けるのは意味がないでしょう。しかし、その場合はおそらく開発中か少なくともデプロイ直後に気づくと思われます。 @@ -161,7 +165,7 @@ FastAPIでWeb APIを構築する際に、コードにエラーがある場合、 あなたはおそらく**外部コンポーネント**がアプリケーションの再起動を担当することを望むと考えます。 なぜなら、その時点でUvicornとPythonを使った同じアプリケーションはすでにクラッシュしており、同じアプリケーションの同じコードに対して何もできないためです。 -### 自動的に再起動するツールの例 +### 自動的に再起動するツールの例 { #example-tools-to-restart-automatically } ほとんどの場合、前述した**起動時にプログラムを実行する**ために使用されるツールは、自動で**再起動**することにも利用されます。 @@ -176,19 +180,19 @@ FastAPIでWeb APIを構築する際に、コードにエラーがある場合、 * クラウドプロバイダーがサービスの一部として内部的に処理 * そのほか... -## レプリケーション - プロセスとメモリー +## レプリケーション - プロセスとメモリ { #replication-processes-and-memory } -FastAPI アプリケーションでは、Uvicorn のようなサーバープログラムを使用し、**1つのプロセス**で1度に複数のクライアントに同時に対応できます。 +FastAPI アプリケーションでは、Uvicorn を実行する `fastapi` コマンドのようなサーバープログラムを使用し、**1つのプロセス**で1度に複数のクライアントに同時に対応できます。 しかし、多くの場合、複数のワーカー・プロセスを同時に実行したいと考えるでしょう。 -### 複数のプロセス - Worker +### 複数のプロセス - Worker { #multiple-processes-workers } クライアントの数が単一のプロセスで処理できる数を超えており(たとえば仮想マシンがそれほど大きくない場合)、かつサーバーの CPU に**複数のコア**がある場合、同じアプリケーションで同時に**複数のプロセス**を実行させ、すべてのリクエストを分散させることができます。 同じAPIプログラムの**複数のプロセス**を実行する場合、それらは一般的に**Worker/ワーカー**と呼ばれます。 -### ワーカー・プロセス と ポート +### ワーカー・プロセス と ポート { #worker-processes-and-ports } [HTTPSについて](https.md){.internal-link target=_blank}のドキュメントで、1つのサーバーで1つのポートとIPアドレスの組み合わせでリッスンできるのは1つのプロセスだけであることを覚えていますでしょうか? @@ -197,13 +201,13 @@ FastAPI アプリケーションでは、Uvicorn のようなサーバープロ そのため、**複数のプロセス**を同時に持つには**ポートでリッスンしている単一のプロセス**が必要であり、それが何らかの方法で各ワーカー・プロセスに通信を送信することが求められます。 -### プロセスあたりのメモリー +### プロセスあたりのメモリ { #memory-per-process } さて、プログラムがメモリにロードする際には、例えば機械学習モデルや大きなファイルの内容を変数に入れたりする場合では、**サーバーのメモリ(RAM)**を少し消費します。 そして複数のプロセスは通常、**メモリを共有しません**。これは、実行中の各プロセスがそれぞれ独自の変数やメモリ等を持っていることを意味します。つまり、コード内で大量のメモリを消費している場合、**各プロセス**は同等の量のメモリを消費することになります。 -### サーバーメモリー +### サーバーメモリ { #server-memory } 例えば、あなたのコードが **1GBのサイズの機械学習モデル**をロードする場合、APIで1つのプロセスを実行すると、少なくとも1GBのRAMを消費します。 @@ -211,7 +215,7 @@ FastAPI アプリケーションでは、Uvicorn のようなサーバープロ リモートサーバーや仮想マシンのRAMが3GBしかない場合、4GB以上のRAMをロードしようとすると問題が発生します。🚨 -### 複数プロセス - 例 +### 複数プロセス - 例 { #multiple-processes-an-example } この例では、2つの**ワーカー・プロセス**を起動し制御する**マネージャー・ プロセス**があります。 @@ -227,7 +231,7 @@ FastAPI アプリケーションでは、Uvicorn のようなサーバープロ 毎回同程度の計算を行うAPIがあり、多くのクライアントがいるのであれば、**CPU使用率**もおそらく**安定**するでしょう(常に急激に上下するのではなく)。 -### レプリケーション・ツールと戦略の例 +### レプリケーション・ツールと戦略の例 { #examples-of-replication-tools-and-strategies } これを実現するにはいくつかのアプローチがありますが、具体的な戦略については次の章(Dockerやコンテナの章など)で詳しく説明します。 @@ -237,25 +241,22 @@ FastAPI アプリケーションでは、Uvicorn のようなサーバープロ 考えられる組み合わせと戦略をいくつか紹介します: -* **Gunicorn**が**Uvicornワーカー**を管理 - * Gunicornは**IP**と**ポート**をリッスンする**プロセスマネージャ**で、レプリケーションは**複数のUvicornワーカー・プロセス**を持つことによって行われる。 -* **Uvicorn**が**Uvicornワーカー**を管理 +* `--workers` を指定した **Uvicorn** * 1つのUvicornの**プロセスマネージャー**が**IP**と**ポート**をリッスンし、**複数のUvicornワーカー・プロセス**を起動する。 * **Kubernetes**やその他の分散**コンテナ・システム** * **Kubernetes**レイヤーの何かが**IP**と**ポート**をリッスンする。レプリケーションは、**複数のコンテナ**にそれぞれ**1つのUvicornプロセス**を実行させることで行われる。 * **クラウド・サービス**によるレプリケーション * クラウド・サービスはおそらく**あなたのためにレプリケーションを処理**します。**実行するプロセス**や使用する**コンテナイメージ**を定義できるかもしれませんが、いずれにせよ、それはおそらく**単一のUvicornプロセス**であり、クラウドサービスはそのレプリケーションを担当するでしょう。 -/// tip +/// tip | 豆知識 これらの**コンテナ**やDockerそしてKubernetesに関する項目が、まだあまり意味をなしていなくても心配しないでください。 - -コンテナ・イメージ、Docker、Kubernetesなどについては、次の章で詳しく説明します: [コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}. +コンテナ・イメージ、Docker、Kubernetesなどについては、将来の章で詳しく説明します: [コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}. /// -## 開始前の事前のステップ +## 開始前の事前のステップ { #previous-steps-before-starting } アプリケーションを**開始する前**に、いくつかのステップを実行したい場合が多くあります。 @@ -271,7 +272,7 @@ FastAPI アプリケーションでは、Uvicorn のようなサーバープロ もちろん、事前のステップを何度も実行しても問題がない場合もあり、その際は対処がかなり楽になります。 -/// tip +/// tip | 豆知識 また、セットアップによっては、アプリケーションを開始する前の**事前のステップ**が必要ない場合もあることを覚えておいてください。 @@ -279,7 +280,7 @@ FastAPI アプリケーションでは、Uvicorn のようなサーバープロ /// -### 事前ステップの戦略例 +### 事前ステップの戦略例 { #examples-of-previous-steps-strategies } これは**システムを**デプロイする方法に**大きく依存**するだろうし、おそらくプログラムの起動方法や再起動の処理などにも関係してくるでしょう。 @@ -289,14 +290,13 @@ FastAPI アプリケーションでは、Uvicorn のようなサーバープロ * 事前のステップを実行し、アプリケーションを起動するbashスクリプト * 利用するbashスクリプトを起動/再起動したり、エラーを検出したりする方法は以前として必要になるでしょう。 -/// tip +/// tip | 豆知識 - -コンテナを使った具体的な例については、次の章で紹介します: [コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}. +コンテナを使った具体的な例については、将来の章で紹介します: [コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}. /// -## リソースの利用 +## リソースの利用 { #resource-utilization } あなたのサーバーは**リソース**であり、プログラムを実行しCPUの計算時間や利用可能なRAMメモリを消費または**利用**することができます。 @@ -319,7 +319,7 @@ FastAPI アプリケーションでは、Uvicorn のようなサーバープロ `htop`のような単純なツールを使って、サーバーで使用されているCPUやRAM、あるいは各プロセスで使用されている量を見ることができます。あるいは、より複雑な監視ツールを使って、サーバに分散して使用することもできます。 -## まとめ +## まとめ { #recap } アプリケーションのデプロイ方法を決定する際に、考慮すべきであろう主要なコンセプトのいくつかを紹介していきました: @@ -327,7 +327,7 @@ FastAPI アプリケーションでは、Uvicorn のようなサーバープロ * 起動時の実行 * 再起動 * レプリケーション(実行中のプロセス数) -* メモリー +* メモリ * 開始前の事前ステップ これらの考え方とその適用方法を理解することで、デプロイメントを設定したり調整したりする際に必要な直感的な判断ができるようになるはずです。🤓 diff --git a/docs/ja/docs/deployment/docker.md b/docs/ja/docs/deployment/docker.md index 53fc851f1e..6c182448c9 100644 --- a/docs/ja/docs/deployment/docker.md +++ b/docs/ja/docs/deployment/docker.md @@ -1,20 +1,17 @@ -# コンテナ内のFastAPI - Docker +# コンテナ内のFastAPI - Docker { #fastapi-in-containers-docker } -FastAPIアプリケーションをデプロイする場合、一般的なアプローチは**Linuxコンテナ・イメージ**をビルドすることです。 - -基本的には **Docker**を用いて行われます。生成されたコンテナ・イメージは、いくつかの方法のいずれかでデプロイできます。 +FastAPIアプリケーションをデプロイする場合、一般的なアプローチは**Linuxコンテナ・イメージ**をビルドすることです。基本的には **Docker**を用いて行われます。生成されたコンテナ・イメージは、いくつかの方法のいずれかでデプロイできます。 Linuxコンテナの使用には、**セキュリティ**、**反復可能性(レプリカビリティ)**、**シンプリシティ**など、いくつかの利点があります。 -/// tip +/// tip | 豆知識 -TODO: なぜか遷移できない お急ぎで、すでにこれらの情報をご存じですか? [以下の`Dockerfile`の箇所👇](#build-a-docker-image-for-fastapi)へジャンプしてください。 ///
-Dockerfile プレビュー 👀 +Dockerfile Preview 👀 ```Dockerfile FROM python:3.9 @@ -27,15 +24,15 @@ 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"] ```
-## コンテナとは何か +## コンテナとは何か { #what-is-a-container } コンテナ(主にLinuxコンテナ)は、同じシステム内の他のコンテナ(他のアプリケーションやコンポーネント)から隔離された状態を保ちながら、すべての依存関係や必要なファイルを含むアプリケーションをパッケージ化する非常に**軽量**な方法です。 @@ -45,7 +42,7 @@ Linuxコンテナは、ホスト(マシン、仮想マシン、クラウドサ コンテナはまた、独自の**分離された**実行プロセス(通常は1つのプロセスのみ)や、ファイルシステム、ネットワークを持ちます。 このことはデプロイ、セキュリティ、開発などを簡素化させます。 -## コンテナ・イメージとは何か +## コンテナ・イメージとは何か { #what-is-a-container-image } **コンテナ**は、**コンテナ・イメージ**から実行されます。 @@ -53,23 +50,17 @@ Linuxコンテナは、ホスト(マシン、仮想マシン、クラウドサ 保存された静的コンテンツである「**コンテナイメージ**」とは対照的に、「**コンテナ**」は通常、実行中のインスタンス、つまり**実行**されているものを指します。 -**コンテナ**が起動され実行されるとき(**コンテナイメージ**から起動されるとき)、ファイルや環境変数などが作成されたり変更されたりする可能性があります。 - -これらの変更はそのコンテナ内にのみ存在しますが、基盤となるコンテナ・イメージには残りません(ディスクに保存されません)。 +**コンテナ**が起動され実行されるとき(**コンテナイメージ**から起動されるとき)、ファイルや環境変数などが作成されたり変更されたりする可能性があります。これらの変更はそのコンテナ内にのみ存在しますが、基盤となるコンテナ・イメージには残りません(ディスクに保存されません)。 コンテナイメージは **プログラム** ファイルやその内容、例えば `python` と `main.py` ファイルに匹敵します。 -そして、**コンテナ**自体は(**コンテナイメージ**とは対照的に)イメージをもとにした実際の実行中のインスタンスであり、**プロセス**に匹敵します。 +そして、**コンテナ**自体は(**コンテナイメージ**とは対照的に)イメージをもとにした実際の実行中のインスタンスであり、**プロセス**に匹敵します。実際、コンテナが実行されているのは、**プロセスが実行されている**ときだけです(通常は単一のプロセスだけです)。 コンテナ内で実行中のプロセスがない場合、コンテナは停止します。 -実際、コンテナが実行されているのは、**プロセスが実行されている**ときだけです(通常は単一のプロセスだけです)。 コンテナ内で実行中のプロセスがない場合、コンテナは停止します。 - -## コンテナ・イメージ +## コンテナ・イメージ { #container-images } Dockerは、**コンテナ・イメージ**と**コンテナ**を作成・管理するための主要なツールの1つです。 -そして、DockerにはDockerイメージ(コンテナ)を共有するDocker Hubというものがあります。 - -Docker Hubは 多くのツールや環境、データベース、アプリケーションに対応している予め作成された**公式のコンテナ・イメージ**をパブリックに提供しています。 +そして、多くのツールや環境、データベース、アプリケーションに対応している予め作成された**公式のコンテナ・イメージ**をパブリックに提供しているDocker Hubというものがあります。 例えば、公式イメージの1つにPython Imageがあります。 @@ -88,7 +79,7 @@ Docker Hubは 多くのツールや環境、データベース、アプリケー すべてのコンテナ管理システム(DockerやKubernetesなど)には、こうしたネットワーキング機能が統合されています。 -## コンテナとプロセス +## コンテナとプロセス { #containers-and-processes } 通常、**コンテナ・イメージ**はそのメタデータに**コンテナ**の起動時に実行されるデフォルトのプログラムまたはコマンドと、そのプログラムに渡されるパラメータを含みます。コマンドラインでの操作とよく似ています。 @@ -100,7 +91,7 @@ Docker Hubは 多くのツールや環境、データベース、アプリケー しかし、**少なくとも1つの実行中のプロセス**がなければ、実行中のコンテナを持つことはできないです。メイン・プロセスが停止すれば、コンテナも停止します。 -## Build a Docker Image for FastAPI +## FastAPI用のDockerイメージをビルドする { #build-a-docker-image-for-fastapi } ということで、何か作りましょう!🚀 @@ -112,7 +103,7 @@ FastAPI用の**Dockerイメージ**を、**公式Python**イメージに基づ * **Raspberry Pi**で実行する場合 * コンテナ・イメージを実行してくれるクラウド・サービスなどを利用する場合 -### パッケージ要件(package requirements) +### パッケージ要件 { #package-requirements } アプリケーションの**パッケージ要件**は通常、何らかのファイルに記述されているはずです。 @@ -125,9 +116,8 @@ FastAPI用の**Dockerイメージ**を、**公式Python**イメージに基づ 例えば、`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` を使ってこれらのパッケージの依存関係をインストールします: @@ -137,28 +127,24 @@ uvicorn>=0.15.0,<0.16.0 ```console $ pip install -r requirements.txt ---> 100% -Successfully installed fastapi pydantic uvicorn +Successfully installed fastapi pydantic ```
-/// info +/// info | 情報 パッケージの依存関係を定義しインストールするためのフォーマットやツールは他にもあります。 -Poetryを使った例は、後述するセクションでご紹介します。👇 - /// -### **FastAPI**コードを作成する +### **FastAPI**コードを作成する { #create-the-fastapi-code } -* `app` ディレクトリを作成し、その中に入ります -* 空のファイル `__init__.py` を作成します -* `main.py` ファイルを作成します: +* `app` ディレクトリを作成し、その中に入ります。 +* 空のファイル `__init__.py` を作成します。 +* 次の内容で `main.py` ファイルを作成します: ```Python -from typing import Union - from fastapi import FastAPI app = FastAPI() @@ -170,32 +156,32 @@ 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: 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. 公式のPythonベースイメージから始めます @@ -211,9 +197,10 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] このファイルは**頻繁には変更されない**ので、Dockerはこのステップではそれを検知し**キャッシュ**を使用し、次のステップでもキャッシュを有効にします。 4. 要件ファイルにあるパッケージの依存関係をインストールします + `--no-cache-dir` オプションはダウンロードしたパッケージをローカルに保存しないように `pip` に指示します。これは、同じパッケージをインストールするために `pip` を再度実行する場合にのみ有効ですが、コンテナで作業する場合はそうではないです。 - /// note + /// note | 備考 `--no-cache-dir`は`pip`に関連しているだけで、Dockerやコンテナとは何の関係もないです。 @@ -225,26 +212,56 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] このステップでキャッシュを使用すると、開発中にイメージを何度もビルドする際に、**毎回**すべての依存関係を**ダウンロードしてインストールする**代わりに多くの**時間**を**節約**できます。 -5. ./app` ディレクトリを `/code` ディレクトリの中にコピーする。 +5. `./app` ディレクトリを `/code` ディレクトリの中にコピーする。 これには**最も頻繁に変更される**すべてのコードが含まれているため、Dockerの**キャッシュ**は**これ以降のステップ**に簡単に使用されることはありません。 そのため、コンテナイメージのビルド時間を最適化するために、`Dockerfile`の **最後** にこれを置くことが重要です。 -6. `uvicorn`サーバーを実行するための**コマンド**を設定します +6. 内部でUvicornを使用する `fastapi run` を使うための**コマンド**を設定します `CMD` は文字列のリストを取り、それぞれの文字列はスペースで区切られたコマンドラインに入力するものです。 このコマンドは **現在の作業ディレクトリ**から実行され、上記の `WORKDIR /code` にて設定した `/code` ディレクトリと同じです。 - そのためプログラムは `/code` で開始しその中にあなたのコードがある `./app` ディレクトリがあるので、**Uvicorn** は `app.main` から `app` を参照し、**インポート** することができます。 +/// tip | 豆知識 -/// tip - -コード内の"+"の吹き出しをクリックして、各行が何をするのかをレビューしてください。👆 +コード内の各番号バブルをクリックして、各行が何をするのかをレビューしてください。👆 /// +/// warning | 注意 + +以下で説明する通り、`CMD` 命令は**常に** **exec形式**を使用してください。 + +/// + +#### `CMD` を使う - Exec形式 { #use-cmd-exec-form } + +Docker命令 `CMD` は2つの形式で書けます: + +✅ **Exec** 形式: + +```Dockerfile +# ✅ Do this +CMD ["fastapi", "run", "app/main.py", "--port", "80"] +``` + +⛔️ **Shell** 形式: + +```Dockerfile +# ⛔️ Don't do this +CMD fastapi run app/main.py --port 80 +``` + +FastAPIが正常にシャットダウンでき、[lifespan events](../advanced/events.md){.internal-link target=_blank}がトリガーされるように、常に **exec** 形式を使用してください。 + +詳しくは、shell形式とexec形式に関するDockerドキュメントをご覧ください。 + +これは `docker compose` を使用する場合にかなり目立つことがあります。より技術的な詳細は、このDocker ComposeのFAQセクションをご覧ください:Why do my services take 10 seconds to recreate or stop?。 + +#### ディレクトリ構造 { #directory-structure } + これで、次のようなディレクトリ構造になるはずです: ``` @@ -256,17 +273,15 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] └── requirements.txt ``` -#### TLS Termination Proxyの裏側 +#### TLS Termination Proxyの裏側 { #behind-a-tls-termination-proxy } -Nginx や Traefik のような TLS Termination Proxy (ロードバランサ) の後ろでコンテナを動かしている場合は、`--proxy-headers`オプションを追加します。 - -このオプションは、Uvicornにプロキシ経由でHTTPSで動作しているアプリケーションに対して、送信されるヘッダを信頼するよう指示します。 +Nginx や Traefik のような TLS Termination Proxy (ロードバランサ) の後ろでコンテナを動かしている場合は、`--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キャッシュ +#### Dockerキャッシュ { #docker-cache } この`Dockerfile`には重要なトリックがあり、まず**依存関係だけのファイル**をコピーします。その理由を説明します。 @@ -300,11 +315,11 @@ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt COPY ./app /code/app ``` -### Dockerイメージをビルドする +### Dockerイメージをビルドする { #build-the-docker-image } すべてのファイルが揃ったので、コンテナ・イメージをビルドしましょう。 -* プロジェクトディレクトリに移動します(`Dockerfile`がある場所で、`app`ディレクトリがあります) +* プロジェクトディレクトリに移動します(`Dockerfile`がある場所で、`app`ディレクトリがあります)。 * FastAPI イメージをビルドします:
@@ -317,7 +332,7 @@ $ docker build -t myimage .
-/// tip +/// tip | 豆知識 末尾の `.` に注目してほしいです。これは `./` と同じ意味です。 これはDockerにコンテナイメージのビルドに使用するディレクトリを指示します。 @@ -325,7 +340,7 @@ $ docker build -t myimage . /// -### Dockerコンテナの起動する +### Dockerコンテナの起動する { #start-the-docker-container } * イメージに基づいてコンテナを実行します: @@ -337,7 +352,7 @@ $ docker run -d --name mycontainer -p 80:80 myimage
-## 確認する +## 確認する { #check-it } Dockerコンテナのhttp://192.168.99.100/items/5?q=somequeryhttp://127.0.0.1/items/5?q=somequery (またはそれに相当するDockerホストを使用したもの)といったURLで確認できるはずです。 @@ -347,7 +362,7 @@ Dockerコンテナのhttp://192.168.99.100/docshttp://127.0.0.1/docs (またはそれに相当するDockerホストを使用したもの) @@ -355,7 +370,7 @@ Dockerコンテナのhttp://192.168.99.100/redochttp://127.0.0.1/redoc (またはそれに相当するDockerホストを使用したもの)にもアクセスできます。 @@ -363,9 +378,10 @@ DockerコンテナのTraefikのように、**HTTPS**と**証明書**の**自動**取得を扱う別のコンテナである可能性もあります。 -/// tip +/// tip | 豆知識 TraefikはDockerやKubernetesなどと統合されているので、コンテナ用のHTTPSの設定や構成はとても簡単です。 @@ -428,7 +444,7 @@ TraefikはDockerやKubernetesなどと統合されているので、コンテナ あるいは、(コンテナ内でアプリケーションを実行しながら)クラウド・プロバイダーがサービスの1つとしてHTTPSを処理することもできます。 -## 起動時および再起動時の実行 +## 起動時および再起動時の実行 { #running-on-startup-and-restarts } 通常、コンテナの**起動と実行**を担当する別のツールがあります。 @@ -438,21 +454,21 @@ TraefikはDockerやKubernetesなどと統合されているので、コンテナ コンテナを使わなければ、アプリケーションを起動時や再起動時に実行させるのは面倒で難しいかもしれません。しかし、**コンテナ**で作業する場合、ほとんどのケースでその機能はデフォルトで含まれています。✨ -## レプリケーション - プロセス数 +## レプリケーション - プロセス数 { #replication-number-of-processes } -**Kubernetes** や Docker Swarm モード、Nomad、あるいは複数のマシン上で分散コンテナを管理するための同様の複雑なシステムを使ってマシンのクラスターを構成している場合、 各コンテナで(Workerを持つGunicornのような)**プロセスマネージャ**を使用する代わりに、**クラスター・レベル**で**レプリケーション**を処理したいと思うでしょう。 +**Kubernetes** や Docker Swarm モード、Nomad、あるいは複数のマシン上で分散コンテナを管理するための同様の複雑なシステムを使ってマシンのclusterを構成している場合、 各コンテナで(Workerを持つUvicornのような)**プロセスマネージャ**を使用する代わりに、**クラスター・レベル**で**レプリケーション**を処理したいと思うでしょう。 Kubernetesのような分散コンテナ管理システムの1つは通常、入ってくるリクエストの**ロードバランシング**をサポートしながら、**コンテナのレプリケーション**を処理する統合された方法を持っています。このことはすべて**クラスタレベル**にてです。 -そのような場合、UvicornワーカーでGunicornのようなものを実行するのではなく、[上記の説明](#dockerfile)のように**Dockerイメージをゼロから**ビルドし、依存関係をインストールして、**単一のUvicornプロセス**を実行したいでしょう。 +そのような場合、[上記の説明](#dockerfile)のように**Dockerイメージをゼロから**ビルドし、依存関係をインストールして、**単一のUvicornプロセス**を実行したいでしょう。複数のUvicornワーカーを使う代わりにです。 -### ロードバランサー +### ロードバランサー { #load-balancer } コンテナを使用する場合、通常はメイン・ポート**でリスニング**しているコンポーネントがあるはずです。それはおそらく、**HTTPS**を処理するための**TLS Termination Proxy**でもある別のコンテナであったり、同様のツールであったりするでしょう。 このコンポーネントはリクエストの **負荷** を受け、 (うまくいけば) その負荷を**バランスよく** ワーカーに分配するので、一般に **ロードバランサ** とも呼ばれます。 -/// tip +/// tip | 豆知識 HTTPSに使われるものと同じ**TLS Termination Proxy**コンポーネントは、おそらく**ロードバランサー**にもなるでしょう。 @@ -460,7 +476,7 @@ HTTPSに使われるものと同じ**TLS Termination Proxy**コンポーネン そしてコンテナで作業する場合、コンテナの起動と管理に使用する同じシステムには、**ロードバランサー**(**TLS Termination Proxy**の可能性もある)から**ネットワーク通信**(HTTPリクエストなど)をアプリのあるコンテナ(複数可)に送信するための内部ツールが既にあるはずです。 -### 1つのロードバランサー - 複数のワーカーコンテナー +### 1つのロードバランサー - 複数のワーカーコンテナー { #one-load-balancer-multiple-worker-containers } **Kubernetes**や同様の分散コンテナ管理システムで作業する場合、その内部のネットワーキングのメカニズムを使用することで、メインの**ポート**でリッスンしている単一の**ロードバランサー**が、アプリを実行している可能性のある**複数のコンテナ**に通信(リクエスト)を送信できるようになります。 @@ -470,56 +486,61 @@ HTTPSに使われるものと同じ**TLS Termination Proxy**コンポーネン そして通常、この**ロードバランサー**は、クラスタ内の*他の*アプリケーション(例えば、異なるドメインや異なるURLパスのプレフィックスの配下)へのリクエストを処理することができ、その通信をクラスタ内で実行されている*他の*アプリケーションのための適切なコンテナに送信します。 -### 1コンテナにつき1プロセス +### 1コンテナにつき1プロセス { #one-process-per-container } この種のシナリオでは、すでにクラスタ・レベルでレプリケーションを処理しているため、おそらくコンテナごとに**単一の(Uvicorn)プロセス**を持ちたいでしょう。 -この場合、Uvicornワーカーを持つGunicornのようなプロセスマネージャーや、Uvicornワーカーを使うUvicornは**避けたい**でしょう。**コンテナごとにUvicornのプロセスは1つだけ**にしたいでしょう(おそらく複数のコンテナが必要でしょう)。 +この場合、例えばコマンドラインオプションの `--workers` で、コンテナ内に複数のワーカーを持つことは**避けたい**でしょう。**コンテナごとにUvicornのプロセスは1つだけ**にしたいでしょう(おそらく複数のコンテナが必要でしょう)。 -(GunicornやUvicornがUvicornワーカーを管理するように)コンテナ内に別のプロセスマネージャーを持つことは、クラスターシステムですでに対処しているであろう**不要な複雑さ**を追加するだけです。 +(複数のワーカーの場合のように)コンテナ内に別のプロセスマネージャーを持つことは、クラスターシステムですでに対処しているであろう**不要な複雑さ**を追加するだけです。 -### Containers with Multiple Processes and Special Cases +### 複数プロセスのコンテナと特殊なケース { #containers-with-multiple-processes-and-special-cases } -もちろん、**特殊なケース**として、**Gunicornプロセスマネージャ**を持つ**コンテナ**内で複数の**Uvicornワーカープロセス**を起動させたい場合があります。 +もちろん、**特殊なケース**として、**コンテナ**内で複数の**Uvicornワーカープロセス**を起動させたい場合があります。 -このような場合、**公式のDockerイメージ**を使用することができます。このイメージには、複数の**Uvicornワーカープロセス**を実行するプロセスマネージャとして**Gunicorn**が含まれており、現在のCPUコアに基づいてワーカーの数を自動的に調整するためのデフォルト設定がいくつか含まれています。詳しくは後述の[Gunicornによる公式Dockerイメージ - Uvicorn](#gunicorndocker-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"] +``` + +1. ここでは `--workers` コマンドラインオプションを使って、ワーカー数を4に設定しています。 以下は、それが理にかなっている場合の例です: -#### シンプルなアプリケーション +#### シンプルなアプリ { #a-simple-app } -アプリケーションを**シンプル**な形で実行する場合、プロセス数の細かい調整が必要ない場合、自動化されたデフォルトを使用するだけで、コンテナ内にプロセスマネージャが必要かもしれません。例えば、公式Dockerイメージでシンプルな設定が可能です。 +アプリケーションが、クラスタではなく**単一サーバ**で実行できるほど**シンプル**である場合、コンテナ内にプロセスマネージャが欲しくなることがあります。 -#### Docker Compose +#### Docker Compose { #docker-compose } -Docker Composeで**シングルサーバ**(クラスタではない)にデプロイすることもできますので、共有ネットワークと**ロードバランシング**を維持しながら(Docker Composeで)コンテナのレプリケーションを管理する簡単な方法はないでしょう。 +Docker Composeで**単一サーバ**(クラスタではない)にデプロイすることもできますので、共有ネットワークと**ロードバランシング**を維持しながら(Docker Composeで)コンテナのレプリケーションを管理する簡単な方法はないでしょう。 その場合、**単一のコンテナ**で、**プロセスマネージャ**が内部で**複数のワーカープロセス**を起動するようにします。 -#### Prometheusとその他の理由 - -また、**1つのコンテナ**に**1つのプロセス**を持たせるのではなく、**1つのコンテナ**に**複数のプロセス**を持たせる方が簡単だという**他の理由**もあるでしょう。 - -例えば、(セットアップにもよりますが)Prometheusエクスポーターのようなツールを同じコンテナ内に持つことができます。 - -この場合、**複数のコンテナ**があると、デフォルトでは、Prometheusが**メトリクスを**読みに来たとき、すべてのレプリケートされたコンテナの**蓄積されたメトリクス**を取得するのではなく、毎回**単一のコンテナ**(その特定のリクエストを処理したコンテナ)のものを取得することになります。 - -その場合、**複数のプロセス**を持つ**1つのコンテナ**を用意し、同じコンテナ上のローカルツール(例えばPrometheusエクスポーター)がすべての内部プロセスのPrometheusメトリクスを収集し、その1つのコンテナ上でそれらのメトリクスを公開する方がシンプルかもしれません。 - --- -重要なのは、盲目的に従わなければならない普遍のルールはないということです。 - -これらのアイデアは、**あなた自身のユースケース**を評価し、あなたのシステムに最適なアプローチを決定するために使用することができます: +重要なのは、これらのどれも、盲目的に従わなければならない「**絶対的なルール**」ではないということです。これらのアイデアは、**あなた自身のユースケース**を評価し、あなたのシステムに最適なアプローチを決定するために使用できます。次の概念をどう管理するかを確認してください: * セキュリティ - HTTPS * 起動時の実行 * 再起動 -* **レプリケーション(実行中のプロセス数)** +* レプリケーション(実行中のプロセス数) * メモリ * 開始前の事前ステップ -## メモリー +## メモリ { #memory } コンテナごとに**単一のプロセスを実行する**と、それらのコンテナ(レプリケートされている場合は1つ以上)によって消費される多かれ少なかれ明確に定義された、安定し制限された量のメモリを持つことになります。 @@ -531,109 +552,47 @@ Docker Composeで**シングルサーバ**(クラスタではない)にデ しかし、**多くのメモリを使用**している場合(たとえば**機械学習**モデルなど)、どれだけのメモリを消費しているかを確認し、**各マシンで実行するコンテナの数**を調整する必要があります(そしておそらくクラスタにマシンを追加します)。 -**コンテナごとに複数のプロセス**を実行する場合(たとえば公式のDockerイメージで)、起動するプロセスの数が**利用可能なメモリ以上に消費しない**ようにする必要があります。 +**コンテナごとに複数のプロセス**を実行する場合、起動するプロセスの数が**利用可能なメモリ以上に消費しない**ようにする必要があります。 -## 開始前の事前ステップとコンテナ +## 開始前の事前ステップとコンテナ { #previous-steps-before-starting-and-containers } コンテナ(DockerやKubernetesなど)を使っている場合、主に2つのアプローチがあります。 -### 複数のコンテナ +### 複数のコンテナ { #multiple-containers } -複数の**コンテナ**があり、おそらくそれぞれが**単一のプロセス**を実行している場合(**Kubernetes**クラスタなど)、レプリケートされたワーカーコンテナを実行する**前に**、単一のコンテナで**事前のステップ**の作業を行う**別のコンテナ**を持ちたいと思うでしょう。 +複数の**コンテナ**があり、おそらくそれぞれが**単一のプロセス**を実行している場合(例えば、**Kubernetes**クラスタなど)、レプリケートされたワーカーコンテナを実行する**前に**、単一のコンテナで**事前のステップ**の作業を行う**別のコンテナ**を持ちたいと思うでしょう。 -/// info +/// info | 情報 -もしKubernetesを使用している場合, これはおそらくInit コンテナでしょう。 +もしKubernetesを使用している場合, これはおそらくInit Containerでしょう。 /// -ユースケースが事前のステップを**並列で複数回**実行するのに問題がない場合(例:データベースの準備チェック)、メインプロセスを開始する前に、それらのステップを各コンテナに入れることが可能です。 +ユースケースが事前のステップを**並列で複数回**実行するのに問題がない場合(例:データベースマイグレーションを実行するのではなく、データベースの準備ができたかをチェックするだけの場合)、メインプロセスを開始する直前に、それらのステップを各コンテナに入れることも可能です。 -### 単一コンテナ +### 単一コンテナ { #single-container } -単純なセットアップで、**単一のコンテナ**で複数の**ワーカー・プロセス**(または1つのプロセスのみ)を起動する場合、アプリでプロセスを開始する直前に、同じコンテナで事前のステップを実行できます。公式Dockerイメージは、内部的にこれをサポートしています。 +単純なセットアップで、**単一のコンテナ**で複数の**ワーカープロセス**(または1つのプロセスのみ)を起動する場合、アプリでプロセスを開始する直前に、同じコンテナで事前のステップを実行できます。 -## Gunicornによる公式Dockerイメージ - Uvicorn +### ベースDockerイメージ { #base-docker-image } -前の章で詳しく説明したように、Uvicornワーカーで動作するGunicornを含む公式のDockerイメージがあります: [Server Workers - Gunicorn と Uvicorn](server-workers.md){.internal-link target=_blank}で詳しく説明しています。 +以前は、公式のFastAPI Dockerイメージがありました:tiangolo/uvicorn-gunicorn-fastapi。しかし、現在は非推奨です。⛔️ -このイメージは、主に上記で説明した状況で役に立つでしょう: [複数のプロセスと特殊なケースを持つコンテナ(Containers with Multiple Processes and Special Cases)](#containers-with-multiple-processes-and-special-cases) +おそらく、このベースDockerイメージ(またはその他の類似のもの)は**使用しない**方がよいでしょう。 -* tiangolo/uvicorn-gunicorn-fastapi. +すでに**Kubernetes**(または他のもの)を使用していて、複数の**コンテナ**で、クラスタレベルで**レプリケーション**を設定している場合。そのような場合は、上記で説明したように**ゼロから**イメージを構築する方がよいでしょう:[FastAPI用のDockerイメージをビルドする](#build-a-docker-image-for-fastapi)。 -/// warning +また、複数のワーカーが必要な場合は、単純に `--workers` コマンドラインオプションを使用できます。 -このベースイメージや類似のイメージは**必要ない**可能性が高いので、[上記の: FastAPI用のDockerイメージをビルドする(Build a Docker Image for FastAPI)](#build-a-docker-image-for-fastapi)のようにゼロからイメージをビルドする方が良いでしょう。 +/// note | 技術詳細 + +このDockerイメージは、Uvicornが停止したワーカーの管理と再起動をサポートしていなかった頃に作成されたため、Uvicornと一緒にGunicornを使う必要がありました。これは、GunicornにUvicornワーカープロセスの管理と再起動をさせるだけのために、かなりの複雑さを追加していました。 + +しかし現在は、Uvicorn(および `fastapi` コマンド)が `--workers` をサポートしているため、自分でビルドする代わりにベースDockerイメージを使う理由はありません(コード量もだいたい同じです 😅)。 /// -このイメージには、利用可能なCPUコアに基づいて**ワーカー・プロセスの数**を設定する**オートチューニング**メカニズムが含まれています。 - -これは**賢明なデフォルト**を備えていますが、**環境変数**や設定ファイルを使ってすべての設定を変更したり更新したりすることができます。 - -また、スクリプトで**開始前の事前ステップ**を実行することもサポートしている。 - -/// tip - -すべての設定とオプションを見るには、Dockerイメージのページをご覧ください: tiangolo/uvicorn-gunicorn-fastapi - -/// - -### 公式Dockerイメージのプロセス数 - -このイメージの**プロセス数**は、利用可能な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 -``` - -### いつ使うのか - -おそらく、**Kubernetes**(または他のもの)を使用していて、すでにクラスタレベルで複数の**コンテナ**で**レプリケーション**を設定している場合は、この公式ベースイメージ(または他の類似のもの)は**使用すべきではありません**。 - -そのような場合は、上記のように**ゼロから**イメージを構築する方がよいでしょう: [FastAPI用のDockerイメージをビルドする(Build a Docker Image for FastAPI)](#build-a-docker-image-for-fastapi) を参照してください。 - -このイメージは、主に上記の[複数のプロセスと特殊なケースを持つコンテナ(Containers with Multiple Processes and Special Cases)](#containers-with-multiple-processes-and-special-cases)で説明したような特殊なケースで役に立ちます。 - -例えば、アプリケーションが**シンプル**で、CPUに応じたデフォルトのプロセス数を設定すればうまくいく場合や、クラスタレベルでレプリケーションを手動で設定する手間を省きたい場合、アプリで複数のコンテナを実行しない場合などです。 - -または、**Docker Compose**でデプロイし、単一のサーバで実行している場合などです。 - -## コンテナ・イメージのデプロイ +## コンテナ・イメージのデプロイ { #deploy-the-container-image } コンテナ(Docker)イメージを手に入れた後、それをデプロイするにはいくつかの方法があります。 @@ -645,104 +604,21 @@ COPY ./app /app/app * Nomadのような別のツール * コンテナ・イメージをデプロイするクラウド・サービス -## Poetryを利用したDockerイメージ +## `uv` を使ったDockerイメージ { #docker-image-with-uv } -もしプロジェクトの依存関係を管理するためにPoetryを利用する場合、マルチステージビルドを使うと良いでしょう。 +uv を使ってプロジェクトのインストールと管理をしている場合は、uv Docker guideに従ってください。 -```{ .dockerfile .annotate } -# (1) -FROM python:3.9 as requirements-stage - -# (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. このDockerステージにPoetryをインストールします - -4. pyproject.toml`と`poetry.lock`ファイルを`/tmp` ディレクトリにコピーします - - `./poetry.lock*`(末尾に`*`)を使用するため、そのファイルがまだ利用できない場合でもクラッシュすることはないです。 -5. requirements.txt`ファイルを生成します - -6. これは最後のステージであり、ここにあるものはすべて最終的なコンテナ・イメージに保存されます -7. 現在の作業ディレクトリを `/code` に設定します -8. `requirements.txt`ファイルを `/code` ディレクトリにコピーします - このファイルは前のDockerステージにしか存在しないため、`--from-requirements-stage`を使ってコピーします。 -9. 生成された `requirements.txt` ファイルにあるパッケージの依存関係をインストールします -10. app` ディレクトリを `/code` ディレクトリにコピーします -11. uvicorn` コマンドを実行して、`app.main` からインポートした `app` オブジェクトを使用するように指示します -/// tip - -"+"の吹き出しをクリックすると、それぞれの行が何をするのかを見ることができます - -/// - -**Dockerステージ**は`Dockerfile`の一部で、**一時的なコンテナイメージ**として動作します。 - -最初のステージは **Poetryのインストール**と Poetry の `pyproject.toml` ファイルからプロジェクトの依存関係を含む**`requirements.txt`を生成**するためだけに使用されます。 - -この `requirements.txt` ファイルは後半の **次のステージ**で `pip` と共に使用されます。 - -最終的なコンテナイメージでは、**最終ステージ**のみが保存されます。前のステージは破棄されます。 - -Poetryを使用する場合、**Dockerマルチステージビルド**を使用することは理にかなっています。 - -なぜなら、最終的なコンテナイメージにPoetryとその依存関係がインストールされている必要はなく、**必要なのは**プロジェクトの依存関係をインストールするために生成された `requirements.txt` ファイルだけだからです。 - -そして次の(そして最終的な)ステージでは、前述とほぼ同じ方法でイメージをビルドします。 - -### TLS Termination Proxyの裏側 - Poetry - -繰り返しになりますが、NginxやTraefikのようなTLS Termination Proxy(ロードバランサー)の後ろでコンテナを動かしている場合は、`--proxy-headers`オプションをコマンドに追加します: - -```Dockerfile -CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] -``` - -## まとめ +## まとめ { #recap } コンテナ・システム(例えば**Docker**や**Kubernetes**など)を使えば、すべての**デプロイメントのコンセプト**を扱うのがかなり簡単になります: -* セキュリティ - HTTPS +* HTTPS * 起動時の実行 * 再起動 -* **レプリケーション(実行中のプロセス数)** +* レプリケーション(実行中のプロセス数) * メモリ * 開始前の事前ステップ ほとんどの場合、ベースとなるイメージは使用せず、公式のPython Dockerイメージをベースにした**コンテナイメージをゼロからビルド**します。 -`Dockerfile`と**Dockerキャッシュ**内の命令の**順番**に注意することで、**ビルド時間を最小化**することができ、生産性を最大化することができます(そして退屈を避けることができます)。😎 - -特別なケースでは、FastAPI用の公式Dockerイメージを使いたいかもしれません。🤓 +`Dockerfile`と**Dockerキャッシュ**内の命令の**順番**に注意することで、**ビルド時間を最小化**し、生産性を最大化できます(そして退屈を避けることができます)。😎 diff --git a/docs/ja/docs/deployment/https.md b/docs/ja/docs/deployment/https.md index 7b0f567aa5..d5a6daf0c0 100644 --- a/docs/ja/docs/deployment/https.md +++ b/docs/ja/docs/deployment/https.md @@ -1,10 +1,10 @@ -# HTTPS について +# HTTPS について { #about-https } HTTPSは単に「有効」か「無効」かで決まるものだと思いがちです。 しかし、それよりもはるかに複雑です。 -/// tip +/// tip | 豆知識 もし急いでいたり、HTTPSの仕組みについて気にしないのであれば、次のセクションに進み、さまざまなテクニックを使ってすべてをセットアップするステップ・バイ・ステップの手順をご覧ください。 @@ -22,25 +22,19 @@ HTTPSは単に「有効」か「無効」かで決まるものだと思いがち * 接続の暗号化は**TCPレベル**で行われます。 * それは**HTTPの1つ下**のレイヤーです。 * つまり、**証明書と暗号化**の処理は、**HTTPの前**に行われます。 -* **TCPは "ドメイン "について知りません**。IPアドレスについてのみ知っています。 +* **TCPは「ドメイン」について知りません**。IPアドレスについてのみ知っています。 * 要求された**特定のドメイン**に関する情報は、**HTTPデータ**に入ります。 * **HTTPS証明書**は、**特定のドメイン**を「証明」しますが、プロトコルと暗号化はTCPレベルで行われ、どのドメインが扱われているかを**知る前**に行われます。 * **デフォルトでは**、**IPアドレスごとに1つのHTTPS証明書**しか持てないことになります。 * これは、サーバーの規模やアプリケーションの規模に寄りません。 * しかし、これには**解決策**があります。 -* **TLS**プロトコル(HTTPの前に、TCPレベルで暗号化を処理するもの)には、**SNI**と呼ばれる**拡張**があります。 +* **TLS**プロトコル(HTTPの前に、TCPレベルで暗号化を処理するもの)には、**SNI**と呼ばれる**拡張**があります。 * このSNI拡張機能により、1つのサーバー(**単一のIPアドレス**を持つ)が**複数のHTTPS証明書**を持ち、**複数のHTTPSドメイン/アプリケーション**にサービスを提供できるようになります。 * これが機能するためには、**パブリックIPアドレス**でリッスンしている、サーバー上で動作している**単一の**コンポーネント(プログラム)が、サーバー内の**すべてのHTTPS証明書**を持っている必要があります。 - * セキュアな接続を取得した**後**でも、通信プロトコルは**HTTPのまま**です。 * コンテンツは**HTTPプロトコル**で送信されているにもかかわらず、**暗号化**されています。 - -サーバー(マシン、ホストなど)上で**1つのプログラム/HTTPサーバー**を実行させ、**HTTPSに関する全てのこと**を管理するのが一般的です。 - -**暗号化された HTTPS リクエスト** を受信し、**復号化された HTTP リクエスト** を同じサーバーで実行されている実際の HTTP アプリケーション(この場合は **FastAPI** アプリケーション)に送信し、アプリケーションから **HTTP レスポンス** を受け取り、適切な **HTTPS 証明書** を使用して **暗号化** し、そして**HTTPS** を使用してクライアントに送り返します。 - -このサーバーはしばしば **TLS Termination Proxy**と呼ばれます。 +サーバー(マシン、ホストなど)上で**1つのプログラム/HTTPサーバー**を実行させ、**HTTPSに関する全てのこと**を管理するのが一般的です。**暗号化された HTTPS リクエスト** を受信し、**復号化された HTTP リクエスト** を同じサーバーで実行されている実際の HTTP アプリケーション(この場合は **FastAPI** アプリケーション)に送信し、アプリケーションから **HTTP レスポンス** を受け取り、適切な **HTTPS 証明書** を使用して **暗号化** し、そして**HTTPS** を使用してクライアントに送り返します。このサーバーはしばしば **TLS Termination Proxy**と呼ばれます。 TLS Termination Proxyとして使えるオプションには、以下のようなものがあります: @@ -50,7 +44,7 @@ TLS Termination Proxyとして使えるオプションには、以下のよう * HAProxy -## Let's Encrypt +## Let's Encrypt { #lets-encrypt } Let's Encrypt以前は、これらの**HTTPS証明書**は信頼できる第三者によって販売されていました。 @@ -64,27 +58,27 @@ Let's Encrypt以前は、これらの**HTTPS証明書**は信頼できる第三 このアイデアは、これらの証明書の取得と更新を自動化することで、**安全なHTTPSを、無料で、永遠に**利用できるようにすることです。 -## 開発者のための HTTPS +## 開発者のための HTTPS { #https-for-developers } ここでは、HTTPS APIがどのように見えるかの例を、主に開発者にとって重要なアイデアに注意を払いながら、ステップ・バイ・ステップで説明します。 -### ドメイン名 +### ドメイン名 { #domain-name } ステップの初めは、**ドメイン名**を**取得すること**から始まるでしょう。その後、DNSサーバー(おそらく同じクラウドプロバイダー)に設定します。 -おそらくクラウドサーバー(仮想マシン)かそれに類するものを手に入れ、固定の **パブリックIPアドレス**を持つことになるでしょう。 +おそらくクラウドサーバー(仮想マシン)かそれに類するものを手に入れ、fixed **パブリックIPアドレス**を持つことになるでしょう。 -DNSサーバーでは、**取得したドメイン**をあなたのサーバーのパプリック**IPアドレス**に向けるレコード(「`Aレコード`」)を設定します。 +DNSサーバーでは、**取得したドメイン**をあなたのサーバーのパプリック**IPアドレス**に向けるレコード(「`A record`」)を設定します。 これはおそらく、最初の1回だけあり、すべてをセットアップするときに行うでしょう。 -/// tip +/// tip | 豆知識 ドメイン名の話はHTTPSに関する話のはるか前にありますが、すべてがドメインとIPアドレスに依存するため、ここで言及する価値があります。 /// -### DNS +### DNS { #dns } では、実際のHTTPSの部分に注目してみよう。 @@ -94,7 +88,7 @@ DNSサーバーは、ブラウザに特定の**IPアドレス**を使用する -### TLS Handshake の開始 +### TLS Handshake の開始 { #tls-handshake-start } ブラウザはIPアドレスと**ポート443**(HTTPSポート)で通信します。 @@ -104,7 +98,7 @@ DNSサーバーは、ブラウザに特定の**IPアドレス**を使用する TLS接続を確立するためのクライアントとサーバー間のこのやりとりは、**TLSハンドシェイク**と呼ばれます。 -### SNI拡張機能付きのTLS +### SNI拡張機能付きのTLS { #tls-with-sni-extension } サーバー内の**1つのプロセス**だけが、特定 の**IPアドレス**の特定の**ポート** で待ち受けることができます。 @@ -112,7 +106,7 @@ TLS接続を確立するためのクライアントとサーバー間のこの TLS(HTTPS)はデフォルトで`443`という特定のポートを使用する。つまり、これが必要なポートです。 -このポートをリッスンできるのは1つのプロセスだけなので、これを実行するプロセスは**TLS Termination Proxy**となります。 +このポートをリクエストできるのは1つのプロセスだけなので、これを実行するプロセスは**TLS Termination Proxy**となります。 TLS Termination Proxyは、1つ以上の**TLS証明書**(HTTPS証明書)にアクセスできます。 @@ -130,13 +124,13 @@ TLS Termination Proxyは、1つ以上の**TLS証明書**(HTTPS証明書)に これが**HTTPS**であり、純粋な(暗号化されていない)TCP接続ではなく、**セキュアなTLS接続**の中に**HTTP**があるだけです。 -/// tip +/// tip | 豆知識 通信の暗号化は、HTTPレベルではなく、**TCPレベル**で行われることに注意してください。 /// -### HTTPS リクエスト +### HTTPS リクエスト { #https-request } これでクライアントとサーバー(具体的にはブラウザとTLS Termination Proxy)は**暗号化されたTCP接続**を持つことになり、**HTTP通信**を開始することができます。 @@ -144,19 +138,19 @@ TLS Termination Proxyは、1つ以上の**TLS証明書**(HTTPS証明書)に -### リクエストの復号化 +### リクエストの復号化 { #decrypt-the-request } TLS Termination Proxy は、合意が取れている暗号化を使用して、**リクエストを復号化**し、**プレーン (復号化された) HTTP リクエスト** をアプリケーションを実行しているプロセス (例えば、FastAPI アプリケーションを実行している Uvicorn を持つプロセス) に送信します。 -### HTTP レスポンス +### HTTP レスポンス { #http-response } アプリケーションはリクエストを処理し、**プレーン(暗号化されていない)HTTPレスポンス** をTLS Termination Proxyに送信します。 -### HTTPS レスポンス +### HTTPS レスポンス { #https-response } TLS Termination Proxyは次に、事前に合意が取れている暗号(`someapp.example.com`の証明書から始まる)を使って**レスポンスを暗号化し**、ブラウザに送り返す。 @@ -166,7 +160,7 @@ TLS Termination Proxyは次に、事前に合意が取れている暗号(`someap クライアント(ブラウザ)は、レスポンスが正しいサーバーから来たことを知ることができます。 なぜなら、そのサーバーは、以前に**HTTPS証明書**を使って合意した暗号を使っているからです。 -### 複数のアプリケーション +### 複数のアプリケーション { #multiple-applications } 同じサーバー(または複数のサーバー)に、例えば他のAPIプログラムやデータベースなど、**複数のアプリケーション**が存在する可能性があります。 @@ -176,7 +170,7 @@ TLS Termination Proxyは次に、事前に合意が取れている暗号(`someap そうすれば、TLS Termination Proxy は、**複数のドメイン**や複数のアプリケーションのHTTPSと証明書を処理し、それぞれのケースで適切なアプリケーションにリクエストを送信することができます。 -### 証明書の更新 +### 証明書の更新 { #certificate-renewal } 将来のある時点で、各証明書は(取得後約3ヶ月で)**失効**します。 @@ -200,10 +194,42 @@ TLS Termination Proxyは次に、事前に合意が取れている暗号(`someap アプリを提供しながらこのような更新処理を行うことは、アプリケーション・サーバー(Uvicornなど)でTLS証明書を直接使用するのではなく、TLS Termination Proxyを使用して**HTTPSを処理する別のシステム**を用意したくなる主な理由の1つです。 -## まとめ +## プロキシ転送ヘッダー { #proxy-forwarded-headers } + +プロキシを使ってHTTPSを処理する場合、**アプリケーションサーバー**(たとえばFastAPI CLI経由のUvicorn)はHTTPS処理について何も知らず、**TLS Termination Proxy**とはプレーンなHTTPで通信します。 + +この**プロキシ**は通常、リクエストを**アプリケーションサーバー**に転送する前に、その場でいくつかのHTTPヘッダーを設定し、リクエストがプロキシによって**転送**されていることをアプリケーションサーバーに知らせます。 + +/// note | 技術詳細 + +プロキシヘッダーは次のとおりです: + +* X-Forwarded-For +* X-Forwarded-Proto +* X-Forwarded-Host + +/// + +それでも、**アプリケーションサーバー**は信頼できる**プロキシ**の背後にあることを知らないため、デフォルトではそれらのヘッダーを信頼しません。 + +しかし、**アプリケーションサーバー**が**プロキシ**から送信される*forwarded*ヘッダーを信頼するように設定できます。FastAPI CLIを使用している場合は、*CLI Option* `--forwarded-allow-ips` を使って、どのIPからの*forwarded*ヘッダーを信頼すべきかを指定できます。 + +たとえば、**アプリケーションサーバー**が信頼できる**プロキシ**からの通信のみを受け取っている場合、`--forwarded-allow-ips="*"` に設定して、受信するすべてのIPを信頼するようにできます。受け取るリクエストは、**プロキシ**が使用するIPからのものだけになるためです。 + +こうすることで、アプリケーションは、HTTPSを使用しているかどうか、ドメインなど、自身のパブリックURLが何であるかを把握できるようになります。 + +これは、たとえばリダイレクトを適切に処理するのに便利です。 + +/// tip | 豆知識 + +これについては、[Behind a Proxy - Enable Proxy Forwarded Headers](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank} のドキュメントで詳しく学べます。 + +/// + +## まとめ { #recap } **HTTPS**を持つことは非常に重要であり、ほとんどの場合、かなり**クリティカル**です。開発者として HTTPS に関わる労力のほとんどは、これらの**概念とその仕組みを理解する**ことです。 しかし、ひとたび**開発者向けHTTPS**の基本的な情報を知れば、簡単な方法ですべてを管理するために、さまざまなツールを組み合わせて設定することができます。 -次の章では、**FastAPI** アプリケーションのために **HTTPS** をセットアップする方法について、いくつかの具体例を紹介します。🔒 +次の章のいくつかでは、**FastAPI** アプリケーションのために **HTTPS** をセットアップする方法について、いくつかの具体例を紹介します。🔒 diff --git a/docs/ja/docs/deployment/index.md b/docs/ja/docs/deployment/index.md index 897956e38f..eba6eae6ea 100644 --- a/docs/ja/docs/deployment/index.md +++ b/docs/ja/docs/deployment/index.md @@ -1,7 +1,23 @@ -# デプロイ +# デプロイ { #deployment } -**FastAPI** 製のアプリケーションは比較的容易にデプロイできます。 +**FastAPI** アプリケーションのデプロイは比較的簡単です。 -ユースケースや使用しているツールによっていくつかの方法に分かれます。 +## デプロイとは { #what-does-deployment-mean } -次のセクションでより詳しくそれらの方法について説明します。 +アプリケーションを**デプロイ**するとは、**ユーザーが利用できるようにする**ために必要な手順を実行することを意味します。 + +**Web API** の場合、通常は **リモートマシン** 上に配置し、優れたパフォーマンス、安定性などを提供する **サーバープログラム** と組み合わせて、**ユーザー** が中断や問題なく効率的にアプリケーションへ**アクセス**できるようにします。 + +これは **開発** 段階とは対照的です。開発では、コードを常に変更し、壊しては直し、開発サーバーを停止したり再起動したりします。 + +## デプロイ戦略 { #deployment-strategies } + +具体的なユースケースや使用するツールによって、いくつかの方法があります。 + +複数のツールを組み合わせて自分で**サーバーをデプロイ**することもできますし、作業の一部を代行してくれる **クラウドサービス** を使うこともできます。ほかにも選択肢があります。 + +たとえば、FastAPI の開発チームである私たちは、クラウドへの FastAPI アプリのデプロイを可能な限り合理化し、FastAPI を使って開発するのと同じ開発者体験を提供するために、**FastAPI Cloud** を構築しました。 + +**FastAPI** アプリケーションをデプロイする際に、おそらく念頭に置くべき主要な概念をいくつか紹介します(ただし、そのほとんどは他の種類の Web アプリケーションにも当てはまります)。 + +次のセクションでは、留意すべき点の詳細や、それを実現するためのいくつかの手法を確認します。 ✨ diff --git a/docs/ja/docs/deployment/server-workers.md b/docs/ja/docs/deployment/server-workers.md index 38ceab0172..933b875d76 100644 --- a/docs/ja/docs/deployment/server-workers.md +++ b/docs/ja/docs/deployment/server-workers.md @@ -1,4 +1,4 @@ -# Server Workers - Gunicorn と Uvicorn +# Server Workers - ワーカー付きUvicorn { #server-workers-uvicorn-with-workers } 前回のデプロイメントのコンセプトを振り返ってみましょう: @@ -9,124 +9,79 @@ * メモリ * 開始前の事前ステップ -ここまでのドキュメントのチュートリアルでは、おそらくUvicornのような**サーバープログラム**を**単一のプロセス**で実行しています。 +ここまでのドキュメントのチュートリアルでは、おそらく `fastapi` コマンドなど(Uvicornを実行するもの)を使って、**単一のプロセス**として動作する**サーバープログラム**を実行してきたはずです。 アプリケーションをデプロイする際には、**複数のコア**を利用し、そしてより多くのリクエストを処理できるようにするために、プロセスの**レプリケーション**を持つことを望むでしょう。 前のチャプターである[デプロイメントのコンセプト](concepts.md){.internal-link target=_blank}にて見てきたように、有効な戦略がいくつかあります。 -ここでは**Gunicorn**が**Uvicornのワーカー・プロセス**を管理する場合の使い方について紹介していきます。 +ここでは、`fastapi` コマンド、または `uvicorn` コマンドを直接使って、**ワーカープロセス**付きの **Uvicorn** を使う方法を紹介します。 -/// info +/// info | 情報 - -DockerやKubernetesなどのコンテナを使用している場合は、次の章で詳しく説明します: [コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank} +DockerやKubernetesなどのコンテナを使用している場合は、次の章で詳しく説明します: [コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}。 -特に**Kubernetes**上で実行する場合は、おそらく**Gunicornを使用せず**、**コンテナごとに単一のUvicornプロセス**を実行することになりますが、それについてはこの章の後半で説明します。 +特に**Kubernetes**上で実行する場合は、おそらくワーカーは使わず、代わりに**コンテナごとに単一のUvicornプロセス**を実行したいはずですが、それについてはその章の後半で説明します。 /// -## GunicornによるUvicornのワーカー・プロセスの管理 +## 複数ワーカー { #multiple-workers } -**Gunicorn**は**WSGI標準**のアプリケーションサーバーです。このことは、GunicornはFlaskやDjangoのようなアプリケーションにサービスを提供できることを意味します。Gunicornそれ自体は**FastAPI**と互換性がないですが、というのもFastAPIは最新の**ASGI 標準**を使用しているためです。 +`--workers` コマンドラインオプションで複数のワーカーを起動できます。 -しかし、Gunicornは**プロセスマネージャー**として動作し、ユーザーが特定の**ワーカー・プロセスクラス**を使用するように指示することができます。するとGunicornはそのクラスを使い1つ以上の**ワーカー・プロセス**を開始します。 +//// tab | `fastapi` -そして**Uvicorn**には**Gunicorn互換のワーカークラス**があります。 - -この組み合わせで、Gunicornは**プロセスマネージャー**として動作し、**ポート**と**IP**をリッスンします。そして、**Uvicornクラス**を実行しているワーカー・プロセスに通信を**転送**します。 - -そして、Gunicorn互換の**Uvicornワーカー**クラスが、FastAPIが使えるように、Gunicornから送られてきたデータをASGI標準に変換する役割を担います。 - -## GunicornとUvicornをインストールする +`fastapi` コマンドを使う場合:
```console -$ pip install "uvicorn[standard]" gunicorn +$ fastapi run --workers 4 main.py ----> 100% + FastAPI Starting production server 🚀 + + Searching for package file structure from directories with + __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with the + following code: + + from main import app + + app Using import string: main:app + + server Server started at http://0.0.0.0:8000 + server Documentation at http://0.0.0.0:8000/docs + + Logs: + + INFO Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to + quit) + INFO Started parent process [27365] + INFO Started server process [27368] + INFO Started server process [27369] + INFO Started server process [27370] + INFO Started server process [27367] + INFO Waiting for application startup. + INFO Waiting for application startup. + INFO Waiting for application startup. + INFO Waiting for application startup. + INFO Application startup complete. + INFO Application startup complete. + INFO Application startup complete. + INFO Application startup complete. ```
-これによりUvicornと(高性能を得るための)標準(`standard`)の追加パッケージとGunicornの両方がインストールされます。 +//// -## UvicornのワーカーとともにGunicornを実行する +//// tab | `uvicorn` -Gunicornを以下のように起動させることができます: - -
- -```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. -``` - -
- -それぞれのオプションの意味を見てみましょう: - -* `main:app`: `main`は"`main`"という名前のPythonモジュール、つまりファイル`main.py`を意味します。そして `app` は **FastAPI** アプリケーションの変数名です。 - * main:app`はPythonの`import`文と同じようなものだと想像できます: - - ```Python - from main import app - ``` - - * つまり、`main:app`のコロンは、`from main import app`のPythonの`import`の部分と同じになります。 - -* `--workers`: 使用するワーカー・プロセスの数で、それぞれがUvicornのワーカーを実行します。 - -* `--worker-class`: ワーカー・プロセスで使用するGunicorn互換のワーカークラスです。 - * ここではGunicornがインポートして使用できるクラスを渡します: - - ```Python - import uvicorn.workers.UvicornWorker - ``` - -* `--bind`: GunicornにリッスンするIPとポートを伝えます。コロン(`:`)でIPとポートを区切ります。 - * Uvicornを直接実行している場合は、`--bind 0.0.0.0:80` (Gunicornのオプション)の代わりに、`--host 0.0.0.0`と `--port 80`を使います。 - -出力では、各プロセスの**PID**(プロセスID)が表示されているのがわかります(単なる数字です)。 - -以下の通りです: - -* Gunicornの**プロセス・マネージャー**はPID `19499`(あなたの場合は違う番号でしょう)で始まります。 -* 次に、`Listening at: http://0.0.0.0:80`を開始します。 -* それから `uvicorn.workers.UvicornWorker` でワーカークラスを使用することを検出します。 -* そして、**4つのワーカー**を起動します。それぞれのワーカーのPIDは、`19511`、`19513`、`19514`、`19515`です。 - -Gunicornはまた、ワーカーの数を維持するために必要であれば、**ダウンしたプロセス**を管理し、**新しいプロセスを**再起動**させます。そのため、上記のリストにある**再起動**の概念に一部役立ちます。 - -しかしながら、必要であればGunicornを**再起動**させ、**起動時に実行**させるなど、外部のコンポーネントを持たせることも必要かもしれません。 - -## Uvicornとワーカー - -Uvicornには複数の**ワーカー・プロセス**を起動し実行するオプションもあります。 - -とはいうものの、今のところUvicornのワーカー・プロセスを扱う機能はGunicornよりも制限されています。そのため、このレベル(Pythonレベル)でプロセスマネージャーを持ちたいのであれば、Gunicornをプロセスマネージャーとして使ってみた方が賢明かもしれないです。 - -どんな場合であれ、以下のように実行します: +`uvicorn` コマンドを直接使いたい場合:
@@ -150,36 +105,35 @@ $ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
-ここで唯一の新しいオプションは `--workers` で、Uvicornに4つのワーカー・プロセスを起動するように指示しています。 +//// -各プロセスの **PID** が表示され、親プロセスの `27365` (これは **プロセスマネージャ**) と、各ワーカー・プロセスの **PID** が表示されます: `27368`、`27369`、`27370`、`27367`になります。 +ここで唯一の新しいオプションは `--workers` で、Uvicornに4つのワーカープロセスを起動するように指示しています。 -## デプロイメントのコンセプト +各プロセスの **PID** も表示されていて、親プロセス(これは**プロセスマネージャー**)が `27365`、各ワーカープロセスがそれぞれ `27368`、`27369`、`27370`、`27367` です。 -ここでは、アプリケーションの実行を**並列化**し、CPUの**マルチコア**を活用し、**より多くのリクエスト**に対応できるようにするために、**Gunicorn**(またはUvicorn)を使用して**Uvicornワーカー・プロセス**を管理する方法を見ていきました。 +## デプロイメントのコンセプト { #deployment-concepts } -上記のデプロイのコンセプトのリストから、ワーカーを使うことは主に**レプリケーション**の部分と、**再起動**を少し助けてくれます: +ここでは、複数の **ワーカー** を使ってアプリケーションの実行を**並列化**し、CPUの**複数コア**を活用して、**より多くのリクエスト**を処理できるようにする方法を見てきました。 -* セキュリティ - HTTPS -* 起動時の実行 -* 再起動 +上のデプロイメントのコンセプトのリストから、ワーカーを使うことは主に**レプリケーション**の部分と、**再起動**を少し助けてくれますが、それ以外については引き続き対処が必要です: + +* **セキュリティ - HTTPS** +* **起動時の実行** +* ***再起動*** * レプリケーション(実行中のプロセス数) -* メモリー -* 開始前の事前のステップ +* **メモリ** +* **開始前の事前ステップ** +## コンテナとDocker { #containers-and-docker } -## コンテナとDocker - -次章の[コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}では、その他の**デプロイのコンセプト**を扱うために実施するであろう戦略をいくつか紹介します。 +次章の[コンテナ内のFastAPI - Docker](docker.md){.internal-link target=_blank}では、その他の**デプロイメントのコンセプト**を扱うために使える戦略をいくつか説明します。 -また、**GunicornとUvicornワーカー**を含む**公式Dockerイメージ**と、簡単なケースに役立ついくつかのデフォルト設定も紹介します。 +単一のUvicornプロセスを実行するために、**ゼロから独自のイメージを構築する**方法も紹介します。これは簡単なプロセスで、**Kubernetes**のような分散コンテナ管理システムを使う場合に、おそらくやりたいことでしょう。 -また、(Gunicornを使わずに)Uvicornプロセスを1つだけ実行するために、**ゼロから独自のイメージを**構築する方法も紹介します。これは簡単なプロセスで、おそらく**Kubernetes**のような分散コンテナ管理システムを使うときにやりたいことでしょう。 +## まとめ { #recap } -## まとめ +`fastapi` または `uvicorn` コマンドで `--workers` CLIオプションを使うことで、**マルチコアCPU**を活用し、**複数のプロセスを並列実行**できるように複数のワーカープロセスを利用できます。 -Uvicornワーカーを使ったプロセスマネージャとして**Gunicorn**(またはUvicorn)を使えば、**マルチコアCPU**を活用して**複数のプロセスを並列実行**できます。 +他のデプロイメントのコンセプトを自分で対応しながら、**独自のデプロイシステム**を構築している場合にも、これらのツールやアイデアを使えます。 -これらのツールやアイデアは、**あなた自身のデプロイシステム**をセットアップしながら、他のデプロイコンセプトを自分で行う場合にも使えます。 - -次の章では、コンテナ(DockerやKubernetesなど)を使った**FastAPI**について学んでいきましょう。これらのツールには、他の**デプロイのコンセプト**も解決する簡単な方法があることがわかるでしょう。✨ +次の章で、コンテナ(例:DockerやKubernetes)を使った **FastAPI** について学びましょう。これらのツールにも、他の**デプロイメントのコンセプト**を解決する簡単な方法があることがわかります。✨ diff --git a/docs/ja/docs/deployment/versions.md b/docs/ja/docs/deployment/versions.md index 7575fc4f70..7980b8be2a 100644 --- a/docs/ja/docs/deployment/versions.md +++ b/docs/ja/docs/deployment/versions.md @@ -1,93 +1,93 @@ -# FastAPIのバージョンについて +# FastAPIのバージョンについて { #about-fastapi-versions } -**FastAPI** は既に多くのアプリケーションやシステムに本番環境で使われています。また、100%のテストカバレッジを維持しています。しかし、活発な開発が続いています。 +**FastAPI** はすでに多くのアプリケーションやシステムで本番環境にて使われています。また、テストカバレッジは 100% に維持されています。しかし、開発は依然として急速に進んでいます。 -高頻度で新機能が追加され、定期的にバグが修正され、実装は継続的に改善されています。 +新機能が高頻度で追加され、定期的にバグが修正され、コードは継続的に改善されています。 これが現在のバージョンがいまだに `0.x.x` な理由であり、それぞれのバージョンは破壊的な変更がなされる可能性があります。これは、セマンティック バージョニングの規則に則っています。 -**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 } + +セマンティック バージョニングの規約に従って、`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 } -```txt -fastapi>=0.45.0,<0.46.0 -``` +アプリケーションにテストを追加すべきです。 -破壊的な変更と新機能実装は「マイナー」バージョンで加えられます。 +**FastAPI** では非常に簡単に実現できます(Starlette のおかげです)。ドキュメントを確認して下さい: [テスト](../tutorial/testing.md){.internal-link target=_blank} -/// tip | 豆知識 +テストを追加したら、**FastAPI** のバージョンをより新しいものにアップグレードし、テストを実行することで全てのコードが正しく動作するか確認できます。 -「マイナー」は真ん中の数字です。例えば、`0.2.3` ではマイナーバージョンは `2` です。 +全てが動作する、または必要な変更を行った後に全てのテストが通るなら、その新しいバージョンに `fastapi` を固定できます。 -/// +## Starletteについて { #about-starlette } -## FastAPIのバージョンのアップグレード +`starlette` のバージョンは固定すべきではありません。 -アプリケーションにテストを加えるべきです。 +**FastAPI** のバージョンが異なれば、Starlette の特定のより新しいバージョンが使われます。 -**FastAPI** では非常に簡単に実現できます (Starletteのおかげで)。ドキュメントを確認して下さい: [テスト](../tutorial/testing.md){.internal-link target=_blank} +そのため、正しい Starlette バージョンを **FastAPI** に任せればよいです。 -テストを加えた後で、**FastAPI** のバージョンをより最新のものにアップグレードし、テストを実行することで全てのコードが正常に動作するか確認できます。 +## Pydanticについて { #about-pydantic } -全てが動作するか、修正を行った上で全てのテストを通過した場合、使用している`fastapi` のバージョンをより最新のバージョンに固定できます。 +Pydantic は自身のテストに **FastAPI** のテストも含んでいるため、Pydantic の新しいバージョン(`1.0.0` より上)は常に FastAPI と互換性があります。 -## Starletteについて - -`Starlette` のバージョンは固定すべきではありません。 - -**FastAPI** は、バージョン毎にStarletteのより新しいバージョンを使用します。 - -よって、最適なStarletteのバージョン選択を**FastAPI** に任せることができます。 - -## Pydanticについて - -Pydanticは自身のテストだけでなく**FastAPI** のためのテストを含んでいます。なので、Pydanticの新たなバージョン ( `1.0.0` 以降) は全てFastAPIと整合性があります。 - -Pydanticのバージョンを、動作が保証できる`1.0.0`以降のいずれかのバージョンから`2.0.0` 未満の間に固定できます。 +Pydantic は、自分にとって動作する `1.0.0` より上の任意のバージョンに固定できます。 例えば: ```txt -pydantic>=1.2.0,<2.0.0 +pydantic>=2.7.0,<3.0.0 ``` diff --git a/docs/ja/docs/environment-variables.md b/docs/ja/docs/environment-variables.md index 507af3a0cc..45dbfc71fd 100644 --- a/docs/ja/docs/environment-variables.md +++ b/docs/ja/docs/environment-variables.md @@ -1,18 +1,18 @@ -# 環境変数 +# 環境変数 { #environment-variables } -/// tip +/// tip | 豆知識 もし、「環境変数」とは何か、それをどう使うかを既に知っている場合は、このセクションをスキップして構いません。 /// -環境変数(**env var**とも呼ばれる)はPythonコードの**外側**、つまり**OS**に存在する変数で、Pythonから読み取ることができます。(他のプログラムでも同様に読み取れます。) +環境変数(「**env var**」とも呼ばれます)とは、Pythonコードの**外側**、つまり**オペレーティングシステム**に存在する変数で、Pythonコード(または他のプログラム)から読み取れます。 -環境変数は、アプリケーションの**設定**の管理や、Pythonの**インストール**などに役立ちます。 +環境変数は、アプリケーションの**設定**の扱い、Pythonの**インストール**の一部などで役立ちます。 -## 環境変数の作成と使用 +## 環境変数の作成と使用 { #create-and-use-env-vars } -環境変数は**シェル(ターミナル)**内で**作成**して使用でき、それらにPythonは不要です。 +環境変数は、Pythonを必要とせず、**シェル(ターミナル)**で**作成**して使用できます。 //// tab | Linux, macOS, Windows Bash @@ -36,7 +36,6 @@ Hello Wade Wilson
- ```console // Create an env var MY_NAME $ $Env:MY_NAME = "Wade Wilson" @@ -51,9 +50,9 @@ Hello Wade Wilson //// -## Pythonで環境変数を読み取る +## Pythonで環境変数を読み取る { #read-env-vars-in-python } -環境変数をPythonの**外側**、ターミナル(や他の方法)で作成し、**Python内で読み取る**こともできます。 +環境変数はPythonの**外側**(ターミナル、またはその他の方法)で作成し、その後に**Pythonで読み取る**こともできます。 例えば、以下のような`main.py`ファイルを用意します: @@ -64,11 +63,11 @@ name = os.getenv("MY_NAME", "World") print(f"Hello {name} from Python") ``` -/// tip +/// tip | 豆知識 -`os.getenv()` の第2引数は、デフォルトで返される値を指定します。 +`os.getenv()` の第2引数は、デフォルトで返される値です。 -この引数を省略するとデフォルト値として`None`が返されますが、ここではデフォルト値として`"World"`を指定しています。 +指定しない場合、デフォルトは`None`ですが、ここでは使用するデフォルト値として`"World"`を指定しています。 /// @@ -128,11 +127,11 @@ Hello Wade Wilson from Python //// -環境変数はコードの外側で設定し、内側から読み取ることができるので、他のファイルと一緒に(`git`に)保存する必要がありません。そのため、環境変数をコンフィグレーションや**設定**に使用することが一般的です。 +環境変数はコードの外側で設定でき、コードから読み取れ、他のファイルと一緒に(`git`に)保存(コミット)する必要がないため、設定や**settings**に使うのが一般的です。 -また、**特定のプログラムの呼び出し**のための環境変数を、そのプログラムのみ、その実行中に限定して利用できるよう作成できます。 +また、**特定のプログラムの呼び出し**のためだけに、そのプログラムでのみ、実行中の間だけ利用できる環境変数を作成することもできます。 -そのためには、プログラム起動コマンドと同じコマンドライン上の、起動コマンド直前で環境変数を作成してください。 +そのためには、同じ行で、プログラム自体の直前に作成してください。
@@ -152,25 +151,25 @@ Hello World from Python
-/// tip +/// tip | 豆知識 詳しくは The Twelve-Factor App: Config を参照してください。 /// -## 型とバリデーション +## 型とバリデーション { #types-and-validation } -環境変数は**テキスト文字列**のみを扱うことができます。これは、環境変数がPython外部に存在し、他のプログラムやシステム全体(Linux、Windows、macOS間の互換性を含む)と連携する必要があるためです。 +これらの環境変数が扱えるのは**テキスト文字列**のみです。環境変数はPythonの外部にあり、他のプログラムやシステム全体(Linux、Windows、macOSなど異なるオペレーティングシステム間も)との互換性が必要になるためです。 -つまり、Pythonが環境変数から読み取る**あらゆる値**は **`str`型となり**、他の型への変換やバリデーションはコード内で行う必要があります。 +つまり、環境変数からPythonで読み取る**あらゆる値**は **`str`になり**、他の型への変換やバリデーションはコード内で行う必要があります。 -環境変数を使用して**アプリケーション設定**を管理する方法については、[高度なユーザーガイド - Settings and Environment Variables](./advanced/settings.md){.internal-link target=_blank}で詳しく学べます。 +環境変数を使って**アプリケーション設定**を扱う方法については、[高度なユーザーガイド - Settings and Environment Variables](./advanced/settings.md){.internal-link target=_blank} で詳しく学べます。 -## `PATH`環境変数 +## `PATH`環境変数 { #path-environment-variable } -**`PATH`**という**特別な**環境変数があります。この環境変数は、OS(Linux、macOS、Windows)が実行するプログラムを発見するために使用されます。 +**`PATH`**という**特別な**環境変数があります。これはオペレーティングシステム(Linux、macOS、Windows)が実行するプログラムを見つけるために使用されます。 -`PATH`変数は、複数のディレクトリのパスから成る長い文字列です。このパスはLinuxやMacOSの場合は`:`で、Windowsの場合は`;`で区切られています。 +変数`PATH`の値は長い文字列で、LinuxとmacOSではコロン`:`、Windowsではセミコロン`;`で区切られたディレクトリで構成されます。 例えば、`PATH`環境変数は次のような文字列かもしれません: @@ -180,7 +179,7 @@ Hello World from Python /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin ``` -これは、OSはプログラムを見つけるために以下のディレクトリを探す、ということを意味します: +これは、システムが次のディレクトリでプログラムを探すことを意味します: * `/usr/local/bin` * `/usr/bin` @@ -196,7 +195,7 @@ Hello World from Python C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32 ``` -これは、OSはプログラムを見つけるために以下のディレクトリを探す、ということを意味します: +これは、システムが次のディレクトリでプログラムを探すことを意味します: * `C:\Program Files\Python312\Scripts` * `C:\Program Files\Python312` @@ -204,63 +203,61 @@ C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System3 //// -ターミナル上で**コマンド**を入力すると、 OSはそのプログラムを見つけるために、`PATH`環境変数のリストに記載された**それぞれのディレクトリを探し**ます。 +ターミナル上で**コマンド**を入力すると、オペレーティングシステムは`PATH`環境変数に記載された**それぞれのディレクトリ**の中からプログラムを**探し**ます。 -例えば、ターミナル上で`python`を入力すると、OSは`python`によって呼ばれるプログラムを見つけるために、そのリストの**先頭のディレクトリ**を最初に探します。 +例えば、ターミナルで`python`と入力すると、オペレーティングシステムはそのリストの**最初のディレクトリ**で`python`というプログラムを探します。 -OSは、もしそのプログラムをそこで発見すれば**実行し**ますが、そうでなければリストの**他のディレクトリ**を探していきます。 +見つかればそれを**使用**します。見つからなければ、**他のディレクトリ**を探し続けます。 -### PythonのインストールとPATH環境変数の更新 +### Pythonのインストールと`PATH`の更新 { #installing-python-and-updating-the-path } -Pythonのインストール時に`PATH`環境変数を更新したいか聞かれるかもしれません。 +Pythonのインストール時に、`PATH`環境変数を更新するかどうかを尋ねられるかもしれません。 -/// tab | Linux, macOS +//// tab | Linux, macOS -Pythonをインストールして、そのプログラムが`/opt/custompython/bin`というディレクトリに配置されたとします。 +Pythonをインストールして、その結果`/opt/custompython/bin`というディレクトリに配置されたとします。 -もし、`PATH`環境変数を更新するように答えると、`PATH`環境変数に`/opt/custompython/bin`が追加されます。 +`PATH`環境変数を更新することに同意すると、インストーラーは`PATH`環境変数に`/opt/custompython/bin`を追加します。 -`PATH`環境変数は以下のように更新されるでしょう: +例えば次のようになります: -``` plaintext +```plaintext /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin ``` -このようにして、ターミナルで`python`と入力したときに、OSは`/opt/custompython/bin`(リストの末尾のディレクトリ)にあるPythonプログラムを見つけ、使用します。 +このようにして、ターミナルで`python`と入力すると、システムは`/opt/custompython/bin`(最後のディレクトリ)にあるPythonプログラムを見つけ、それを使用します。 -/// +//// -/// tab | Windows +//// tab | Windows -Pythonをインストールして、そのプログラムが`C:\opt\custompython\bin`というディレクトリに配置されたとします。 +Pythonをインストールして、その結果`C:\opt\custompython\bin`というディレクトリに配置されたとします。 -もし、`PATH`環境変数を更新するように答えると、`PATH`環境変数に`C:\opt\custompython\bin`が追加されます。 - -`PATH`環境変数は以下のように更新されるでしょう: +`PATH`環境変数を更新することに同意すると、インストーラーは`PATH`環境変数に`C:\opt\custompython\bin`を追加します。 ```plaintext C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin ``` -このようにして、ターミナルで`python`と入力したときに、OSは`C:\opt\custompython\bin\python`(リストの末尾のディレクトリ)にあるPythonプログラムを見つけ、使用します。 +このようにして、ターミナルで`python`と入力すると、システムは`C:\opt\custompython\bin`(最後のディレクトリ)にあるPythonプログラムを見つけ、それを使用します。 -/// +//// -つまり、ターミナルで以下のコマンドを入力すると: +つまり、ターミナルで次のように入力すると:
-``` console +```console $ python ```
-/// tab | Linux, macOS +//// tab | Linux, macOS -OSは`/opt/custompython/bin`にある`python`プログラムを**見つけ**て実行します。 +システムは`/opt/custompython/bin`にある`python`プログラムを**見つけ**て実行します。 -これは、次のコマンドを入力した場合とほとんど同等です: +これは、次のように入力するのとおおむね同等です:
@@ -270,13 +267,13 @@ $ /opt/custompython/bin/python
-/// +//// -/// tab | Windows +//// tab | Windows -OSは`C:\opt\custompython\bin\python`にある`python`プログラムを**見つけ**て実行します。 +システムは`C:\opt\custompython\bin\python`にある`python`プログラムを**見つけ**て実行します。 -これは、次のコマンドを入力した場合とほとんど同等です: +これは、次のように入力するのとおおむね同等です:
@@ -286,16 +283,16 @@ $ C:\opt\custompython\bin\python
-/// +//// -この情報は、[Virtual Environments](virtual-environments.md) について学ぶ際にも役立ちます。 +この情報は、[Virtual Environments](virtual-environments.md){.internal-link target=_blank} について学ぶ際にも役立ちます。 -## まとめ +## まとめ { #conclusion } これで、**環境変数**とは何か、Pythonでどのように使用するかについて、基本的な理解が得られたはずです。 -環境変数についての詳細は、Wikipedia: Environment Variable を参照してください。 +環境変数についての詳細は、Wikipedia for Environment Variable も参照してください。 -環境変数の用途や適用方法が最初は直感的ではないかもしれませんが、開発中のさまざまなシナリオで繰り返し登場します。そのため、基本を知っておくことが重要です。 +多くの場合、環境変数がどのように役立ち、すぐに適用できるのかはあまり明確ではありません。しかし、開発中のさまざまなシナリオで何度も登場するため、知っておくとよいでしょう。 -たとえば、この情報は次のセクションで扱う[Virtual Environments](virtual-environments.md)にも関連します。 +例えば、次のセクションの[Virtual Environments](virtual-environments.md)でこの情報が必要になります。 diff --git a/docs/ja/docs/how-to/conditional-openapi.md b/docs/ja/docs/how-to/conditional-openapi.md index bfaa9e6d75..9478f5c032 100644 --- a/docs/ja/docs/how-to/conditional-openapi.md +++ b/docs/ja/docs/how-to/conditional-openapi.md @@ -1,8 +1,8 @@ -# 条件付き OpenAPI +# 条件付き OpenAPI { #conditional-openapi } 必要であれば、設定と環境変数を利用して、環境に応じて条件付きでOpenAPIを構成することが可能です。また、完全にOpenAPIを無効にすることもできます。 -## セキュリティとAPI、およびドキュメントについて +## セキュリティとAPI、およびドキュメントについて { #about-security-apis-and-docs } 本番環境においてドキュメントのUIを非表示にすることによって、APIを保護しようと *すべきではありません*。 @@ -17,19 +17,19 @@ * リクエストボディとレスポンスのためのPydanticモデルの定義を見直す。 * 依存関係に基づきすべての必要なパーミッションとロールを設定する。 * パスワードを絶対に平文で保存しない。パスワードハッシュのみを保存する。 -* PasslibやJWTトークンに代表される、よく知られた暗号化ツールを使って実装する。 +* pwdlibやJWTトークンに代表される、よく知られた暗号化ツールを使って実装する。 * そして必要なところでは、もっと細かいパーミッション制御をOAuth2スコープを使って行う。 -* など +* ...など それでも、例えば本番環境のような特定の環境のみで、あるいは環境変数の設定によってAPIドキュメントをどうしても無効にしたいという、非常に特殊なユースケースがあるかもしれません。 -## 設定と環境変数による条件付き OpenAPI +## 設定と環境変数による条件付き OpenAPI { #conditional-openapi-from-settings-and-env-vars } 生成するOpenAPIとドキュメントUIの構成は、共通のPydanticの設定を使用して簡単に切り替えられます。 例えば、 -{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *} +{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *} ここでは `openapi_url` の設定を、デフォルトの `"/openapi.json"` のまま宣言しています。 diff --git a/docs/ja/docs/index.md b/docs/ja/docs/index.md index 8dee1ee035..67e01ed535 100644 --- a/docs/ja/docs/index.md +++ b/docs/ja/docs/index.md @@ -1,14 +1,14 @@ -# FastAPI +# FastAPI { #fastapi }

- FastAPI + FastAPI

- FastAPI framework, high performance, easy to learn, fast to code, ready for production + FastAPI フレームワーク、高パフォーマンス、学びやすい、素早くコーディングできる、本番運用に対応

@@ -27,129 +27,138 @@ --- -**ドキュメント**: https://fastapi.tiangolo.com +**ドキュメント**: https://fastapi.tiangolo.com **ソースコード**: https://github.com/fastapi/fastapi --- -FastAPI は、Pythonの標準である型ヒントに基づいてPython 以降でAPI を構築するための、モダンで、高速(高パフォーマンス)な、Web フレームワークです。 +FastAPI は、Python の標準である型ヒントに基づいて Python で API を構築するための、モダンで、高速(高パフォーマンス)な Web フレームワークです。 主な特徴: -- **高速**: **NodeJS** や **Go** 並みのとても高いパフォーマンス (Starlette と Pydantic のおかげです)。 [最も高速な Python フレームワークの一つです](#_10). +* **高速**: **NodeJS** や **Go** 並みのとても高いパフォーマンス(Starlette と Pydantic のおかげです)。 [利用可能な最も高速な Python フレームワークの一つです](#performance)。 +* **高速なコーディング**: 開発速度を約 200%〜300% 向上させます。* +* **少ないバグ**: 開発者起因のヒューマンエラーを約 40% 削減します。* +* **直感的**: 素晴らしいエディタサポート。あらゆる場所で 補完 が使えます。デバッグ時間を削減します。 +* **簡単**: 簡単に利用・習得できるようにデザインされています。ドキュメントを読む時間を削減します。 +* **短い**: コードの重複を最小限にします。各パラメータ宣言から複数の機能を得られます。バグも減ります。 +* **堅牢性**: 自動対話型ドキュメントにより、本番環境向けのコードが得られます。 +* **Standards-based**: API のオープンスタンダードに基づいており(そして完全に互換性があります)、OpenAPI(以前は Swagger として知られていました)や JSON Schema をサポートします。 -- **高速なコーディング**: 開発速度を約 200%~300%向上させます。 \* -- **少ないバグ**: 開発者起因のヒューマンエラーを約 40%削減します。 \* -- **直感的**: 素晴らしいエディタのサポートや オートコンプリート。 デバッグ時間を削減します。 -- **簡単**: 簡単に利用、習得できるようにデザインされています。ドキュメントを読む時間を削減します。 -- **短い**: コードの重複を最小限にしています。各パラメータからの複数の機能。少ないバグ。 -- **堅牢性**: 自動対話ドキュメントを使用して、本番環境で使用できるコードを取得します。 -- **Standards-based**: API のオープンスタンダードに基づいており、完全に互換性があります: OpenAPI (以前は Swagger として知られていました) や JSON スキーマ. +* 本番アプリケーションを構築している社内開発チームのテストに基づく見積もりです。 -\* 本番アプリケーションを構築している開発チームのテストによる見積もり。 - -## Sponsors +## Sponsors { #sponsors } -{% if sponsors %} +### Keystone Sponsor { #keystone-sponsor } + +{% for sponsor in sponsors.keystone -%} + +{% endfor -%} + +### Gold and Silver Sponsors { #gold-and-silver-sponsors } + {% for sponsor in sponsors.gold -%} {% endfor -%} {%- for sponsor in sponsors.silver -%} {% endfor %} -{% endif %} -Other sponsors +その他のスポンサー -## 評価 +## 評価 { #opinions } -"_[...] 最近 **FastAPI** を使っています。 [...] 実際に私のチームの全ての **Microsoft の機械学習サービス** で使用する予定です。 そのうちのいくつかのコアな**Windows**製品と**Office**製品に統合されつつあります。_" +"_[...] 最近 **FastAPI** を使っています。 [...] 実際に私のチームの全ての **Microsoft の機械学習サービス** で使用する予定です。 そのうちのいくつかのコアな **Windows** 製品と **Office** 製品に統合されつつあります。_"

Kabir Khan - Microsoft (ref)
--- -"_FastAPIライブラリを採用し、クエリで**予測値**を取得できる**REST**サーバを構築しました。 [for Ludwig]_" +"_FastAPIライブラリを採用し、クエリで **予測値** を取得できる **REST** サーバを構築しました。 [for Ludwig]_"
Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber (ref)
--- -"_**Netflix** は、**危機管理**オーケストレーションフレームワーク、**Dispatch**のオープンソースリリースを発表できることをうれしく思います。 [built with **FastAPI**]_" +"_**Netflix** は、**危機管理**オーケストレーションフレームワーク、**Dispatch** のオープンソースリリースを発表できることをうれしく思います。 [built with **FastAPI**]_"
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix (ref)
--- -"_私は**FastAPI**にワクワクしています。 めちゃくちゃ楽しいです!_" +"_私は **FastAPI** にワクワクしています。 めちゃくちゃ楽しいです!_"
Brian Okken - Python Bytes podcast host (ref)
--- -"_正直、超堅実で洗練されているように見えます。いろんな意味で、それは私がハグしたかったものです。_" +"_正直、あなたが作ったものは超堅実で洗練されているように見えます。いろんな意味で、それは私が **Hug** にそうなってほしかったものです。誰かがそれを作るのを見るのは本当に刺激的です。_"
Timothy Crosley - Hug creator (ref)
--- -"_REST API を構築するための**モダンなフレームワーク**を学びたい方は、**FastAPI** [...] をチェックしてみてください。 [...] 高速で, 使用、習得が簡単です。[...]_" +"_REST API を構築するための **モダンなフレームワーク** を学びたい方は、**FastAPI** [...] をチェックしてみてください。 [...] 高速で、使用・習得が簡単です [...]_" -"_私たちの**API**は**FastAPI**に切り替えました。[...] きっと気に入ると思います。 [...]_" +"_私たちの **API** は **FastAPI** に切り替えました [...] きっと気に入ると思います [...]_"
Ines Montani - Matthew Honnibal - Explosion AI founders - spaCy creators (ref) - (ref)
--- -## **Typer**, the FastAPI of CLIs +"_本番運用の Python API を構築したい方には、**FastAPI** を強くおすすめします。**美しく設計**されており、**使いやすく**、**高いスケーラビリティ**があります。私たちの API ファースト開発戦略の **主要コンポーネント** となり、Virtual TAC Engineer などの多くの自動化やサービスを推進しています。_" + +
Deon Pillsbury - Cisco (ref)
+ +--- + +## FastAPI ミニドキュメンタリー { #fastapi-mini-documentary } + +2025 年末に公開された FastAPI ミニドキュメンタリーがあります。オンラインで視聴できます: + +FastAPI Mini Documentary + +## **Typer**、CLI 版 FastAPI { #typer-the-fastapi-of-clis } -もし Web API の代わりにターミナルで使用するCLIアプリを構築する場合は、**Typer**を確認してください。 +Web API の代わりにターミナルで使用する CLI アプリを構築する場合は、**Typer** を確認してください。 -**Typer**は FastAPI の弟分です。そして、**CLI 版 の FastAPI**を意味しています。 +**Typer** は FastAPI の弟分です。そして、**CLI 版 FastAPI** を意図しています。 ⌨️ 🚀 -## 必要条件 +## 必要条件 { #requirements } FastAPI は巨人の肩の上に立っています。 -- Web の部分はStarlette -- データの部分はPydantic +* Web の部分は Starlette +* データの部分は Pydantic -## インストール +## インストール { #installation } + +virtual environment を作成して有効化し、それから FastAPI をインストールします。
```console -$ pip install fastapi +$ pip install "fastapi[standard]" ---> 100% ```
-本番環境では、Uvicorn または、 Hypercornのような、 ASGI サーバーが必要になります。 +**注**: すべてのターミナルで動作するように、`"fastapi[standard]"` は必ずクォートで囲んでください。 -
+## アプリケーション例 { #example } -```console -$ pip install "uvicorn[standard]" +### 作成 { #create-it } ----> 100% -``` - -
- -## アプリケーション例 - -### アプリケーションの作成 - -- `main.py` を作成し、以下のコードを入力します: +`main.py` ファイルを作成し、以下のコードを入力します。 ```Python from fastapi import FastAPI @@ -163,16 +172,16 @@ def read_root(): @app.get("/items/{item_id}") -def read_item(item_id: int, q: str = None): +def read_item(item_id: int, q: str | None = None): return {"item_id": item_id, "q": q} ```
-またはasync defを使います... +または async def を使います... -`async` / `await`を使用するときは、 `async def`を使います: +コードで `async` / `await` を使用する場合は、`async def` を使います。 -```Python hl_lines="7 12" +```Python hl_lines="7 12" from fastapi import FastAPI app = FastAPI() @@ -184,28 +193,41 @@ async def read_root(): @app.get("/items/{item_id}") -async def read_item(item_id: int, q: str = None): +async def read_item(item_id: int, q: str | None = None): return {"item_id": item_id, "q": q} ``` **注**: -わからない場合は、ドキュメントの`async` と `await`にある"In a hurry?"セクションをチェックしてください。 +わからない場合は、ドキュメントの `async` と `await` の _"In a hurry?"_ セクションを確認してください。
-### 実行 +### 実行 { #run-it } -以下のコマンドでサーバーを起動します: +以下のコマンドでサーバーを起動します。
```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. ``` @@ -213,56 +235,56 @@ INFO: Application startup complete.
-uvicorn main:app --reloadコマンドについて +fastapi dev main.py コマンドについて -`uvicorn main:app`コマンドは以下の項目を参照します: +`fastapi dev` コマンドは `main.py` ファイルを読み取り、その中の **FastAPI** アプリを検出し、Uvicorn を使用してサーバーを起動します。 -- `main`: `main.py`ファイル (Python "モジュール") -- `app`: `main.py` の`app = FastAPI()`の行で生成されたオブジェクト -- `--reload`: コードを変更したらサーバーを再起動します。このオプションは開発環境でのみ使用します +デフォルトでは、`fastapi dev` はローカル開発向けに自動リロードを有効にして起動します。 + +詳しくは FastAPI CLI docs を参照してください。
-### 動作確認 +### 動作確認 { #check-it } -ブラウザからhttp://127.0.0.1:8000/items/5?q=somequeryを開きます。 +ブラウザで http://127.0.0.1:8000/items/5?q=somequery を開きます。 -以下の JSON のレスポンスが確認できます: +以下の JSON のレスポンスが確認できます。 ```JSON {"item_id": 5, "q": "somequery"} ``` -もうすでに以下の API が作成されています: +すでに以下の API が作成されています。 -- `/` と `/items/{item_id}`のパスで HTTP リクエストを受けます。 -- どちらのパスも `GET` 操作 を取ります。(HTTP メソッドとしても知られています。) -- `/items/{item_id}` パスのパスパラメータ `item_id` は `int` でなければなりません。 -- パス `/items/{item_id}` はオプションの `str` クエリパラメータ `q` を持ちます。 +* _パス_ `/` と `/items/{item_id}` で HTTP リクエストを受け取ります。 +* 両方の _パス_ は `GET` 操作(HTTP _メソッド_ としても知られています)を取ります。 +* _パス_ `/items/{item_id}` は `int` であるべき _パスパラメータ_ `item_id` を持ちます。 +* _パス_ `/items/{item_id}` はオプションの `str` _クエリパラメータ_ `q` を持ちます。 -### 自動対話型の API ドキュメント +### 自動対話型 API ドキュメント { #interactive-api-docs } -http://127.0.0.1:8000/docsにアクセスしてみてください。 +次に、http://127.0.0.1:8000/docs にアクセスします。 -自動対話型の API ドキュメントが表示されます。 (Swagger UIが提供しています。): +自動対話型 API ドキュメントが表示されます(Swagger UI が提供しています)。 ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) -### 代替の API ドキュメント +### 代替 API ドキュメント { #alternative-api-docs } -http://127.0.0.1:8000/redocにアクセスしてみてください。 +次に、http://127.0.0.1:8000/redoc にアクセスします。 -代替の自動ドキュメントが表示されます。(ReDocが提供しています。): +代替の自動ドキュメントが表示されます(ReDoc が提供しています)。 ![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) -## アップグレード例 +## アップグレード例 { #example-upgrade } -`PUT`リクエストからボディを受け取るために`main.py`を修正しましょう。 +次に、`PUT` リクエストからボディを受け取るために `main.py` ファイルを修正しましょう。 -Pydantic によって、Python の標準的な型を使ってボディを宣言します。 +Pydantic によって、標準的な Python の型を使ってボディを宣言します。 -```Python hl_lines="2 7 8 9 10 23 24 25" +```Python hl_lines="2 7-10 23-25" from fastapi import FastAPI from pydantic import BaseModel @@ -272,7 +294,7 @@ app = FastAPI() class Item(BaseModel): name: str price: float - is_offer: bool = None + is_offer: bool | None = None @app.get("/") @@ -281,7 +303,7 @@ def read_root(): @app.get("/items/{item_id}") -def read_item(item_id: int, q: str = None): +def read_item(item_id: int, q: str | None = None): return {"item_id": item_id, "q": q} @@ -290,173 +312,248 @@ 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 } -http://127.0.0.1:8000/docsにアクセスしましょう。 +次に、http://127.0.0.1:8000/docs にアクセスします。 -- 自動対話型の 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 } -http://127.0.0.1:8000/redocにアクセスしましょう。 +次に、http://127.0.0.1:8000/redoc にアクセスします。 -- 代替の API ドキュメントにも新しいクエリパラメータやボディが反映されます。 +* 代替のドキュメントにも新しいクエリパラメータやボディが反映されます。 ![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) -### まとめ +### まとめ { #recap } -要約すると、関数のパラメータとして、パラメータやボディ などの型を**一度だけ**宣言します。 +要約すると、関数のパラメータとして、パラメータやボディなどの型を **一度だけ** 宣言します。 -標準的な最新の Python の型を使っています。 +標準的な最新の Python の型を使います。 新しい構文や特定のライブラリのメソッドやクラスなどを覚える必要はありません。 -単なる標準的な**3.8 以降の Python**です。 +単なる標準的な **Python** です。 -例えば、`int`の場合: +例えば、`int` の場合: ```Python item_id: int ``` -または、より複雑な`Item`モデルの場合: +または、より複雑な `Item` モデルの場合: ```Python item: Item ``` -...そして、この一度の宣言で、以下のようになります: +...そして、この一度の宣言で、以下のようになります。 -- 以下を含むエディタサポート: - - 補完 - - タイプチェック -- データの検証: - - データが無効な場合に自動でエラーをクリアします。 - - 深い入れ子になった JSON オブジェクトでも検証が可能です。 -- 入力データの変換: ネットワークから Python のデータや型に変換してから読み取ります: - - JSON. - - パスパラメータ - - クエリパラメータ - - クッキー - - ヘッダー - - フォーム - - ファイル -- 出力データの変換: Python のデータや型からネットワークデータへ変換します (JSON として): - - Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc). - - `datetime` オブジェクト - - `UUID` オブジェクト - - データベースモデル - - ...などなど -- 2 つの代替ユーザーインターフェースを含む自動インタラクティブ API ドキュメント: - - Swagger UI. - - ReDoc. +* 以下を含むエディタサポート: + * 補完。 + * 型チェック。 +* データの検証: + * データが無効な場合に自動で明確なエラーを返します。 + * 深い入れ子になった JSON オブジェクトでも検証が可能です。 +* 入力データの 変換: ネットワークから Python のデータや型へ。以下から読み取ります: + * JSON。 + * パスパラメータ。 + * クエリパラメータ。 + * Cookie。 + * ヘッダー。 + * フォーム。 + * ファイル。 +* 出力データの 変換: Python のデータや型からネットワークデータへ(JSON として)変換します: + * Python の型(`str`、`int`、`float`、`bool`、`list` など)の変換。 + * `datetime` オブジェクト。 + * `UUID` オブジェクト。 + * データベースモデル。 + * ...などなど。 +* 2 つの代替ユーザーインターフェースを含む自動対話型 API ドキュメント: + * Swagger UI。 + * ReDoc。 --- -コード例に戻りましょう、**FastAPI** は次のようになります: +前のコード例に戻ると、**FastAPI** は次のように動作します。 -- `GET`および`PUT`リクエストのパスに`item_id` があることを検証します。 -- `item_id`が`GET`および`PUT`リクエストに対して`int` 型であることを検証します。 - - そうでない場合は、クライアントは有用で明確なエラーが表示されます。 -- `GET` リクエストに対してオプションのクエリパラメータ `q` (`http://127.0.0.1:8000/items/foo?q=somequery` のように) が存在するかどうかを調べます。 - - パラメータ `q` は `= None` で宣言されているので、オプションです。 - - `None`がなければ必須になります(`PUT`の場合のボディと同様です)。 -- `PUT` リクエストを `/items/{item_id}` に送信する場合は、ボディを JSON として読み込みます: - - 必須の属性 `name` を確認してください。 それは `str` であるべきです。 - - 必須の属性 `price` を確認してください。それは `float` でなければならないです。 - - オプションの属性 `is_offer` を確認してください。値がある場合は、`bool` であるべきです。 - - これらはすべて、深くネストされた JSON オブジェクトに対しても動作します。 -- JSON から JSON に自動的に変換します。 -- OpenAPIですべてを文書化し、以下を使用することができます: - - 対話的なドキュメントシステム。 - - 多くの言語に対応した自動クライアントコード生成システム。 -- 2 つの対話的なドキュメントのWebインターフェイスを直接提供します。 +* `GET` および `PUT` リクエストのパスに `item_id` があることを検証します。 +* `GET` および `PUT` リクエストに対して `item_id` が `int` 型であることを検証します。 + * そうでない場合、クライアントは有用で明確なエラーを受け取ります。 +* `GET` リクエストに対して、`q` という名前のオプションのクエリパラメータ(`http://127.0.0.1:8000/items/foo?q=somequery` のような)が存在するかどうかを調べます。 + * `q` パラメータは `= None` で宣言されているため、オプションです。 + * `None` がなければ必須になります(`PUT` の場合のボディと同様です)。 +* `PUT` リクエストを `/items/{item_id}` に送信する場合、ボディを JSON として読み込みます: + * 必須の属性 `name` があり、`str` であるべきことを確認します。 + * 必須の属性 `price` があり、`float` でなければならないことを確認します。 + * オプションの属性 `is_offer` があり、存在する場合は `bool` であるべきことを確認します。 + * これらはすべて、深くネストされた JSON オブジェクトに対しても動作します。 +* JSON への/からの変換を自動的に行います。 +* OpenAPI ですべてを文書化し、以下で利用できます: + * 対話型ドキュメントシステム。 + * 多くの言語に対応した自動クライアントコード生成システム。 +* 2 つの対話型ドキュメント Web インターフェースを直接提供します。 --- -まだ表面的な部分に触れただけですが、もう全ての仕組みは分かっているはずです。 +まだ表面的な部分に触れただけですが、仕組みはすでにイメージできているはずです。 -以下の行を変更してみてください: +以下の行を変更してみてください。 ```Python return {"item_name": item.name, "item_id": item_id} ``` -...以下を: +...以下の: ```Python ... "item_name": item.name ... ``` -...以下のように: +...を: ```Python ... "item_price": item.price ... ``` -...そして、エディタが属性を自動補完し、そのタイプを知る方法を確認してください。: +...に変更し、エディタが属性を自動補完し、その型を知ることを確認してください。 ![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png) -より多くの機能を含む、より完全な例については、チュートリアル - ユーザーガイドをご覧ください。 +より多くの機能を含む、より完全な例については、Tutorial - User Guide を参照してください。 -**ネタバレ注意**: チュートリアル - ユーザーガイドは以下の情報が含まれています: +**ネタバレ注意**: tutorial - user guide には以下が含まれます。 -- **ヘッダー**、**クッキー**、**フォームフィールド**、**ファイル**などの他の場所からの **パラメータ** 宣言。 -- `maximum_length`や`regex`のような**検証や制約**を設定する方法。 -- 非常に強力で使いやすい **依存性注入**システム。 -- **JWT トークン**を用いた **OAuth2** や **HTTP Basic 認証** のサポートを含む、セキュリティと認証。 -- **深くネストされた JSON モデル**を宣言するためのより高度な(しかし同様に簡単な)技術(Pydantic のおかげです)。 -- 以下のようなたくさんのおまけ機能(Starlette のおかげです): - - **WebSockets** - - **GraphQL** - - `httpx` や `pytest`をもとにした極限に簡単なテスト - - **CORS** - - **クッキーセッション** - - ...などなど。 +* **ヘッダー**、**Cookie**、**フォームフィールド**、**ファイル**など、他のさまざまな場所からの **パラメータ** 宣言。 +* `maximum_length` や `regex` のような **検証制約** を設定する方法。 +* 非常に強力で使いやすい **依存性注入** システム。 +* **JWT トークン**を用いた **OAuth2** や **HTTP Basic** 認証のサポートを含む、セキュリティと認証。 +* **深くネストされた JSON モデル**を宣言するための、より高度な(しかし同様に簡単な)手法(Pydantic のおかげです)。 +* Strawberry および他のライブラリによる **GraphQL** 統合。 +* 以下のようなたくさんのおまけ機能(Starlette のおかげです): + * **WebSockets** + * HTTPX と `pytest` に基づく極めて簡単なテスト + * **CORS** + * **Cookie Sessions** + * ...などなど。 -## パフォーマンス +### アプリをデプロイ(任意) { #deploy-your-app-optional } -独立した TechEmpower のベンチマークでは、Uvicorn で動作する**FastAPI**アプリケーションが、Python フレームワークの中で最も高速なものの 1 つであり、Starlette と Uvicorn(FastAPI で内部的に使用されています)にのみ下回っていると示されています。 +必要に応じて FastAPI アプリを FastAPI Cloud にデプロイできます。まだの場合はウェイティングリストに参加してください。 🚀 -詳細はベンチマークセクションをご覧ください。 +すでに **FastAPI Cloud** アカウント(ウェイティングリストから招待されました 😉)がある場合は、1 コマンドでアプリケーションをデプロイできます。 -## オプションの依存関係 +デプロイ前に、ログインしていることを確認してください。 + +
+ +```console +$ fastapi login + +You are logged in to FastAPI Cloud 🚀 +``` + +
+ +次に、アプリをデプロイします。 + +
+ +```console +$ fastapi deploy + +Deploying to FastAPI Cloud... + +✅ Deployment successful! + +🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev +``` + +
+ +これで完了です!その URL でアプリにアクセスできます。 ✨ + +#### FastAPI Cloud について { #about-fastapi-cloud } + +**FastAPI Cloud** は **FastAPI** の作者と同じチームによって作られています。 + +最小限の労力で API を **構築**、**デプロイ**、**アクセス** するためのプロセスを効率化します。 + +FastAPI でアプリを構築するのと同じ **開発者体験** を、クラウドへの **デプロイ** にももたらします。 🎉 + +FastAPI Cloud は *FastAPI and friends* オープンソースプロジェクトの主要スポンサーであり、資金提供元です。 ✨ + +#### 他のクラウドプロバイダにデプロイ { #deploy-to-other-cloud-providers } + +FastAPI はオープンソースであり、標準に基づいています。選択した任意のクラウドプロバイダに FastAPI アプリをデプロイできます。 + +各クラウドプロバイダのガイドに従って、FastAPI アプリをデプロイしてください。 🤓 + +## パフォーマンス { #performance } + +独立した TechEmpower のベンチマークでは、Uvicorn で動作する **FastAPI** アプリケーションが、利用可能な最も高速な Python フレームワークの一つであり、Starlette と Uvicorn(FastAPI で内部的に使用されています)にのみ下回っていると示されています。(*) + +詳細は Benchmarks セクションをご覧ください。 + +## 依存関係 { #dependencies } + +FastAPI は Pydantic と Starlette に依存しています。 + +### `standard` 依存関係 { #standard-dependencies } + +FastAPI を `pip install "fastapi[standard]"` でインストールすると、`standard` グループのオプション依存関係が含まれます。 Pydantic によって使用されるもの: -- email-validator - E メールの検証 +* email-validator - メール検証のため。 Starlette によって使用されるもの: -- httpx - `TestClient`を使用するために必要です。 -- jinja2 - デフォルトのテンプレート設定を使用する場合は必要です。 -- python-multipart - "parsing"`request.form()`からの変換をサポートしたい場合は必要です。 -- itsdangerous - `SessionMiddleware` サポートのためには必要です。 -- pyyaml - Starlette の `SchemaGenerator` サポートのために必要です。 (FastAPI では必要ないでしょう。) -- graphene - `GraphQLApp` サポートのためには必要です。 +* httpx - `TestClient` を使用したい場合に必要です。 +* jinja2 - デフォルトのテンプレート設定を使用したい場合に必要です。 +* python-multipart - `request.form()` とともに、フォームの 「parsing」 をサポートしたい場合に必要です。 -FastAPI / Starlette に使用されるもの: +FastAPI によって使用されるもの: -- uvicorn - アプリケーションをロードしてサーブするサーバーのため。 -- orjson - `ORJSONResponse`を使用したい場合は必要です。 -- ujson - `UJSONResponse`を使用する場合は必須です。 +* uvicorn - アプリケーションをロードして提供するサーバーのため。これには `uvicorn[standard]` も含まれ、高性能なサービングに必要な依存関係(例: `uvloop`)が含まれます。 +* `fastapi-cli[standard]` - `fastapi` コマンドを提供します。 + * これには `fastapi-cloud-cli` が含まれ、FastAPI アプリケーションを FastAPI Cloud にデプロイできます。 -これらは全て `pip install fastapi[all]`でインストールできます。 +### `standard` 依存関係なし { #without-standard-dependencies } -## ライセンス +`standard` のオプション依存関係を含めたくない場合は、`pip install "fastapi[standard]"` の代わりに `pip install fastapi` でインストールできます。 -このプロジェクトは MIT ライセンスです。 +### `fastapi-cloud-cli` なし { #without-fastapi-cloud-cli } + +標準の依存関係を含めつつ `fastapi-cloud-cli` を除外して FastAPI をインストールしたい場合は、`pip install "fastapi[standard-no-fastapi-cloud-cli]"` でインストールできます。 + +### 追加のオプション依存関係 { #additional-optional-dependencies } + +追加でインストールしたい依存関係があります。 + +追加のオプション Pydantic 依存関係: + +* pydantic-settings - 設定管理のため。 +* pydantic-extra-types - Pydantic で使用する追加の型のため。 + +追加のオプション FastAPI 依存関係: + +* orjson - `ORJSONResponse` を使用したい場合に必要です。 +* ujson - `UJSONResponse` を使用したい場合に必要です。 + +## ライセンス { #license } + +このプロジェクトは MIT ライセンスの条項の下でライセンスされています。 diff --git a/docs/ja/docs/learn/index.md b/docs/ja/docs/learn/index.md index 2f24c670a7..bcdb1e37ee 100644 --- a/docs/ja/docs/learn/index.md +++ b/docs/ja/docs/learn/index.md @@ -1,5 +1,5 @@ -# 学習 +# 学習 { #learn } ここでは、**FastAPI** を学習するための入門セクションとチュートリアルを紹介します。 -これは、FastAPIを学習するにあたっての**書籍**や**コース**であり、**公式**かつ推奨される方法とみなすことができます 😎 +これは、**書籍**や**コース**、FastAPIを学習するための**公式**かつ推奨される方法とみなすことができます。😎 diff --git a/docs/ja/docs/project-generation.md b/docs/ja/docs/project-generation.md index daef52efae..c930fb557c 100644 --- a/docs/ja/docs/project-generation.md +++ b/docs/ja/docs/project-generation.md @@ -1,84 +1,28 @@ -# プロジェクト生成 - テンプレート +# Full Stack FastAPI テンプレート { #full-stack-fastapi-template } -プロジェクトジェネレーターは、初期設定、セキュリティ、データベース、初期APIエンドポイントなどの多くが含まれているため、プロジェクトの開始に利用できます。 +テンプレートは通常、特定のセットアップが含まれていますが、柔軟でカスタマイズできるように設計されています。これにより、プロジェクトの要件に合わせて変更・適応でき、優れた出発点になります。🏁 -プロジェクトジェネレーターは常に非常に意見が分かれる設定がされており、ニーズに合わせて更新および調整する必要があります。しかしきっと、プロジェクトの良い出発点となるでしょう。 +このテンプレートを使って開始できます。初期セットアップ、セキュリティ、データベース、いくつかのAPIエンドポイントがすでに用意されています。 -## フルスタック FastAPI PostgreSQL +GitHubリポジトリ: Full Stack FastAPI Template -GitHub: https://github.com/tiangolo/full-stack-fastapi-postgresql +## Full Stack FastAPI テンプレート - 技術スタックと機能 { #full-stack-fastapi-template-technology-stack-and-features } -### フルスタック FastAPI PostgreSQL - 機能 - -* 完全な**Docker**インテグレーション (Dockerベース)。 -* Docker Swarm モードデプロイ。 -* ローカル開発環境向けの**Docker Compose**インテグレーションと最適化。 -* UvicornとGunicornを使用した**リリース可能な** Python web サーバ。 -* Python **FastAPI** バックエンド: - * **高速**: **NodeJS** や **Go** 並みのとても高いパフォーマンス (Starlette と Pydantic のおかげ)。 - * **直感的**: 素晴らしいエディタのサポートや 補完。 デバッグ時間の短縮。 - * **簡単**: 簡単に利用、習得できるようなデザイン。ドキュメントを読む時間を削減。 - * **短い**: コードの重複を最小限に。パラメータ宣言による複数の機能。 - * **堅牢性**: 自動対話ドキュメントを使用した、本番環境で使用できるコード。 - * **標準規格準拠**: API のオープンスタンダードに基く、完全な互換性: OpenAPIJSON スキーマ。 - * 自動バリデーション、シリアライゼーション、対話的なドキュメント、OAuth2 JWTトークンを用いた認証などを含む、**その他多くの機能**。 -* **セキュアなパスワード** ハッシュ化 (デフォルトで)。 -* **JWTトークン** 認証。 -* **SQLAlchemy** モデル (Flask用の拡張と独立しているので、Celeryワーカーと直接的に併用できます)。 -* 基本的なユーザーモデル (任意の修正や削除が可能)。 -* **Alembic** マイグレーション。 -* **CORS** (Cross Origin Resource Sharing (オリジン間リソース共有))。 -* **Celery** ワーカー。バックエンドの残りの部分からモデルとコードを選択的にインポートし、使用可能。 -* Dockerと統合された**Pytest**ベースのRESTバックエンドテスト。データベースに依存せずに、全てのAPIをテスト可能。Docker上で動作するので、毎回ゼロから新たなデータストアを構築可能。(ElasticSearch、MongoDB、CouchDBなどを使用して、APIの動作をテスト可能) -* Atom HydrogenやVisual Studio Code Jupyterなどの拡張機能を使用した、リモートまたはDocker開発用の**Jupyterカーネル**との簡単なPython統合。 -* **Vue** フロントエンド: - * Vue CLIにより生成。 - * **JWT認証**の処理。 - * ログインビュー。 - * ログイン後の、メインダッシュボードビュー。 - * メインダッシュボードでのユーザー作成と編集。 - * セルフユーザー版 - * **Vuex**。 - * **Vue-router**。 - * 美しいマテリアルデザインコンポーネントのための**Vuetify**。 - * **TypeScript**。 - * **Nginx**ベースのDockerサーバ (Vue-routerとうまく協調する構成)。 - * Dockerマルチステージビルド。コンパイルされたコードの保存やコミットが不要。 - * ビルド時にフロントエンドテスト実行 (無効化も可能)。 - * 可能な限りモジュール化されているのでそのまま使用できますが、Vue CLIで再生成したり、必要に応じて作成したりして、必要なものを再利用可能。 -* PostgreSQLデータベースのための**PGAdmin**。(PHPMyAdminとMySQLを使用できるように簡単に変更可能) -* Celeryジョブ監視のための**Flower**。 -* **Traefik**を使用してフロントエンドとバックエンド間をロードバランシング。同一ドメインに配置しパスで区切る、ただし、異なるコンテナで処理。 -* Traefik統合。Let's Encrypt **HTTPS**証明書の自動生成を含む。 -* GitLab **CI** (継続的インテグレーション)。フロントエンドおよびバックエンドテストを含む。 - -## フルスタック FastAPI Couchbase - -GitHub: https://github.com/tiangolo/full-stack-fastapi-couchbase - -⚠️ **警告** ⚠️ - -ゼロから新規プロジェクトを始める場合は、ここで代替案を確認してください。 - -例えば、フルスタック FastAPI PostgreSQLのプロジェクトジェネレーターは、積極的にメンテナンスされ、利用されているのでより良い代替案かもしれません。また、すべての新機能と改善点が含まれています。 - -Couchbaseベースのジェネレーターは今も無償提供されています。恐らく正常に動作するでしょう。また、すでにそのジェネレーターで生成されたプロジェクトが存在する場合でも (ニーズに合わせてアップデートしているかもしれません)、同様に正常に動作するはずです。 - -詳細はレポジトリのドキュメントを参照して下さい。 - -## フルスタック FastAPI MongoDB - -...時間の都合等によっては、今後作成されるかもしれません。😅 🎉 - -## spaCyとFastAPIを使用した機械学習モデル - -GitHub: https://github.com/microsoft/cookiecutter-spacy-fastapi - -### spaCyとFastAPIを使用した機械学習モデル - 機能 - -* **spaCy** のNERモデルの統合。 -* **Azure Cognitive Search** のリクエストフォーマットを搭載。 -* **リリース可能な** UvicornとGunicornを使用したPythonウェブサーバ。 -* **Azure DevOps** のKubernetes (AKS) CI/CD デプロイを搭載。 -* **多言語** プロジェクトのために、セットアップ時に言語を容易に選択可能 (spaCyに組み込まれている言語の中から)。 -* **簡単に拡張可能**。spaCyだけでなく、他のモデルフレームワーク (Pytorch、Tensorflow) へ。 +- ⚡ PythonバックエンドAPI向けの [**FastAPI**](https://fastapi.tiangolo.com/ja)。 + - 🧰 PythonのSQLデータベース操作(ORM)向けの [SQLModel](https://sqlmodel.tiangolo.com)。 + - 🔍 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)。 + - 🤖 自動生成されたフロントエンドクライアント。 + - 🧪 End-to-Endテスト向けの [Playwright](https://playwright.dev)。 + - 🦇 ダークモードのサポート。 +- 🐋 開発および本番向けの [Docker Compose](https://www.docker.com)。 +- 🔒 デフォルトでの安全なパスワードハッシュ化。 +- 🔑 JWT(JSON Web Token)認証。 +- 📫 メールベースのパスワードリカバリ。 +- ✅ [Pytest](https://pytest.org) によるテスト。 +- 📞 リバースプロキシ / ロードバランサとしての [Traefik](https://traefik.io)。 +- 🚢 Docker Composeを使用したデプロイ手順(自動HTTPS証明書を処理するフロントエンドTraefikプロキシのセットアップ方法を含む)。 +- 🏭 GitHub Actionsに基づくCI(continuous integration)とCD(continuous deployment)。 diff --git a/docs/ja/docs/python-types.md b/docs/ja/docs/python-types.md index a847ce5d54..26a9e2193c 100644 --- a/docs/ja/docs/python-types.md +++ b/docs/ja/docs/python-types.md @@ -1,16 +1,16 @@ -# Pythonの型の紹介 +# Pythonの型の紹介 { #python-types-intro } -**Python 3.6以降** では「型ヒント」オプションがサポートされています。 +Pythonではオプションの「型ヒント」(「型アノテーション」とも呼ばれます)がサポートされています。 -これらの **"型ヒント"** は変数のを宣言することができる新しい構文です。(Python 3.6以降) +これらの **「型ヒント」** またはアノテーションは、変数のを宣言できる特別な構文です。 -変数に型を宣言することでエディターやツールがより良いサポートを提供することができます。 +変数に型を宣言することで、エディターやツールがより良いサポートを提供できます。 -ここではPythonの型ヒントについての **クイックチュートリアル/リフレッシュ** で、**FastAPI**でそれらを使用するために必要な最低限のことだけをカバーしています。...実際には本当に少ないです。 +これはPythonの型ヒントについての **クイックチュートリアル/リフレッシュ** にすぎません。**FastAPI** で使用するために必要な最低限のことだけをカバーしています。...実際には本当に少ないです。 **FastAPI** はすべてこれらの型ヒントに基づいており、多くの強みと利点を与えてくれます。 -しかしたとえまったく **FastAPI** を使用しない場合でも、それらについて少し学ぶことで利点を得ることができるでしょう。 +しかし、たとえ **FastAPI** をまったく使用しない場合でも、それらについて少し学ぶことで利点を得られます。 /// note | 備考 @@ -18,14 +18,13 @@ /// -## 動機 +## 動機 { #motivation } 簡単な例から始めてみましょう: -{* ../../docs_src/python_types/tutorial001.py *} +{* ../../docs_src/python_types/tutorial001_py39.py *} - -このプログラムを実行すると以下が出力されます: +このプログラムを呼び出すと、以下が出力されます: ``` John Doe @@ -35,12 +34,11 @@ John Doe * `first_name`と`last_name`を取得します。 * `title()`を用いて、それぞれの最初の文字を大文字に変換します。 -* 真ん中にスペースを入れて連結します。 +* 真ん中にスペースを入れて連結します。 -{* ../../docs_src/python_types/tutorial001.py hl[2] *} +{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *} - -### 編集 +### 編集 { #edit-it } これはとても簡単なプログラムです。 @@ -50,7 +48,7 @@ John Doe しかし、そうすると「最初の文字を大文字に変換するあのメソッド」を呼び出す必要があります。 -それは`upper`でしたか?`uppercase`でしたか?それとも`first_uppercase`?または`capitalize`? +それは`upper`でしたか?`uppercase`でしたか?`first_uppercase`?`capitalize`? そして、古くからプログラマーの友人であるエディタで自動補完を試してみます。 @@ -58,13 +56,13 @@ John Doe しかし、悲しいことに、これはなんの役にも立ちません: - + -### 型の追加 +### 型の追加 { #add-types } 先ほどのコードから一行変更してみましょう。 -以下の関数のパラメータ部分を: +関数のパラメータである次の断片を、以下から: ```Python first_name, last_name @@ -80,8 +78,7 @@ John Doe それが「型ヒント」です: -{* ../../docs_src/python_types/tutorial002.py hl[1] *} - +{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *} これは、以下のようにデフォルト値を宣言するのと同じではありません: @@ -95,41 +92,39 @@ John Doe そして、通常、型ヒントを追加しても、それらがない状態と起こることは何も変わりません。 -しかし今、あなたが再びその関数を作成している最中に、型ヒントを使っていると想像してみて下さい。 +しかし今、あなたが再びその関数を作成している最中に、型ヒントを使っていると想像してみてください。 同じタイミングで`Ctrl+Space`で自動補完を実行すると、以下のようになります: - + -これであれば、あなたは「ベルを鳴らす」一つを見つけるまで、オプションを見て、スクロールすることができます: +これであれば、あなたは「ベルを鳴らす」ものを見つけるまで、オプションを見てスクロールできます: - + -## より強い動機 +## より強い動機 { #more-motivation } この関数を見てください。すでに型ヒントを持っています: -{* ../../docs_src/python_types/tutorial003.py hl[1] *} +{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *} +エディタは変数の型を知っているので、補完だけでなく、エラーチェックをすることもできます: -エディタは変数の型を知っているので、補完だけでなく、エラーチェックをすることもできます。 - - + これで`age`を`str(age)`で文字列に変換して修正する必要があることがわかります: -{* ../../docs_src/python_types/tutorial004.py hl[2] *} +{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *} +## 型の宣言 { #declaring-types } -## 型の宣言 - -関数のパラメータとして、型ヒントを宣言している主な場所を確認しました。 +型ヒントを宣言する主な場所を見てきました。関数のパラメータです。 これは **FastAPI** で使用する主な場所でもあります。 -### 単純な型 +### 単純な型 { #simple-types } -`str`だけでなく、Pythonの標準的な型すべてを宣言することができます。 +`str`だけでなく、Pythonの標準的な型すべてを宣言できます。 例えば、以下を使用可能です: @@ -138,40 +133,47 @@ John Doe * `bool` * `bytes` -{* ../../docs_src/python_types/tutorial005.py hl[1] *} +{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *} +### 型パラメータを持つジェネリック型 { #generic-types-with-type-parameters } -### 型パラメータを持つジェネリック型 +データ構造の中には、`dict`、`list`、`set`、`tuple`のように他の値を含むことができるものがあります。また内部の値も独自の型を持つことができます。 -データ構造の中には、`dict`、`list`、`set`、そして`tuple`のように他の値を含むことができるものがあります。また内部の値も独自の型を持つことができます。 +内部の型を持つこれらの型は「**generic**」型と呼ばれます。そして、内部の型も含めて宣言することが可能です。 -これらの型や内部の型を宣言するには、Pythonの標準モジュール`typing`を使用します。 +これらの型や内部の型を宣言するには、Pythonの標準モジュール`typing`を使用できます。これらの型ヒントをサポートするために特別に存在しています。 -これらの型ヒントをサポートするために特別に存在しています。 +#### 新しいPythonバージョン { #newer-versions-of-python } -#### `List` +`typing`を使う構文は、Python 3.6から最新バージョンまで(Python 3.9、Python 3.10などを含む)すべてのバージョンと **互換性** があります。 + +Pythonが進化するにつれ、**新しいバージョン** ではこれらの型アノテーションへのサポートが改善され、多くの場合、型アノテーションを宣言するために`typing`モジュールをインポートして使う必要すらなくなります。 + +プロジェクトでより新しいPythonバージョンを選べるなら、その追加のシンプルさを活用できます。 + +ドキュメント全体で、Pythonの各バージョンと互換性のある例(差分がある場合)を示しています。 + +例えば「**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 } 例えば、`str`の`list`の変数を定義してみましょう。 -`typing`から`List`をインポートします(大文字の`L`を含む): +同じコロン(`:`)の構文で変数を宣言します。 -{* ../../docs_src/python_types/tutorial006.py hl[1] *} +型として、`list`を指定します。 +リストはいくつかの内部の型を含む型なので、それらを角括弧で囲みます: -同じようにコロン(`:`)の構文で変数を宣言します。 +{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *} -型として、`List`を入力します。 - -リストはいくつかの内部の型を含む型なので、それらを角括弧で囲んでいます。 - -{* ../../docs_src/python_types/tutorial006.py hl[4] *} - - -/// tip | 豆知識 +/// info | 情報 角括弧内の内部の型は「型パラメータ」と呼ばれています。 -この場合、`str`は`List`に渡される型パラメータです。 +この場合、`str`は`list`に渡される型パラメータです。 /// @@ -179,86 +181,203 @@ John Doe そうすることで、エディタはリストの項目を処理している間にもサポートを提供できます。 - + -タイプがなければ、それはほぼ不可能です。 +型がなければ、それはほぼ不可能です。 変数`item`はリスト`items`の要素の一つであることに注意してください。 それでも、エディタはそれが`str`であることを知っていて、そのためのサポートを提供しています。 -#### `Tuple` と `Set` +#### Tuple と Set { #tuple-and-set } `tuple`と`set`の宣言も同様です: -{* ../../docs_src/python_types/tutorial007.py hl[1,4] *} - +{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *} つまり: -* 変数`items_t`は`int`、`int`、`str`の3つの項目を持つ`tuple`です +* 変数`items_t`は`int`、別の`int`、`str`の3つの項目を持つ`tuple`です。 +* 変数`items_s`は`set`であり、その各項目は`bytes`型です。 -* 変数`items_s`はそれぞれの項目が`bytes`型である`set`です。 +#### Dict { #dict } -#### `Dict` - -`dict`を宣言するためには、カンマ区切りで2つの型パラメータを渡します。 +`dict`を定義するには、カンマ区切りで2つの型パラメータを渡します。 最初の型パラメータは`dict`のキーです。 -2番目の型パラメータは`dict`の値です。 - -{* ../../docs_src/python_types/tutorial008.py hl[1,4] *} +2番目の型パラメータは`dict`の値です: +{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *} つまり: -* 変数`prices`は`dict`であり: - * この`dict`のキーは`str`型です。(つまり、各項目の名前) - * この`dict`の値は`float`型です。(つまり、各項目の価格) +* 変数`prices`は`dict`です: + * この`dict`のキーは`str`型です(例えば、各項目の名前)。 + * この`dict`の値は`float`型です(例えば、各項目の価格)。 -#### `Optional` +#### Union { #union } -また、`Optional`を使用して、変数が`str`のような型を持つことを宣言することもできますが、それは「オプション」であり、`None`にすることもできます。 +変数が**複数の型のいずれか**になり得ることを宣言できます。例えば、`int`または`str`です。 -```Python hl_lines="1 4" -{!../../docs_src/python_types/tutorial009.py!} +Python 3.6以上(Python 3.10を含む)では、`typing`の`Union`型を使い、角括弧の中に受け付ける可能性のある型を入れられます。 + +Python 3.10では、受け付ける可能性のある型を縦棒(`|`)で区切って書ける **新しい構文** もあります。 + +//// tab | Python 3.10+ + +```Python hl_lines="1" +{!> ../../docs_src/python_types/tutorial008b_py310.py!} ``` -ただの`str`の代わりに`Optional[str]`を使用することで、エディタは値が常に`str`であると仮定している場合に実際には`None`である可能性があるエラーを検出するのに役立ちます。 +//// -#### ジェネリック型 +//// tab | Python 3.9+ -以下のように角括弧で型パラメータを取る型を: +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial008b_py39.py!} +``` -* `List` -* `Tuple` -* `Set` -* `Dict` +//// + +どちらの場合も、`item`は`int`または`str`になり得ることを意味します。 + +#### `None`の可能性 { #possibly-none } + +値が`str`のような型を持つ可能性がある一方で、`None`にもなり得ることを宣言できます。 + +Python 3.6以上(Python 3.10を含む)では、`typing`モジュールから`Optional`をインポートして使うことで宣言できます。 + +```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`より`Union`をおすすめします。というのも「**optional**」という単語は値がオプションであることを示唆するように見えますが、実際には「`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-types } + +角括弧で型パラメータを取るこれらの型は、例えば次のように **Generic types** または **Generics** と呼ばれます: + +//// tab | Python 3.10+ + +同じ組み込み型をジェネリクスとして(角括弧と内部の型で)使えます: + +* `list` +* `tuple` +* `set` +* `dict` + +また、これまでのPythonバージョンと同様に、`typing`モジュールから: + +* `Union` * `Optional` -* ...など +* ...and others. -**ジェネリック型** または **ジェネリクス** と呼びます。 +Python 3.10では、ジェネリクスの`Union`や`Optional`を使う代替として、型のユニオンを宣言するために縦棒(`|`)を使えます。これはずっと良く、よりシンプルです。 -### 型としてのクラス +//// + +//// tab | Python 3.9+ + +同じ組み込み型をジェネリクスとして(角括弧と内部の型で)使えます: + +* `list` +* `tuple` +* `set` +* `dict` + +そして`typing`モジュールのジェネリクス: + +* `Union` +* `Optional` +* ...and others. + +//// + +### 型としてのクラス { #classes-as-types } 変数の型としてクラスを宣言することもできます。 -例えば、`Person`クラスという名前のクラスがあるとしましょう: +名前を持つ`Person`クラスがあるとしましょう: -{* ../../docs_src/python_types/tutorial010.py hl[1,2,3] *} +{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *} +変数を`Person`型として宣言できます: -変数の型を`Person`として宣言することができます: - -{* ../../docs_src/python_types/tutorial010.py hl[6] *} - +{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *} そして、再び、すべてのエディタのサポートを得ることができます: - + -## Pydanticのモデル +これは「`one_person`はクラス`Person`の**インスタンス**である」ことを意味します。 + +「`one_person`は`Person`という名前の**クラス**である」という意味ではありません。 + +## Pydanticのモデル { #pydantic-models } Pydantic はデータ検証を行うためのPythonライブラリです。 @@ -266,18 +385,17 @@ John Doe そして、それぞれの属性は型を持ちます。 -さらに、いくつかの値を持つクラスのインスタンスを作成すると、その値を検証し、適切な型に変換して(もしそうであれば)全てのデータを持つオブジェクトを提供してくれます。 +さらに、いくつかの値を持つクラスのインスタンスを作成すると、その値を検証し、適切な型に変換して(もしそうであれば)すべてのデータを持つオブジェクトを提供してくれます。 また、その結果のオブジェクトですべてのエディタのサポートを受けることができます。 -Pydanticの公式ドキュメントから引用: - -{* ../../docs_src/python_types/tutorial011.py *} +Pydanticの公式ドキュメントからの例: +{* ../../docs_src/python_types/tutorial011_py310.py *} /// info | 情報 -Pydanticについてより学びたい方はドキュメントを参照してください. +Pydanticの詳細はドキュメントを参照してください。 /// @@ -285,30 +403,62 @@ Pydanticについてより学びたい方はRequired Optional fieldsを参照してください。 + +/// + +## メタデータアノテーション付き型ヒント { #type-hints-with-metadata-annotations } + +Pythonには、`Annotated`を使って型ヒントに**追加のメタデータ**を付与できる機能もあります。 + +Python 3.9以降、`Annotated`は標準ライブラリの一部なので、`typing`からインポートできます。 + +{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *} + +Python自体は、この`Annotated`で何かをするわけではありません。また、エディタや他のツールにとっても、型は依然として`str`です。 + +しかし、`Annotated`内のこのスペースを使って、アプリケーションをどのように動作させたいかについての追加メタデータを **FastAPI** に提供できます。 + +覚えておくべき重要な点は、`Annotated`に渡す**最初の*型パラメータ***が**実際の型**であることです。残りは、他のツール向けのメタデータにすぎません。 + +今のところは、`Annotated`が存在し、それが標準のPythonであることを知っておけば十分です。😎 + +後で、これがどれほど**強力**になり得るかを見ることになります。 + +/// tip | 豆知識 + +これが **標準のPython** であるという事実は、エディタで、使用しているツール(コードの解析やリファクタリングなど)とともに、**可能な限り最高の開発体験**が得られることを意味します。 ✨ + +また、あなたのコードが他の多くのPythonツールやライブラリとも非常に互換性が高いことも意味します。 🚀 + +/// + +## **FastAPI**での型ヒント { #type-hints-in-fastapi } **FastAPI** はこれらの型ヒントを利用していくつかのことを行います。 -**FastAPI** では型ヒントを使って型パラメータを宣言すると以下のものが得られます: +**FastAPI** では型ヒントを使ってパラメータを宣言すると以下のものが得られます: -* **エディタサポート**. -* **型チェック**. +* **エディタサポート**。 +* **型チェック**。 -...そして **FastAPI** は同じように宣言をすると、以下のことを行います: +...そして **FastAPI** は同じ宣言を使って、以下のことを行います: -* **要件の定義**: リクエストパスパラメータ、クエリパラメータ、ヘッダー、ボディ、依存関係などから要件を定義します。 -* **データの変換**: リクエストのデータを必要な型に変換します。 -* **データの検証**: リクエストごとに: +* **要件の定義**: リクエストのパスパラメータ、クエリパラメータ、ヘッダー、ボディ、依存関係などから要件を定義します。 +* **データの変換**: リクエストから必要な型にデータを変換します。 +* **データの検証**: 各リクエストから来るデータについて: * データが無効な場合にクライアントに返される **自動エラー** を生成します。 -* **ドキュメント** OpenAPIを使用したAPI: - * 自動的に対話型ドキュメントのユーザーインターフェイスで使用されます。 +* OpenAPIを使用してAPIを**ドキュメント化**します: + * これは自動の対話型ドキュメントのユーザーインターフェイスで使われます。 すべてが抽象的に聞こえるかもしれません。心配しないでください。 この全ての動作は [チュートリアル - ユーザーガイド](tutorial/index.md){.internal-link target=_blank}で見ることができます。 -重要なのは、Pythonの標準的な型を使うことで、(クラスやデコレータなどを追加するのではなく)1つの場所で **FastAPI** が多くの作業を代わりにやってくれているということです。 +重要なのは、Pythonの標準的な型を使うことで、(クラスやデコレータなどを追加するのではなく)1つの場所で **FastAPI** が多くの作業を代わりにやってくれているということです。 /// info | 情報 -すでにすべてのチュートリアルを終えて、型についての詳細を見るためにこのページに戻ってきた場合は、`mypy`のチートシートを参照してください +すでにすべてのチュートリアルを終えて、型についての詳細を見るためにこのページに戻ってきた場合は、良いリソースとして`mypy`の「チートシート」があります。 /// diff --git a/docs/ja/docs/tutorial/background-tasks.md b/docs/ja/docs/tutorial/background-tasks.md index b289faf12d..0ed41ce114 100644 --- a/docs/ja/docs/tutorial/background-tasks.md +++ b/docs/ja/docs/tutorial/background-tasks.md @@ -1,4 +1,4 @@ -# バックグラウンドタスク +# バックグラウンドタスク { #background-tasks } レスポンスを返した *後に* 実行されるバックグラウンドタスクを定義できます。 @@ -9,17 +9,17 @@ * 作業実行後のメール通知: * メールサーバーへの接続とメールの送信は「遅い」(数秒) 傾向があるため、すぐにレスポンスを返し、バックグラウンドでメール通知ができます。 * データ処理: - * たとえば、時間のかかる処理を必要とするファイル受信時には、「受信済み」(HTTP 202) のレスポンスを返し、バックグラウンドで処理できます。 + * たとえば、時間のかかる処理を必要とするファイル受信時には、「Accepted」(HTTP 202) のレスポンスを返し、バックグラウンドで処理できます。 -## `BackgroundTasks` の使用 +## `BackgroundTasks` の使用 { #using-backgroundtasks } -まず初めに、`BackgroundTasks` をインポートし、` BackgroundTasks` の型宣言と共に、*path operation 関数* のパラメーターを定義します: +まず初めに、`BackgroundTasks` をインポートし、`BackgroundTasks` の型宣言と共に、*path operation function* のパラメーターを定義します: -{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *} +{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *} **FastAPI** は、`BackgroundTasks` 型のオブジェクトを作成し、そのパラメーターに渡します。 -## タスク関数の作成 +## タスク関数の作成 { #create-a-task-function } バックグラウンドタスクとして実行される関数を作成します。 @@ -31,13 +31,13 @@ また、書き込み操作では `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 } -*path operations 関数* 内で、`.add_task()` メソッドを使用してタスク関数を *background tasks* オブジェクトに渡します。 +*path operation function* 内で、`.add_task()` メソッドを使用してタスク関数を *background tasks* オブジェクトに渡します。 -{* ../../docs_src/background_tasks/tutorial001.py hl[14] *} +{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *} `.add_task()` は以下の引数を受け取ります: @@ -45,40 +45,42 @@ * タスク関数に順番に渡す必要のある引数の列 (`email`)。 * タスク関数に渡す必要のあるキーワード引数 (`message="some notification"`)。 -## 依存性注入 +## 依存性注入 { #dependency-injection } -`BackgroundTasks` の使用は依存性注入システムでも機能し、様々な階層 (*path operations 関数*、依存性 (依存可能性)、サブ依存性など) で `BackgroundTasks` 型のパラメーターを宣言できます。 +`BackgroundTasks` の使用は依存性注入システムでも機能し、様々な階層 (*path operation function*、依存性 (dependable)、サブ依存性など) で `BackgroundTasks` 型のパラメーターを宣言できます。 -**FastAPI** は、それぞれの場合の処理​​方法と同じオブジェクトの再利用方法を知っているため、すべてのバックグラウンドタスクがマージされ、バックグラウンドで後で実行されます。 +**FastAPI** は、それぞれの場合の処理​​方法と同じオブジェクトの再利用方法を知っているため、すべてのバックグラウンドタスクがマージされ、バックグラウンドで後で実行されます: + + +{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *} -{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *} この例では、レスポンスが送信された *後* にメッセージが `log.txt` ファイルに書き込まれます。 リクエストにクエリがあった場合、バックグラウンドタスクでログに書き込まれます。 -そして、*path operations 関数* で生成された別のバックグラウンドタスクは、`email` パスパラメータを使用してメッセージを書き込みます。 +そして、*path operation function* で生成された別のバックグラウンドタスクは、`email` パスパラメータを使用してメッセージを書き込みます。 -## 技術的な詳細 +## 技術的な詳細 { #technical-details } `BackgroundTasks` クラスは、`starlette.background`から直接取得されます。 これは、FastAPI に直接インポート/インクルードされるため、`fastapi` からインポートできる上に、`starlette.background`から別の `BackgroundTask` (末尾に `s` がない) を誤ってインポートすることを回避できます。 -`BackgroundTasks`のみを使用することで (`BackgroundTask` ではなく)、`Request` オブジェクトを直接使用する場合と同様に、それを *path operations 関数* パラメーターとして使用し、**FastAPI** に残りの処理を任せることができます。 +`BackgroundTasks`のみを使用することで (`BackgroundTask` ではなく)、`Request` オブジェクトを直接使用する場合と同様に、それを *path operation function* パラメーターとして使用し、**FastAPI** に残りの処理を任せることができます。 それでも、FastAPI で `BackgroundTask` を単独で使用することは可能ですが、コード内でオブジェクトを作成し、それを含むStarlette `Response` を返す必要があります。 -詳細については、バックグラウンドタスクに関する Starlette の公式ドキュメントを参照して下さい。 +詳細については、Starlette のバックグラウンドタスクに関する公式ドキュメントを参照して下さい。 -## 警告 +## 注意 { #caveat } -大量のバックグラウンド計算が必要であり、必ずしも同じプロセスで実行する必要がない場合 (たとえば、メモリや変数などを共有する必要がない場合)、Celery のようなより大きな他のツールを使用するとメリットがあるかもしれません。 +大量のバックグラウンド計算が必要であり、必ずしも同じプロセスで実行する必要がない場合 (たとえば、メモリや変数などを共有する必要がない場合)、Celery のようなより大きな他のツールを使用するとメリットがあるかもしれません。 これらは、より複雑な構成、RabbitMQ や Redis などのメッセージ/ジョブキューマネージャーを必要とする傾向がありますが、複数のプロセス、特に複数のサーバーでバックグラウンドタスクを実行できます。 ただし、同じ **FastAPI** アプリから変数とオブジェクトにアクセスする必要がある場合、または小さなバックグラウンドタスク (電子メール通知の送信など) を実行する必要がある場合は、単に `BackgroundTasks` を使用できます。 -## まとめ +## まとめ { #recap } -`BackgroundTasks` をインポートして、*path operations 関数* や依存関係のパラメータに `BackgroundTasks`を使用し、バックグラウンドタスクを追加して下さい。 +*path operation functions* と依存性のパラメータで `BackgroundTasks`をインポートして使用し、バックグラウンドタスクを追加して下さい。 diff --git a/docs/ja/docs/tutorial/body-fields.md b/docs/ja/docs/tutorial/body-fields.md index ce5630351e..234c99d529 100644 --- a/docs/ja/docs/tutorial/body-fields.md +++ b/docs/ja/docs/tutorial/body-fields.md @@ -1,12 +1,13 @@ -# ボディ - フィールド +# ボディ - フィールド { #body-fields } `Query`や`Path`、`Body`を使って *path operation関数* のパラメータに追加のバリデーションやメタデータを宣言するのと同じように、Pydanticの`Field`を使ってPydanticモデルの内部でバリデーションやメタデータを宣言することができます。 -## `Field`のインポート +## `Field`のインポート { #import-field } まず、以下のようにインポートします: -{* ../../docs_src/body_fields/tutorial001.py hl[4] *} +{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *} + /// warning | 注意 @@ -14,11 +15,11 @@ /// -## モデルの属性の宣言 +## モデルの属性の宣言 { #declare-model-attributes } 以下のように`Field`をモデルの属性として使用することができます: -{* ../../docs_src/body_fields/tutorial001.py hl[11,12,13,14] *} +{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *} `Field`は`Query`や`Path`、`Body`と同じように動作し、全く同様のパラメータなどを持ちます。 @@ -40,13 +41,20 @@ /// -## 追加情報の追加 +## 追加情報の追加 { #add-extra-information } 追加情報は`Field`や`Query`、`Body`などで宣言することができます。そしてそれは生成されたJSONスキーマに含まれます。 後に例を用いて宣言を学ぶ際に、追加情報を追加する方法を学べます。 -## まとめ +/// warning | 注意 + +`Field`に渡された追加のキーは、結果として生成されるアプリケーションのOpenAPIスキーマにも含まれます。 +これらのキーは必ずしもOpenAPI仕様の一部であるとは限らないため、例えば[OpenAPI validator](https://validator.swagger.io/)などの一部のOpenAPIツールは、生成されたスキーマでは動作しない場合があります。 + +/// + +## まとめ { #recap } Pydanticの`Field`を使用して、モデルの属性に追加のバリデーションやメタデータを宣言することができます。 diff --git a/docs/ja/docs/tutorial/body-multiple-params.md b/docs/ja/docs/tutorial/body-multiple-params.md index cbfdda4b21..4ce77cc0dc 100644 --- a/docs/ja/docs/tutorial/body-multiple-params.md +++ b/docs/ja/docs/tutorial/body-multiple-params.md @@ -1,24 +1,24 @@ -# ボディ - 複数のパラメータ +# ボディ - 複数のパラメータ { #body-multiple-parameters } -これまで`Path`と`Query`をどう使うかを見てきましたが、リクエストボディの宣言のより高度な使い方を見てみましょう。 +これまで`Path`と`Query`をどう使うかを見てきましたが、リクエストボディ宣言のより高度な使い方を見てみましょう。 -## `Path`、`Query`とボディパラメータを混ぜる +## `Path`、`Query`とボディパラメータを混ぜる { #mix-path-query-and-body-parameters } -まず、もちろん、`Path`と`Query`とリクエストボディのパラメータの宣言は自由に混ぜることができ、 **FastAPI** は何をするべきかを知っています。 +まず、もちろん、`Path`と`Query`とリクエストボディのパラメータ宣言は自由に混ぜることができ、 **FastAPI** は何をするべきかを知っています。 -また、デフォルトの`None`を設定することで、ボディパラメータをオプションとして宣言することもできます: +また、デフォルトを`None`に設定することで、ボディパラメータをオプションとして宣言することもできます: -{* ../../docs_src/body_multiple_params/tutorial001.py hl[19,20,21] *} +{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *} /// note | 備考 -この場合、ボディから取得する`item`はオプションであることに注意してください。デフォルト値は`None`です。 +この場合、ボディから取得する`item`はオプションであることに注意してください。デフォルト値が`None`になっているためです。 /// -## 複数のボディパラメータ +## 複数のボディパラメータ { #multiple-body-parameters } -上述の例では、*path operations*は`item`の属性を持つ以下のようなJSONボディを期待していました: +上述の例では、*path operations*は`Item`の属性を持つ以下のようなJSONボディを期待していました: ```JSON { @@ -31,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モデルである2つのパラメータ)があることに気付きます。 -そのため、パラメータ名をボディのキー(フィールド名)として使用し、以下のようなボディを期待しています: +この場合、**FastAPI**は関数内に複数のボディパラメータがあることに気付きます(Pydanticモデルである2つのパラメータがあります)。 + +そのため、パラメータ名をボディのキー(フィールド名)として使用し、以下のようなボディを期待します: ```JSON { @@ -62,7 +63,7 @@ 複合データの検証を行い、OpenAPIスキーマや自動ドキュメントのように文書化してくれます。 -## ボディ内の単数値 +## ボディ内の単数値 { #singular-values-in-body } クエリとパスパラメータの追加データを定義するための `Query` と `Path` があるのと同じように、 **FastAPI** は同等の `Body` を提供します。 @@ -72,12 +73,11 @@ しかし、`Body`を使用して、**FastAPI** に別のボディキーとして扱うように指示することができます: +{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *} -{* ../../docs_src/body_multiple_params/tutorial003.py hl[23] *} この場合、**FastAPI** は以下のようなボディを期待します: - ```JSON { "item": { @@ -96,41 +96,48 @@ 繰り返しになりますが、データ型の変換、検証、文書化などを行います。 -## 複数のボディパラメータとクエリ +## 複数のボディパラメータとクエリ { #multiple-body-params-and-query } もちろん、ボディパラメータに加えて、必要に応じて追加のクエリパラメータを宣言することもできます。 -デフォルトでは、単数値はクエリパラメータとして解釈されるので、明示的に `Query` を追加する必要はありません。 +デフォルトでは、単数値はクエリパラメータとして解釈されるので、明示的に `Query` を追加する必要はなく、次のようにできます: ```Python -q: str = None +q: str | None = None ``` -以下において: +またはPython 3.10以上では: -{* ../../docs_src/body_multiple_params/tutorial004.py hl[27] *} +```Python +q: Union[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`を1つだけ持っているとしましょう。 +Pydanticモデル`Item`の単一の`item`ボディパラメータしかないとしましょう。 デフォルトでは、**FastAPI**はそのボディを直接期待します。 -しかし、追加のボディパラメータを宣言したときのように、キー `item` を持つ JSON とその中のモデルの内容を期待したい場合は、特別な `Body` パラメータ `embed` を使うことができます: +しかし、追加のボディパラメータを宣言したときのように、キー `item` を持つ JSON と、その中のモデル内容を期待したい場合は、特別な `Body` パラメータ `embed` を使うことができます: ```Python -item: Item = Body(..., embed=True) +item: Item = Body(embed=True) ``` 以下において: -{* ../../docs_src/body_multiple_params/tutorial005.py hl[17] *} +{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *} + この場合、**FastAPI** は以下のようなボディを期待します: @@ -156,9 +163,9 @@ item: Item = Body(..., embed=True) } ``` -## まとめ +## まとめ { #recap } -リクエストが単一のボディしか持てない場合でも、*path operation関数*に複数のボディパラメータを追加することができます。 +リクエストが単一のボディしか持てない場合でも、*path operation function*に複数のボディパラメータを追加することができます。 しかし、**FastAPI** はそれを処理し、関数内の正しいデータを与え、*path operation*内の正しいスキーマを検証し、文書化します。 diff --git a/docs/ja/docs/tutorial/body-nested-models.md b/docs/ja/docs/tutorial/body-nested-models.md index a1680d10f2..24eb302082 100644 --- a/docs/ja/docs/tutorial/body-nested-models.md +++ b/docs/ja/docs/tutorial/body-nested-models.md @@ -1,36 +1,26 @@ -# ボディ - ネストされたモデル +# ボディ - ネストされたモデル { #body-nested-models } **FastAPI** を使用すると、深くネストされた任意のモデルを定義、検証、文書化、使用することができます(Pydanticのおかげです)。 -## リストのフィールド +## リストのフィールド { #list-fields } -属性をサブタイプとして定義することができます。例えば、Pythonの`list`は以下のように定義できます: +属性をサブタイプとして定義することができます。例えば、Pythonの`list`: -{* ../../docs_src/body_nested_models/tutorial001.py hl[12] *} +{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *} -これにより、各項目の型は宣言されていませんが、`tags`はある項目のリストになります。 +これにより、各項目の型は宣言されていませんが、`tags`はリストになります。 -## タイプパラメータを持つリストのフィールド +## タイプパラメータを持つリストのフィールド { #list-fields-with-type-parameter } -しかし、Pythonには型や「タイプパラメータ」を使ってリストを宣言する方法があります: +しかし、Pythonには内部の型、または「タイプパラメータ」を使ってリストを宣言するための特定の方法があります: -### typingの`List`をインポート +### タイプパラメータを持つ`list`の宣言 { #declare-a-list-with-a-type-parameter } -まず、Pythonの標準の`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] ``` 型宣言の標準的なPythonの構文はこれだけです。 @@ -39,17 +29,17 @@ 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 } しかし、よく考えてみると、タグは繰り返すべきではなく、おそらくユニークな文字列になるのではないかと気付いたとします。 そして、Pythonにはユニークな項目のセットのための特別なデータ型`set`があります。 -そのため、以下のように、`Set`をインポートして`str`の`set`として`tags`を宣言することができます: +そして、`tags`を文字列のセットとして宣言できます: -{* ../../docs_src/body_nested_models/tutorial003.py hl[1,14] *} +{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *} これを使えば、データが重複しているリクエストを受けた場合でも、ユニークな項目のセットに変換されます。 @@ -57,27 +47,27 @@ my_list: List[str] また、それに応じて注釈をつけたり、文書化したりします。 -## ネストされたモデル +## ネストされたモデル { #nested-models } Pydanticモデルの各属性には型があります。 しかし、その型はそれ自体が別のPydanticモデルである可能性があります。 -そのため、特定の属性名、型、バリデーションを指定して、深くネストしたJSON`object`を宣言することができます。 +そのため、特定の属性名、型、バリデーションを指定して、深くネストしたJSON「オブジェクト」を宣言することができます。 すべては、任意のネストにされています。 -### サブモデルの定義 +### サブモデルの定義 { #define-a-submodel } 例えば、`Image`モデルを定義することができます: -{* ../../docs_src/body_nested_models/tutorial004.py hl[9,10,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** が以下のようなボディを期待することを意味します: @@ -102,23 +92,23 @@ Pydanticモデルの各属性には型があります。 * データの検証 * 自動文書化 -## 特殊な型とバリデーション +## 特殊な型とバリデーション { #special-types-and-validation } -`str`や`int`、`float`のような通常の単数型の他にも、`str`を継承したより複雑な単数型を使うこともできます。 +`str`や`int`、`float`などの通常の単数型の他にも、`str`を継承したより複雑な単数型を使うこともできます。 -すべてのオプションをみるには、Pydanticのエキゾチック な型のドキュメントを確認してください。次の章でいくつかの例をみることができます。 +すべてのオプションをみるには、Pydanticの型の概要を確認してください。次の章でいくつかの例をみることができます。 -例えば、`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 } Pydanticモデルを`list`や`set`などのサブタイプとして使用することもできます: -{* ../../docs_src/body_nested_models/tutorial006.py hl[20] *} +{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *} これは、次のようなJSONボディを期待します(変換、検証、ドキュメントなど): @@ -152,59 +142,59 @@ Pydanticモデルを`list`や`set`などのサブタイプとして使用する /// -## 深くネストされたモデル +## 深くネストされたモデル { #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`は`Item`のリストであり、オプションの`Image`のリストを持っていることに注目してください。 +`Offer`は`Item`のリストであり、それらがさらにオプションの`Image`のリストを持っていることに注目してください。 /// -## 純粋なリストのボディ +## 純粋なリストのボディ { #bodies-of-pure-lists } 期待するJSONボディのトップレベルの値がJSON`array`(Pythonの`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 } -エディタのサポートもどこでも受けることができます。 +そして、あらゆる場所でエディタサポートを得られます。 以下のようにリストの中の項目でも: - + Pydanticモデルではなく、`dict`を直接使用している場合はこのようなエディタのサポートは得られません。 -しかし、それらについて心配する必要はありません。入力された辞書は自動的に変換され、出力も自動的にJSONに変換されます。 +しかし、それらについて心配する必要はありません。入力されたdictは自動的に変換され、出力も自動的にJSONに変換されます。 -## 任意の`dict`のボディ +## 任意の`dict`のボディ { #bodies-of-arbitrary-dicts } また、ある型のキーと別の型の値を持つ`dict`としてボディを宣言することもできます。 -有効なフィールド・属性名を事前に知る必要がありません(Pydanticモデルの場合のように)。 +この方法で、有効なフィールド/属性名を事前に知る必要がありません(Pydanticモデルの場合のように)。 -これは、まだ知らないキーを受け取りたいときに便利だと思います。 +これは、まだ知らないキーを受け取りたいときに便利です。 --- -他にも、`int`のように他の型のキーを持ちたい場合などに便利です。 +もうひとつ便利なケースは、別の型(例: `int`)のキーを持ちたい場合です。 -それをここで見ていきましょう。 +それをここで見ていきます。 この場合、`int`のキーと`float`の値を持つものであれば、どんな`dict`でも受け入れることができます: -{* ../../docs_src/body_nested_models/tutorial009.py hl[15] *} +{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *} /// tip | 豆知識 @@ -218,14 +208,14 @@ JSONはキーとして`str`しかサポートしていないことに注意し /// -## まとめ +## まとめ { #recap } **FastAPI** を使用すると、Pydanticモデルが提供する最大限の柔軟性を持ちながら、コードをシンプルに短く、エレガントに保つことができます。 -以下のような利点があります: +しかし、以下のような利点があります: * エディタのサポート(どこでも補完!) -* データ変換(別名:構文解析・シリアライズ) +* データ変換(別名:構文解析 / シリアライズ) * データの検証 * スキーマ文書 -* 自動文書化 +* 自動ドキュメント diff --git a/docs/ja/docs/tutorial/body-updates.md b/docs/ja/docs/tutorial/body-updates.md index ffbe52e1db..e888d5a0d9 100644 --- a/docs/ja/docs/tutorial/body-updates.md +++ b/docs/ja/docs/tutorial/body-updates.md @@ -1,16 +1,16 @@ -# ボディ - 更新 +# ボディ - 更新 { #body-updates } -## `PUT`による置換での更新 +## `PUT`による置換での更新 { #update-replacing-with-put } 項目を更新するにはHTTPの`PUT`操作を使用することができます。 -`jsonable_encoder`を用いて、入力データをJSON形式で保存できるデータに変換することができます(例:NoSQLデータベース)。例えば、`datetime`を`str`に変換します。 +`jsonable_encoder`を用いて、入力データをJSONとして保存できるデータに変換することができます(例:NoSQLデータベース)。例えば、`datetime`を`str`に変換します。 -{* ../../docs_src/body_updates/tutorial001.py hl[30,31,32,33,34,35] *} +{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *} -既存のデータを置き換えるべきデータを受け取るために`PUT`は使用されます。 +`PUT`は、既存のデータを置き換えるべきデータを受け取るために使用されます。 -### 置換についての注意 +### 置換についての注意 { #warning-about-replacing } つまり、`PUT`を使用して以下のボディで項目`bar`を更新したい場合は: @@ -22,11 +22,11 @@ } ``` -すでに格納されている属性`"tax": 20.2`を含まないため、入力モデルのデフォルト値は`"tax": 10.5`です。 +すでに格納されている属性`"tax": 20.2`を含まないため、入力モデルは`"tax": 10.5`のデフォルト値を取ります。 そして、データはその「新しい」`10.5`の`tax`と共に保存されます。 -## `PATCH`による部分的な更新 +## `PATCH`による部分的な更新 { #partial-updates-with-patch } また、HTTPの`PATCH`操作でデータを*部分的に*更新することもできます。 @@ -44,27 +44,27 @@ /// -### Pydanticの`exclude_unset`パラメータの使用 +### Pydanticの`exclude_unset`パラメータの使用 { #using-pydantics-exclude-unset-parameter } -部分的な更新を受け取りたい場合は、Pydanticモデルの`.dict()`の`exclude_unset`パラメータを使用すると非常に便利です。 +部分的な更新を受け取りたい場合は、Pydanticモデルの`.model_dump()`の`exclude_unset`パラメータを使用すると非常に便利です。 -`item.dict(exclude_unset=True)`のように。 +`item.model_dump(exclude_unset=True)`のように。 これにより、`item`モデルの作成時に設定されたデータのみを持つ`dict`が生成され、デフォルト値は除外されます。 これを使うことで、デフォルト値を省略して、設定された(リクエストで送られた)データのみを含む`dict`を生成することができます: -{* ../../docs_src/body_updates/tutorial002.py hl[34] *} +{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *} -### Pydanticの`update`パラメータ +### Pydanticの`update`パラメータの使用 { #using-pydantics-update-parameter } -ここで、`.copy()`を用いて既存のモデルのコピーを作成し、`update`パラメータに更新するデータを含む`dict`を渡すことができます。 +ここで、`.model_copy()`を用いて既存のモデルのコピーを作成し、`update`パラメータに更新するデータを含む`dict`を渡すことができます。 -`stored_item_model.copy(update=update_data)`のように: +`stored_item_model.model_copy(update=update_data)`のように: -{* ../../docs_src/body_updates/tutorial002.py hl[35] *} +{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *} -### 部分的更新のまとめ +### 部分的更新のまとめ { #partial-updates-recap } まとめると、部分的な更新を適用するには、次のようにします: @@ -75,11 +75,11 @@ * この方法では、モデル内のデフォルト値ですでに保存されている値を上書きするのではなく、ユーザーが実際に設定した値のみを更新することができます。 * 保存されているモデルのコピーを作成し、受け取った部分的な更新で属性を更新します(`update`パラメータを使用します)。 * コピーしたモデルをDBに保存できるものに変換します(例えば、`jsonable_encoder`を使用します)。 - * これはモデルの`.dict()`メソッドを再度利用することに匹敵しますが、値をJSONに変換できるデータ型、例えば`datetime`を`str`に変換します。 + * これはモデルの`.model_dump()`メソッドを再度利用することに匹敵しますが、値をJSONに変換できるデータ型になるようにし(変換し)、例えば`datetime`を`str`に変換します。 * データをDBに保存します。 * 更新されたモデルを返します。 -{* ../../docs_src/body_updates/tutorial002.py hl[30,31,32,33,34,35,36,37] *} +{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *} /// tip | 豆知識 diff --git a/docs/ja/docs/tutorial/body.md b/docs/ja/docs/tutorial/body.md index 1298eec7eb..a219faed04 100644 --- a/docs/ja/docs/tutorial/body.md +++ b/docs/ja/docs/tutorial/body.md @@ -1,40 +1,41 @@ -# リクエストボディ +# リクエストボディ { #request-body } -クライアント (ブラウザなど) からAPIにデータを送信する必要があるとき、データを **リクエストボディ (request body)** として送ります。 +クライアント(例えばブラウザ)からAPIにデータを送信する必要がある場合、**リクエストボディ**として送信します。 -**リクエスト** ボディはクライアントによってAPIへ送られます。**レスポンス** ボディはAPIがクライアントに送るデータです。 +**リクエスト**ボディは、クライアントからAPIへ送信されるデータです。**レスポンス**ボディは、APIがクライアントに送信するデータです。 -APIはほとんどの場合 **レスポンス** ボディを送らなければなりません。しかし、クライアントは必ずしも **リクエスト** ボディを送らなければいけないわけではありません。 +APIはほとんどの場合 **レスポンス** ボディを送信する必要があります。しかしクライアントは、常に **リクエストボディ** を送信する必要があるとは限りません。場合によっては、クエリパラメータ付きのパスだけをリクエストして、ボディを送信しないこともあります。 -**リクエスト** ボディを宣言するために Pydantic モデルを使用します。そして、その全てのパワーとメリットを利用します。 +**リクエスト**ボディを宣言するには、Pydantic モデルを使用し、その強力な機能とメリットをすべて利用します。 /// info | 情報 -データを送るには、`POST` (もっともよく使われる)、`PUT`、`DELETE` または `PATCH` を使うべきです。 +データを送信するには、`POST`(より一般的)、`PUT`、`DELETE`、`PATCH` のいずれかを使用すべきです。 -GET リクエストでボディを送信することは、仕様では未定義の動作ですが、FastAPI でサポートされており、非常に複雑な(極端な)ユースケースにのみ対応しています。 +`GET` リクエストでボディを送信することは仕様上は未定義の動作ですが、それでもFastAPIではサポートされています。ただし、非常に複雑/極端なユースケースのためだけです。 -非推奨なので、Swagger UIを使った対話型のドキュメントにはGETのボディ情報は表示されません。さらに、中継するプロキシが対応していない可能性があります。 +推奨されないため、Swagger UIによる対話的ドキュメントでは `GET` 使用時のボディのドキュメントは表示されず、途中のプロキシが対応していない可能性もあります。 /// -## Pydanticの `BaseModel` をインポート +## Pydanticの `BaseModel` をインポート { #import-pydantics-basemodel } -ます初めに、 `pydantic` から `BaseModel` をインポートする必要があります: +まず、`pydantic` から `BaseModel` をインポートする必要があります: -{* ../../docs_src/body/tutorial001.py hl[4] *} +{* ../../docs_src/body/tutorial001_py310.py hl[2] *} -## データモデルの作成 +## データモデルの作成 { #create-your-data-model } -そして、`BaseModel` を継承したクラスとしてデータモデルを宣言します。 +次に、`BaseModel` を継承するクラスとしてデータモデルを宣言します。 -すべての属性にpython標準の型を使用します: +すべての属性に標準のPython型を使用します: -{* ../../docs_src/body/tutorial001.py hl[7:11] *} +{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *} -クエリパラメータの宣言と同様に、モデル属性がデフォルト値をもつとき、必須な属性ではなくなります。それ以外は必須になります。オプショナルな属性にしたい場合は `None` を使用してください。 -例えば、上記のモデルは以下の様なJSON「`オブジェクト`」(もしくはPythonの `dict` ) を宣言しています: +クエリパラメータの宣言と同様に、モデル属性がデフォルト値を持つ場合は必須ではありません。そうでなければ必須です。単にオプションにするには `None` を使用してください。 + +例えば、上記のモデルは次のようなJSON「`object`」(またはPythonの `dict`)を宣言します: ```JSON { @@ -45,7 +46,7 @@ GET リクエストでボディを送信することは、仕様では未定義 } ``` -...`description` と `tax` はオプショナル (デフォルト値は `None`) なので、以下のJSON「`オブジェクト`」も有効です: +...`description` と `tax` はオプション(デフォルト値が `None`)なので、このJSON「`object`」も有効です: ```JSON { @@ -54,109 +55,112 @@ GET リクエストでボディを送信することは、仕様では未定義 } ``` -## パラメータとして宣言 +## パラメータとして宣言 { #declare-it-as-a-parameter } -*パスオペレーション* に加えるために、パスパラメータやクエリパラメータと同じ様に宣言します: +*path operation* に追加するには、パスパラメータやクエリパラメータを宣言したのと同じ方法で宣言します: -{* ../../docs_src/body/tutorial001.py hl[18] *} +{* ../../docs_src/body/tutorial001_py310.py hl[16] *} -...そして、作成したモデル `Item` で型を宣言します。 +...そして、作成したモデル `Item` を型として宣言します。 -## 結果 +## 結果 { #results } -そのPythonの型宣言だけで **FastAPI** は以下のことを行います: +そのPythonの型宣言だけで **FastAPI** は以下を行います: -* リクエストボディをJSONとして読み取ります。 -* 適当な型に変換します(必要な場合)。 +* リクエストのボディをJSONとして読み取ります。 +* 対応する型に変換します(必要な場合)。 * データを検証します。 - * データが無効な場合は、明確なエラーが返され、どこが不正なデータであったかを示します。 -* 受け取ったデータをパラメータ `item` に変換します。 - * 関数内で `Item` 型であると宣言したので、すべての属性とその型に対するエディタサポート(補完など)をすべて使用できます。 -* モデルのJSONスキーマ定義を生成し、好きな場所で使用することができます。 -* これらのスキーマは、生成されたOpenAPIスキーマの一部となり、自動ドキュメントのUIに使用されます。 + * データが無効な場合は、どこで何が不正なデータだったのかを正確に示す、分かりやすい明確なエラーを返します。 +* 受け取ったデータをパラメータ `item` に渡します。 + * 関数内で `Item` 型として宣言したため、すべての属性とその型について、エディタサポート(補完など)も利用できます。 +* モデル向けの JSON Schema 定義を生成します。プロジェクトにとって意味があるなら、他の場所でも好きなように利用できます。 +* それらのスキーマは生成されるOpenAPIスキーマの一部となり、自動ドキュメントの UIs で使用されます。 -## 自動ドキュメント生成 +## 自動ドキュメント { #automatic-docs } -モデルのJSONスキーマはOpenAPIで生成されたスキーマの一部になり、対話的なAPIドキュメントに表示されます: +モデルのJSON Schemaは、OpenAPIで生成されたスキーマの一部になり、対話的なAPIドキュメントに表示されます: -そして、それらが使われる *パスオペレーション* のそれぞれのAPIドキュメントにも表示されます: +また、それらが必要な各 *path operation* 内のAPIドキュメントでも使用されます: -## エディターサポート +## エディタサポート { #editor-support } -エディターによる型ヒントと補完が関数内で利用できます (Pydanticモデルではなく `dict` を受け取ると、同じサポートは受けられません): +エディタ上で、関数内のあらゆる場所で型ヒントと補完が得られます(Pydanticモデルの代わりに `dict` を受け取った場合は起きません): -型によるエラーチェックも可能です: +不正な型操作に対するエラーチェックも得られます: -これは偶然ではなく、このデザインに基づいてフレームワークが作られています。 +これは偶然ではなく、フレームワーク全体がその設計を中心に構築されています。 -全てのエディターで機能することを確認するために、実装前の設計時に徹底的にテストしました。 +そして、すべてのエディタで動作することを確実にするために、実装前の設計フェーズで徹底的にテストされました。 -これをサポートするためにPydantic自体にもいくつかの変更がありました。 +これをサポートするために、Pydantic自体にもいくつかの変更が加えられました。 -上記のスクリーンショットはVisual Studio Codeを撮ったものです。 +前述のスクリーンショットは Visual Studio Code で撮影されたものです。 -しかし、PyCharmやほとんどのPythonエディタでも同様なエディターサポートを受けられます: +ただし、PyCharm や、他のほとんどのPythonエディタでも同じエディタサポートを得られます: /// tip | 豆知識 -PyCharmエディタを使用している場合は、Pydantic PyCharm Pluginが使用可能です。 +エディタとして PyCharm を使用している場合、Pydantic PyCharm Plugin を使用できます。 -以下のエディターサポートが強化されます: +以下により、Pydanticモデルに対するエディタサポートが改善されます: -* 自動補完 -* 型チェック -* リファクタリング -* 検索 -* インスペクション +* auto-completion +* type checks +* refactoring +* searching +* inspections /// -## モデルの使用 +## モデルを使用する { #use-the-model } -関数内部で、モデルの全ての属性に直接アクセスできます: +関数内では、モデルオブジェクトのすべての属性に直接アクセスできます: -{* ../../docs_src/body/tutorial002.py hl[21] *} +{* ../../docs_src/body/tutorial002_py310.py *} -## リクエストボディ + パスパラメータ +## リクエストボディ + パスパラメータ { #request-body-path-parameters } パスパラメータとリクエストボディを同時に宣言できます。 -**FastAPI** はパスパラメータである関数パラメータは**パスから受け取り**、Pydanticモデルによって宣言された関数パラメータは**リクエストボディから受け取る**ということを認識します。 +**FastAPI** は、パスパラメータに一致する関数パラメータは **パスから取得** し、Pydanticモデルとして宣言された関数パラメータは **リクエストボディから取得** すべきだと認識します。 -{* ../../docs_src/body/tutorial003.py hl[17:18] *} +{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *} -## リクエストボディ + パスパラメータ + クエリパラメータ -また、**ボディ**と**パス**と**クエリ**のパラメータも同時に宣言できます。 +## リクエストボディ + パス + クエリパラメータ { #request-body-path-query-parameters } -**FastAPI** はそれぞれを認識し、適切な場所からデータを取得します。 +**body**、**path**、**query** パラメータもすべて同時に宣言できます。 -{* ../../docs_src/body/tutorial004.py hl[18] *} +**FastAPI** はそれぞれを認識し、正しい場所からデータを取得します。 -関数パラメータは以下の様に認識されます: +{* ../../docs_src/body/tutorial004_py310.py hl[16] *} -* パラメータが**パス**で宣言されている場合は、優先的にパスパラメータとして扱われます。 -* パラメータが**単数型** (`int`、`float`、`str`、`bool` など)の場合は**クエリ**パラメータとして解釈されます。 -* パラメータが **Pydantic モデル**型で宣言された場合、リクエスト**ボディ**として解釈されます。 +関数パラメータは以下のように認識されます: + +* パラメータが **path** でも宣言されている場合、パスパラメータとして使用されます。 +* パラメータが **単数型**(`int`、`float`、`str`、`bool` など)の場合、**query** パラメータとして解釈されます。 +* パラメータが **Pydanticモデル** の型として宣言されている場合、リクエスト **body** として解釈されます。 /// note | 備考 -FastAPIは、`= None`があるおかげで、`q`がオプショナルだとわかります。 +FastAPIは、デフォルト値 `= None` があるため、`q` の値が必須ではないことを認識します。 -`Optional[str]` の`Optional` はFastAPIでは使用されていません(FastAPIは`str`の部分のみ使用します)。しかし、`Optional[str]` はエディタがコードのエラーを見つけるのを助けてくれます。 +`str | None`(Python 3.10+)や `Union[str, None]`(Python 3.9+)の `Union` は、値が必須ではないことを判断するためにFastAPIでは使用されません。`= None` というデフォルト値があるため、必須ではないことを認識します。 + +しかし、型アノテーションを追加すると、エディタがより良いサポートを提供し、エラーを検出できるようになります。 /// -## Pydanticを使わない方法 +## Pydanticを使わない方法 { #without-pydantic } -もしPydanticモデルを使用したくない場合は、**Body**パラメータが利用できます。[Body - Multiple Parameters: Singular values in 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} のドキュメントを参照してください。 diff --git a/docs/ja/docs/tutorial/cookie-param-models.md b/docs/ja/docs/tutorial/cookie-param-models.md index 8285f44efd..10ffb2566e 100644 --- a/docs/ja/docs/tutorial/cookie-param-models.md +++ b/docs/ja/docs/tutorial/cookie-param-models.md @@ -1,8 +1,8 @@ -# クッキーパラメータモデル +# クッキーパラメータモデル { #cookie-parameter-models } もし関連する**複数のクッキー**から成るグループがあるなら、それらを宣言するために、**Pydanticモデル**を作成できます。🍪 -こうすることで、**複数の場所**で**そのPydanticモデルを再利用**でき、バリデーションやメタデータを、すべてのクッキーパラメータに対して一度に宣言できます。😎 +こうすることで、**複数の場所**で**そのPydanticモデルを再利用**でき、バリデーションやメタデータを、すべてのパラメータに対して一度に宣言できます。😎 /// note | 備考 @@ -16,15 +16,15 @@ /// -## クッキーにPydanticモデルを使用する +## Pydanticモデルを使用したクッキー { #cookies-with-a-pydantic-model } -必要な複数の**クッキー**パラメータを**Pydanticモデル**で宣言し、さらに、それを `Cookie` として宣言しましょう: +必要な複数の**クッキー**パラメータを**Pydanticモデル**で宣言し、さらに、パラメータを `Cookie` として宣言しましょう: {* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *} -**FastAPI**は、リクエストの**クッキー**から**それぞれのフィールド**のデータを**抽出**し、定義された**Pydanticモデル**を提供します。 +**FastAPI**は、リクエストで受け取った**クッキー**から**それぞれのフィールド**のデータを**抽出**し、定義したPydanticモデルを提供します。 -## ドキュメントの確認 +## ドキュメントの確認 { #check-the-docs } 対話的APIドキュメントUI `/docs` で、定義されているクッキーを確認できます: @@ -32,18 +32,17 @@
-/// info | 備考 - +/// info | 情報 **ブラウザがクッキーを処理し**ていますが、特別な方法で内部的に処理を行っているために、**JavaScript**からは簡単に操作**できない**ことに留意してください。 -**対話的APIドキュメントUI** `/docs` にアクセスすれば、*パスオペレーション*に関するクッキーの**ドキュメンテーション**を確認できます。 +**APIドキュメントUI** `/docs` にアクセスすれば、*path operation*に関するクッキーの**ドキュメンテーション**を確認できます。 -しかし、たとえ**クッキーデータを入力して**「Execute」をクリックしても、対話的APIドキュメントUIは**JavaScript**で動作しているためクッキーは送信されず、まるで値を入力しなかったかのような**エラー**メッセージが表示されます。 +しかし、たとえ**データを入力して**「Execute」をクリックしても、ドキュメントUIは**JavaScript**で動作しているためクッキーは送信されず、まるで値を入力しなかったかのような**エラー**メッセージが表示されます。 /// -## 余分なクッキーを禁止する +## 余分なクッキーを禁止する { #forbid-extra-cookies } 特定の(あまり一般的ではないかもしれない)ケースで、受け付けるクッキーを**制限**する必要があるかもしれません。 @@ -51,7 +50,7 @@ Pydanticのモデルの Configuration を利用して、 `extra` フィールドを `forbid` とすることができます。 -{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *} +{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *} もしクライアントが**余分なクッキー**を送ろうとすると、**エラー**レスポンスが返されます。 @@ -72,6 +71,6 @@ Pydanticのモデルの Configuration を利用して、 `extra` フィールド } ``` -## まとめ +## まとめ { #summary } **FastAPI**では、**クッキー**を宣言するために、**Pydanticモデル**を使用できます。😎 diff --git a/docs/ja/docs/tutorial/cookie-params.md b/docs/ja/docs/tutorial/cookie-params.md index 13af6d3c77..1e5a0d3cfd 100644 --- a/docs/ja/docs/tutorial/cookie-params.md +++ b/docs/ja/docs/tutorial/cookie-params.md @@ -1,20 +1,20 @@ -# クッキーのパラメータ +# クッキーのパラメータ { #cookie-parameters } クッキーのパラメータは、`Query`や`Path`のパラメータを定義するのと同じ方法で定義できます。 -## `Cookie`をインポート +## `Cookie`をインポート { #import-cookie } まず、`Cookie`をインポートします: -{* ../../docs_src/cookie_params/tutorial001.py hl[3] *} +{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *} -## `Cookie`のパラメータを宣言 +## `Cookie`のパラメータを宣言 { #declare-cookie-parameters } 次に、`Path`や`Query`と同じ構造を使ってクッキーのパラメータを宣言します。 最初の値がデフォルト値で、追加の検証パラメータや注釈パラメータをすべて渡すことができます: -{* ../../docs_src/cookie_params/tutorial001.py hl[9] *} +{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *} /// note | 技術詳細 @@ -30,6 +30,16 @@ /// -## まとめ +/// info | 情報 -クッキーは`Cookie`を使って宣言し、`Query`や`Path`と同じパターンを使用する。 +**ブラウザがクッキーを**特殊な方法で裏側で扱うため、**JavaScript** から簡単には触れられないことを念頭に置いてください。 + +`/docs` の **API docs UI** に移動すると、*path operation* のクッキーに関する **documentation** を確認できます。 + +しかし、データを **入力** して「Execute」をクリックしても、docs UI は **JavaScript** で動作するためクッキーは送信されず、値を何も書かなかったかのような **error** メッセージが表示されます。 + +/// + +## まとめ { #recap } + +クッキーは`Cookie`を使って宣言し、`Query`や`Path`と同じ共通のパターンを使用する。 diff --git a/docs/ja/docs/tutorial/cors.md b/docs/ja/docs/tutorial/cors.md index f7bd59b709..a1dfe8e624 100644 --- a/docs/ja/docs/tutorial/cors.md +++ b/docs/ja/docs/tutorial/cors.md @@ -1,8 +1,8 @@ -# CORS (オリジン間リソース共有) +# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing } -CORSまたは「オリジン間リソース共有」 は、ブラウザで実行されているフロントエンドにバックエンドと通信するJavaScriptコードがあり、そのバックエンドがフロントエンドとは異なる「オリジン」にある状況を指します。 +CORSまたは「Cross-Origin Resource Sharing」 は、ブラウザで実行されているフロントエンドにバックエンドと通信するJavaScriptコードがあり、そのバックエンドがフロントエンドとは異なる「オリジン」にある状況を指します。 -## オリジン +## オリジン { #origin } オリジンはプロトコル (`http`、`https`) とドメイン (`myapp.com`、`localhost`、`localhost.tiangolo.com`) とポート (`80`、`443`、`8080`) の組み合わせです。 @@ -14,25 +14,25 @@ すべて `localhost` であっても、異なるプロトコルやポートを使用するので、異なる「オリジン」です。 -## ステップ +## ステップ { #steps } そして、ブラウザ上で実行されているフロントエンド (`http://localhost:8080`) があり、そのJavaScriptが `http://localhost` で実行されているバックエンドと通信するとします。(ポートを指定していないので、ブラウザはデフォルトの`80`ポートを使用します) -次に、ブラウザはHTTPの `OPTIONS` リクエストをバックエンドに送信します。そして、バックエンドがこの異なるオリジン (`http://localhost:8080`) からの通信を許可する適切なヘッダーを送信すると、ブラウザはフロントエンドのJavaScriptにバックエンドへのリクエストを送信させます。 +次に、ブラウザはHTTPの `OPTIONS` リクエストを `:80` のバックエンドに送信します。そして、バックエンドがこの異なるオリジン (`http://localhost:8080`) からの通信を許可する適切なヘッダーを送信すると、`:8080` のブラウザはフロントエンドのJavaScriptに `:80` のバックエンドへのリクエストを送信させます。 -これを実現するには、バックエンドに「許可されたオリジン」のリストがなければなりません。 +これを実現するには、`:80` のバックエンドに「許可されたオリジン」のリストがなければなりません。 -この場合、フロントエンドを正しく機能させるには、そのリストに `http://localhost:8080` を含める必要があります。 +この場合、`:8080` のフロントエンドを正しく機能させるには、そのリストに `http://localhost:8080` を含める必要があります。 -## ワイルドカード +## ワイルドカード { #wildcards } -リストを `"*"` (ワイルドカード) と宣言して、すべてを許可することもできます。 +リストを `"*"` (「ワイルドカード」) と宣言して、すべてを許可することもできます。 -ただし、Bearer Tokenで使用されるような認証ヘッダーやCookieなどのクレデンシャル情報に関するものを除いて、特定の種類の通信のみが許可されます。 +ただし、クレデンシャル情報に関するもの、つまりCookie、Bearer Tokenで使用されるようなAuthorizationヘッダーなどを含むものは除外され、特定の種類の通信のみが許可されます。 したがって、すべてを正しく機能させるために、許可されたオリジンの明示的な指定をお勧めします。 -## `CORSMiddleware` の使用 +## `CORSMiddleware` の使用 { #use-corsmiddleware } **FastAPI** アプリケーションでは `CORSMiddleware` を使用して、CORSに関する設定ができます。 @@ -42,39 +42,43 @@ 以下も、バックエンドに許可させるかどうか指定できます: -* クレデンシャル情報 (認証ヘッダー、Cookieなど) 。 +* クレデンシャル情報 (Authorizationヘッダー、Cookieなど) 。 * 特定のHTTPメソッド (`POST`、`PUT`) またはワイルドカード `"*"` を使用してすべて許可。 * 特定のHTTPヘッダー、またはワイルドカード `"*"`を使用してすべて許可。 -{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *} +{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *} -`CORSMiddleware` 実装のデフォルトのパラメータはCORSに関して制限を与えるものになっているので、ブラウザにドメインを跨いで特定のオリジン、メソッド、またはヘッダーを使用可能にするためには、それらを明示的に有効にする必要があります + +`CORSMiddleware` 実装で使用されるデフォルトのパラメータはデフォルトで制限が厳しいため、ブラウザがクロスドメインのコンテキストでそれらを使用できるようにするには、特定のオリジン、メソッド、またはヘッダーを明示的に有効にする必要があります。 以下の引数がサポートされています: * `allow_origins` - オリジン間リクエストを許可するオリジンのリスト。例えば、`['https://example.org', 'https://www.example.org']`。`['*']`を使用して任意のオリジンを許可できます。 * `allow_origin_regex` - オリジン間リクエストを許可するオリジンの正規表現文字列。例えば、`'https://.*\.example\.org'`。 * `allow_methods` - オリジン間リクエストで許可するHTTPメソッドのリスト。デフォルトは `['GET']` です。`['*']`を使用してすべての標準メソッドを許可できます。 -* `allow_headers` - オリジン間リクエストでサポートするHTTPリクエストヘッダーのリスト。デフォルトは `[]` です。`['*']`を使用して、すべてのヘッダーを許可できます。CORSリクエストでは、 `Accept` 、 `Accept-Language` 、 `Content-Language` 、 `Content-Type` ヘッダーが常に許可されます。 +* `allow_headers` - オリジン間リクエストでサポートするHTTPリクエストヘッダーのリスト。デフォルトは `[]` です。`['*']`を使用して、すべてのヘッダーを許可できます。シンプルなCORSリクエストでは、 `Accept` 、 `Accept-Language` 、 `Content-Language` 、 `Content-Type` ヘッダーが常に許可されます。 * `allow_credentials` - オリジン間リクエストでCookieをサポートする必要があることを示します。デフォルトは `False` です。 + + `allow_credentials` が `True` に設定されている場合、`allow_origins`、`allow_methods`、`allow_headers` のいずれも `['*']` に設定できません。これらはすべて明示的に指定する必要があります。 + * `expose_headers` - ブラウザからアクセスできるようにするレスポンスヘッダーを示します。デフォルトは `[]` です。 * `max_age` - ブラウザがCORSレスポンスをキャッシュする最大時間を秒単位で設定します。デフォルトは `600` です。 このミドルウェアは2種類のHTTPリクエストに応答します... -### CORSプリフライトリクエスト +### CORSプリフライトリクエスト { #cors-preflight-requests } これらは、 `Origin` ヘッダーと `Access-Control-Request-Method` ヘッダーを持つ `OPTIONS` リクエストです。 この場合、ミドルウェアはリクエストを横取りし、適切なCORSヘッダーと共に情報提供のために `200` または `400` のレスポンスを返します。 -### シンプルなリクエスト +### シンプルなリクエスト { #simple-requests } `Origin` ヘッダーのあるリクエスト。この場合、ミドルウェアは通常どおりリクエストに何もしないですが、レスポンスに適切なCORSヘッダーを加えます。 -## より詳しい情報 +## より詳しい情報 { #more-info } -CORSについてより詳しい情報は、Mozilla CORS documentation を参照して下さい。 +CORSについてより詳しい情報は、Mozilla CORS documentation を参照して下さい。 /// note | 技術詳細 diff --git a/docs/ja/docs/tutorial/debugging.md b/docs/ja/docs/tutorial/debugging.md index 6c29679efd..8fe5b2d5d3 100644 --- a/docs/ja/docs/tutorial/debugging.md +++ b/docs/ja/docs/tutorial/debugging.md @@ -1,14 +1,14 @@ -# デバッグ +# デバッグ { #debugging } Visual Studio CodeやPyCharmなどを使用して、エディター上でデバッガーと連携できます。 -## `uvicorn` の実行 +## `uvicorn` を呼び出す { #call-uvicorn } FastAPIアプリケーション上で、`uvicorn` を直接インポートして実行します: -{* ../../docs_src/debugging/tutorial001.py hl[1,15] *} +{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *} -### `__name__ == "__main__"` について +### `__name__ == "__main__"` について { #about-name-main } `__name__ == "__main__"` の主な目的は、ファイルが次のコマンドで呼び出されたときに実行されるコードを用意することです: @@ -26,7 +26,7 @@ $ python myapp.py from myapp import app ``` -#### より詳しい説明 +#### より詳しい説明 { #more-details } ファイルの名前が `myapp.py` だとします。 @@ -62,7 +62,7 @@ from myapp import app # Some more code ``` -`myapp.py` 内の自動変数には、値が `"__main __"` の変数 `__name__` はありません。 +その場合、`myapp.py` 内の自動的に作成された変数 `__name__` は、値として `"__main__"` を持ちません。 したがって、以下の行: @@ -78,7 +78,7 @@ from myapp import app /// -## デバッガーでコードを実行 +## デバッガーでコードを実行 { #run-your-code-with-your-debugger } コードから直接Uvicornサーバーを実行しているため、デバッガーから直接Pythonプログラム (FastAPIアプリケーション) を呼び出せます。 diff --git a/docs/ja/docs/tutorial/dependencies/classes-as-dependencies.md b/docs/ja/docs/tutorial/dependencies/classes-as-dependencies.md index 80153529e5..3cb1fe73d9 100644 --- a/docs/ja/docs/tutorial/dependencies/classes-as-dependencies.md +++ b/docs/ja/docs/tutorial/dependencies/classes-as-dependencies.md @@ -1,12 +1,12 @@ -# 依存関係としてのクラス +# 依存関係としてのクラス { #classes-as-dependencies } **依存性注入** システムを深く掘り下げる前に、先ほどの例をアップグレードしてみましょう。 -## 前の例の`dict` +## 前の例の`dict` { #a-dict-from-the-previous-example } 前の例では、依存関係("dependable")から`dict`を返していました: -{* ../../docs_src/dependencies/tutorial001.py hl[9] *} +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *} しかし、*path operation関数*のパラメータ`commons`に`dict`が含まれています。 @@ -14,7 +14,7 @@ もっとうまくやれるはずです...。 -## 依存関係を作るもの +## 依存関係を作るもの { #what-makes-a-dependency } これまでは、依存関係が関数として宣言されているのを見てきました。 @@ -38,7 +38,7 @@ something(some_argument, some_keyword_argument="foo") これを「呼び出し可能」なものと呼びます。 -## 依存関係としてのクラス +## 依存関係としてのクラス { #classes-as-dependencies_1 } Pythonのクラスのインスタンスを作成する際に、同じ構文を使用していることに気づくかもしれません。 @@ -67,48 +67,66 @@ FastAPIが実際にチェックしているのは、それが「呼び出し可 それは、パラメータが全くない呼び出し可能なものにも適用されます。パラメータのない*path operation関数*と同じように。 -そこで、上で紹介した依存関係の`common_parameters`を`CommonQueryParams`クラスに変更します: +そこで、上で紹介した依存関係の"dependable" `common_parameters`を`CommonQueryParams`クラスに変更します: -{* ../../docs_src/dependencies/tutorial002.py hl[11,12,13,14,15] *} +{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *} クラスのインスタンスを作成するために使用される`__init__`メソッドに注目してください: -{* ../../docs_src/dependencies/tutorial002.py hl[12] *} +{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *} ...以前の`common_parameters`と同じパラメータを持っています: -{* ../../docs_src/dependencies/tutorial001.py hl[8] *} +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *} これらのパラメータは **FastAPI** が依存関係を「解決」するために使用するものです。 どちらの場合も以下を持っています: -* オプショナルの`q`クエリパラメータ。 -* `skip`クエリパラメータ、デフォルトは`0`。 -* `limit`クエリパラメータ、デフォルトは`100`。 +* `str`であるオプショナルの`q`クエリパラメータ。 +* デフォルトが`0`である`int`の`skip`クエリパラメータ。 +* デフォルトが`100`である`int`の`limit`クエリパラメータ。 どちらの場合も、データは変換され、検証され、OpenAPIスキーマなどで文書化されます。 -## 使用 +## 使用 { #use-it } これで、このクラスを使用して依存関係を宣言することができます。 -{* ../../docs_src/dependencies/tutorial002.py hl[19] *} +{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *} **FastAPI** は`CommonQueryParams`クラスを呼び出します。これにより、そのクラスの「インスタンス」が作成され、インスタンスはパラメータ`commons`として関数に渡されます。 -## 型注釈と`Depends` +## 型注釈と`Depends` { #type-annotation-vs-depends } 上のコードでは`CommonQueryParams`を2回書いていることに注目してください: +//// tab | Python 3.9+ + +```Python +commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] +``` + +//// + +//// tab | Python 3.9+ 注釈なし + +/// tip | 豆知識 + +可能であれば`Annotated`バージョンを使用することを推奨します。 + +/// + ```Python commons: CommonQueryParams = Depends(CommonQueryParams) ``` +//// + 以下にある最後の`CommonQueryParams`: ```Python -... = Depends(CommonQueryParams) +... Depends(CommonQueryParams) ``` ...は、**FastAPI** が依存関係を知るために実際に使用するものです。 @@ -119,55 +137,145 @@ commons: CommonQueryParams = Depends(CommonQueryParams) この場合、以下にある最初の`CommonQueryParams`: +//// tab | Python 3.9+ + +```Python +commons: Annotated[CommonQueryParams, ... +``` + +//// + +//// tab | Python 3.9+ 注釈なし + +/// tip | 豆知識 + +可能であれば`Annotated`バージョンを使用することを推奨します。 + +/// + ```Python commons: CommonQueryParams ... ``` -...は **FastAPI** に対して特別な意味をもちません。FastAPIはデータ変換や検証などには使用しません(それらのためには`= Depends(CommonQueryParams)`を使用しています)。 +//// + +...は **FastAPI** に対して特別な意味をもちません。FastAPIはデータ変換や検証などには使用しません(それらのためには`Depends(CommonQueryParams)`を使用しています)。 実際には以下のように書けばいいだけです: +//// tab | Python 3.9+ + +```Python +commons: Annotated[Any, Depends(CommonQueryParams)] +``` + +//// + +//// tab | Python 3.9+ 注釈なし + +/// tip | 豆知識 + +可能であれば`Annotated`バージョンを使用することを推奨します。 + +/// + ```Python commons = Depends(CommonQueryParams) ``` +//// + 以下にあるように: -{* ../../docs_src/dependencies/tutorial003.py hl[19] *} +{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *} しかし、型を宣言することは推奨されています。そうすれば、エディタは`commons`のパラメータとして何が渡されるかを知ることができ、コードの補完や型チェックなどを行うのに役立ちます: - + -## ショートカット +## ショートカット { #shortcut } しかし、ここでは`CommonQueryParams`を2回書くというコードの繰り返しが発生していることがわかります: +//// tab | Python 3.9+ + +```Python +commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] +``` + +//// + +//// tab | Python 3.9+ 注釈なし + +/// tip | 豆知識 + +可能であれば`Annotated`バージョンを使用することを推奨します。 + +/// + ```Python commons: CommonQueryParams = Depends(CommonQueryParams) ``` +//// + 依存関係が、クラス自体のインスタンスを作成するために**FastAPI**が「呼び出す」*特定の*クラスである場合、**FastAPI** はこれらのケースのショートカットを提供しています。 それらの具体的なケースについては以下のようにします: 以下のように書く代わりに: +//// tab | Python 3.9+ + +```Python +commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] +``` + +//// + +//// tab | Python 3.9+ 注釈なし + +/// tip | 豆知識 + +可能であれば`Annotated`バージョンを使用することを推奨します。 + +/// + ```Python commons: CommonQueryParams = Depends(CommonQueryParams) ``` +//// + ...以下のように書きます: +//// tab | Python 3.9+ + +```Python +commons: Annotated[CommonQueryParams, Depends()] +``` + +//// + +//// tab | Python 3.9+ 注釈なし + +/// tip | 豆知識 + +可能であれば`Annotated`バージョンを使用することを推奨します。 + +/// + ```Python commons: CommonQueryParams = Depends() ``` +//// + パラメータの型として依存関係を宣言し、`Depends()`の中でパラメータを指定せず、`Depends()`をその関数のパラメータの「デフォルト」値(`=`のあとの値)として使用することで、`Depends(CommonQueryParams)`の中でクラス全体を*もう一度*書かなくてもよくなります。 同じ例では以下のようになります: -{* ../../docs_src/dependencies/tutorial004.py hl[19] *} +{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *} ...そして **FastAPI** は何をすべきか知っています。 diff --git a/docs/ja/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md b/docs/ja/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md index 0fb15ae02c..2051afc05b 100644 --- a/docs/ja/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md +++ b/docs/ja/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md @@ -1,57 +1,69 @@ -# path operationデコレータの依存関係 +# path operation デコレータの依存関係 { #dependencies-in-path-operation-decorators } -場合によっては*path operation関数*の中で依存関係の戻り値を本当に必要としないこともあります。 +場合によっては、*path operation 関数*の中で依存関係の戻り値を実際には必要としないことがあります。 -もしくは、依存関係が値を返さない場合もあります。 +または、依存関係が値を返さない場合もあります。 -しかし、それでも実行・解決する必要があります。 +しかし、それでも実行・解決される必要があります。 -このような場合、*path operation関数*のパラメータを`Depends`で宣言する代わりに、*path operation decorator*に`dependencies`の`list`を追加することができます。 +そのような場合、`Depends` で *path operation 関数* のパラメータを宣言する代わりに、*path operation デコレータ*に `dependencies` の `list` を追加できます。 -## *path operationデコレータ*への`dependencies`の追加 +## *path operation デコレータ*に`dependencies`を追加 { #add-dependencies-to-the-path-operation-decorator } -*path operationデコレータ*はオプショナルの引数`dependencies`を受け取ります。 +*path operation デコレータ*はオプション引数`dependencies`を受け取ります。 それは`Depends()`の`list`であるべきです: -{* ../../docs_src/dependencies/tutorial006.py hl[17] *} +{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *} -これらの依存関係は、通常の依存関係と同様に実行・解決されます。しかし、それらの値(何かを返す場合)は*path operation関数*には渡されません。 +これらの依存関係は、通常の依存関係と同様に実行・解決されます。しかし、それらの値(何かを返す場合)は*path operation 関数*には渡されません。 /// tip | 豆知識 -エディタによっては、未使用の関数パラメータをチェックしてエラーとして表示するものもあります。 +一部のエディタは、未使用の関数パラメータをチェックしてエラーとして表示します。 -`dependencies`を`path operationデコレータ`で使用することで、エディタやツールのエラーを回避しながら確実に実行することができます。 +これらの`dependencies`を*path operation デコレータ*で使用することで、エディタ/ツールのエラーを回避しつつ、確実に実行されるようにできます。 -また、コードの未使用のパラメータがあるのを見て、それが不要だと思ってしまうような新しい開発者の混乱を避けるのにも役立つかもしれません。 +また、コード内の未使用のパラメータを見た新しい開発者が、それを不要だと思って混乱するのを避ける助けにもなるかもしれません。 /// -## 依存関係のエラーと戻り値 +/// info | 情報 -通常使用している依存関係の*関数*と同じものを使用することができます。 +この例では、架空のカスタムヘッダー `X-Key` と `X-Token` を使用しています。 -### 依存関係の要件 +しかし実際のケースでセキュリティを実装する際は、統合された[Security utilities(次の章)](../security/index.md){.internal-link target=_blank}を使うことで、より多くの利点を得られます。 -これらはリクエストの要件(ヘッダのようなもの)やその他のサブ依存関係を宣言することができます: +/// -{* ../../docs_src/dependencies/tutorial006.py hl[6,11] *} +## 依存関係のエラーと戻り値 { #dependencies-errors-and-return-values } -### 例外の発生 +通常使用している依存関係の*関数*と同じものを使用できます。 -これらの依存関係は通常の依存関係と同じように、例外を`raise`発生させることができます: +### 依存関係の要件 { #dependency-requirements } -{* ../../docs_src/dependencies/tutorial006.py hl[8,13] *} +これらはリクエストの要件(ヘッダーのようなもの)やその他のサブ依存関係を宣言できます: -### 戻り値 +{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *} + +### 例外の発生 { #raise-exceptions } + +これらの依存関係は、通常の依存関係と同じように例外を`raise`できます: + +{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *} + +### 戻り値 { #return-values } そして、値を返すことも返さないこともできますが、値は使われません。 -つまり、すでにどこかで使っている通常の依存関係(値を返すもの)を再利用することができ、値は使われなくても依存関係は実行されます: +つまり、すでにどこかで使っている通常の依存関係(値を返すもの)を再利用でき、値は使われなくても依存関係は実行されます: -{* ../../docs_src/dependencies/tutorial006.py hl[9,14] *} +{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *} -## *path operations*のグループに対する依存関係 +## *path operation*のグループに対する依存関係 { #dependencies-for-a-group-of-path-operations } -後で、より大きなアプリケーションの構造([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank})について読む時に、おそらく複数のファイルを使用して、*path operations*のグループに対して単一の`dependencies`パラメータを宣言する方法を学ぶでしょう。 +後で、より大きなアプリケーションを(おそらく複数ファイルで)構造化する方法([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank})について読むときに、*path operation*のグループに対して単一の`dependencies`パラメータを宣言する方法を学びます。 + +## グローバル依存関係 { #global-dependencies } + +次に、`FastAPI`アプリケーション全体に依存関係を追加して、各*path operation*に適用する方法を見ていきます。 diff --git a/docs/ja/docs/tutorial/dependencies/dependencies-with-yield.md b/docs/ja/docs/tutorial/dependencies/dependencies-with-yield.md index 35a69de0df..8095114c3f 100644 --- a/docs/ja/docs/tutorial/dependencies/dependencies-with-yield.md +++ b/docs/ja/docs/tutorial/dependencies/dependencies-with-yield.md @@ -1,24 +1,12 @@ -# yieldを持つ依存関係 +# `yield`を持つ依存関係 { #dependencies-with-yield } -FastAPIは、いくつかの終了後の追加のステップを行う依存関係をサポートしています。 +FastAPIは、いくつかの終了後の追加のステップを行う依存関係をサポートしています。 -これを行うには、`return`の代わりに`yield`を使い、その後に追加のステップを書きます。 +これを行うには、`return`の代わりに`yield`を使い、その後に追加のステップ(コード)を書きます。 /// tip | 豆知識 -`yield`は必ず一度だけ使用するようにしてください。 - -/// - -/// info | 情報 - -これを動作させるには、**Python 3.7** 以上を使用するか、**Python 3.6** では"backports"をインストールする必要があります: - -``` -pip install async-exit-stack async-generator -``` - -これによりasync-exit-stackasync-generatorがインストールされます。 +`yield`は必ず依存関係ごとに1回だけ使用するようにしてください。 /// @@ -35,21 +23,21 @@ pip install async-exit-stack async-generator /// -## `yield`を持つデータベースの依存関係 +## `yield`を持つデータベースの依存関係 { #a-database-dependency-with-yield } 例えば、これを使ってデータベースセッションを作成し、終了後にそれを閉じることができます。 -レスポンスを送信する前に`yield`文を含む前のコードのみが実行されます。 +レスポンスを作成する前に、`yield`文より前のコード(および`yield`文を含む)が実行されます: -{* ../../docs_src/dependencies/tutorial007.py hl[2,3,4] *} +{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *} 生成された値は、*path operations*や他の依存関係に注入されるものです: -{* ../../docs_src/dependencies/tutorial007.py hl[4] *} +{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *} -`yield`文に続くコードは、レスポンスが送信された後に実行されます: +`yield`文に続くコードは、レスポンスの後に実行されます: -{* ../../docs_src/dependencies/tutorial007.py hl[5,6] *} +{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *} /// tip | 豆知識 @@ -59,27 +47,27 @@ pip install async-exit-stack async-generator /// -## `yield`と`try`を持つ依存関係 +## `yield`と`try`を持つ依存関係 { #a-dependency-with-yield-and-try } -`yield`を持つ依存関係で`try`ブロックを使用した場合、その依存関係を使用した際に発生した例外を受け取ることになります。 +`yield`を持つ依存関係で`try`ブロックを使用した場合、その依存関係を使用した際にスローされたあらゆる例外を受け取ることになります。 -例えば、途中のどこかの時点で、別の依存関係や*path operation*の中で、データベーストランザクションを「ロールバック」したり、その他のエラーを作成したりするコードがあった場合、依存関係の中で例外を受け取ることになります。 +例えば、途中のどこかの時点で、別の依存関係や*path operation*の中で、データベーストランザクションを「ロールバック」したり、その他の例外を作成したりするコードがあった場合、依存関係の中で例外を受け取ることになります。 そのため、依存関係の中にある特定の例外を`except SomeException`で探すことができます。 同様に、`finally`を用いて例外があったかどうかにかかわらず、終了ステップを確実に実行することができます。 -{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *} +{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *} -## `yield`を持つサブ依存関係 +## `yield`を持つサブ依存関係 { #sub-dependencies-with-yield } 任意の大きさや形のサブ依存関係やサブ依存関係の「ツリー」を持つことができ、その中で`yield`を使用することができます。 **FastAPI** は、`yield`を持つ各依存関係の「終了コード」が正しい順番で実行されていることを確認します。 -例えば、`dependency_c`は`dependency_b`と`dependency_b`に依存する`dependency_a`に、依存することができます: +例えば、`dependency_c`は`dependency_b`に、そして`dependency_b`は`dependency_a`に依存することができます: -{* ../../docs_src/dependencies/tutorial008.py hl[4,12,20] *} +{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *} そして、それらはすべて`yield`を使用することができます。 @@ -87,11 +75,11 @@ pip install async-exit-stack async-generator そして、`dependency_b`は`dependency_a`(ここでは`dep_a`という名前)の値を終了コードで利用できるようにする必要があります。 -{* ../../docs_src/dependencies/tutorial008.py hl[16,17,24,25] *} +{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *} -同様に、`yield`と`return`が混在した依存関係を持つこともできます。 +同様に、`yield`を持つ依存関係と`return`を持つ他の依存関係をいくつか持ち、それらの一部が他の一部に依存するようにもできます。 -また、単一の依存関係を持っていて、`yield`などの他の依存関係をいくつか必要とすることもできます。 +また、単一の依存関係を持っていて、`yield`を持つ他の依存関係をいくつか必要とすることもできます。 依存関係の組み合わせは自由です。 @@ -105,32 +93,46 @@ pip install async-exit-stack async-generator /// -## `yield`と`HTTPException`を持つ依存関係 +## `yield`と`HTTPException`を持つ依存関係 { #dependencies-with-yield-and-httpexception } -`yield`と例外をキャッチする`try`ブロックを持つことができる依存関係を使用することができることがわかりました。 +`yield`を持つ依存関係を使い、何らかのコードを実行し、その後に`finally`の後で終了コードを実行しようとする`try`ブロックを持てることが分かりました。 -`yield`の後の終了コードで`HTTPException`などを発生させたくなるかもしれません。しかし**それはうまくいきません** +また、`except`を使って発生した例外をキャッチし、それに対して何かをすることもできます。 -`yield`を持つ依存関係の終了コードは[例外ハンドラ](../handling-errors.md#_4){.internal-link target=_blank}の*後に*実行されます。依存関係によって投げられた例外を終了コード(`yield`の後)でキャッチするものはなにもありません。 - -つまり、`yield`の後に`HTTPException`を発生させた場合、`HTTTPException`をキャッチしてHTTP 400のレスポンスを返すデフォルトの(あるいは任意のカスタムの)例外ハンドラは、その例外をキャッチすることができなくなります。 - -これは、依存関係に設定されているもの(例えば、DBセッション)を、例えば、バックグラウンドタスクで使用できるようにするものです。 - -バックグラウンドタスクはレスポンスが送信された*後*に実行されます。そのため、*すでに送信されている*レスポンスを変更する方法すらないので、`HTTPException`を発生させる方法はありません。 - -しかし、バックグラウンドタスクがDBエラーを発生させた場合、少なくとも`yield`で依存関係のセッションをロールバックしたり、きれいに閉じたりすることができ、エラーをログに記録したり、リモートのトラッキングシステムに報告したりすることができます。 - -例外が発生する可能性があるコードがある場合は、最も普通の「Python流」なことをして、コードのその部分に`try`ブロックを追加してください。 - -レスポンスを返したり、レスポンスを変更したり、`HTTPException`を発生させたりする*前に*処理したいカスタム例外がある場合は、[カスタム例外ハンドラ](../handling-errors.md#_4){.internal-link target=_blank}を作成してください。 +例えば、`HTTPException`のように別の例外を発生させることができます。 /// tip | 豆知識 -`HTTPException`を含む例外は、`yield`の*前*でも発生させることができます。ただし、後ではできません。 +これはやや高度なテクニックで、ほとんどの場合は本当に必要にはなりません。例えば、*path operation 関数*など、アプリケーションコードの他の場所から(`HTTPException`を含む)例外を発生させられるためです。 + +ただし必要であれば使えます。 🤓 /// +{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *} + +例外をキャッチして、それに基づいてカスタムレスポンスを作成したい場合は、[カスタム例外ハンドラ](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}を作成してください。 + +## `yield`と`except`を持つ依存関係 { #dependencies-with-yield-and-except } + +`yield`を持つ依存関係で`except`を使って例外をキャッチし、それを再度raiseしない(または新しい例外をraiseしない)場合、通常のPythonと同じように、FastAPIは例外があったことに気づけません: + +{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *} + +この場合、(`HTTPException`やそれに類するものをraiseしていないため)クライアントには適切に*HTTP 500 Internal Server Error*レスポンスが返りますが、サーバーには**ログが一切残らず**、何がエラーだったのかを示す他の手がかりもありません。 😱 + +### `yield`と`except`を持つ依存関係では常に`raise`する { #always-raise-in-dependencies-with-yield-and-except } + +`yield`を持つ依存関係で例外をキャッチした場合、別の`HTTPException`などをraiseするのでない限り、**元の例外を再raiseすべきです**。 + +`raise`を使うと同じ例外を再raiseできます: + +{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *} + +これでクライアントは同じ*HTTP 500 Internal Server Error*レスポンスを受け取りますが、サーバーのログにはカスタムの`InternalError`が残ります。 😎 + +## `yield`を持つ依存関係の実行 { #execution-of-dependencies-with-yield } + 実行の順序は多かれ少なかれ以下の図のようになります。時間は上から下へと流れていきます。そして、各列はコードを相互作用させたり、実行したりしている部分の一つです。 ```mermaid @@ -142,32 +144,29 @@ participant dep as Dep with yield participant operation as Path Operation participant tasks as Background tasks - Note over client,tasks: Can raise exception for dependency, handled after response is sent - Note over client,operation: Can raise HTTPException and can change the response + Note over client,operation: Can raise exceptions, including HTTPException client ->> dep: Start request Note over dep: Run code up to yield - opt raise - dep -->> handler: Raise HTTPException + opt raise Exception + dep -->> handler: Raise Exception handler -->> client: HTTP error response - dep -->> dep: Raise other exception end dep ->> operation: Run dependency, e.g. DB session opt raise - operation -->> handler: Raise HTTPException + operation -->> dep: Raise Exception (e.g. HTTPException) + opt handle + dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception + end handler -->> client: HTTP error response - operation -->> dep: Raise other exception end + operation ->> client: Return response to client Note over client,operation: Response is already sent, can't change it anymore opt Tasks operation -->> tasks: Send background tasks end opt Raise other exception - tasks -->> dep: Raise other exception - end - Note over dep: After yield - opt Handle other exception - dep -->> dep: Handle exception, can't change response. E.g. close DB session. + tasks -->> tasks: Handle exceptions in the background task code end ``` @@ -181,15 +180,63 @@ participant tasks as Background tasks /// tip | 豆知識 -この図は`HTTPException`を示していますが、[カスタム例外ハンドラ](../handling-errors.md#_4){.internal-link target=_blank}を作成することで、他の例外を発生させることもできます。そして、その例外は依存関係の終了コードではなく、そのカスタム例外ハンドラによって処理されます。 - -しかし例外ハンドラで処理されない例外を発生させた場合は、依存関係の終了コードで処理されます。 +*path operation 関数*のコードで例外をraiseした場合、`HTTPException`を含め、それはyieldを持つ依存関係に渡されます。ほとんどの場合、その例外が正しく処理されるように、`yield`を持つ依存関係から同じ例外、または新しい例外を再raiseしたくなるでしょう。 /// -## コンテキストマネージャ +## 早期終了と`scope` { #early-exit-and-scope } -### 「コンテキストマネージャ」とは +通常、`yield`を持つ依存関係の終了コードは、クライアントに**レスポンスが送信された後**に実行されます。 + +しかし、*path operation 関数*からreturnした後に依存関係を使う必要がないと分かっている場合は、`Depends(scope="function")`を使って、**レスポンスが送信される前**に、*path operation 関数*のreturn後に依存関係を閉じるべきだとFastAPIに伝えられます。 + +{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *} + +`Depends()`は、以下のいずれかを取る`scope`パラメータを受け取ります: + +* `"function"`: リクエストを処理する*path operation 関数*の前に依存関係を開始し、*path operation 関数*の終了後に依存関係を終了しますが、クライアントにレスポンスが返される**前**に終了します。つまり、依存関係関数は*path operation 関数*の**周囲**で実行されます。 +* `"request"`: リクエストを処理する*path operation 関数*の前に依存関係を開始し(`"function"`を使用する場合と同様)、クライアントにレスポンスが返された**後**に終了します。つまり、依存関係関数は**リクエスト**とレスポンスのサイクルの**周囲**で実行されます。 + +指定されておらず、依存関係に`yield`がある場合、デフォルトで`scope`は`"request"`になります。 + +### サブ依存関係の`scope` { #scope-for-sub-dependencies } + +`scope="request"`(デフォルト)を持つ依存関係を宣言する場合、どのサブ依存関係も`"request"`の`scope`を持つ必要があります。 + +しかし、`"function"`の`scope`を持つ依存関係は、`"function"`と`"request"`の`scope`を持つ依存関係を持てます。 + +これは、いずれの依存関係も、サブ依存関係より前に終了コードを実行できる必要があるためです(終了コードの実行中にサブ依存関係をまだ使う必要がある可能性があるためです)。 + +```mermaid +sequenceDiagram + +participant client as Client +participant dep_req as Dep scope="request" +participant dep_func as Dep scope="function" +participant operation as Path Operation + + client ->> dep_req: Start request + Note over dep_req: Run code up to yield + dep_req ->> dep_func: Pass dependency + Note over dep_func: Run code up to yield + dep_func ->> operation: Run path operation with dependency + operation ->> dep_func: Return from path operation + Note over dep_func: Run code after yield + Note over dep_func: ✅ Dependency closed + dep_func ->> client: Send response to client + Note over client: Response sent + Note over dep_req: Run code after yield + Note over dep_req: ✅ Dependency closed +``` + +## `yield`、`HTTPException`、`except`、バックグラウンドタスクを持つ依存関係 { #dependencies-with-yield-httpexception-except-and-background-tasks } + +`yield`を持つ依存関係は、さまざまなユースケースをカバーし、いくつかの問題を修正するために、時間とともに進化してきました。 + +FastAPIの異なるバージョンで何が変わったのかを知りたい場合は、上級ガイドの[上級の依存関係 - `yield`、`HTTPException`、`except`、バックグラウンドタスクを持つ依存関係](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks){.internal-link target=_blank}で詳しく読めます。 +## コンテキストマネージャ { #context-managers } + +### 「コンテキストマネージャ」とは { #what-are-context-managers } 「コンテキストマネージャ」とは、`with`文の中で使用できるPythonオブジェクトのことです。 @@ -205,9 +252,9 @@ with open("./somefile.txt") as f: `with`ブロックが終了すると、例外があったとしてもファイルを確かに閉じます。 -`yield`を依存関係を作成すると、**FastAPI** は内部的にそれをコンテキストマネージャに変換し、他の関連ツールと組み合わせます。 +`yield`を持つ依存関係を作成すると、**FastAPI** は内部的にそれをコンテキストマネージャに変換し、他の関連ツールと組み合わせます。 -### `yield`を持つ依存関係でのコンテキストマネージャの使用 +### `yield`を持つ依存関係でのコンテキストマネージャの使用 { #using-context-managers-in-dependencies-with-yield } /// warning | 注意 @@ -221,7 +268,7 @@ Pythonでは、依存性注入** システムを持っています。 +**FastAPI** は非常に強力でありながら直感的な **Dependency Injection** システムを持っています。 それは非常にシンプルに使用できるように設計されており、開発者が他のコンポーネント **FastAPI** と統合するのが非常に簡単になるように設計されています。 -## 「依存性注入」とは +## 「Dependency Injection」とは { #what-is-dependency-injection } -**「依存性注入」** とは、プログラミングにおいて、コード(この場合は、*path operation関数*)が動作したり使用したりするために必要なもの(「依存関係」)を宣言する方法があることを意味します: +**「Dependency Injection」** とは、プログラミングにおいて、コード(この場合は、*path operation 関数*)が動作したり使用したりするために必要なもの(「依存関係」)を宣言する方法があることを意味します: そして、そのシステム(この場合は、**FastAPI**)は、必要な依存関係をコードに提供するために必要なことは何でも行います(依存関係を「注入」します)。 @@ -19,27 +19,27 @@ これらすべてを、コードの繰り返しを最小限に抑えながら行います。 -## 最初のステップ +## 最初のステップ { #first-steps } 非常にシンプルな例を見てみましょう。あまりにもシンプルなので、今のところはあまり参考にならないでしょう。 -しかし、この方法では **依存性注入** システムがどのように機能するかに焦点を当てることができます。 +しかし、この方法では **Dependency Injection** システムがどのように機能するかに焦点を当てることができます。 -### 依存関係の作成 +### 依存関係(「dependable」)の作成 { #create-a-dependency-or-dependable } まずは依存関係に注目してみましょう。 -以下のように、*path operation関数*と同じパラメータを全て取ることができる関数にすぎません: +以下のように、*path operation 関数*と同じパラメータを全て取ることができる関数にすぎません: -{* ../../docs_src/dependencies/tutorial001.py hl[8,9] *} +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *} これだけです。 **2行**。 -そして、それはすべての*path operation関数*が持っているのと同じ形と構造を持っています。 +そして、それはすべての*path operation 関数*が持っているのと同じ形と構造を持っています。 -「デコレータ」を含まない(`@app.get("/some-path")`を含まない)*path operation関数*と考えることもできます。 +「デコレータ」を含まない(`@app.get("/some-path")`を含まない)*path operation 関数*と考えることもできます。 そして何でも返すことができます。 @@ -51,15 +51,25 @@ そして、これらの値を含む`dict`を返します。 -### `Depends`のインポート +/// info | 情報 -{* ../../docs_src/dependencies/tutorial001.py hl[3] *} +FastAPI はバージョン 0.95.0 で `Annotated` のサポートを追加し(そして推奨し始めました)。 -### "dependant"での依存関係の宣言 +古いバージョンを使用している場合、`Annotated` を使おうとするとエラーになります。 -*path operation関数*のパラメータに`Body`や`Query`などを使用するのと同じように、新しいパラメータに`Depends`を使用することができます: +`Annotated` を使用する前に、少なくとも 0.95.1 まで [FastAPI のバージョンをアップグレード](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} してください。 -{* ../../docs_src/dependencies/tutorial001.py hl[13,18] *} +/// + +### `Depends`のインポート { #import-depends } + +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *} + +### 「dependant」での依存関係の宣言 { #declare-the-dependency-in-the-dependant } + +*path operation 関数*のパラメータに`Body`や`Query`などを使用するのと同じように、新しいパラメータに`Depends`を使用することができます: + +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *} 関数のパラメータに`Depends`を使用するのは`Body`や`Query`などと同じですが、`Depends`の動作は少し異なります。 @@ -67,7 +77,9 @@ このパラメータは関数のようなものである必要があります。 -そして、その関数は、*path operation関数*が行うのと同じ方法でパラメータを取ります。 +直接**呼び出しません**(末尾に括弧を付けません)。`Depends()` のパラメータとして渡すだけです。 + +そして、その関数は、*path operation 関数*が行うのと同じ方法でパラメータを取ります。 /// tip | 豆知識 @@ -79,7 +91,7 @@ * 依存関係("dependable")関数を正しいパラメータで呼び出します。 * 関数の結果を取得します。 -* *path operation関数*のパラメータにその結果を代入してください。 +* *path operation 関数*のパラメータにその結果を代入してください。 ```mermaid graph TB @@ -92,7 +104,7 @@ common_parameters --> read_items common_parameters --> read_users ``` -この方法では、共有されるコードを一度書き、**FastAPI** が*path operations*のための呼び出しを行います。 +この方法では、共有されるコードを一度書き、**FastAPI** が*path operation*のための呼び出しを行います。 /// check | 確認 @@ -102,59 +114,85 @@ common_parameters --> read_users /// -## `async`にするかどうか +## `Annotated` 依存関係の共有 { #share-annotated-dependencies } -依存関係は **FastAPI**(*path operation関数*と同じ)からも呼び出されるため、関数を定義する際にも同じルールが適用されます。 +上の例では、ほんの少し **コードの重複** があることがわかります。 + +`common_parameters()` 依存関係を使う必要があるときは、型アノテーションと `Depends()` を含むパラメータ全体を書く必要があります: + +```Python +commons: Annotated[dict, Depends(common_parameters)] +``` + +しかし、`Annotated` を使用しているので、その `Annotated` 値を変数に格納して複数箇所で使えます: + +{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *} + +/// tip | 豆知識 + +これはただの標準 Python で、「type alias」と呼ばれ、**FastAPI** 固有のものではありません。 + +しかし **FastAPI** は `Annotated` を含む Python 標準に基づいているため、このテクニックをコードで使えます。 😎 + +/// + +依存関係は期待どおりに動作し続け、**一番良い点** は **型情報が保持される** ことです。つまり、エディタは **自動補完**、**インラインエラー** などを提供し続けられます。`mypy` のような他のツールでも同様です。 + +これは **大規模なコードベース** で、**同じ依存関係** を **多くの *path operation*** で何度も使う場合に特に役立ちます。 + +## `async`にするかどうか { #to-async-or-not-to-async } + +依存関係は **FastAPI**(*path operation 関数*と同じ)からも呼び出されるため、関数を定義する際にも同じルールが適用されます。 `async def`や通常の`def`を使用することができます。 -また、通常の`def`*path operation関数*の中に`async def`を入れて依存関係を宣言したり、`async def`*path operation関数*の中に`def`を入れて依存関係を宣言したりすることなどができます。 +また、通常の`def`*path operation 関数*の中に`async def`を入れて依存関係を宣言したり、`async def`*path operation 関数*の中に`def`を入れて依存関係を宣言したりすることなどができます。 それは重要ではありません。**FastAPI** は何をすべきかを知っています。 /// note | 備考 -わからない場合は、ドキュメントの[Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank}の中の`async`と`await`についてのセクションを確認してください。 +わからない場合は、ドキュメントの[Async: *"In a hurry?"*](../../async.md#in-a-hurry){.internal-link target=_blank}の中の`async`と`await`についてのセクションを確認してください。 /// -## OpenAPIとの統合 +## OpenAPIとの統合 { #integrated-with-openapi } 依存関係(およびサブ依存関係)のすべてのリクエスト宣言、検証、および要件は、同じOpenAPIスキーマに統合されます。 つまり、対話型ドキュメントにはこれらの依存関係から得られる全ての情報も含まれているということです: - + -## 簡単な使い方 +## 簡単な使い方 { #simple-usage } -見てみると、*path*と*operation*が一致した時に*path operation関数*が宣言されていて、**FastAPI** が正しいパラメータで関数を呼び出してリクエストからデータを抽出する処理をしています。 +見てみると、*path*と*operation*が一致した時に*path operation 関数*が宣言されていて、**FastAPI** が正しいパラメータで関数を呼び出してリクエストからデータを抽出する処理をしています。 実は、すべての(あるいはほとんどの)Webフレームワークは、このように動作します。 これらの関数を直接呼び出すことはありません。これらの関数はフレームワーク(この場合は、**FastAPI**)によって呼び出されます。 -依存性注入システムでは、**FastAPI** に*path operation*もまた、*path operation関数*の前に実行されるべき他の何かに「依存」していることを伝えることができ、**FastAPI** がそれを実行し、結果を「注入」することを引き受けます。 +Dependency Injection システムでは、**FastAPI** に*path operation 関数*もまた、*path operation 関数*の前に実行されるべき他の何かに「依存」していることを伝えることができ、**FastAPI** がそれを実行し、結果を「注入」することを引き受けます。 -他にも、「依存性注入」と同じような考えの一般的な用語があります: +他にも、「dependency injection」と同じような考えの一般的な用語があります: -* リソース -* プロバイダ -* サービス -* インジェクタブル -* コンポーネント +* resources +* providers +* services +* injectables +* components -## **FastAPI** プラグイン +## **FastAPI** プラグイン { #fastapi-plug-ins } -統合や「プラグイン」は **依存性注入** システムを使って構築することができます。しかし、実際には、**「プラグイン」を作成する必要はありません**。依存関係を使用することで、無限の数の統合やインタラクションを宣言することができ、それが**path operation関数*で利用可能になるからです。 +統合や「プラグイン」は **Dependency Injection** システムを使って構築することができます。しかし、実際には、**「プラグイン」を作成する必要はありません**。依存関係を使用することで、無限の数の統合やインタラクションを宣言することができ、それが*path operation 関数*で利用可能になるからです。 依存関係は非常にシンプルで直感的な方法で作成することができ、必要なPythonパッケージをインポートするだけで、*文字通り*数行のコードでAPI関数と統合することができます。 次の章では、リレーショナルデータベースやNoSQLデータベース、セキュリティなどについて、その例を見ていきます。 -## **FastAPI** 互換性 +## **FastAPI** 互換性 { #fastapi-compatibility } -依存性注入システムがシンプルなので、**FastAPI** は以下のようなものと互換性があります: +dependency injection システムがシンプルなので、**FastAPI** は以下のようなものと互換性があります: * すべてのリレーショナルデータベース * NoSQLデータベース @@ -165,15 +203,15 @@ common_parameters --> read_users * レスポンスデータ注入システム * など。 -## シンプルでパワフル +## シンプルでパワフル { #simple-and-powerful } -階層依存性注入システムは、定義や使用方法が非常にシンプルであるにもかかわらず、非常に強力なものとなっています。 +階層的な dependency injection システムは、定義や使用方法が非常にシンプルであるにもかかわらず、非常に強力なものとなっています。 -依存関係事態を定義する依存関係を定義することができます。 +依存関係が、さらに依存関係を定義することもできます。 -最終的には、依存関係の階層ツリーが構築され、**依存性注入**システムが、これらの依存関係(およびそのサブ依存関係)をすべて解決し、各ステップで結果を提供(注入)します。 +最終的には、依存関係の階層ツリーが構築され、**Dependency Injection**システムが、これらの依存関係(およびそのサブ依存関係)をすべて解決し、各ステップで結果を提供(注入)します。 -例えば、4つのAPIエンドポイント(*path operations*)があるとします: +例えば、4つのAPIエンドポイント(*path operation*)があるとします: * `/items/public/` * `/items/private/` @@ -205,8 +243,8 @@ admin_user --> activate_user paying_user --> pro_items ``` -## **OpenAPI** との統合 +## **OpenAPI** との統合 { #integrated-with-openapi_1 } -これら全ての依存関係は、要件を宣言すると同時に、*path operations*にパラメータやバリデーションを追加します。 +これら全ての依存関係は、要件を宣言すると同時に、*path operation*にパラメータやバリデーションを追加します。 **FastAPI** はそれをすべてOpenAPIスキーマに追加して、対話型のドキュメントシステムに表示されるようにします。 diff --git a/docs/ja/docs/tutorial/dependencies/sub-dependencies.md b/docs/ja/docs/tutorial/dependencies/sub-dependencies.md index 211a86a0ab..007c320f33 100644 --- a/docs/ja/docs/tutorial/dependencies/sub-dependencies.md +++ b/docs/ja/docs/tutorial/dependencies/sub-dependencies.md @@ -1,4 +1,4 @@ -# サブ依存関係 +# サブ依存関係 { #sub-dependencies } **サブ依存関係** を持つ依存関係を作成することができます。 @@ -6,21 +6,21 @@ **FastAPI** はそれらを解決してくれます。 -### 最初の依存関係「依存可能なもの」 +## 最初の依存関係「依存可能なもの」 { #first-dependency-dependable } 以下のような最初の依存関係(「依存可能なもの」)を作成することができます: -{* ../../docs_src/dependencies/tutorial005.py hl[8,9] *} +{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *} これはオプショナルのクエリパラメータ`q`を`str`として宣言し、それを返すだけです。 これは非常にシンプルです(あまり便利ではありません)が、サブ依存関係がどのように機能するかに焦点を当てるのに役立ちます。 -### 第二の依存関係 「依存可能なもの」と「依存」 +## 第二の依存関係 「依存可能なもの」と「依存」 { #second-dependency-dependable-and-dependant } そして、別の依存関数(「依存可能なもの」)を作成して、同時にそれ自身の依存関係を宣言することができます(つまりそれ自身も「依存」です): -{* ../../docs_src/dependencies/tutorial005.py hl[13] *} +{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *} 宣言されたパラメータに注目してみましょう: @@ -29,15 +29,15 @@ * また、オプショナルの`last_query`クッキーを`str`として宣言します。 * ユーザーがクエリ`q`を提供しなかった場合、クッキーに保存していた最後に使用したクエリを使用します。 -### 依存関係の使用 +## 依存関係の使用 { #use-the-dependency } 以下のように依存関係を使用することができます: -{* ../../docs_src/dependencies/tutorial005.py hl[21] *} +{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *} /// info | 情報 -*path operation関数*の中で宣言している依存関係は`query_or_cookie_extractor`の1つだけであることに注意してください。 +*path operation 関数*の中で宣言している依存関係は`query_or_cookie_extractor`の1つだけであることに注意してください。 しかし、**FastAPI** は`query_extractor`を最初に解決し、その結果を`query_or_cookie_extractor`を呼び出す時に渡す必要があることを知っています。 @@ -54,24 +54,43 @@ read_query["/items/"] query_extractor --> query_or_cookie_extractor --> read_query ``` -## 同じ依存関係の複数回の使用 +## 同じ依存関係の複数回の使用 { #using-the-same-dependency-multiple-times } -依存関係の1つが同じ*path operation*に対して複数回宣言されている場合、例えば、複数の依存関係が共通のサブ依存関係を持っている場合、**FastAPI** はリクエストごとに1回だけそのサブ依存関係を呼び出します。 +依存関係の1つが同じ*path operation*に対して複数回宣言されている場合、例えば、複数の依存関係が共通のサブ依存関係を持っている場合、**FastAPI** はリクエストごとに1回だけそのサブ依存関係を呼び出します。 -そして、返された値を「キャッシュ」に保存し、同じリクエストに対して依存関係を何度も呼び出す代わりに、特定のリクエストでそれを必要とする全ての「依存関係」に渡すことになります。 +そして、返された値を「キャッシュ」に保存し、同じリクエストに対して依存関係を何度も呼び出す代わりに、その特定のリクエストでそれを必要とする全ての「依存」に渡すことになります。 -高度なシナリオでは、「キャッシュされた」値を使うのではなく、同じリクエストの各ステップ(おそらく複数回)で依存関係を呼び出す必要があることがわかっている場合、`Depens`を使用する際に、`use_cache=False`というパラメータを設定することができます。 +高度なシナリオでは、「キャッシュされた」値を使うのではなく、同じリクエストの各ステップ(おそらく複数回)で依存関係を呼び出す必要があることがわかっている場合、`Depends`を使用する際に、`use_cache=False`というパラメータを設定することができます: + +//// tab | Python 3.9+ + +```Python hl_lines="1" +async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]): + return {"fresh_value": fresh_value} +``` + +//// + +//// tab | Python 3.9+ 非Annotated + +/// tip | 豆知識 + +可能であれば`Annotated`版を使うことを推奨します。 + +/// ```Python hl_lines="1" async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)): return {"fresh_value": fresh_value} ``` -## まとめ +//// -ここで使われている派手な言葉は別にして、**依存性注入** システムは非常にシンプルです。 +## まとめ { #recap } -*path operation関数*と同じように見えるただの関数です。 +ここで使われている派手な言葉は別にして、**Dependency Injection** システムは非常にシンプルです。 + +*path operation 関数*と同じように見えるただの関数です。 しかし、それでも非常に強力で、任意の深くネストされた依存関係「グラフ」(ツリー)を宣言することができます。 diff --git a/docs/ja/docs/tutorial/encoder.md b/docs/ja/docs/tutorial/encoder.md index 309cf88577..33cc6ae48c 100644 --- a/docs/ja/docs/tutorial/encoder.md +++ b/docs/ja/docs/tutorial/encoder.md @@ -1,16 +1,16 @@ -# JSON互換エンコーダ +# JSON互換エンコーダ { #json-compatible-encoder } -データ型(Pydanticモデルのような)をJSONと互換性のあるもの(`dict`や`list`など)に変更する必要がある場合があります。 +データ型(Pydanticモデルのような)をJSONと互換性のあるもの(`dict`や`list`など)に変換する必要があるケースがあります。 例えば、データベースに保存する必要がある場合です。 そのために、**FastAPI** は`jsonable_encoder()`関数を提供しています。 -## `jsonable_encoder`の使用 +## `jsonable_encoder`の使用 { #using-the-jsonable-encoder } JSON互換のデータのみを受信するデータベース`fake_db`があるとしましょう。 -例えば、`datetime`オブジェクトはJSONと互換性がないので、このデーターベースには受け取られません。 +例えば、`datetime`オブジェクトはJSONと互換性がないので、受け取られません。 そのため、`datetime`オブジェクトはISO形式のデータを含む`str`に変換されなければなりません。 @@ -20,7 +20,7 @@ JSON互換のデータのみを受信するデータベース`fake_db`がある Pydanticモデルのようなオブジェクトを受け取り、JSON互換版を返します: -{* ../../docs_src/encoder/tutorial001.py hl[5,22] *} +{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *} この例では、Pydanticモデルを`dict`に、`datetime`を`str`に変換します。 diff --git a/docs/ja/docs/tutorial/extra-data-types.md b/docs/ja/docs/tutorial/extra-data-types.md index 1be1c3f923..4ed84e86f8 100644 --- a/docs/ja/docs/tutorial/extra-data-types.md +++ b/docs/ja/docs/tutorial/extra-data-types.md @@ -1,6 +1,6 @@ -# 追加データ型 +# 追加データ型 { #extra-data-types } -今までは、以下のような一般的なデータ型を使用してきました: +今まで、以下のような一般的なデータ型を使用してきました: * `int` * `float` @@ -11,13 +11,13 @@ そして、今まで見てきたのと同じ機能を持つことになります: -* 素晴らしいエディタのサポート -* 受信したリクエストからのデータ変換 -* レスポンスデータのデータ変換 -* データの検証 -* 自動注釈と文書化 +* 素晴らしいエディタのサポート。 +* 受信したリクエストからのデータ変換。 +* レスポンスデータのデータ変換。 +* データの検証。 +* 自動注釈と文書化。 -## 他のデータ型 +## 他のデータ型 { #other-data-types } ここでは、使用できる追加のデータ型のいくつかを紹介します: @@ -26,17 +26,17 @@ * リクエストとレスポンスでは`str`として表現されます。 * `datetime.datetime`: * Pythonの`datetime.datetime`です。 - * リクエストとレスポンスはISO 8601形式の`str`で表現されます: `2008-09-15T15:53:00+05:00` + * リクエストとレスポンスはISO 8601形式の`str`で表現されます(例: `2008-09-15T15:53:00+05:00`)。 * `datetime.date`: - * Pythonの`datetime.date`です。 - * リクエストとレスポンスはISO 8601形式の`str`で表現されます: `2008-09-15` + * Python `datetime.date`。 + * リクエストとレスポンスはISO 8601形式の`str`で表現されます(例: `2008-09-15`)。 * `datetime.time`: - * Pythonの`datetime.time`. - * リクエストとレスポンスはISO 8601形式の`str`で表現されます: `14:23:55.003` + * Pythonの`datetime.time`。 + * リクエストとレスポンスはISO 8601形式の`str`で表現されます(例: `14:23:55.003`)。 * `datetime.timedelta`: * Pythonの`datetime.timedelta`です。 * リクエストとレスポンスでは合計秒数の`float`で表現されます。 - * Pydanticでは「ISO 8601 time diff encoding」として表現することも可能です。詳細はドキュメントを参照してください。 + * Pydanticでは「ISO 8601 time diff encoding」として表現することも可能です。詳細はドキュメントを参照してください。 * `frozenset`: * リクエストとレスポンスでは`set`と同じように扱われます: * リクエストでは、リストが読み込まれ、重複を排除して`set`に変換されます。 @@ -45,18 +45,18 @@ * `bytes`: * Pythonの標準的な`bytes`です。 * リクエストとレスポンスでは`str`として扱われます。 - * 生成されたスキーマは`str`で`binary`の「フォーマット」持つことを指定します。 + * 生成されたスキーマは`str`で`binary`の「フォーマット」を持つことを指定します。 * `Decimal`: * Pythonの標準的な`Decimal`です。 - * リクエストやレスポンスでは`float`と同じように扱います。 + * リクエストとレスポンスでは`float`と同じように扱われます。 +* Pydanticの全ての有効な型はこちらで確認できます: Pydantic data types。 -* Pydanticの全ての有効な型はこちらで確認できます: Pydantic data types。 -## 例 +## 例 { #example } ここでは、上記の型のいくつかを使用したパラメータを持つ*path operation*の例を示します。 -{* ../../docs_src/extra_data_types/tutorial001.py hl[1,2,12:16] *} +{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *} -関数内のパラメータは自然なデータ型を持っていることに注意してください。そして、以下のように通常の日付操作を行うことができます: +関数内のパラメータは自然なデータ型を持っていることに注意してください。そして、例えば、以下のように通常の日付操作を行うことができます: -{* ../../docs_src/extra_data_types/tutorial001.py hl[18,19] *} +{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *} diff --git a/docs/ja/docs/tutorial/extra-models.md b/docs/ja/docs/tutorial/extra-models.md index b7e2154099..05e267818c 100644 --- a/docs/ja/docs/tutorial/extra-models.md +++ b/docs/ja/docs/tutorial/extra-models.md @@ -1,6 +1,6 @@ -# モデル - より詳しく +# Extra Models { #extra-models } -先ほどの例に続き、複数の関連モデルを持つことが一般的です。 +先ほどの例に続き、複数の関連モデルを持つことは一般的です。 これはユーザーモデルの場合は特にそうです。なぜなら: @@ -8,27 +8,27 @@ * **出力モデル**はパスワードをもつべきではありません。 * **データベースモデル**はおそらくハッシュ化されたパスワードが必要になるでしょう。 -/// danger | 危険 +/// danger -ユーザーの平文のパスワードは絶対に保存しないでください。常に認証に利用可能な「安全なハッシュ」を保存してください。 +ユーザーの平文のパスワードは絶対に保存しないでください。常に検証できる「安全なハッシュ」を保存してください。 知らない方は、[セキュリティの章](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}で「パスワードハッシュ」とは何かを学ぶことができます。 /// -## 複数のモデル +## Multiple models { #multiple-models } ここでは、パスワードフィールドをもつモデルがどのように見えるのか、また、どこで使われるのか、大まかなイメージを紹介します: -{* ../../docs_src/extra_models/tutorial001.py hl[9,11,16,22,24,29:30,33:35,40:41] *} +{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *} -### `**user_in.dict()`について +### About `**user_in.model_dump()` { #about-user-in-model-dump } -#### Pydanticの`.dict()` +#### Pydanticの`.model_dump()` { #pydantics-model-dump } `user_in`は`UserIn`クラスのPydanticモデルです。 -Pydanticモデルには、モデルのデータを含む`dict`を返す`.dict()`メソッドがあります。 +Pydanticモデルには、モデルのデータを含む`dict`を返す`.model_dump()`メソッドがあります。 そこで、以下のようなPydanticオブジェクト`user_in`を作成すると: @@ -39,7 +39,7 @@ user_in = UserIn(username="john", password="secret", email="john.doe@example.com そして呼び出すと: ```Python -user_dict = user_in.dict() +user_dict = user_in.model_dump() ``` これで変数`user_dict`のデータを持つ`dict`ができました。(これはPydanticモデルのオブジェクトの代わりに`dict`です)。 @@ -61,7 +61,7 @@ print(user_dict) } ``` -#### `dict`の展開 +#### `dict`の展開 { #unpacking-a-dict } `user_dict`のような`dict`を受け取り、それを`**user_dict`を持つ関数(またはクラス)に渡すと、Pythonはそれを「展開」します。これは`user_dict`のキーと値を直接キー・バリューの引数として渡します。 @@ -93,31 +93,31 @@ UserInDB( ) ``` -#### 別のモデルからつくるPydanticモデル +#### 別のモデルの内容からつくるPydanticモデル { #a-pydantic-model-from-the-contents-of-another } -上述の例では`user_in.dict()`から`user_dict`をこのコードのように取得していますが: +上述の例では`user_in.model_dump()`から`user_dict`をこのコードのように取得していますが: ```Python -user_dict = user_in.dict() +user_dict = user_in.model_dump() UserInDB(**user_dict) ``` これは以下と同等です: ```Python -UserInDB(**user_in.dict()) +UserInDB(**user_in.model_dump()) ``` -...なぜなら`user_in.dict()`は`dict`であり、`**`を付与して`UserInDB`を渡してPythonに「展開」させているからです。 +...なぜなら`user_in.model_dump()`は`dict`であり、`**`を付与して`UserInDB`を渡してPythonに「展開」させているからです。 そこで、別のPydanticモデルのデータからPydanticモデルを取得します。 -#### `dict`の展開と追加引数 +#### `dict`の展開と追加キーワード { #unpacking-a-dict-and-extra-keywords } そして、追加のキーワード引数`hashed_password=hashed_password`を以下のように追加すると: ```Python -UserInDB(**user_in.dict(), hashed_password=hashed_password) +UserInDB(**user_in.model_dump(), hashed_password=hashed_password) ``` ...以下のようになります: @@ -132,13 +132,13 @@ UserInDB( ) ``` -/// warning | 注意 +/// warning -サポートしている追加機能は、データの可能な流れをデモするだけであり、もちろん本当のセキュリティを提供しているわけではありません。 +追加のサポート関数`fake_password_hasher`と`fake_save_user`は、データの可能な流れをデモするだけであり、もちろん本当のセキュリティを提供しているわけではありません。 /// -## 重複の削減 +## Reduce duplication { #reduce-duplication } コードの重複を減らすことは、**FastAPI**の中核的なアイデアの1つです。 @@ -152,40 +152,60 @@ UserInDB( データの変換、検証、文書化などはすべて通常通りに動作します。 -このようにして、モデル間の違いだけを宣言することができます: +このようにして、モデル間の違いだけを宣言することができます(平文の`password`、`hashed_password`、パスワードなし): -{* ../../docs_src/extra_models/tutorial002.py hl[9,15,16,19,20,23,24] *} +{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *} -## `Union`または`anyOf` +## `Union` or `anyOf` { #union-or-anyof } -レスポンスを2つの型の`Union`として宣言することができます。 +レスポンスを2つ以上の型の`Union`として宣言できます。つまり、そのレスポンスはそれらのいずれかになります。 OpenAPIでは`anyOf`で定義されます。 そのためには、標準的なPythonの型ヒント`typing.Union`を使用します: -{* ../../docs_src/extra_models/tutorial003.py hl[1,14,15,18,19,20,33] *} +/// note | 備考 -## モデルのリスト +`Union`を定義する場合は、最も具体的な型を先に、その後により具体性の低い型を含めてください。以下の例では、より具体的な`PlaneItem`が`Union[PlaneItem, CarItem]`内で`CarItem`より前に来ています。 -同じように、オブジェクトのリストのレスポンスを宣言することができます。 +/// -そのためには、標準のPythonの`typing.List`を使用する: +{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *} -{* ../../docs_src/extra_models/tutorial004.py hl[1,20] *} +### Python 3.10の`Union` { #union-in-python-3-10 } -## 任意の`dict`を持つレスポンス +この例では、引数`response_model`の値として`Union[PlaneItem, CarItem]`を渡しています。 + +**型アノテーション**に書くのではなく、**引数の値**として渡しているため、Python 3.10でも`Union`を使う必要があります。 + +型アノテーションであれば、次のように縦棒を使用できました: + +```Python +some_variable: PlaneItem | CarItem +``` + +しかし、これを代入で`response_model=PlaneItem | CarItem`のように書くと、Pythonはそれを型アノテーションとして解釈するのではなく、`PlaneItem`と`CarItem`の間で**無効な操作**を行おうとしてしまうため、エラーになります。 + +## List of models { #list-of-models } + +同じように、オブジェクトのリストのレスポンスを宣言できます。 + +そのためには、標準のPythonの`typing.List`(またはPython 3.9以降では単に`list`)を使用します: + +{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *} + +## Response with arbitrary `dict` { #response-with-arbitrary-dict } また、Pydanticモデルを使用せずに、キーと値の型だけを定義した任意の`dict`を使ってレスポンスを宣言することもできます。 これは、有効なフィールド・属性名(Pydanticモデルに必要なもの)を事前に知らない場合に便利です。 -この場合、`typing.Dict`を使用することができます: +この場合、`typing.Dict`(またはPython 3.9以降では単に`dict`)を使用できます: -{* ../../docs_src/extra_models/tutorial005.py hl[1,8] *} +{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *} -## まとめ +## Recap { #recap } 複数のPydanticモデルを使用し、ケースごとに自由に継承します。 -エンティティが異なる「状態」を持たなければならない場合は、エンティティごとに単一のデータモデルを持つ必要はありません。`password` や `password_hash` やパスワードなしなどのいくつかの「状態」をもつユーザー「エンティティ」の場合の様にすれば良いです。 +エンティティが異なる「状態」を持たなければならない場合は、エンティティごとに単一のデータモデルを持つ必要はありません。`password`、`password_hash`、パスワードなしを含む状態を持つユーザー「エンティティ」の場合と同様です。 diff --git a/docs/ja/docs/tutorial/first-steps.md b/docs/ja/docs/tutorial/first-steps.md index 77f9cba43b..ecad2f6ff9 100644 --- a/docs/ja/docs/tutorial/first-steps.md +++ b/docs/ja/docs/tutorial/first-steps.md @@ -1,8 +1,8 @@ -# 最初のステップ +# 最初のステップ { #first-steps } 最もシンプルなFastAPIファイルは以下のようになります: -{* ../../docs_src/first_steps/tutorial001.py *} +{* ../../docs_src/first_steps/tutorial001_py39.py *} これを`main.py`にコピーします。 @@ -11,27 +11,43 @@
```console -$ uvicorn main:app --reload +$ fastapi dev main.py -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: Waiting for application startup. -INFO: Application startup complete. + FastAPI Starting development server 🚀 + + Searching for package file structure from directories + with __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with + the following code: + + from main import app + + app Using import string: main:app + + server Server started at http://127.0.0.1:8000 + server Documentation at http://127.0.0.1:8000/docs + + tip Running in development mode, for production use: + fastapi run + + Logs: + + 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 [383138] using WatchFiles + INFO Started server process [383153] + INFO Waiting for application startup. + INFO Application startup complete. ```
-/// note | 備考 - -`uvicorn main:app`は以下を示します: - -* `main`: `main.py`ファイル (Python "module")。 -* `app`: `main.py`内部で作られるobject(`app = FastAPI()`のように記述される)。 -* `--reload`: コードの変更時にサーバーを再起動させる。開発用。 - -/// - 出力には次のような行があります: ```hl_lines="4" @@ -40,7 +56,7 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) この行はローカルマシンでアプリが提供されているURLを示しています。 -### チェック +### チェック { #check-it } ブラウザでhttp://127.0.0.1:8000を開きます。 @@ -50,7 +66,7 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) {"message": "Hello World"} ``` -### 対話的APIドキュメント +### 対話的APIドキュメント { #interactive-api-docs } 次に、http://127.0.0.1:8000/docsにアクセスします。 @@ -58,7 +74,7 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) -### 他のAPIドキュメント +### 代替APIドキュメント { #alternative-api-docs } 次に、http://127.0.0.1:8000/redocにアクセスします。 @@ -66,31 +82,31 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) -### OpenAPI +### OpenAPI { #openapi } **FastAPI**は、APIを定義するための**OpenAPI**標準規格を使用して、すべてのAPIの「スキーマ」を生成します。 -#### 「スキーマ」 +#### 「スキーマ」 { #schema } 「スキーマ」は定義または説明です。実装コードではなく、単なる抽象的な説明です。 -#### API「スキーマ」 +#### API「スキーマ」 { #api-schema } ここでは、OpenAPIはAPIのスキーマ定義の方法を規定する仕様です。 このスキーマ定義はAPIパス、受け取り可能なパラメータなどが含まれます。 -#### データ「スキーマ」 +#### データ「スキーマ」 { #data-schema } 「スキーマ」という用語は、JSONコンテンツなどの一部のデータの形状を指す場合もあります。 そのような場合、スキーマはJSON属性とそれらが持つデータ型などを意味します。 -#### OpenAPIおよびJSONスキーマ +#### OpenAPIおよびJSONスキーマ { #openapi-and-json-schema } OpenAPIはAPIのためのAPIスキーマを定義します。そして、そのスキーマは**JSONデータスキーマ**の標準規格に準拠したJSONスキーマを利用するAPIによって送受されるデータの定義(または「スキーマ」)を含んでいます。 -#### `openapi.json`を確認 +#### `openapi.json`を確認 { #check-the-openapi-json } 素のOpenAPIスキーマがどのようなものか興味がある場合、FastAPIはすべてのAPIの説明を含むJSON(スキーマ)を自動的に生成します。 @@ -100,7 +116,7 @@ OpenAPIはAPIのためのAPIスキーマを定義します。そして、その ```JSON { - "openapi": "3.0.2", + "openapi": "3.1.0", "info": { "title": "FastAPI", "version": "0.1.0" @@ -119,7 +135,7 @@ OpenAPIはAPIのためのAPIスキーマを定義します。そして、その ... ``` -#### OpenAPIの目的 +#### OpenAPIの目的 { #what-is-openapi-for } OpenAPIスキーマは、FastAPIに含まれている2つのインタラクティブなドキュメントシステムの動力源です。 @@ -127,11 +143,47 @@ OpenAPIスキーマは、FastAPIに含まれている2つのインタラクテ また、APIと通信するクライアント用のコードを自動的に生成するために使用することもできます。たとえば、フロントエンド、モバイル、またはIoTアプリケーションです。 -## ステップ毎の要約 +### アプリをデプロイ(任意) { #deploy-your-app-optional } -### Step 1: `FastAPI`をインポート +任意でFastAPIアプリをFastAPI Cloudにデプロイできます。まだなら、待機リストに登録してください。 🚀 -{* ../../docs_src/first_steps/tutorial001.py hl[1] *} +すでに**FastAPI Cloud**アカウントがある場合(待機リストから招待済みの場合😉)、1コマンドでアプリケーションをデプロイできます。 + +デプロイする前に、ログインしていることを確認してください: + +
+ +```console +$ fastapi login + +You are logged in to FastAPI Cloud 🚀 +``` + +
+ +その後、アプリをデプロイします: + +
+ +```console +$ fastapi deploy + +Deploying to FastAPI Cloud... + +✅ Deployment successful! + +🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev +``` + +
+ +以上です!これで、そのURLでアプリにアクセスできます。 ✨ + +## ステップ毎の要約 { #recap-step-by-step } + +### Step 1: `FastAPI`をインポート { #step-1-import-fastapi } + +{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *} `FastAPI`は、APIのすべての機能を提供するPythonクラスです。 @@ -143,44 +195,16 @@ OpenAPIスキーマは、FastAPIに含まれている2つのインタラクテ /// -### Step 2: `FastAPI`の「インスタンス」を生成 +### Step 2: `FastAPI`の「インスタンス」を生成 { #step-2-create-a-fastapi-instance } -{* ../../docs_src/first_steps/tutorial001.py hl[3] *} +{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *} ここで、`app`変数が`FastAPI`クラスの「インスタンス」になります。 これが、すべてのAPIを作成するための主要なポイントになります。 -この`app`はコマンドで`uvicorn`が参照するものと同じです: +### Step 3: *path operation*を作成 { #step-3-create-a-path-operation } -
- -```console -$ uvicorn main:app --reload - -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -``` - -
- -以下のようなアプリを作成したとき: - -{* ../../docs_src/first_steps/tutorial002.py hl[3] *} - -そして、それを`main.py`ファイルに置き、次のように`uvicorn`を呼び出します: - -
- -```console -$ uvicorn main:my_awesome_api --reload - -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -``` - -
- -### Step 3: *path operation*を作成 - -#### パス +#### パス { #path } ここでの「パス」とは、最初の`/`から始まるURLの最後の部分を指します。 @@ -204,7 +228,7 @@ https://example.com/items/foo APIを構築する際、「パス」は「関心事」と「リソース」を分離するための主要な方法です。 -#### Operation +#### Operation { #operation } ここでの「オペレーション」とは、HTTPの「メソッド」の1つを指します。 @@ -239,15 +263,16 @@ APIを構築するときは、通常、これらの特定のHTTPメソッドを 「**オペレーションズ**」とも呼ぶことにします。 -#### *パスオペレーションデコレータ*を定義 +#### *path operation デコレータ*を定義 { #define-a-path-operation-decorator } + +{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *} -{* ../../docs_src/first_steps/tutorial001.py hl[6] *} `@app.get("/")`は直下の関数が下記のリクエストの処理を担当することを**FastAPI**に伝えます: * パス `/` * get オペレーション -/// info | `@decorator` について +/// info | `@decorator` Info Pythonにおける`@something`シンタックスはデコレータと呼ばれます。 @@ -255,9 +280,9 @@ Pythonにおける`@something`シンタックスはデコレータと呼ばれ 「デコレータ」は直下の関数を受け取り、それを使って何かを行います。 -私たちの場合、このデコレーターは直下の関数が**オペレーション** `get`を使用した**パス**` / `に対応することを**FastAPI** に通知します。 +私たちの場合、このデコレーターは直下の関数が**オペレーション** `get`を使用した**パス** `/`に対応することを**FastAPI** に通知します。 -これが「*パスオペレーションデコレータ*」です。 +これが「*path operation デコレータ*」です。 /// @@ -286,15 +311,15 @@ Pythonにおける`@something`シンタックスはデコレータと呼ばれ /// -### Step 4: **パスオペレーション**を定義 +### Step 4: **path operation 関数**を定義 { #step-4-define-the-path-operation-function } -以下は「**パスオペレーション関数**」です: +以下は「**path operation 関数**」です: * **パス**: は`/`です。 * **オペレーション**: は`get`です。 * **関数**: 「デコレータ」の直下にある関数 (`@app.get("/")`の直下) です。 -{* ../../docs_src/first_steps/tutorial001.py hl[7] *} +{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *} これは、Pythonの関数です。 @@ -306,28 +331,49 @@ Pythonにおける`@something`シンタックスはデコレータと呼ばれ `async def`の代わりに通常の関数として定義することもできます: -{* ../../docs_src/first_steps/tutorial003.py hl[7] *} +{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *} /// note | 備考 -違いが分からない場合は、[Async: *"急いでいますか?"*](../async.md#_1){.internal-link target=_blank}を確認してください。 +違いが分からない場合は、[Async: *"急いでいますか?"*](../async.md#in-a-hurry){.internal-link target=_blank}を確認してください。 /// -### Step 5: コンテンツの返信 +### Step 5: コンテンツの返信 { #step-5-return-the-content } -{* ../../docs_src/first_steps/tutorial001.py hl[8] *} +{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *} -`dict`、`list`、`str`、`int`などを返すことができます。 +`dict`、`list`、`str`、`int`などの単一の値を返すことができます。 Pydanticモデルを返すこともできます(後で詳しく説明します)。 JSONに自動的に変換されるオブジェクトやモデルは他にもたくさんあります(ORMなど)。 お気に入りのものを使ってみてください。すでにサポートされている可能性が高いです。 -## まとめ +### Step 6: デプロイする { #step-6-deploy-it } -* `FastAPI`をインポート -* `app`インスタンスを生成 -* **パスオペレーションデコレータ**を記述 (`@app.get("/")`) -* **パスオペレーション関数**を定義 (上記の`def root(): ...`のように) -* 開発サーバーを起動 (`uvicorn main:app --reload`) +**FastAPI Cloud**に1コマンドでアプリをデプロイします: `fastapi deploy`. 🎉 + +#### FastAPI Cloudについて { #about-fastapi-cloud } + +**FastAPI Cloud**は、**FastAPI**の作者とそのチームによって開発されています。 + +最小限の労力でAPIの**構築**、**デプロイ**、**アクセス**を行うプロセスを合理化します。 + +FastAPIでアプリを構築するのと同じ**開発体験**を、クラウドへの**デプロイ**にもたらします。 🎉 + +FastAPI Cloudは、*FastAPI and friends*のオープンソースプロジェクトに対する主要スポンサーであり、資金提供元です。 ✨ + +#### 他のクラウドプロバイダにデプロイする { #deploy-to-other-cloud-providers } + +FastAPIはオープンソースで、標準に基づいています。選択した任意のクラウドプロバイダにFastAPIアプリをデプロイできます。 + +クラウドプロバイダのガイドに従って、FastAPIアプリをデプロイしてください。 🤓 + +## まとめ { #recap } + +* `FastAPI`をインポートします。 +* `app`インスタンスを生成します。 +* `@app.get("/")`のようなデコレータを使用して、**path operation デコレータ**を記述します。 +* **path operation 関数**を定義します。例: `def root(): ...`。 +* `fastapi dev`コマンドで開発サーバーを起動します。 +* 任意で`fastapi deploy`を使ってアプリをデプロイします。 diff --git a/docs/ja/docs/tutorial/handling-errors.md b/docs/ja/docs/tutorial/handling-errors.md index 8578ca3356..945fe07772 100644 --- a/docs/ja/docs/tutorial/handling-errors.md +++ b/docs/ja/docs/tutorial/handling-errors.md @@ -1,4 +1,4 @@ -# エラーハンドリング +# エラーハンドリング { #handling-errors } APIを使用しているクライアントにエラーを通知する必要がある状況はたくさんあります。 @@ -19,15 +19,15 @@ APIを使用しているクライアントにエラーを通知する必要が **"404 Not Found"** のエラー(およびジョーク)を覚えていますか? -## `HTTPException`の使用 +## `HTTPException`の使用 { #use-httpexception } HTTPレスポンスをエラーでクライアントに返すには、`HTTPException`を使用します。 -### `HTTPException`のインポート +### `HTTPException`のインポート { #import-httpexception } -{* ../../docs_src/handling_errors/tutorial001.py hl[1] *} +{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *} -### コード内での`HTTPException`の発生 +### コード内での`HTTPException`の発生 { #raise-an-httpexception-in-your-code } `HTTPException`は通常のPythonの例外であり、APIに関連するデータを追加したものです。 @@ -39,9 +39,9 @@ Pythonの例外なので、`return`ではなく、`raise`です。 この例では、クライアントが存在しないIDでアイテムを要求した場合、`404`のステータスコードを持つ例外を発生させます: -{* ../../docs_src/handling_errors/tutorial001.py hl[11] *} +{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *} -### レスポンス結果 +### レスポンス結果 { #the-resulting-response } クライアントが`http://example.com/items/foo`(`item_id` `"foo"`)をリクエストすると、HTTPステータスコードが200で、以下のJSONレスポンスが返されます: @@ -69,7 +69,7 @@ Pythonの例外なので、`return`ではなく、`raise`です。 /// -## カスタムヘッダーの追加 +## カスタムヘッダーの追加 { #add-custom-headers } 例えば、いくつかのタイプのセキュリティのために、HTTPエラーにカスタムヘッダを追加できると便利な状況がいくつかあります。 @@ -77,9 +77,9 @@ Pythonの例外なので、`return`ではなく、`raise`です。 しかし、高度なシナリオのために必要な場合には、カスタムヘッダーを追加することができます: -{* ../../docs_src/handling_errors/tutorial002.py hl[14] *} +{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *} -## カスタム例外ハンドラのインストール +## カスタム例外ハンドラのインストール { #install-custom-exception-handlers } カスタム例外ハンドラはStarletteと同じ例外ユーティリティを使用して追加することができます。 @@ -89,7 +89,7 @@ Pythonの例外なので、`return`ではなく、`raise`です。 カスタム例外ハンドラを`@app.exception_handler()`で追加することができます: -{* ../../docs_src/handling_errors/tutorial003.py hl[5,6,7,13,14,15,16,17,18,24] *} +{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *} ここで、`/unicorns/yolo`をリクエストすると、*path operation*は`UnicornException`を`raise`します。 @@ -109,7 +109,7 @@ Pythonの例外なので、`return`ではなく、`raise`です。 /// -## デフォルトの例外ハンドラのオーバーライド +## デフォルトの例外ハンドラのオーバーライド { #override-the-default-exception-handlers } **FastAPI** にはいくつかのデフォルトの例外ハンドラがあります。 @@ -117,7 +117,7 @@ Pythonの例外なので、`return`ではなく、`raise`です。 これらの例外ハンドラを独自のものでオーバーライドすることができます。 -### リクエスト検証の例外のオーバーライド +### リクエスト検証の例外のオーバーライド { #override-request-validation-exceptions } リクエストに無効なデータが含まれている場合、**FastAPI** は内部的に`RequestValidationError`を発生させます。 @@ -125,11 +125,11 @@ Pythonの例外なので、`return`ではなく、`raise`です。 これをオーバーライドするには`RequestValidationError`をインポートして`@app.exception_handler(RequestValidationError)`と一緒に使用して例外ハンドラをデコレートします。 -この例外ハンドラは`Requset`と例外を受け取ります。 +この例外ハンドラは`Request`と例外を受け取ります。 -{* ../../docs_src/handling_errors/tutorial004.py hl[2,14,15,16] *} +{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *} -これで、`/items/foo`にアクセスすると、デフォルトのJSONエラーの代わりに以下が返されます: +これで、`/items/foo`にアクセスすると、以下のデフォルトのJSONエラーの代わりに: ```JSON { @@ -146,39 +146,20 @@ Pythonの例外なので、`return`ではなく、`raise`です。 } ``` -以下のようなテキスト版を取得します: +以下のテキスト版を取得します: ``` -1 validation error -path -> item_id - value is not a valid integer (type=type_error.integer) +Validation errors: +Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer ``` -#### `RequestValidationError`と`ValidationError` - -/// warning | 注意 - -これらは今のあなたにとって重要でない場合は省略しても良い技術的な詳細です。 - -/// - -`RequestValidationError`はPydanticの`ValidationError`のサブクラスです。 - -**FastAPI** は`response_model`でPydanticモデルを使用していて、データにエラーがあった場合、ログにエラーが表示されるようにこれを使用しています。 - -しかし、クライアントやユーザーはそれを見ることはありません。その代わりに、クライアントはHTTPステータスコード`500`の「Internal Server Error」を受け取ります。 - -*レスポンス*やコードのどこか(クライアントの*リクエスト*ではなく)にPydanticの`ValidationError`がある場合、それは実際にはコードのバグなのでこのようにすべきです。 - -また、あなたがそれを修正している間は、セキュリティの脆弱性が露呈する場合があるため、クライアントやユーザーがエラーに関する内部情報にアクセスできないようにしてください。 - -### エラーハンドラ`HTTPException`のオーバーライド +### `HTTPException`エラーハンドラのオーバーライド { #override-the-httpexception-error-handler } 同様に、`HTTPException`ハンドラをオーバーライドすることもできます。 例えば、これらのエラーに対しては、JSONではなくプレーンテキストを返すようにすることができます: -{* ../../docs_src/handling_errors/tutorial004.py hl[3,4,9,10,11,22] *} +{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *} /// note | 技術詳細 @@ -188,13 +169,21 @@ path -> item_id /// -### `RequestValidationError`のボディの使用 +/// warning | 注意 + +`RequestValidationError`には、検証エラーが発生したファイル名と行番号の情報が含まれているため、必要であれば関連情報と一緒にログに表示できます。 + +しかし、そのまま文字列に変換して直接その情報を返すと、システムに関する情報が多少漏えいする可能性があります。そのため、ここではコードが各エラーを個別に抽出して表示します。 + +/// + +### `RequestValidationError`のボディの使用 { #use-the-requestvalidationerror-body } `RequestValidationError`には無効なデータを含む`body`が含まれています。 -アプリ開発中に本体のログを取ってデバッグしたり、ユーザーに返したりなどに使用することができます。 +アプリ開発中にボディのログを取ってデバッグしたり、ユーザーに返したりなどに使用することができます。 -{* ../../docs_src/handling_errors/tutorial005.py hl[14] *} +{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *} ここで、以下のような無効な項目を送信してみてください: @@ -207,7 +196,7 @@ path -> item_id 受信したボディを含むデータが無効であることを示すレスポンスが表示されます: -```JSON hl_lines="12 13 14 15" +```JSON hl_lines="12-15" { "detail": [ { @@ -226,36 +215,30 @@ path -> item_id } ``` -#### FastAPIの`HTTPException`とStarletteの`HTTPException` +#### FastAPIの`HTTPException`とStarletteの`HTTPException` { #fastapis-httpexception-vs-starlettes-httpexception } **FastAPI**は独自の`HTTPException`を持っています。 -また、 **FastAPI**のエラークラス`HTTPException`はStarletteのエラークラス`HTTPException`を継承しています。 +また、 **FastAPI**の`HTTPException`エラークラスはStarletteの`HTTPException`エラークラスを継承しています。 -唯一の違いは、**FastAPI** の`HTTPException`はレスポンスに含まれるヘッダを追加できることです。 - -これはOAuth 2.0といくつかのセキュリティユーティリティのために内部的に必要とされ、使用されています。 +唯一の違いは、**FastAPI** の`HTTPException`は`detail`フィールドにJSONに変換可能な任意のデータを受け付けるのに対し、Starletteの`HTTPException`は文字列のみを受け付けることです。 そのため、コード内では通常通り **FastAPI** の`HTTPException`を発生させ続けることができます。 -しかし、例外ハンドラを登録する際には、Starletteの`HTTPException`を登録しておく必要があります。 +しかし、例外ハンドラを登録する際には、Starletteの`HTTPException`に対して登録しておく必要があります。 -これにより、Starletteの内部コードやStarletteの拡張機能やプラグインの一部が`HTTPException`を発生させた場合、ハンドラがそれをキャッチして処理することができるようになります。 +これにより、Starletteの内部コードやStarletteの拡張機能やプラグインの一部がStarletteの`HTTPException`を発生させた場合、ハンドラがそれをキャッチして処理できるようになります。 -以下の例では、同じコード内で両方の`HTTPException`を使用できるようにするために、Starletteの例外の名前を`StarletteHTTPException`に変更しています: +この例では、同じコード内で両方の`HTTPException`を使用できるようにするために、Starletteの例外を`StarletteHTTPException`にリネームしています: ```Python from starlette.exceptions import HTTPException as StarletteHTTPException ``` -### **FastAPI** の例外ハンドラの再利用 +### **FastAPI** の例外ハンドラの再利用 { #reuse-fastapis-exception-handlers } -また、何らかの方法で例外を使用することもできますが、**FastAPI** から同じデフォルトの例外ハンドラを使用することもできます。 +**FastAPI** から同じデフォルトの例外ハンドラと一緒に例外を使用したい場合は、`fastapi.exception_handlers`からデフォルトの例外ハンドラをインポートして再利用できます: -デフォルトの例外ハンドラを`fastapi.exception_handlers`からインポートして再利用することができます: +{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *} -{* ../../docs_src/handling_errors/tutorial006.py hl[2,3,4,5,15,21] *} - -この例では、非常に表現力のあるメッセージでエラーを`print`しています。 - -しかし、例外を使用して、デフォルトの例外ハンドラを再利用することができるということが理解できます。 +この例では、非常に表現力のあるメッセージでエラーを`print`しているだけですが、要点は理解できるはずです。例外を使用し、その後デフォルトの例外ハンドラを再利用できます。 diff --git a/docs/ja/docs/tutorial/header-params.md b/docs/ja/docs/tutorial/header-params.md index ac89afbdba..1916baf613 100644 --- a/docs/ja/docs/tutorial/header-params.md +++ b/docs/ja/docs/tutorial/header-params.md @@ -1,20 +1,20 @@ -# ヘッダーのパラメータ +# ヘッダーのパラメータ { #header-parameters } ヘッダーのパラメータは、`Query`や`Path`、`Cookie`のパラメータを定義するのと同じように定義できます。 -## `Header`をインポート +## `Header`をインポート { #import-header } まず、`Header`をインポートします: -{* ../../docs_src/header_params/tutorial001.py hl[3] *} +{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *} -## `Header`のパラメータの宣言 +## `Header`のパラメータの宣言 { #declare-header-parameters } 次に、`Path`や`Query`、`Cookie`と同じ構造を用いてヘッダーのパラメータを宣言します。 -最初の値がデフォルト値で、追加の検証パラメータや注釈パラメータをすべて渡すことができます。 +デフォルト値に加えて、追加の検証パラメータや注釈パラメータをすべて定義できます: -{* ../../docs_src/header_params/tutorial001.py hl[9] *} +{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *} /// note | 技術詳細 @@ -30,23 +30,23 @@ /// -## 自動変換 +## 自動変換 { #automatic-conversion } `Header`は`Path`や`Query`、`Cookie`が提供する機能に加え、少しだけ追加の機能を持っています。 -ほとんどの標準ヘッダーは、「マイナス記号」(`-`)としても知られる「ハイフン」で区切られています。 +ほとんどの標準ヘッダーは、「マイナス記号」(`-`)としても知られる「ハイフン」文字で区切られています。 しかし、`user-agent`のような変数はPythonでは無効です。 -そのため、デフォルトでは、`Header`はパラメータの文字をアンダースコア(`_`)からハイフン(`-`)に変換して、ヘッダーを抽出して文書化します。 +そのため、デフォルトでは、`Header`はパラメータ名の文字をアンダースコア(`_`)からハイフン(`-`)に変換して、ヘッダーを抽出して文書化します。 また、HTTPヘッダは大文字小文字を区別しないので、Pythonの標準スタイル(別名「スネークケース」)で宣言することができます。 そのため、`User_Agent`などのように最初の文字を大文字にする必要はなく、通常のPythonコードと同じように`user_agent`を使用することができます。 -もしなんらかの理由でアンダースコアからハイフンへの自動変換を無効にする必要がある場合は、`Header`の`convert_underscores`に`False`を設定してください: +もしなんらかの理由でアンダースコアからハイフンへの自動変換を無効にする必要がある場合は、`Header`のパラメータ`convert_underscores`を`False`に設定してください: -{* ../../docs_src/header_params/tutorial002.py hl[9] *} +{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *} /// warning | 注意 @@ -54,26 +54,26 @@ /// -## ヘッダーの重複 +## ヘッダーの重複 { #duplicate-headers } 受信したヘッダーが重複することがあります。つまり、同じヘッダーで複数の値を持つということです。 -これらの場合、リストの型宣言を使用して定義することができます。 +これらの場合、型宣言でリストを使用して定義することができます。 重複したヘッダーのすべての値をPythonの`list`として受け取ることができます。 例えば、複数回出現する可能性のある`X-Token`のヘッダを定義するには、以下のように書くことができます: -{* ../../docs_src/header_params/tutorial003.py hl[9] *} +{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *} -もし、その*path operation*で通信する場合は、次のように2つのHTTPヘッダーを送信します: +その*path operation*と通信する際に、次のように2つのHTTPヘッダーを送信する場合: ``` X-Token: foo X-Token: bar ``` -このレスポンスは以下のようになります: +レスポンスは以下のようになります: ```JSON { @@ -84,8 +84,8 @@ X-Token: bar } ``` -## まとめ +## まとめ { #recap } -ヘッダーは`Header`で宣言し、`Query`や`Path`、`Cookie`と同じパターンを使用する。 +ヘッダーは`Header`で宣言し、`Query`や`Path`、`Cookie`と同じ共通パターンを使用します。 また、変数のアンダースコアを気にする必要はありません。**FastAPI** がそれらの変換をすべて取り持ってくれます。 diff --git a/docs/ja/docs/tutorial/index.md b/docs/ja/docs/tutorial/index.md index 87d3751fd9..d298abc62d 100644 --- a/docs/ja/docs/tutorial/index.md +++ b/docs/ja/docs/tutorial/index.md @@ -1,83 +1,95 @@ -# チュートリアル - ユーザーガイド +# チュートリアル - ユーザーガイド { #tutorial-user-guide } -このチュートリアルは**FastAPI**のほぼすべての機能の使い方を段階的に紹介します。 +このチュートリアルでは、**FastAPI**のほとんどの機能を使う方法を段階的に紹介します。 -各セクションは前のセクションを踏まえた内容になっています。しかし、トピックごとに分割されているので、特定のAPIの要求を満たすようなトピックに直接たどり着けるようになっています。 +各セクションは前のセクションを踏まえた内容になっています。しかし、トピックごとに分割されているので、特定のAPIのニーズを満たすために、任意の特定のトピックに直接進めるようになっています。 -また、将来的にリファレンスとして機能するように構築されています。 +また、将来的にリファレンスとして機能するように構築されているので、後で戻ってきて必要なものを正確に確認できます。 -従って、後でこのチュートリアルに戻ってきて必要なものを確認できます。 - -## コードを実行する +## コードを実行する { #run-the-code } すべてのコードブロックをコピーして直接使用できます(実際にテストされたPythonファイルです)。 -いずれかの例を実行するには、コードを `main.py`ファイルにコピーし、` uvicorn`を次のように起動します: +いずれかの例を実行するには、コードを `main.py`ファイルにコピーし、次のように `fastapi dev` を起動します:
```console -$ uvicorn main:app --reload +$ fastapi dev main.py -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: Waiting for application startup. -INFO: Application startup complete. + FastAPI Starting development server 🚀 + + Searching for package file structure from directories + with __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with + the following code: + + from main import app + + app Using import string: main:app + + server Server started at http://127.0.0.1:8000 + server Documentation at http://127.0.0.1:8000/docs + + tip Running in development mode, for production use: + fastapi run + + Logs: + + 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 [383138] using WatchFiles + INFO Started server process [383153] + INFO Waiting for application startup. + INFO Application startup complete. ```
-コードを記述またはコピーし、編集してローカルで実行することを**強くお勧めします**。 +コードを記述またはコピーし、編集してローカルで実行することを**強く推奨します**。 -また、エディターで使用することで、書く必要のあるコードの少なさ、すべての型チェック、自動補完などのFastAPIの利点を実感できます。 +エディターで使用することで、書く必要のあるコードの少なさ、すべての型チェック、自動補完など、FastAPIの利点を本当に実感できます。 --- -## FastAPIをインストールする +## FastAPIをインストールする { #install-fastapi } 最初のステップは、FastAPIのインストールです。 -チュートリアルのために、すべてのオプションの依存関係と機能をインストールしたいとき: +[仮想環境](../virtual-environments.md){.internal-link target=_blank} を作成して有効化し、それから **FastAPIをインストール** してください:
```console -$ pip install "fastapi[all]" +$ pip install "fastapi[standard]" ---> 100% ```
-...これには、コードを実行するサーバーとして使用できる `uvicorn`も含まれます。 - /// note | 備考 -パーツ毎にインストールすることも可能です。 +`pip install "fastapi[standard]"` でインストールすると、`fastapi-cloud-cli` を含むいくつかのデフォルトのオプション標準依存関係が付属します。これにより、FastAPI Cloud にデプロイできます。 -以下は、アプリケーションを本番環境にデプロイする際に行うであろうものです: +これらのオプション依存関係が不要な場合は、代わりに `pip install fastapi` をインストールできます。 -``` -pip install fastapi -``` - -また、サーバーとして動作するように`uvicorn` をインストールします: - -``` -pip install "uvicorn[standard]" -``` - -そして、使用したい依存関係をそれぞれ同様にインストールします。 +標準依存関係はインストールしたいが `fastapi-cloud-cli` は不要な場合は、`pip install "fastapi[standard-no-fastapi-cloud-cli]"` でインストールできます。 /// -## 高度なユーザーガイド +## 高度なユーザーガイド { #advanced-user-guide } -**高度なユーザーガイド**もあり、**チュートリアル - ユーザーガイド**の後で読むことができます。 +この **チュートリアル - ユーザーガイド** の後で、後から読める **高度なユーザーガイド** もあります。 -**高度なユーザーガイド**は**チュートリアル - ユーザーガイド**に基づいており、同じ概念を使用し、いくつかの追加機能を紹介しています。 +**高度なユーザーガイド** は本チュートリアルをベースにしており、同じ概念を使用し、いくつかの追加機能を教えます。 -ただし、最初に**チュートリアル - ユーザーガイド**(現在読んでいる内容)をお読みください。 +ただし、最初に **チュートリアル - ユーザーガイド**(今読んでいる内容)をお読みください。 -**チュートリアル-ユーザーガイド**だけで完全なアプリケーションを構築できるように設計されています。加えて、**高度なユーザーガイド**の中からニーズに応じたアイデアを使用して、様々な拡張が可能です。 +**チュートリアル - ユーザーガイド** だけで完全なアプリケーションを構築できるように設計されており、その後ニーズに応じて、**高度なユーザーガイド** の追加のアイデアのいくつかを使って、さまざまな方法で拡張できます。 diff --git a/docs/ja/docs/tutorial/metadata.md b/docs/ja/docs/tutorial/metadata.md index b93dedcb91..0ffb8f3505 100644 --- a/docs/ja/docs/tutorial/metadata.md +++ b/docs/ja/docs/tutorial/metadata.md @@ -1,47 +1,66 @@ -# メタデータとドキュメントのURL +# メタデータとドキュメントのURL { #metadata-and-docs-urls } -**FastAPI** アプリケーションのいくつかのメタデータの設定をカスタマイズできます。 +**FastAPI** アプリケーションのいくつかのメタデータ設定をカスタマイズできます。 -## タイトル、説明文、バージョン +## APIのメタデータ { #metadata-for-api } -以下を設定できます: +OpenAPI仕様および自動APIドキュメントUIで使用される次のフィールドを設定できます: -* **タイトル**: OpenAPIおよび自動APIドキュメントUIでAPIのタイトル/名前として使用される。 -* **説明文**: OpenAPIおよび自動APIドキュメントUIでのAPIの説明文。 -* **バージョン**: APIのバージョン。例: `v2` または `2.5.0`。 - *たとえば、以前のバージョンのアプリケーションがあり、OpenAPIも使用している場合に便利です。 +| パラメータ | 型 | 説明 | +|------------|------|-------------| +| `title` | `str` | APIのタイトルです。 | +| `summary` | `str` | APIの短い要約です。 OpenAPI 3.1.0、FastAPI 0.99.0 以降で利用できます。 | +| `description` | `str` | APIの短い説明です。Markdownを使用できます。 | +| `version` | `string` | APIのバージョンです。これはOpenAPIのバージョンではなく、あなた自身のアプリケーションのバージョンです。たとえば `2.5.0` です。 | +| `terms_of_service` | `str` | APIの利用規約へのURLです。指定する場合、URLである必要があります。 | +| `contact` | `dict` | 公開されるAPIの連絡先情報です。複数のフィールドを含められます。
contact fields
ParameterTypeDescription
namestr連絡先の個人/組織を識別する名前です。
urlstr連絡先情報を指すURLです。URL形式である必要があります。
emailstr連絡先の個人/組織のメールアドレスです。メールアドレス形式である必要があります。
| +| `license_info` | `dict` | 公開されるAPIのライセンス情報です。複数のフィールドを含められます。
license_info fields
ParameterTypeDescription
namestr必須license_info が設定されている場合)。APIに使用されるライセンス名です。
identifierstrAPIの SPDX ライセンス式です。identifier フィールドは url フィールドと同時に指定できません。 OpenAPI 3.1.0、FastAPI 0.99.0 以降で利用できます。
urlstrAPIに使用されるライセンスへのURLです。URL形式である必要があります。
| -これらを設定するには、パラメータ `title`、`description`、`version` を使用します: +以下のように設定できます: -{* ../../docs_src/metadata/tutorial001.py hl[4:6] *} +{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *} -この設定では、自動APIドキュメントは以下の様になります: +/// tip | 豆知識 + +`description` フィールドにはMarkdownを書けて、出力ではレンダリングされます。 + +/// + +この設定では、自動APIドキュメントは以下のようになります: -## タグのためのメタデータ +## ライセンス識別子 { #license-identifier } -さらに、パラメータ `openapi_tags` を使うと、path operations をグループ分けするための複数のタグに関するメタデータを追加できます。 +OpenAPI 3.1.0 および FastAPI 0.99.0 以降では、`license_info` を `url` の代わりに `identifier` で設定することもできます。 -それぞれのタグ毎にひとつの辞書を含むリストをとります。 +例: -それぞれの辞書は以下をもつことができます: +{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *} -* `name` (**必須**): *path operations* および `APIRouter` の `tags` パラメーターで使用するのと同じタグ名である `str`。 -* `description`: タグの簡単な説明文である `str`。 Markdownで記述でき、ドキュメントUIに表示されます。 -* `externalDocs`: 外部ドキュメントを説明するための `dict`: - * `description`: 外部ドキュメントの簡単な説明文である `str`。 - * `url` (**必須**): 外部ドキュメントのURLである `str`。 +## タグのメタデータ { #metadata-for-tags } -### タグのためのメタデータの作成 +パラメータ `openapi_tags` を使うと、path operation をグループ分けするために使用する各タグに追加のメタデータを追加できます。 -`users` と `items` のタグを使った例でメタデータの追加を試してみましょう。 +それぞれのタグごとに1つの辞書を含むリストを取ります。 -タグのためのメタデータを作成し、それを `openapi_tags` パラメータに渡します。 +それぞれの辞書は以下を含められます: -{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *} +* `name` (**必須**): *path operation* および `APIRouter` の `tags` パラメータで使用するのと同じタグ名の `str`。 +* `description`: タグの短い説明の `str`。Markdownを含められ、ドキュメントUIに表示されます。 +* `externalDocs`: 外部ドキュメントを説明する `dict`。以下を含みます: + * `description`: 外部ドキュメントの短い説明の `str`。 + * `url` (**必須**): 外部ドキュメントのURLの `str`。 -説明文 (description) の中で Markdown を使用できることに注意してください。たとえば、「login」は太字 (**login**) で表示され、「fancy」は斜体 (_fancy_) で表示されます。 +### タグのメタデータの作成 { #create-metadata-for-tags } + +`users` と `items` のタグを使った例で試してみましょう。 + +タグのメタデータを作成し、それを `openapi_tags` パラメータに渡します: + +{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *} + +説明の中でMarkdownを使用できることに注意してください。たとえば「login」は太字 (**login**) で表示され、「fancy」は斜体 (_fancy_) で表示されます。 /// tip | 豆知識 @@ -49,31 +68,31 @@ /// -### 自作タグの使用 +### タグの使用 { #use-your-tags } -`tags` パラメーターを使用して、それぞれの *path operations* (および `APIRouter`) を異なるタグに割り当てます: +*path operation*(および `APIRouter`)の `tags` パラメータを使用して、それらを異なるタグに割り当てます: -{* ../../docs_src/metadata/tutorial004.py hl[21,26] *} +{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *} /// info | 情報 -タグのより詳しい説明を知りたい場合は [Path Operation Configuration](path-operation-configuration.md#tags){.internal-link target=_blank} を参照して下さい。 +タグの詳細は [Path Operation Configuration](path-operation-configuration.md#tags){.internal-link target=_blank} を参照してください。 /// -### ドキュメントの確認 +### ドキュメントの確認 { #check-the-docs } -ここで、ドキュメントを確認すると、追加したメタデータがすべて表示されます: +ここでドキュメントを確認すると、追加したメタデータがすべて表示されます: -### タグの順番 +### タグの順番 { #order-of-tags } タグのメタデータ辞書の順序は、ドキュメントUIに表示される順序の定義にもなります。 -たとえば、`users` はアルファベット順では `items` の後に続きます。しかし、リストの最初に `users` のメタデータ辞書を追加したため、ドキュメントUIでは `users` が先に表示されます。 +たとえば、`users` はアルファベット順では `items` の後に続きますが、リストの最初の辞書としてメタデータを追加したため、それより前に表示されます。 -## OpenAPI URL +## OpenAPI URL { #openapi-url } デフォルトでは、OpenAPIスキーマは `/openapi.json` で提供されます。 @@ -81,21 +100,21 @@ たとえば、`/api/v1/openapi.json` で提供されるように設定するには: -{* ../../docs_src/metadata/tutorial002.py hl[3] *} +{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *} OpenAPIスキーマを完全に無効にする場合は、`openapi_url=None` を設定できます。これにより、それを使用するドキュメントUIも無効になります。 -## ドキュメントのURL +## ドキュメントのURL { #docs-urls } -以下の2つのドキュメントUIを構築できます: +含まれている2つのドキュメントUIを設定できます: * **Swagger UI**: `/docs` で提供されます。 - * URL はパラメータ `docs_url` で設定できます。 - * `docs_url=None` を設定することで無効にできます。 -* ReDoc: `/redoc` で提供されます。 - * URL はパラメータ `redoc_url` で設定できます。 - * `redoc_url=None` を設定することで無効にできます。 + * URL はパラメータ `docs_url` で設定できます。 + * `docs_url=None` を設定することで無効にできます。 +* **ReDoc**: `/redoc` で提供されます。 + * URL はパラメータ `redoc_url` で設定できます。 + * `redoc_url=None` を設定することで無効にできます。 たとえば、`/documentation` でSwagger UIが提供されるように設定し、ReDocを無効にするには: -{* ../../docs_src/metadata/tutorial003.py hl[3] *} +{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *} diff --git a/docs/ja/docs/tutorial/middleware.md b/docs/ja/docs/tutorial/middleware.md index 539ec5b8c9..12fb57a640 100644 --- a/docs/ja/docs/tutorial/middleware.md +++ b/docs/ja/docs/tutorial/middleware.md @@ -1,4 +1,4 @@ -# ミドルウェア +# ミドルウェア { #middleware } **FastAPI** アプリケーションにミドルウェアを追加できます。 @@ -15,11 +15,11 @@ `yield` を使った依存関係をもつ場合は、終了コードはミドルウェアの *後に* 実行されます。 -バックグラウンドタスク (後述) がある場合は、それらは全てのミドルウェアの *後に* 実行されます。 +バックグラウンドタスク ([バックグラウンドタスク](background-tasks.md){.internal-link target=_blank} セクションで説明します。後で確認できます) がある場合は、それらは全てのミドルウェアの *後に* 実行されます。 /// -## ミドルウェアの作成 +## ミドルウェアの作成 { #create-a-middleware } ミドルウェアを作成するには、関数の上部でデコレータ `@app.middleware("http")` を使用します。 @@ -31,13 +31,13 @@ * 次に、対応する*path operation*によって生成された `response` を返します。 * その後、`response` を返す前にさらに `response` を変更することもできます。 -{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *} +{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *} /// tip | 豆知識 -'X-'プレフィックスを使用してカスタムの独自ヘッダーを追加できます。 +カスタムの独自ヘッダーは `X-` プレフィックスを使用して追加できる点に注意してください。 -ただし、ブラウザのクライアントに表示させたいカスタムヘッダーがある場合は、StarletteのCORSドキュメントに記載されているパラメータ `expose_headers` を使用して、それらをCORS設定に追加する必要があります ([CORS (オリジン間リソース共有)](cors.md){.internal-link target=_blank}) +ただし、ブラウザのクライアントに表示させたいカスタムヘッダーがある場合は、StarletteのCORSドキュメントに記載されているパラメータ `expose_headers` を使用して、それらをCORS設定に追加する必要があります ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank})。 /// @@ -49,7 +49,7 @@ /// -### `response` の前後 +### `response` の前後 { #before-and-after-the-response } *path operation* が `request` を受け取る前に、 `request` とともに実行されるコードを追加できます。 @@ -57,9 +57,38 @@ 例えば、リクエストの処理とレスポンスの生成にかかった秒数を含むカスタムヘッダー `X-Process-Time` を追加できます: -{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *} +{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *} -## その他のミドルウェア +/// tip | 豆知識 + +ここでは、これらのユースケースに対してより正確になり得るため、`time.time()` の代わりに `time.perf_counter()` を使用しています。 🤓 + +/// + +## 複数ミドルウェアの実行順序 { #multiple-middleware-execution-order } + +`@app.middleware()` デコレータまたは `app.add_middleware()` メソッドのいずれかを使って複数のミドルウェアを追加すると、新しく追加された各ミドルウェアがアプリケーションをラップし、スタックを形成します。最後に追加されたミドルウェアが *最も外側*、最初に追加されたミドルウェアが *最も内側* になります。 + +リクエスト経路では、*最も外側* のミドルウェアが最初に実行されます。 + +レスポンス経路では、最後に実行されます。 + +例: + +```Python +app.add_middleware(MiddlewareA) +app.add_middleware(MiddlewareB) +``` + +これにより、実行順序は次のようになります: + +* **リクエスト**: MiddlewareB → MiddlewareA → route + +* **レスポンス**: route → MiddlewareA → MiddlewareB + +このスタック動作により、ミドルウェアが予測可能で制御しやすい順序で実行されることが保証されます。 + +## その他のミドルウェア { #other-middlewares } 他のミドルウェアの詳細については、[高度なユーザーガイド: 高度なミドルウェア](../advanced/middleware.md){.internal-link target=_blank}を参照してください。 diff --git a/docs/ja/docs/tutorial/path-operation-configuration.md b/docs/ja/docs/tutorial/path-operation-configuration.md index 0cc38cb255..eb6b6b11a5 100644 --- a/docs/ja/docs/tutorial/path-operation-configuration.md +++ b/docs/ja/docs/tutorial/path-operation-configuration.md @@ -1,14 +1,14 @@ -# Path Operationの設定 +# Path Operationの設定 { #path-operation-configuration } *path operationデコレータ*を設定するためのパラメータがいくつかあります。 /// warning | 注意 -これらのパラメータは*path operation関数*ではなく、*path operationデコレータ*に直接渡されることに注意してください。 +これらのパラメータは*path operationデコレータ*に直接渡され、*path operation関数*に渡されないことに注意してください。 /// -## レスポンスステータスコード +## レスポンスステータスコード { #response-status-code } *path operation*のレスポンスで使用する(HTTP)`status_code`を定義することができます。 @@ -16,55 +16,65 @@ しかし、それぞれの番号コードが何のためのものか覚えていない場合は、`status`のショートカット定数を使用することができます: -{* ../../docs_src/path_operation_configuration/tutorial001.py hl[3,17] *} +{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *} そのステータスコードはレスポンスで使用され、OpenAPIスキーマに追加されます。 /// note | 技術詳細 -また、`from starlette import status`を使用することもできます。 +`from starlette import status`を使用することもできます。 **FastAPI** は開発者の利便性を考慮して、`fastapi.status`と同じ`starlette.status`を提供しています。しかし、これはStarletteから直接提供されています。 /// -## タグ +## タグ { #tags } `tags`パラメータを`str`の`list`(通常は1つの`str`)と一緒に渡すと、*path operation*にタグを追加できます: -{* ../../docs_src/path_operation_configuration/tutorial002.py hl[17,22,27] *} +{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *} これらはOpenAPIスキーマに追加され、自動ドキュメントのインターフェースで使用されます: - + -## 概要と説明 +### Enumを使ったタグ { #tags-with-enums } + +大きなアプリケーションの場合、**複数のタグ**が蓄積されていき、関連する*path operations*に対して常に**同じタグ**を使っていることを確認したくなるかもしれません。 + +このような場合、タグを`Enum`に格納すると理にかなっています。 + +**FastAPI** は、プレーンな文字列の場合と同じ方法でそれをサポートしています: + +{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *} + +## 概要と説明 { #summary-and-description } `summary`と`description`を追加できます: -{* ../../docs_src/path_operation_configuration/tutorial003.py hl[20:21] *} +{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *} -## docstringを用いた説明 +## docstringを用いた説明 { #description-from-docstring } -説明文は長くて複数行におよぶ傾向があるので、関数docstring内に*path operation*の説明文を宣言できます。すると、**FastAPI** は説明文を読み込んでくれます。 +説明文は長くて複数行におよぶ傾向があるので、関数docstring内に*path operation*の説明文を宣言できます。すると、**FastAPI** は説明文を読み込んでくれます。 docstringにMarkdownを記述すれば、正しく解釈されて表示されます。(docstringのインデントを考慮して) -{* ../../docs_src/path_operation_configuration/tutorial004.py hl[19:27] *} +{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *} これは対話的ドキュメントで使用されます: - + -## レスポンスの説明 +## レスポンスの説明 { #response-description } `response_description`パラメータでレスポンスの説明をすることができます。 -{* ../../docs_src/path_operation_configuration/tutorial005.py hl[21] *} +{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *} /// info | 情報 -`respnse_description`は具体的にレスポンスを参照し、`description`は*path operation*全般を参照していることに注意してください。 +`response_description`は具体的にレスポンスを参照し、`description`は*path operation*全般を参照していることに注意してください。 /// @@ -76,22 +86,22 @@ OpenAPIは*path operation*ごとにレスポンスの説明を必要としてい /// - + -## 非推奨の*path operation* +## *path operation*を非推奨にする { #deprecate-a-path-operation } -*path operation*をdeprecatedとしてマークする必要があるが、それを削除しない場合は、`deprecated`パラメータを渡します: +*path operation*をdeprecatedとしてマークする必要があるが、それを削除しない場合は、`deprecated`パラメータを渡します: -{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *} +{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *} 対話的ドキュメントでは非推奨と明記されます: - + *path operations*が非推奨である場合とそうでない場合でどのように見えるかを確認してください: - + -## まとめ +## まとめ { #recap } *path operationデコレータ*にパラメータを渡すことで、*path operations*のメタデータを簡単に設定・追加することができます。 diff --git a/docs/ja/docs/tutorial/path-params-numeric-validations.md b/docs/ja/docs/tutorial/path-params-numeric-validations.md index a1810ae37b..6a9ecc4e7b 100644 --- a/docs/ja/docs/tutorial/path-params-numeric-validations.md +++ b/docs/ja/docs/tutorial/path-params-numeric-validations.md @@ -1,40 +1,52 @@ -# パスパラメータと数値の検証 +# パスパラメータと数値の検証 { #path-parameters-and-numeric-validations } クエリパラメータに対して`Query`でより多くのバリデーションとメタデータを宣言できるのと同じように、パスパラメータに対しても`Path`で同じ種類のバリデーションとメタデータを宣言することができます。 -## Pathのインポート +## `Path`のインポート { #import-path } -まず初めに、`fastapi`から`Path`をインポートします: +まず初めに、`fastapi`から`Path`をインポートし、`Annotated`もインポートします: -{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[1] *} +{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *} -## メタデータの宣言 +/// info | 情報 + +FastAPI はバージョン 0.95.0 で`Annotated`のサポートを追加し(そして推奨し始めました)。 + +古いバージョンの場合、`Annotated`を使おうとするとエラーになります。 + +`Annotated`を使用する前に、FastAPI のバージョンを少なくとも 0.95.1 まで[アップグレードしてください](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank}。 + +/// + +## メタデータの宣言 { #declare-metadata } パラメータは`Query`と同じものを宣言することができます。 例えば、パスパラメータ`item_id`に対して`title`のメタデータを宣言するには以下のようにします: -{* ../../docs_src/path_params_numeric_validations/tutorial001.py hl[8] *} +{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *} /// note | 備考 -パスの一部でなければならないので、パスパラメータは常に必須です。 - -そのため、`...`を使用して必須と示す必要があります。 - -それでも、`None`で宣言しても、デフォルト値を設定しても、何の影響もなく、常に必要とされていることに変わりはありません。 +パスパラメータはパスの一部でなければならないので、常に必須です。`None`で宣言したりデフォルト値を設定したりしても何も影響せず、常に必須のままです。 /// -## 必要に応じてパラメータを並び替える +## 必要に応じてパラメータを並び替える { #order-the-parameters-as-you-need } + +/// tip | 豆知識 + +`Annotated`を使う場合、これはおそらくそれほど重要でも必要でもありません。 + +/// クエリパラメータ`q`を必須の`str`として宣言したいとしましょう。 また、このパラメータには何も宣言する必要がないので、`Query`を使う必要はありません。 -しかし、パスパラメータ`item_id`のために`Path`を使用する必要があります。 +しかし、パスパラメータ`item_id`のために`Path`を使用する必要があります。そして何らかの理由で`Annotated`を使いたくないとします。 -Pythonは「デフォルト」を持たない値の前に「デフォルト」を持つ値を置くことができません。 +Pythonは「デフォルト」を持つ値を「デフォルト」を持たない値の前に置くとエラーになります。 しかし、それらを並び替えることができ、デフォルト値を持たない値(クエリパラメータ`q`)を最初に持つことができます。 @@ -42,63 +54,88 @@ Pythonは「デフォルト」を持たない値の前に「デフォルト」 そのため、以下のように関数を宣言することができます: -{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[8] *} +{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *} -## 必要に応じてパラメータを並び替えるトリック +ただし、`Annotated`を使う場合はこの問題は起きないことを覚えておいてください。`Query()`や`Path()`に関数パラメータのデフォルト値を使わないためです。 -クエリパラメータ`q`を`Query`やデフォルト値なしで宣言し、パスパラメータ`item_id`を`Path`を用いて宣言し、それらを別の順番に並びたい場合、Pythonには少し特殊な構文が用意されています。 +{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *} + +## 必要に応じてパラメータを並び替えるトリック { #order-the-parameters-as-you-need-tricks } + +/// tip | 豆知識 + +`Annotated`を使う場合、これはおそらくそれほど重要でも必要でもありません。 + +/// + +これは**小さなトリック**で、便利な場合がありますが、頻繁に必要になることはありません。 + +次のことをしたい場合: + +* `q`クエリパラメータを`Query`もデフォルト値もなしで宣言する +* パスパラメータ`item_id`を`Path`を使って宣言する +* それらを別の順番にする +* `Annotated`を使わない + +...Pythonにはそのための少し特殊な構文があります。 関数の最初のパラメータとして`*`を渡します。 Pythonはその`*`で何かをすることはありませんが、それ以降のすべてのパラメータがキーワード引数(キーと値のペア)として呼ばれるべきものであると知っているでしょう。それはkwargsとしても知られています。たとえデフォルト値がなくても。 -{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[8] *} +{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *} -## 数値の検証: 以上 +### `Annotated`のほうがよい { #better-with-annotated } -`Query`と`Path`(、そして後述する他のもの)を用いて、文字列の制約を宣言することができますが、数値の制約も同様に宣言できます。 +`Annotated`を使う場合は、関数パラメータのデフォルト値を使わないため、この問題は起きず、おそらく`*`を使う必要もありません。 -ここで、`ge=1`の場合、`item_id`は`1`「より大きい`g`か、同じ`e`」整数でなれけばなりません。 +{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *} -{* ../../docs_src/path_params_numeric_validations/tutorial004.py hl[8] *} +## 数値の検証: 以上 { #number-validations-greater-than-or-equal } -## 数値の検証: より大きいと小なりイコール +`Query`と`Path`(、そして後述する他のもの)を用いて、数値の制約を宣言できます。 + +ここで、`ge=1`の場合、`item_id`は`1`「より大きい`g`か、同じ`e`」整数でなければなりません。 + +{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *} + +## 数値の検証: より大きいと小なりイコール { #number-validations-greater-than-and-less-than-or-equal } 以下も同様です: -* `gt`: より大きい(`g`reater `t`han) -* `le`: 小なりイコール(`l`ess than or `e`qual) +* `gt`: `g`reater `t`han +* `le`: `l`ess than or `e`qual -{* ../../docs_src/path_params_numeric_validations/tutorial005.py hl[9] *} +{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *} -## 数値の検証: 浮動小数点、 大なり小なり +## 数値の検証: 浮動小数点、 大なり小なり { #number-validations-floats-greater-than-and-less-than } 数値のバリデーションは`float`の値に対しても有効です。 -ここで重要になってくるのはgtだけでなくgeも宣言できることです。これと同様に、例えば、値が`1`より小さくても`0`より大きくなければならないことを要求することができます。 +ここで重要になってくるのはgtだけでなくgeも宣言できることです。これと同様に、例えば、値が`1`より小さくても`0`より大きくなければならないことを要求することができます。 したがって、`0.5`は有効な値ですが、`0.0`や`0`はそうではありません。 -これはltも同じです。 +これはltも同じです。 -{* ../../docs_src/path_params_numeric_validations/tutorial006.py hl[11] *} +{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *} -## まとめ +## まとめ { #recap } `Query`と`Path`(そしてまだ見たことない他のもの)では、[クエリパラメータと文字列の検証](query-params-str-validations.md){.internal-link target=_blank}と同じようにメタデータと文字列の検証を宣言することができます。 また、数値のバリデーションを宣言することもできます: -* `gt`: より大きい(`g`reater `t`han) -* `ge`: 以上(`g`reater than or `e`qual) -* `lt`: より小さい(`l`ess `t`han) -* `le`: 以下(`l`ess than or `e`qual) +* `gt`: `g`reater `t`han +* `ge`: `g`reater than or `e`qual +* `lt`: `l`ess `t`han +* `le`: `l`ess than or `e`qual /// info | 情報 -`Query`、`Path`などは後に共通の`Param`クラスのサブクラスを見ることになります。(使う必要はありません) +`Query`、`Path`、および後で見る他のクラスは、共通の`Param`クラスのサブクラスです。 -そして、それらすべては、これまで見てきた追加のバリデーションとメタデータと同じパラメータを共有しています。 +それらはすべて、これまで見てきた追加のバリデーションとメタデータの同じパラメータを共有しています。 /// diff --git a/docs/ja/docs/tutorial/path-params.md b/docs/ja/docs/tutorial/path-params.md index 1893ec12f4..96a1fe9d10 100644 --- a/docs/ja/docs/tutorial/path-params.md +++ b/docs/ja/docs/tutorial/path-params.md @@ -1,22 +1,22 @@ -# パスパラメータ +# パスパラメータ { #path-parameters } Pythonのformat文字列と同様のシンタックスで「パスパラメータ」や「パス変数」を宣言できます: -{* ../../docs_src/path_params/tutorial001.py hl[6,7] *} +{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *} パスパラメータ `item_id` の値は、引数 `item_id` として関数に渡されます。 -しがたって、この例を実行して http://127.0.0.1:8000/items/foo にアクセスすると、次のレスポンスが表示されます。 +したがって、この例を実行して http://127.0.0.1:8000/items/foo にアクセスすると、次のレスポンスが表示されます。 ```JSON {"item_id":"foo"} ``` -## パスパラメータと型 +## 型付きパスパラメータ { #path-parameters-with-types } 標準のPythonの型アノテーションを使用して、関数内のパスパラメータの型を宣言できます: -{* ../../docs_src/path_params/tutorial002.py hl[7] *} +{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *} ここでは、 `item_id` は `int` として宣言されています。 @@ -26,7 +26,7 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー /// -## データ変換 +## データ変換 { #data-conversion } この例を実行し、ブラウザで http://127.0.0.1:8000/items/3 を開くと、次のレスポンスが表示されます: @@ -38,68 +38,69 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー 関数が受け取った(および返した)値は、文字列の `"3"` ではなく、Pythonの `int` としての `3` であることに注意してください。 -したがって、型宣言を使用すると、**FastAPI**は自動リクエスト "解析" を行います。 +したがって、その型宣言を使うと、**FastAPI**は自動リクエスト "解析" を行います。 /// -## データバリデーション +## データバリデーション { #data-validation } しかしブラウザで http://127.0.0.1:8000/items/foo を開くと、次のHTTPエラーが表示されます: ```JSON { - "detail": [ - { - "loc": [ - "path", - "item_id" - ], - "msg": "value is not a valid integer", - "type": "type_error.integer" - } - ] + "detail": [ + { + "type": "int_parsing", + "loc": [ + "path", + "item_id" + ], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "foo" + } + ] } ``` これは、パスパラメータ `item_id` が `int` ではない値 `"foo"` だからです。 -http://127.0.0.1:8000/items/4.2 で見られるように、intのかわりに `float` が与えられた場合にも同様なエラーが表示されます。 +http://127.0.0.1:8000/items/4.2 で見られるように、`int` のかわりに `float` が与えられた場合にも同様なエラーが表示されます。 /// check | 確認 -したがって、Pythonの型宣言を使用することで、**FastAPI**はデータのバリデーションを行います。 +したがって、同じPythonの型宣言を使用することで、**FastAPI**はデータのバリデーションを行います。 -表示されたエラーには問題のある箇所が明確に指摘されていることに注意してください。 +表示されたエラーには、バリデーションが通らなかった箇所が明確に示されていることに注意してください。 -これは、APIに関連するコードの開発およびデバッグに非常に役立ちます。 +これは、APIとやり取りするコードを開発・デバッグする際に非常に役立ちます。 /// -## ドキュメント +## ドキュメント { #documentation } -そしてブラウザで http://127.0.0.1:8000/docs を開くと、以下の様な自動的に生成された対話的なドキュメントが表示されます。 +そしてブラウザで http://127.0.0.1:8000/docs を開くと、以下の様な自動的に生成された対話的なAPIドキュメントが表示されます。 /// check | 確認 -繰り返しになりますが、Python型宣言を使用するだけで、**FastAPI**は対話的なAPIドキュメントを自動的に生成します(Swagger UIを統合)。 +繰り返しになりますが、同じPython型宣言を使用するだけで、**FastAPI**は対話的なドキュメントを自動的に生成します(Swagger UIを統合)。 パスパラメータが整数として宣言されていることに注意してください。 /// -## 標準であることのメリット、ドキュメンテーションの代替物 +## 標準ベースのメリット、ドキュメンテーションの代替物 { #standards-based-benefits-alternative-documentation } -また、生成されたスキーマが OpenAPI 標準に従っているので、互換性のあるツールが多数あります。 +また、生成されたスキーマが OpenAPI 標準に従っているので、互換性のあるツールが多数あります。 このため、**FastAPI**自体が代替のAPIドキュメントを提供します(ReDocを使用)。これは、 http://127.0.0.1:8000/redoc にアクセスすると確認できます。 -同様に、互換性のあるツールが多数あります(多くの言語用のコード生成ツールを含む)。 +同様に、互換性のあるツールが多数あります。多くの言語用のコード生成ツールを含みます。 -## Pydantic +## Pydantic { #pydantic } すべてのデータバリデーションは Pydantic によって内部で実行されるため、Pydanticの全てのメリットが得られます。そして、安心して利用することができます。 @@ -107,7 +108,7 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー これらのいくつかについては、チュートリアルの次の章で説明します。 -## 順序の問題 +## 順序の問題 { #order-matters } *path operations* を作成する際、固定パスをもつ状況があり得ます。 @@ -117,29 +118,29 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー *path operations* は順に評価されるので、 `/users/me` が `/users/{user_id}` よりも先に宣言されているか確認する必要があります: -{* ../../docs_src/path_params/tutorial003.py hl[6,11] *} +{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *} -それ以外の場合、 `/users/{users_id}` は `/users/me` としてもマッチします。値が「"me"」であるパラメータ `user_id` を受け取ると「考え」ます。 +それ以外の場合、 `/users/{user_id}` は `/users/me` としてもマッチします。値が `"me"` であるパラメータ `user_id` を受け取ると「考え」ます。 -## 定義済みの値 +同様に、path operation を再定義することはできません: -*パスパラメータ*を受け取る *path operation* をもち、有効な*パスパラメータ*の値を事前に定義したい場合は、標準のPython `Enum` を利用できます。 +{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *} -### `Enum` クラスの作成 +パスは最初にマッチしたものが常に使われるため、最初のものが常に使用されます。 + +## 定義済みの値 { #predefined-values } + +*パスパラメータ*を受け取る *path operation* をもち、有効な*パスパラメータ*の値を事前に定義したい場合は、標準のPython `Enum` を利用できます。 + +### `Enum` クラスの作成 { #create-an-enum-class } `Enum` をインポートし、 `str` と `Enum` を継承したサブクラスを作成します。 -`str` を継承することで、APIドキュメントは値が `文字列` でなければいけないことを知り、正確にレンダリングできるようになります。 +`str` を継承することで、APIドキュメントは値が `string` 型でなければいけないことを知り、正確にレンダリングできるようになります。 そして、固定値のクラス属性を作ります。すると、その値が使用可能な値となります: -{* ../../docs_src/path_params/tutorial005.py hl[1,6,7,8,9] *} - -/// info | 情報 - -Enumerations (もしくは、enums)はPython 3.4以降で利用できます。 - -/// +{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *} /// tip | 豆知識 @@ -147,33 +148,33 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー /// -### *パスパラメータ*の宣言 +### *パスパラメータ*の宣言 { #declare-a-path-parameter } 次に、作成したenumクラスである`ModelName`を使用した型アノテーションをもつ*パスパラメータ*を作成します: -{* ../../docs_src/path_params/tutorial005.py hl[16] *} +{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *} -### ドキュメントの確認 +### ドキュメントの確認 { #check-the-docs } *パスパラメータ*の利用可能な値が事前に定義されているので、対話的なドキュメントで適切に表示できます: -### Python*列挙型*の利用 +### Python*列挙型*の利用 { #working-with-python-enumerations } *パスパラメータ*の値は*列挙型メンバ*となります。 -#### *列挙型メンバ*の比較 +#### *列挙型メンバ*の比較 { #compare-enumeration-members } これは、作成した列挙型 `ModelName` の*列挙型メンバ*と比較できます: -{* ../../docs_src/path_params/tutorial005.py hl[17] *} +{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *} -#### *列挙値*の取得 +#### *列挙値*の取得 { #get-the-enumeration-value } `model_name.value` 、もしくは一般に、 `your_enum_member.value` を使用して実際の値 (この場合は `str`) を取得できます。 -{* ../../docs_src/path_params/tutorial005.py hl[20] *} +{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *} /// tip | 豆知識 @@ -181,13 +182,13 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー /// -#### *列挙型メンバ*の返却 +#### *列挙型メンバ*の返却 { #return-enumeration-members } -*path operation* から*列挙型メンバ*を返すことができます。JSONボディ(`dict` など)でネストすることもできます。 +*path operation* から*列挙型メンバ*を返すことができます。JSONボディ(例: `dict`)でネストすることもできます。 それらはクライアントに返される前に適切な値 (この場合は文字列) に変換されます。 -{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *} +{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *} クライアントは以下の様なJSONレスポンスを得ます: @@ -198,23 +199,23 @@ Pythonのformat文字列と同様のシンタックスで「パスパラメー } ``` -## パスを含んだパスパラメータ +## パスを含んだパスパラメータ { #path-parameters-containing-paths } パス `/files/{file_path}` となる *path operation* を持っているとしましょう。 ただし、 `home/johndoe/myfile.txt` のような*パス*を含んだ `file_path` が必要です。 -したがって、URLは `/files/home/johndoe/myfile.txt` の様になります。 +したがって、そのファイルのURLは `/files/home/johndoe/myfile.txt` の様になります。 -### OpenAPIサポート +### OpenAPIサポート { #openapi-support } OpenAPIはテストや定義が困難なシナリオにつながる可能性があるため、内部に*パス*を含む*パスパラメータ*の宣言をサポートしていません。 それにも関わらず、Starletteの内部ツールのひとつを使用することで、**FastAPI**はそれが実現できます。 -そして、パラメータがパスを含むべきであることを明示的にドキュメントに追加することなく、機能します。 +そして、パラメータがパスを含むべきであることを示すドキュメントを追加しなくても、ドキュメントは動作します。 -### パス変換 +### パスコンバーター { #path-convertor } Starletteのオプションを直接使用することで、以下のURLの様な*パス*を含んだ、*パスパラメータ*の宣言ができます: @@ -226,17 +227,17 @@ Starletteのオプションを直接使用することで、以下のURLの様 したがって、以下の様に使用できます: -{* ../../docs_src/path_params/tutorial004.py hl[6] *} +{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *} /// tip | 豆知識 -最初のスラッシュ (`/`)が付いている `/home/johndoe/myfile.txt` をパラメータが含んでいる必要があります。 +最初のスラッシュ (`/`)が付いている `/home/johndoe/myfile.txt` をパラメータが含んでいる必要があるかもしれません。 この場合、URLは `files` と `home` の間にダブルスラッシュ (`//`) のある、 `/files//home/johndoe/myfile.txt` になります。 /// -## まとめ +## まとめ { #recap } 簡潔で、本質的で、標準的なPythonの型宣言を使用することで、**FastAPI**は以下を行います: diff --git a/docs/ja/docs/tutorial/query-param-models.md b/docs/ja/docs/tutorial/query-param-models.md index 053d0740bc..d892a57d22 100644 --- a/docs/ja/docs/tutorial/query-param-models.md +++ b/docs/ja/docs/tutorial/query-param-models.md @@ -1,8 +1,8 @@ -# クエリパラメータモデル +# クエリパラメータモデル { #query-parameter-models } もし関連する**複数のクエリパラメータ**から成るグループがあるなら、それらを宣言するために、**Pydanticモデル**を作成できます。 -こうすることで、**複数の場所**で**そのPydanticモデルを再利用**でき、バリデーションやメタデータを、すべてのクエリパラメータに対して一度に宣言できます。😎 +こうすることで、**複数の場所**で**そのモデルを再利用**でき、バリデーションやメタデータを、すべてのパラメータに対して一度に宣言できます。😎 /// note | 備考 @@ -10,15 +10,15 @@ /// -## クエリパラメータにPydanticモデルを使用する +## Pydanticモデルを使ったクエリパラメータ { #query-parameters-with-a-pydantic-model } -必要な**複数のクエリパラメータ**を**Pydanticモデル**で宣言し、さらに、それを `Query` として宣言しましょう: +必要な**クエリパラメータ**を**Pydanticモデル**で宣言し、さらに、そのパラメータを `Query` として宣言しましょう: {* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *} -**FastAPI**は、リクエストの**クエリパラメータ**からそれぞれの**フィールド**のデータを**抽出**し、定義された**Pydanticモデル**を提供します。 +**FastAPI**は、リクエストの**クエリパラメータ**からそれぞれの**フィールド**のデータを**抽出**し、定義したPydanticモデルを提供します。 -## ドキュメントの確認 +## ドキュメントの確認 { #check-the-docs } 対話的APIドキュメント `/docs` でクエリパラメータを確認できます: @@ -26,11 +26,11 @@
-## 余分なクエリパラメータを禁止する +## 余分なクエリパラメータを禁止する { #forbid-extra-query-parameters } -特定の(あまり一般的ではないかもしれない)ケースで、受け付けるクエリパラメータを**制限**する必要があるかもしれません。 +特定の(あまり一般的ではないかもしれない)ユースケースで、受け取りたいクエリパラメータを**制限**したい場合があります。 -Pydanticのモデルの Configuration を利用して、 `extra` フィールドを `forbid` とすることができます。 +Pydanticのモデル設定を使って、あらゆる `extra` フィールドを `forbid` にできます。 {* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *} @@ -42,7 +42,7 @@ Pydanticのモデルの Configuration を利用して、 `extra` フィールド https://example.com/items/?limit=10&tool=plumbus ``` -クエリパラメータ `tool` が許可されていないことを通知する**エラー**レスポンスが返されます。 +クエリパラメータ `tool` が許可されていないことを伝える**エラー**レスポンスが返されます。 ```json { @@ -57,7 +57,7 @@ https://example.com/items/?limit=10&tool=plumbus } ``` -## まとめ +## まとめ { #summary } **FastAPI**では、**クエリパラメータ**を宣言するために、**Pydanticモデル**を使用できます。😎 diff --git a/docs/ja/docs/tutorial/query-params-str-validations.md b/docs/ja/docs/tutorial/query-params-str-validations.md index 22b89e452f..e230ef29af 100644 --- a/docs/ja/docs/tutorial/query-params-str-validations.md +++ b/docs/ja/docs/tutorial/query-params-str-validations.md @@ -1,120 +1,228 @@ -# クエリパラメータと文字列の検証 +# クエリパラメータと文字列の検証 { #query-parameters-and-string-validations } **FastAPI** ではパラメータの追加情報とバリデーションを宣言することができます。 以下のアプリケーションを例にしてみましょう: -{* ../../docs_src/query_params_str_validations/tutorial001.py hl[9] *} +{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *} -クエリパラメータ `q` は `Optional[str]` 型で、`None` を許容する `str` 型を意味しており、デフォルトは `None` です。そのため、FastAPIはそれが必須ではないと理解します。 +クエリパラメータ `q` は `str | None` 型で、`str` 型ですが `None` にもなり得ることを意味し、実際にデフォルト値は `None` なので、FastAPIはそれが必須ではないと理解します。 /// note | 備考 -FastAPIは、 `q` はデフォルト値が `=None` であるため、必須ではないと理解します。 +FastAPIは、 `q` はデフォルト値が `= None` であるため、必須ではないと理解します。 -`Optional[str]` における `Optional` はFastAPIには利用されませんが、エディターによるより良いサポートとエラー検出を可能にします。 +`str | None` を使うことで、エディターによるより良いサポートとエラー検出を可能にします。 /// -## バリデーションの追加 +## バリデーションの追加 { #additional-validation } -`q`はオプショナルですが、もし値が渡されてきた場合には、**50文字を超えないこと**を強制してみましょう。 +`q`はオプショナルですが、もし値が渡されてきた場合には、**長さが50文字を超えないこと**を強制してみましょう。 -### `Query`のインポート +### `Query` と `Annotated` のインポート { #import-query-and-annotated } -そのために、まずは`fastapi`から`Query`をインポートします: +そのために、まずは以下をインポートします: -{* ../../docs_src/query_params_str_validations/tutorial002.py hl[3] *} +* `fastapi` から `Query` +* `typing` から `Annotated` -## デフォルト値として`Query`を使用 +{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *} -パラメータのデフォルト値として使用し、パラメータ`max_length`を50に設定します: +/// info | 情報 -{* ../../docs_src/query_params_str_validations/tutorial002.py hl[9] *} +FastAPI はバージョン 0.95.0 で `Annotated` のサポートを追加し(推奨し始め)ました。 -デフォルト値`None`を`Query(default=None)`に置き換える必要があるので、`Query`の最初の引数はデフォルト値を定義するのと同じです。 +古いバージョンの場合、`Annotated` を使おうとするとエラーになります。 + +`Annotated` を使う前に、FastAPI のバージョンを少なくとも 0.95.1 にするために、[FastAPI のバージョンをアップグレード](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank}してください。 + +/// + +## `q` パラメータの型で `Annotated` を使う { #use-annotated-in-the-type-for-the-q-parameter } + +以前、[Python Types Intro](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank} で `Annotated` を使ってパラメータにメタデータを追加できると説明したことを覚えていますか? + +いよいよ FastAPI で使うときです。 🚀 + +次の型アノテーションがありました: + +//// tab | Python 3.10+ + +```Python +q: str | None = None +``` + +//// + +//// tab | Python 3.9+ + +```Python +q: Union[str, None] = None +``` + +//// + +これを `Annotated` で包んで、次のようにします: + +//// tab | Python 3.10+ + +```Python +q: Annotated[str | None] = None +``` + +//// + +//// tab | Python 3.9+ + +```Python +q: Annotated[Union[str, None]] = None +``` + +//// + +どちらも同じ意味で、`q` は `str` または `None` になり得るパラメータで、デフォルトでは `None` です。 + +では、面白いところに進みましょう。 🎉 + +## `q` パラメータの `Annotated` に `Query` を追加する { #add-query-to-annotated-in-the-q-parameter } + +追加情報(この場合は追加のバリデーション)を入れられる `Annotated` ができたので、`Annotated` の中に `Query` を追加し、パラメータ `max_length` を `50` に設定します: + +{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *} + +デフォルト値は引き続き `None` なので、このパラメータは依然としてオプショナルです。 + +しかし、`Annotated` の中に `Query(max_length=50)` を入れることで、この値に **追加のバリデーション** をしたい、最大 50 文字にしたい、と FastAPI に伝えています。 😎 + +/// tip | 豆知識 + +ここでは **クエリパラメータ** なので `Query()` を使っています。後で `Path()`、`Body()`、`Header()`、`Cookie()` など、`Query()` と同じ引数を受け取れるものも見ていきます。 + +/// + +FastAPI は次を行います: + +* 最大長が 50 文字であることを確かめるようデータを **検証** する +* データが有効でないときに、クライアントに **明確なエラー** を表示する +* OpenAPI スキーマの *path operation* にパラメータを **ドキュメント化** する(その結果、**自動ドキュメント UI** に表示されます) + +## 代替(古い方法): デフォルト値としての `Query` { #alternative-old-query-as-the-default-value } + +FastAPI の以前のバージョン(0.95.0 より前)では、パラメータのデフォルト値として `Query` を使う必要があり、`Annotated` の中に入れるのではありませんでした。これを使ったコードを見かける可能性が高いので、説明します。 + +/// tip | 豆知識 + +新しいコードでは、可能な限り上で説明したとおり `Annotated` を使ってください。複数の利点(後述)があり、欠点はありません。 🍰 + +/// + +関数パラメータのデフォルト値として `Query()` を使い、パラメータ `max_length` を 50 に設定する方法は次のとおりです: + +{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *} + +この場合(`Annotated` を使わない場合)、関数内のデフォルト値 `None` を `Query()` に置き換える必要があるため、`Query(default=None)` のパラメータでデフォルト値を設定する必要があります。これは(少なくとも FastAPI にとっては)そのデフォルト値を定義するのと同じ目的を果たします。 なので: ```Python -q: Optional[str] = Query(default=None) +q: str | None = Query(default=None) ``` -...を以下と同じようにパラメータをオプションにします: +...はデフォルト値 `None` を持つオプショナルなパラメータになり、以下と同じです: + ```Python -q: Optional[str] = None +q: str | None = None ``` -しかし、これはクエリパラメータとして明示的に宣言しています。 - -/// info | 情報 - -FastAPIは以下の部分を気にすることを覚えておいてください: - -```Python -= None -``` - -もしくは: - -```Python -= Query(default=None) -``` - -そして、 `None` を利用することでクエリパラメータが必須ではないと検知します。 - -`Optional` の部分は、エディターによるより良いサポートを可能にします。 - -/// +ただし `Query` のバージョンでは、クエリパラメータであることを明示的に宣言しています。 そして、さらに多くのパラメータを`Query`に渡すことができます。この場合、文字列に適用される、`max_length`パラメータを指定します。 ```Python -q: Union[str, None] = Query(default=None, max_length=50) +q: str | None = Query(default=None, max_length=50) ``` これにより、データを検証し、データが有効でない場合は明確なエラーを表示し、OpenAPIスキーマの *path operation* にパラメータを記載します。 -## バリデーションをさらに追加する +### デフォルト値としての `Query` または `Annotated` 内の `Query` { #query-as-the-default-value-or-in-annotated } + +`Annotated` の中で `Query` を使う場合、`Query` の `default` パラメータは使えないことに注意してください。 + +その代わりに、関数パラメータの実際のデフォルト値を使います。そうしないと整合性が取れなくなります。 + +例えば、これは許可されません: + +```Python +q: Annotated[str, Query(default="rick")] = "morty" +``` + +...なぜなら、デフォルト値が `"rick"` なのか `"morty"` なのかが不明確だからです。 + +そのため、(できれば)次のようにします: + +```Python +q: Annotated[str, Query()] = "rick" +``` + +...または、古いコードベースでは次のようなものが見つかるでしょう: + +```Python +q: str = Query(default="rick") +``` + +### `Annotated` の利点 { #advantages-of-annotated } + +関数パラメータのデフォルト値スタイルではなく、**`Annotated` を使うことが推奨** されます。複数の理由で **より良い** からです。 🤓 + +**関数パラメータ** の **デフォルト値** は **実際のデフォルト値** であり、Python 全般としてより直感的です。 😌 + +FastAPI なしで同じ関数を **別の場所** から **呼び出しても**、**期待どおりに動作** します。**必須** パラメータ(デフォルト値がない)があれば、**エディター** がエラーで知らせてくれますし、**Python** も必須パラメータを渡さずに実行すると文句を言います。 + +`Annotated` を使わずに **(古い)デフォルト値スタイル** を使う場合、FastAPI なしでその関数を **別の場所** で呼び出すとき、正しく動かすために関数へ引数を渡すことを **覚えておく** 必要があります。そうしないと値が期待と異なります(例えば `str` の代わりに `QueryInfo` か、それに類するものになります)。また、エディターも警告せず、Python もその関数の実行で文句を言いません。内部の処理がエラーになるときに初めて問題が出ます。 + +`Annotated` は複数のメタデータアノテーションを持てるので、Typer のような別ツールと同じ関数を使うこともできます。 🚀 + +## バリデーションをさらに追加する { #add-more-validations } パラメータ`min_length`も追加することができます: -{* ../../docs_src/query_params_str_validations/tutorial003.py hl[10] *} +{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *} -## 正規表現の追加 +## 正規表現の追加 { #add-regular-expressions } -パラメータが一致するべき正規表現を定義することができます: +パラメータが一致するべき 正規表現 `pattern` を定義することができます: -{* ../../docs_src/query_params_str_validations/tutorial004.py hl[11] *} +{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *} -この特定の正規表現は受け取ったパラメータの値をチェックします: +この特定の正規表現パターンは受け取ったパラメータの値をチェックします: * `^`: は、これ以降の文字で始まり、これより以前には文字はありません。 * `fixedquery`: は、正確な`fixedquery`を持っています. * `$`: で終わる場合、`fixedquery`以降には文字はありません. -もしこれらすべての **正規表現**のアイデアについて迷っていても、心配しないでください。多くの人にとって難しい話題です。正規表現を必要としなくても、まだ、多くのことができます。 +もしこれらすべての **「正規表現」** のアイデアについて迷っていても、心配しないでください。多くの人にとって難しい話題です。正規表現を必要としなくても、まだ、多くのことができます。 -しかし、あなたがそれらを必要とし、学ぶときにはすでに、 **FastAPI**で直接それらを使用することができます。 +これで、必要になったときにはいつでも **FastAPI** で使えることが分かりました。 -## デフォルト値 +## デフォルト値 { #default-values } -第一引数に`None`を渡して、デフォルト値として使用するのと同じように、他の値を渡すこともできます。 +もちろん、`None` 以外のデフォルト値も使えます。 -クエリパラメータ`q`の`min_length`を`3`とし、デフォルト値を`fixedquery`としてみましょう: +クエリパラメータ `q` の `min_length` を `3` とし、デフォルト値を `"fixedquery"` として宣言したいとします: -{* ../../docs_src/query_params_str_validations/tutorial005.py hl[7] *} +{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *} /// note | 備考 -デフォルト値を指定すると、パラメータは任意になります。 +`None` を含む任意の型のデフォルト値があると、パラメータはオプショナル(必須ではない)になります。 /// -## 必須にする +## 必須パラメータ { #required-parameters } -これ以上、バリデーションやメタデータを宣言する必要のない場合は、デフォルト値を指定しないだけでクエリパラメータ`q`を必須にすることができます。以下のように: +これ以上、バリデーションやメタデータを宣言する必要がない場合は、デフォルト値を宣言しないだけでクエリパラメータ `q` を必須にできます。以下のように: ```Python q: str @@ -123,42 +231,42 @@ q: str 以下の代わりに: ```Python -q: Union[str, None] = None +q: str | None = None ``` -現在は以下の例のように`Query`で宣言しています: +しかし今は、例えば次のように `Query` で宣言しています: ```Python -q: Union[str, None] = Query(default=None, min_length=3) +q: Annotated[str | None, Query(min_length=3)] = None ``` -そのため、`Query`を使用して必須の値を宣言する必要がある場合は、第一引数に`...`を使用することができます: +そのため、`Query` を使いながら値を必須として宣言したい場合は、単にデフォルト値を宣言しません: -{* ../../docs_src/query_params_str_validations/tutorial006.py hl[7] *} +{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *} -/// info | 情報 +### 必須、`None` にできる { #required-can-be-none } -これまで`...`を見たことがない方へ: これは特殊な単一値です。Pythonの一部であり、"Ellipsis"と呼ばれています。 +パラメータが `None` を受け付けるが、それでも必須である、と宣言できます。これにより、値が `None` であってもクライアントは値を送らなければならなくなります。 -/// +そのために、`None` が有効な型であることを宣言しつつ、単にデフォルト値を宣言しません: -これは **FastAPI** にこのパラメータが必須であることを知らせます。 +{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *} -## クエリパラメータのリスト / 複数の値 +## クエリパラメータのリスト / 複数の値 { #query-parameter-list-multiple-values } -クエリパラメータを明示的に`Query`で宣言した場合、値のリストを受け取るように宣言したり、複数の値を受け取るように宣言したりすることもできます。 +クエリパラメータを明示的に `Query` で定義すると、値のリストを受け取るように宣言したり、言い換えると複数の値を受け取るように宣言したりすることもできます。 例えば、URL内に複数回出現するクエリパラメータ`q`を宣言するには以下のように書きます: -{* ../../docs_src/query_params_str_validations/tutorial011.py hl[9] *} +{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *} -そしてURLは以下です: +そして、次のような URL なら: ``` http://localhost:8000/items/?q=foo&q=bar ``` -複数の*クエリパラメータ*の値`q`(`foo`と`bar`)を*path operation関数*内で*関数パラメータ*`q`としてPythonの`list`を受け取ることになります。 +*path operation function* 内の *function parameter* `q` で、複数の `q` *query parameters'* 値(`foo` と `bar`)を Python の `list` として受け取ります。 そのため、このURLのレスポンスは以下のようになります: @@ -179,15 +287,15 @@ http://localhost:8000/items/?q=foo&q=bar 対話的APIドキュメントは複数の値を許可するために自動的に更新されます。 - + -### デフォルト値を持つ、クエリパラメータのリスト / 複数の値 +### デフォルト値を持つ、クエリパラメータのリスト / 複数の値 { #query-parameter-list-multiple-values-with-defaults } -また、値が指定されていない場合はデフォルトの`list`を定義することもできます。 +また、値が指定されていない場合はデフォルトの `list` を定義することもできます。 -{* ../../docs_src/query_params_str_validations/tutorial012.py hl[9] *} +{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *} -以下のURLを開くと: +以下にアクセスすると: ``` http://localhost:8000/items/ @@ -204,21 +312,21 @@ http://localhost:8000/items/ } ``` -#### `list`を使う +#### `list` だけを使う { #using-just-list } -`List[str]`の代わりに直接`list`を使うこともできます: +`list[str]` の代わりに直接 `list` を使うこともできます: -{* ../../docs_src/query_params_str_validations/tutorial013.py hl[7] *} +{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *} /// note | 備考 この場合、FastAPIはリストの内容をチェックしないことを覚えておいてください。 -例えば`List[int]`はリストの内容が整数であるかどうかをチェックします(そして、文書化します)。しかし`list`だけではそうしません。 +例えば`list[int]`はリストの内容が整数であるかどうかをチェックします(そして、文書化します)。しかし`list`だけではそうしません。 /// -## より多くのメタデータを宣言する +## より多くのメタデータを宣言する { #declare-more-metadata } パラメータに関する情報をさらに追加することができます。 @@ -234,13 +342,13 @@ http://localhost:8000/items/ `title`を追加できます: -{* ../../docs_src/query_params_str_validations/tutorial007.py hl[9] *} +{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *} `description`を追加できます: -{* ../../docs_src/query_params_str_validations/tutorial008.py hl[13] *} +{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *} -## エイリアスパラメータ +## エイリアスパラメータ { #alias-parameters } パラメータに`item-query`を指定するとします. @@ -258,23 +366,91 @@ http://127.0.0.1:8000/items/?item-query=foobaritems それならば、`alias`を宣言することができます。エイリアスはパラメータの値を見つけるのに使用されます: -{* ../../docs_src/query_params_str_validations/tutorial009.py hl[9] *} +{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *} -## 非推奨パラメータ +## パラメータを非推奨にする { #deprecating-parameters } -さて、このパラメータが気に入らなくなったとしましょう +さて、このパラメータが気に入らなくなったとしましょう。 -それを使っているクライアントがいるので、しばらくは残しておく必要がありますが、ドキュメントには非推奨と明記しておきたいです。 +それを使っているクライアントがいるので、しばらくは残しておく必要がありますが、ドキュメントにはdeprecatedと明記しておきたいです。 その場合、`Query`にパラメータ`deprecated=True`を渡します: -{* ../../docs_src/query_params_str_validations/tutorial010.py hl[18] *} +{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *} ドキュメントは以下のようになります: - + -## まとめ +## OpenAPI からパラメータを除外する { #exclude-parameters-from-openapi } + +生成される OpenAPI スキーマ(つまり自動ドキュメントシステム)からクエリパラメータを除外するには、`Query` のパラメータ `include_in_schema` を `False` に設定します: + +{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *} + +## カスタムバリデーション { #custom-validation } + +上で示したパラメータではできない **カスタムバリデーション** が必要になる場合があります。 + +その場合、通常のバリデーション(例: 値が `str` であることの検証)の後に適用される **カスタムバリデータ関数** を使えます。 + +これを行うには、`Annotated` の中で Pydantic の `AfterValidator` を使います。 + +/// tip | 豆知識 + +Pydantic には `BeforeValidator` などもあります。 🤓 + +/// + +例えば、このカスタムバリデータは、ISBN の書籍番号なら item ID が `isbn-` で始まること、IMDB の movie URL ID なら `imdb-` で始まることをチェックします: + +{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *} + +/// info | 情報 + +これは Pydantic バージョン 2 以上で利用できます。 😎 + +/// + +/// tip | 豆知識 + +データベースや別の API など、何らかの **外部コンポーネント** との通信が必要なタイプのバリデーションを行う必要がある場合は、代わりに **FastAPI Dependencies** を使うべきです。これについては後で学びます。 + +これらのカスタムバリデータは、リクエストで提供された **同じデータのみ** でチェックできるもの向けです。 + +/// + +### そのコードを理解する { #understand-that-code } + +重要なのは、**`Annotated` の中で関数と一緒に `AfterValidator` を使うこと** だけです。この部分は飛ばしても構いません。 🤸 + +--- + +ただし、この具体的なコード例が気になっていて、まだ興味が続くなら、追加の詳細を示します。 + +#### `value.startswith()` を使う文字列 { #string-with-value-startswith } + +気づきましたか?`value.startswith()` を使う文字列はタプルを受け取れ、そのタプル内の各値をチェックします: + +{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *} + +#### ランダムなアイテム { #a-random-item } + +`data.items()` で、辞書の各アイテムのキーと値を含むタプルを持つ 反復可能オブジェクト を取得します。 + +この反復可能オブジェクトを `list(data.items())` で適切な `list` に変換します。 + +そして `random.choice()` でその `list` から **ランダムな値** を取得するので、`(id, name)` のタプルを得ます。これは `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")` のようになります。 + +次に、そのタプルの **2つの値を代入** して、変数 `id` と `name` に入れます。 + +つまり、ユーザーが item ID を提供しなかった場合でも、ランダムな提案を受け取ります。 + +...これを **単一のシンプルな1行** で行っています。 🤯 Python が好きになりませんか? 🐍 + +{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *} + +## まとめ { #recap } パラメータに追加のバリデーションとメタデータを宣言することができます。 @@ -285,12 +461,14 @@ http://127.0.0.1:8000/items/?item-query=foobaritems * `description` * `deprecated` -文字列のためのバリデーション: +文字列に固有のバリデーション: * `min_length` * `max_length` -* `regex` +* `pattern` -この例では、`str`の値のバリデーションを宣言する方法を見てきました。 +`AfterValidator` を使ったカスタムバリデーション。 + +この例では、`str` の値のバリデーションを宣言する方法を見てきました。 数値のような他の型のバリデーションを宣言する方法は次の章を参照してください。 diff --git a/docs/ja/docs/tutorial/query-params.md b/docs/ja/docs/tutorial/query-params.md index 74e455579e..41e51ed782 100644 --- a/docs/ja/docs/tutorial/query-params.md +++ b/docs/ja/docs/tutorial/query-params.md @@ -1,8 +1,8 @@ -# クエリパラメータ +# クエリパラメータ { #query-parameters } -パスパラメータではない関数パラメータを宣言すると、それらは自動的に "クエリ" パラメータとして解釈されます。 +パスパラメータではない関数パラメータを宣言すると、それらは自動的に「クエリ」パラメータとして解釈されます。 -{* ../../docs_src/query_params/tutorial001.py hl[9] *} +{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *} クエリはURL内で `?` の後に続くキーとバリューの組で、 `&` で区切られています。 @@ -24,11 +24,11 @@ http://127.0.0.1:8000/items/?skip=0&limit=10 パスパラメータに適用される処理と完全に同様な処理がクエリパラメータにも施されます: * エディターサポート (明らかに) -* データ「解析」 +* データ 「解析」 * データバリデーション * 自動ドキュメント生成 -## デフォルト +## デフォルト { #defaults } クエリパラメータはパスの固定部分ではないので、オプショナルとしたり、デフォルト値をもつことができます。 @@ -55,13 +55,13 @@ http://127.0.0.1:8000/items/?skip=20 関数内のパラメータの値は以下の様になります: * `skip=20`: URL内にセットしたため -* `limit=10`: デフォルト値 +* `limit=10`: デフォルト値だったため -## オプショナルなパラメータ +## オプショナルなパラメータ { #optional-parameters } 同様に、デフォルト値を `None` とすることで、オプショナルなクエリパラメータを宣言できます: -{* ../../docs_src/query_params/tutorial002.py hl[9] *} +{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *} この場合、関数パラメータ `q` はオプショナルとなり、デフォルトでは `None` になります。 @@ -71,11 +71,11 @@ http://127.0.0.1:8000/items/?skip=20 /// -## クエリパラメータの型変換 +## クエリパラメータの型変換 { #query-parameter-type-conversion } -`bool` 型も宣言できます。これは以下の様に変換されます: +`bool` 型も宣言でき、変換されます: -{* ../../docs_src/query_params/tutorial003.py hl[9] *} +{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *} この場合、以下にアクセスすると: @@ -109,27 +109,28 @@ http://127.0.0.1:8000/items/foo?short=yes もしくは、他の大文字小文字のバリエーション (アッパーケース、最初の文字だけアッパーケース、など)で、関数は `short` パラメータを `True` な `bool` 値として扱います。それ以外は `False` になります。 -## 複数のパスパラメータとクエリパラメータ -複数のパスパラメータとクエリパラメータを同時に宣言できます。**FastAPI**は互いを区別できます。 +## 複数のパスパラメータとクエリパラメータ { #multiple-path-and-query-parameters } + +複数のパスパラメータとクエリパラメータを同時に宣言できます。**FastAPI**はどれがどれかを把握しています。 そして特定の順序で宣言しなくてもよいです。 名前で判別されます: -{* ../../docs_src/query_params/tutorial004.py hl[8,10] *} +{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *} -## 必須のクエリパラメータ +## 必須のクエリパラメータ { #required-query-parameters } -パスパラメータ以外のパラメータ (今のところ、クエリパラメータのみ説明しました) のデフォルト値を宣言した場合、そのパラメータは必須ではなくなります。 +パスパラメータ以外のパラメータ (今のところ、クエリパラメータのみ見てきました) のデフォルト値を宣言した場合、そのパラメータは必須ではなくなります。 特定の値を与えずにただオプショナルにしたい場合はデフォルト値を `None` にして下さい。 しかしクエリパラメータを必須にしたい場合は、ただデフォルト値を宣言しなければよいです: -{* ../../docs_src/query_params/tutorial005.py hl[6:7] *} +{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *} -ここで、クエリパラメータ `needy` は `str` 型の必須のクエリパラメータです +ここで、クエリパラメータ `needy` は `str` 型の必須のクエリパラメータです。 以下のURLをブラウザで開くと: @@ -141,16 +142,17 @@ http://127.0.0.1:8000/items/foo-item ```JSON { - "detail": [ - { - "loc": [ - "query", - "needy" - ], - "msg": "field required", - "type": "value_error.missing" - } - ] + "detail": [ + { + "type": "missing", + "loc": [ + "query", + "needy" + ], + "msg": "Field required", + "input": null + } + ] } ``` @@ -169,9 +171,9 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy } ``` -そして当然、あるパラメータを必須に、別のパラメータにデフォルト値を設定し、また別のパラメータをオプショナルにできます: +そして当然、あるパラメータを必須に、あるパラメータにデフォルト値を設定し、またあるパラメータを完全にオプショナルにできます: -{* ../../docs_src/query_params/tutorial006.py hl[10] *} +{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *} この場合、3つのクエリパラメータがあります。: @@ -181,6 +183,6 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy /// tip | 豆知識 -[パスパラメータ](path-params.md#_8){.internal-link target=_blank}と同様に `Enum` を使用できます。 +[パスパラメータ](path-params.md#predefined-values){.internal-link target=_blank}と同様に `Enum` を使用できます。 /// diff --git a/docs/ja/docs/tutorial/request-forms-and-files.md b/docs/ja/docs/tutorial/request-forms-and-files.md index 110e3106a6..09e1277c8c 100644 --- a/docs/ja/docs/tutorial/request-forms-and-files.md +++ b/docs/ja/docs/tutorial/request-forms-and-files.md @@ -1,24 +1,28 @@ -# リクエストフォームとファイル +# リクエストフォームとファイル { #request-forms-and-files } `File`と`Form`を同時に使うことでファイルとフォームフィールドを定義することができます。 /// info | 情報 -アップロードされたファイルやフォームデータを受信するには、まず`python-multipart`をインストールします。 +アップロードされたファイルやフォームデータを受信するには、まず`python-multipart`をインストールします。 -例えば、`pip install python-multipart`のように。 +[仮想環境](../virtual-environments.md){.internal-link target=_blank}を作成し、それを有効化してから、例えば次のようにインストールしてください: + +```console +$ pip install python-multipart +``` /// -## `File`と`Form`のインポート +## `File`と`Form`のインポート { #import-file-and-form } -{* ../../docs_src/request_forms_and_files/tutorial001.py hl[1] *} +{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *} -## `File`と`Form`のパラメータの定義 +## `File`と`Form`のパラメータの定義 { #define-file-and-form-parameters } ファイルやフォームのパラメータは`Body`や`Query`の場合と同じように作成します: -{* ../../docs_src/request_forms_and_files/tutorial001.py hl[8] *} +{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *} ファイルとフォームフィールドがフォームデータとしてアップロードされ、ファイルとフォームフィールドを受け取ります。 @@ -32,6 +36,6 @@ /// -## まとめ +## まとめ { #recap } 同じリクエストでデータやファイルを受け取る必要がある場合は、`File` と`Form`を一緒に使用します。 diff --git a/docs/ja/docs/tutorial/request-forms.md b/docs/ja/docs/tutorial/request-forms.md index eca2cd6dc7..1bdc28670b 100644 --- a/docs/ja/docs/tutorial/request-forms.md +++ b/docs/ja/docs/tutorial/request-forms.md @@ -1,4 +1,4 @@ -# フォームデータ +# フォームデータ { #form-data } JSONの代わりにフィールドを受け取る場合は、`Form`を使用します。 @@ -6,27 +6,31 @@ JSONの代わりにフィールドを受け取る場合は、`Form`を使用し フォームを使うためには、まず`python-multipart`をインストールします。 -たとえば、`pip install python-multipart`のように。 +必ず[仮想環境](../virtual-environments.md){.internal-link target=_blank}を作成して有効化してから、例えば次のようにインストールしてください: + +```console +$ pip install python-multipart +``` /// -## `Form`のインポート +## `Form`のインポート { #import-form } `fastapi`から`Form`をインポートします: -{* ../../docs_src/request_forms/tutorial001.py hl[1] *} +{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *} -## `Form`のパラメータの定義 +## `Form`のパラメータの定義 { #define-form-parameters } `Body`や`Query`の場合と同じようにフォームパラメータを作成します: -{* ../../docs_src/request_forms/tutorial001.py hl[7] *} +{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *} 例えば、OAuth2仕様が使用できる方法の1つ(「パスワードフロー」と呼ばれる)では、フォームフィールドとして`username`と`password`を送信する必要があります。 -仕様では、フィールドの名前が`username`と`password`であることと、JSONではなくフォームフィールドとして送信されることを要求しています。 +specでは、フィールドの名前が`username`と`password`であることと、JSONではなくフォームフィールドとして送信されることを要求しています。 -`Form`では`Body`(および`Query`や`Path`、`Cookie`)と同じメタデータとバリデーションを宣言することができます。 +`Form`では`Body`(および`Query`や`Path`、`Cookie`)と同じ設定を宣言することができます。これには、バリデーション、例、エイリアス(例えば`username`の代わりに`user-name`)などが含まれます。 /// info | 情報 @@ -40,7 +44,7 @@ JSONの代わりにフィールドを受け取る場合は、`Form`を使用し /// -## 「フォームフィールド」について +## 「フォームフィールド」について { #about-form-fields } HTMLフォーム(`
`)がサーバにデータを送信する方法は、通常、そのデータに「特別な」エンコーディングを使用していますが、これはJSONとは異なります。 @@ -64,6 +68,6 @@ HTMLフォーム(`
`)がサーバにデータを送信する方 /// -## まとめ +## まとめ { #recap } フォームデータの入力パラメータを宣言するには、`Form`を使用する。 diff --git a/docs/ja/docs/tutorial/response-model.md b/docs/ja/docs/tutorial/response-model.md index b8464a4c73..258eac8e67 100644 --- a/docs/ja/docs/tutorial/response-model.md +++ b/docs/ja/docs/tutorial/response-model.md @@ -1,6 +1,35 @@ -# レスポンスモデル +# レスポンスモデル - 戻り値の型 { #response-model-return-type } -*path operations* のいずれにおいても、`response_model`パラメータを使用して、レスポンスのモデルを宣言することができます: +*path operation 関数*の**戻り値の型**にアノテーションを付けることで、レスポンスに使用される型を宣言できます。 + +関数**パラメータ**の入力データと同じように **型アノテーション** を使用できます。Pydanticモデル、リスト、辞書、整数や真偽値などのスカラー値を使用できます。 + +{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *} + +FastAPIはこの戻り値の型を使って以下を行います: + +* 返却データを**検証**します。 + * データが不正(例: フィールドが欠けている)であれば、それは*あなた*のアプリコードが壊れていて、返すべきものを返していないことを意味し、不正なデータを返す代わりにサーバーエラーを返します。これにより、あなたとクライアントは、期待されるデータとデータ形状を受け取れることを確実にできます。 +* OpenAPIの *path operation* に、レスポンス用の **JSON Schema** を追加します。 + * これは**自動ドキュメント**で使用されます。 + * 自動クライアントコード生成ツールでも使用されます。 + +しかし、最も重要なのは: + +* 戻り値の型で定義された内容に合わせて、出力データを**制限しフィルタリング**します。 + * これは**セキュリティ**の観点で特に重要です。以下で詳しく見ていきます。 + +## `response_model`パラメータ { #response-model-parameter } + +型が宣言している内容とまったく同じではないデータを返す必要がある、またはそうしたいケースがあります。 + +例えば、**辞書を返す**、またはデータベースオブジェクトを返したいが、**Pydanticモデルとして宣言**したい場合があります。こうすることで、Pydanticモデルが返したオブジェクト(例: 辞書やデータベースオブジェクト)のドキュメント化、バリデーションなどをすべて行ってくれます。 + +戻り値の型アノテーションを追加すると、ツールやエディタが(正しく)エラーとして、関数が宣言した型(例: Pydanticモデル)とは異なる型(例: dict)を返していると警告します。 + +そのような場合、戻り値の型の代わりに、*path operation デコレータ*のパラメータ `response_model` を使用できます。 + +`response_model`パラメータは、いずれの *path operation* でも使用できます: * `@app.get()` * `@app.post()` @@ -8,104 +37,211 @@ * `@app.delete()` * など。 -{* ../../docs_src/response_model/tutorial001.py hl[17] *} +{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *} /// note | 備考 -`response_model`は「デコレータ」メソッド(`get`、`post`など)のパラメータであることに注意してください。すべてのパラメータやボディのように、*path operation関数* のパラメータではありません。 +`response_model`は「デコレータ」メソッド(`get`、`post`など)のパラメータであることに注意してください。すべてのパラメータやボディのように、*path operation 関数* のパラメータではありません。 /// -Pydanticモデルの属性に対して宣言するのと同じ型を受け取るので、Pydanticモデルになることもできますが、例えば、`List[Item]`のようなPydanticモデルの`list`になることもできます。 +`response_model`は、Pydanticモデルのフィールドで宣言するのと同じ型を受け取ります。そのため、Pydanticモデルにもできますし、例えば `List[Item]` のように、Pydanticモデルの `list` にもできます。 -FastAPIは`response_model`を使って以下のことをします: +FastAPIはこの `response_model` を使って、データのドキュメント化や検証などを行い、さらに出力データを型宣言に合わせて**変換・フィルタリング**します。 -* 出力データを型宣言に変換します。 -* データを検証します。 -* OpenAPIの *path operation* で、レスポンス用のJSON Schemaを追加します。 -* 自動ドキュメントシステムで使用されます。 +/// tip | 豆知識 -しかし、最も重要なのは: +エディタやmypyなどで厳密な型チェックをしている場合、関数の戻り値の型を `Any` として宣言できます。 -* 出力データをモデルのデータに限定します。これがどのように重要なのか以下で見ていきましょう。 - -/// note | 技術詳細 - -レスポンスモデルは、関数の戻り値のアノテーションではなく、このパラメータで宣言されています。なぜなら、パス関数は実際にはそのレスポンスモデルを返すのではなく、`dict`やデータベースオブジェクト、あるいは他のモデルを返し、`response_model`を使用してフィールドの制限やシリアライズを行うからです。 +そうすると、意図的に何でも返していることをエディタに伝えられます。それでもFastAPIは `response_model` を使って、データのドキュメント化、検証、フィルタリングなどを行います。 /// -## 同じ入力データの返却 +### `response_model`の優先順位 { #response-model-priority } -ここでは`UserIn`モデルを宣言しています。それには平文のパスワードが含まれています: +戻り値の型と `response_model` の両方を宣言した場合、`response_model` が優先され、FastAPIで使用されます。 -{* ../../docs_src/response_model/tutorial002.py hl[9,11] *} +これにより、レスポンスモデルとは異なる型を返している場合でも、エディタやmypyなどのツールで使用するために関数へ正しい型アノテーションを追加できます。それでもFastAPIは `response_model` を使用してデータの検証やドキュメント化などを実行できます。 + +また `response_model=None` を使用して、その*path operation*のレスポンスモデル生成を無効化することもできます。これは、Pydanticのフィールドとして有効ではないものに対して型アノテーションを追加する場合に必要になることがあります。以下のセクションのいずれかで例を示します。 + +## 同じ入力データの返却 { #return-the-same-input-data } + +ここでは `UserIn` モデルを宣言しています。これには平文のパスワードが含まれます: + +{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *} + +/// info | 情報 + +`EmailStr` を使用するには、最初に `email-validator` をインストールしてください。 + +[仮想環境](../virtual-environments.md){.internal-link target=_blank}を作成して有効化してから、例えば次のようにインストールしてください: + +```console +$ pip install email-validator +``` + +または次のようにします: + +```console +$ pip install "pydantic[email]" +``` + +/// そして、このモデルを使用して入力を宣言し、同じモデルを使って出力を宣言しています: -{* ../../docs_src/response_model/tutorial002.py hl[17,18] *} +{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *} これで、ブラウザがパスワードを使ってユーザーを作成する際に、APIがレスポンスで同じパスワードを返すようになりました。 -この場合、ユーザー自身がパスワードを送信しているので問題ないかもしれません。 +この場合、同じユーザーがパスワードを送信しているので問題ないかもしれません。 -しかし、同じモデルを別の*path operation*に使用すると、すべてのクライアントにユーザーのパスワードを送信してしまうことになります。 +しかし、同じモデルを別の*path operation*に使用すると、すべてのクライアントにユーザーのパスワードを送信してしまう可能性があります。 -/// danger | 危険 +/// danger | 警告 -ユーザーの平文のパスワードを保存したり、レスポンスで送信したりすることは絶対にしないでください。 +すべての注意点を理解していて、自分が何をしているか分かっている場合を除き、ユーザーの平文のパスワードを保存したり、このようにレスポンスで送信したりしないでください。 /// -## 出力モデルの追加 +## 出力モデルの追加 { #add-an-output-model } -代わりに、平文のパスワードを持つ入力モデルと、パスワードを持たない出力モデルを作成することができます: +代わりに、平文のパスワードを持つ入力モデルと、パスワードを持たない出力モデルを作成できます: -{* ../../docs_src/response_model/tutorial003.py hl[9,11,16] *} +{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *} -ここでは、*path operation関数*がパスワードを含む同じ入力ユーザーを返しているにもかかわらず: +ここでは、*path operation 関数*がパスワードを含む同じ入力ユーザーを返しているにもかかわらず: -{* ../../docs_src/response_model/tutorial003.py hl[24] *} +{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *} -...`response_model`を`UserOut`と宣言したことで、パスワードが含まれていません: +...`response_model`を、パスワードを含まない `UserOut` モデルとして宣言しました: -{* ../../docs_src/response_model/tutorial003.py hl[22] *} +{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *} -そのため、**FastAPI** は出力モデルで宣言されていない全てのデータをフィルタリングしてくれます(Pydanticを使用)。 +そのため、**FastAPI** は出力モデルで宣言されていないすべてのデータをフィルタリングしてくれます(Pydanticを使用)。 -## ドキュメントを見る +### `response_model`または戻り値の型 { #response-model-or-return-type } -自動ドキュメントを見ると、入力モデルと出力モデルがそれぞれ独自のJSON Schemaを持っていることが確認できます。 +このケースでは2つのモデルが異なるため、関数の戻り値の型を `UserOut` としてアノテーションすると、エディタやツールは、異なるクラスなので不正な型を返していると警告します。 - +そのため、この例では `response_model` パラメータで宣言する必要があります。 -そして、両方のモデルは、対話型のAPIドキュメントに使用されます: +...しかし、これを解決する方法を以下で確認しましょう。 - +## 戻り値の型とデータフィルタリング { #return-type-and-data-filtering } -## レスポンスモデルのエンコーディングパラメータ +前の例から続けます。**関数に1つの型をアノテーション**したい一方で、関数からは実際には**より多くのデータ**を含むものを返せるようにしたいとします。 -レスポンスモデルにはデフォルト値を設定することができます: +FastAPIにはレスポンスモデルを使用してデータを**フィルタリング**し続けてほしいです。つまり、関数がより多くのデータを返しても、レスポンスにはレスポンスモデルで宣言されたフィールドのみが含まれます。 -{* ../../docs_src/response_model/tutorial004.py hl[11,13,14] *} +前の例ではクラスが異なるため `response_model` パラメータを使う必要がありました。しかしそれは、エディタやツールによる関数の戻り値の型チェックのサポートを受けられないことも意味します。 -* `description: str = None`は`None`がデフォルト値です。 -* `tax: float = 10.5`は`10.5`がデフォルト値です。 -* `tags: List[str] = []` は空のリスト(`[]`)がデフォルト値です。 +しかし、このようなことが必要になる多くのケースでは、この例のようにモデルでデータの一部を**フィルタ/削除**したいだけです。 -しかし、実際に保存されていない場合には結果からそれらを省略した方が良いかもしれません。 +そのような場合、クラスと継承を利用して関数の**型アノテーション**を活用し、エディタやツールのサポートを改善しつつ、FastAPIの**データフィルタリング**も得られます。 + +{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *} + +これにより、このコードは型として正しいためエディタやmypyからのツール支援を得られますし、FastAPIによるデータフィルタリングも得られます。 + +これはどのように動作するのでしょうか?確認してみましょう。🤓 + +### 型アノテーションとツール支援 { #type-annotations-and-tooling } + +まず、エディタ、mypy、その他のツールがこれをどう見るかを見てみます。 + +`BaseUser` には基本フィールドがあります。次に `UserIn` が `BaseUser` を継承して `password` フィールドを追加するため、両方のモデルのフィールドがすべて含まれます。 + +関数の戻り値の型を `BaseUser` としてアノテーションしますが、実際には `UserIn` インスタンスを返しています。 + +エディタやmypyなどのツールはこれに文句を言いません。typingの観点では、`UserIn` は `BaseUser` のサブクラスであり、期待されるものが `BaseUser` であれば `UserIn` は*有効*な型だからです。 + +### FastAPIのデータフィルタリング { #fastapi-data-filtering } + +一方FastAPIでは、戻り値の型を見て、返す内容にその型で宣言されたフィールド**だけ**が含まれることを確認します。 + +FastAPIは、返却データのフィルタリングにクラス継承の同じルールが使われてしまわないようにするため、内部でPydanticを使っていくつかの処理を行っています。そうでないと、期待以上に多くのデータを返してしまう可能性があります。 + +この方法で、**ツール支援**付きの型アノテーションと**データフィルタリング**の両方という、いいとこ取りができます。 + +## ドキュメントを見る { #see-it-in-the-docs } + +自動ドキュメントを見ると、入力モデルと出力モデルがそれぞれ独自のJSON Schemaを持っていることが確認できます: + + + +そして、両方のモデルは対話型のAPIドキュメントに使用されます: + + + +## その他の戻り値の型アノテーション { #other-return-type-annotations } + +Pydanticフィールドとして有効ではないものを返し、ツール(エディタやmypyなど)が提供するサポートを得るためだけに、関数でそれをアノテーションするケースがあるかもしれません。 + +### レスポンスを直接返す { #return-a-response-directly } + +最も一般的なケースは、[高度なドキュメントで後述する「Responseを直接返す」](../advanced/response-directly.md){.internal-link target=_blank}場合です。 + +{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *} + +このシンプルなケースは、戻り値の型アノテーションが `Response` のクラス(またはサブクラス)であるため、FastAPIが自動的に処理します。 + +また `RedirectResponse` と `JSONResponse` の両方は `Response` のサブクラスなので、ツールも型アノテーションが正しいとして問題にしません。 + +### `Response`のサブクラスをアノテーションする { #annotate-a-response-subclass } + +型アノテーションで `Response` のサブクラスを使うこともできます: + +{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *} + +これは `RedirectResponse` が `Response` のサブクラスであり、FastAPIがこのシンプルなケースを自動処理するため、同様に動作します。 + +### 無効な戻り値の型アノテーション { #invalid-return-type-annotations } + +しかし、Pydantic型として有効ではない別の任意のオブジェクト(例: データベースオブジェクト)を返し、関数でそのようにアノテーションすると、FastAPIはその型アノテーションからPydanticレスポンスモデルを作成しようとして失敗します。 + +同様に、unionのように、複数の型のうち1つ以上がPydantic型として有効でないものを含む場合も起こります。例えば次は失敗します 💥: + +{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *} + +...これは、型アノテーションがPydantic型ではなく、単一の `Response` クラス(またはサブクラス)でもないために失敗します。`Response` と `dict` の間のunion(どちらか)になっているからです。 + +### レスポンスモデルを無効化する { #disable-response-model } + +上の例を続けると、FastAPIが実行するデフォルトのデータ検証、ドキュメント化、フィルタリングなどを行いたくないこともあるでしょう。 + +しかし、エディタや型チェッカー(例: mypy)などのツール支援を得るために、関数の戻り値の型アノテーションは残したいかもしれません。 + +その場合、`response_model=None` を設定することでレスポンスモデルの生成を無効にできます: + +{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *} + +これによりFastAPIはレスポンスモデル生成をスキップし、FastAPIアプリケーションに影響させずに必要な戻り値の型アノテーションを付けられます。🤓 + +## レスポンスモデルのエンコーディングパラメータ { #response-model-encoding-parameters } + +レスポンスモデルには次のようにデフォルト値を設定できます: + +{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *} + +* `description: Union[str, None] = None`(またはPython 3.10では `str | None = None`)はデフォルトが `None` です。 +* `tax: float = 10.5` はデフォルトが `10.5` です。 +* `tags: List[str] = []` はデフォルトが空のリスト `[]` です。 + +ただし、それらが実際には保存されていない場合、結果から省略したいことがあります。 例えば、NoSQLデータベースに多くのオプション属性を持つモデルがあるが、デフォルト値でいっぱいの非常に長いJSONレスポンスを送信したくない場合です。 -### `response_model_exclude_unset`パラメータの使用 +### `response_model_exclude_unset`パラメータの使用 { #use-the-response-model-exclude-unset-parameter } -*path operation デコレータ*に`response_model_exclude_unset=True`パラメータを設定することができます: +*path operation デコレータ*のパラメータ `response_model_exclude_unset=True` を設定できます: -{* ../../docs_src/response_model/tutorial004.py hl[24] *} +{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *} -そして、これらのデフォルト値はレスポンスに含まれず、実際に設定された値のみが含まれます。 +そうすると、デフォルト値はレスポンスに含まれず、実際に設定された値のみが含まれます。 -そのため、*path operation*にID`foo`が設定されたitemのリクエストを送ると、レスポンスは以下のようになります(デフォルト値を含まない): +そのため、ID `foo` のitemに対してその *path operation* へリクエストを送ると、レスポンスは以下のようになります(デフォルト値を含まない): ```JSON { @@ -116,26 +252,20 @@ FastAPIは`response_model`を使って以下のことをします: /// info | 情報 -FastAPIはこれをするために、Pydanticモデルの`.dict()`をその`exclude_unset`パラメータで使用しています。 - -/// - -/// info | 情報 - -以下も使用することができます: +以下も使用できます: * `response_model_exclude_defaults=True` * `response_model_exclude_none=True` -`exclude_defaults`と`exclude_none`については、Pydanticのドキュメントで説明されている通りです。 +`exclude_defaults` と `exclude_none` については、Pydanticのドキュメントで説明されている通りです。 /// -#### デフォルト値を持つフィールドの値を持つデータ +#### デフォルト値を持つフィールドに値があるデータ { #data-with-values-for-fields-with-defaults } -しかし、ID`bar`のitemのように、デフォルト値が設定されているモデルのフィールドに値が設定されている場合: +しかし、ID `bar` のitemのように、デフォルト値が設定されているモデルのフィールドに値が設定されている場合: -```Python hl_lines="3 5" +```Python hl_lines="3 5" { "name": "Bar", "description": "The bartenders", @@ -146,11 +276,11 @@ FastAPIはこれをするために、Pydanticモデルの`.dict()`を + /// note | 備考 @@ -39,7 +39,7 @@ FastAPIはこれを知っていて、レスポンスボディがないというO /// -## HTTPステータスコードについて +## HTTPステータスコードについて { #about-http-status-codes } /// note | 備考 @@ -47,34 +47,34 @@ FastAPIはこれを知っていて、レスポンスボディがないというO /// -HTTPでは、レスポンスの一部として3桁の数字のステータスコードを送信します。 +HTTPでは、レスポンスの一部として3桁の数字のステータスコードを送信します。 これらのステータスコードは、それらを認識するために関連付けられた名前を持っていますが、重要な部分は番号です。 つまり: -* `100`以上は「情報」のためのものです。。直接使うことはほとんどありません。これらのステータスコードを持つレスポンスはボディを持つことができません。 -* **`200`** 以上は「成功」のレスポンスのためのものです。これらは最も利用するであろうものです。 +* `100 - 199` は「情報」のためのものです。直接使うことはほとんどありません。これらのステータスコードを持つレスポンスはボディを持つことができません。 +* **`200 - 299`** は「成功」のレスポンスのためのものです。これらは最も利用するであろうものです。 * `200`はデフォルトのステータスコードで、すべてが「OK」であったことを意味します。 * 別の例としては、`201`(Created)があります。これはデータベースに新しいレコードを作成した後によく使用されます。 - * 特殊なケースとして、`204`(No Content)があります。このレスポンスはクライアントに返すコンテンツがない場合に使用されます。そしてこのレスポンスはボディを持つことはできません。 -* **`300`** 以上は「リダイレクト」のためのものです。これらのステータスコードを持つレスポンスは`304`(Not Modified)を除き、ボディを持つことも持たないこともできます。 -* **`400`** 以上は「クライアントエラー」のレスポンスのためのものです。これらは、おそらく最も多用するであろう2番目のタイプです。 + * 特殊なケースとして、`204`(No Content)があります。このレスポンスはクライアントに返すコンテンツがない場合に使用されるため、レスポンスはボディを持ってはいけません。 +* **`300 - 399`** は「リダイレクト」のためのものです。これらのステータスコードを持つレスポンスは`304`(Not Modified)を除き、ボディを持つことも持たないこともできます。`304`はボディを持ってはいけません。 +* **`400 - 499`** は「クライアントエラー」のレスポンスのためのものです。これらは、おそらく最も多用するであろう2番目のタイプです。 * 例えば、`404`は「Not Found」レスポンスです。 * クライアントからの一般的なエラーについては、`400`を使用することができます。 -* `500`以上はサーバーエラーのためのものです。これらを直接使うことはほとんどありません。アプリケーションコードやサーバーのどこかで何か問題が発生した場合、これらのステータスコードのいずれかが自動的に返されます。 +* `500 - 599` はサーバーエラーのためのものです。これらを直接使うことはほとんどありません。アプリケーションコードやサーバーのどこかで何か問題が発生した場合、これらのステータスコードのいずれかが自動的に返されます。 /// tip | 豆知識 -それぞれのステータスコードとどのコードが何のためのコードなのかについて詳細はMDN HTTP レスポンスステータスコードについてのドキュメントを参照してください。 +それぞれのステータスコードとどのコードが何のためのコードなのかについて詳細はMDN documentation about HTTP status codesを参照してください。 /// -## 名前を覚えるための近道 +## 名前を覚えるための近道 { #shortcut-to-remember-the-names } 先ほどの例をもう一度見てみましょう: -{* ../../docs_src/response_status_code/tutorial001.py hl[6] *} +{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *} `201`は「作成完了」のためのステータスコードです。 @@ -82,11 +82,11 @@ HTTPでは、レスポンスの一部として3桁の数字のステータス `fastapi.status`の便利な変数を利用することができます。 -{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *} +{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *} -それらは便利です。それらは同じ番号を保持しており、その方法ではエディタの自動補完を使用してそれらを見つけることができます。 +それらは単なる便利なものであり、同じ番号を保持しています。しかし、その方法ではエディタの自動補完を使用してそれらを見つけることができます。 - + /// note | 技術詳細 @@ -96,6 +96,6 @@ HTTPでは、レスポンスの一部として3桁の数字のステータス /// -## デフォルトの変更 +## デフォルトの変更 { #changing-the-default } 後に、[高度なユーザーガイド](../advanced/response-change-status-code.md){.internal-link target=_blank}で、ここで宣言しているデフォルトとは異なるステータスコードを返す方法を見ていきます。 diff --git a/docs/ja/docs/tutorial/schema-extra-example.md b/docs/ja/docs/tutorial/schema-extra-example.md index 1834e67b27..e526685c2f 100644 --- a/docs/ja/docs/tutorial/schema-extra-example.md +++ b/docs/ja/docs/tutorial/schema-extra-example.md @@ -1,55 +1,202 @@ -# スキーマの追加 - 例 +# リクエストのExampleデータの宣言 { #declare-request-example-data } -JSON Schemaに追加する情報を定義することができます。 +アプリが受け取れるデータの例を宣言できます。 -一般的なユースケースはこのドキュメントで示されているように`example`を追加することです。 +ここでは、それを行ういくつかの方法を紹介します。 -JSON Schemaの追加情報を宣言する方法はいくつかあります。 +## Pydanticモデルでの追加JSON Schemaデータ { #extra-json-schema-data-in-pydantic-models } -## Pydanticの`schema_extra` +生成されるJSON Schemaに追加されるPydanticモデルの`examples`を宣言できます。 -Pydanticのドキュメント: スキーマのカスタマイズで説明されているように、`Config`と`schema_extra`を使ってPydanticモデルの例を宣言することができます: +{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *} -{* ../../docs_src/schema_extra_example/tutorial001.py hl[15,16,17,18,19,20,21,22,23] *} +その追加情報は、そのモデルの出力**JSON Schema**にそのまま追加され、APIドキュメントで使用されます。 -その追加情報はそのまま出力され、JSON Schemaに追加されます。 +Pydanticのドキュメント: Configurationで説明されているように、`dict`を受け取る属性`model_config`を使用できます。 -## `Field`の追加引数 +生成されるJSON Schemaに表示したい追加データ(`examples`を含む)を含む`dict`を使って、`"json_schema_extra"`を設定できます。 -後述する`Field`、`Path`、`Query`、`Body`などでは、任意の引数を関数に渡すことでJSON Schemaの追加情報を宣言することもできます: +/// tip | 豆知識 -{* ../../docs_src/schema_extra_example/tutorial002.py hl[4,10,11,12,13] *} +同じ手法を使ってJSON Schemaを拡張し、独自のカスタム追加情報を追加できます。 -/// warning | 注意 - -これらの追加引数が渡されても、文書化のためのバリデーションは追加されず、注釈だけが追加されることを覚えておいてください。 +例えば、フロントエンドのユーザーインターフェースのためのメタデータを追加する、などに使えます。 /// -## `Body`の追加引数 +/// info | 情報 -追加情報を`Field`に渡すのと同じように、`Path`、`Query`、`Body`などでも同じことができます。 +OpenAPI 3.1.0(FastAPI 0.99.0以降で使用)では、**JSON Schema**標準の一部である`examples`がサポートされました。 -例えば、`Body`にボディリクエストの`example`を渡すことができます: +それ以前は、単一の例を持つキーワード`example`のみがサポートされていました。これはOpenAPI 3.1.0でも引き続きサポートされていますが、非推奨であり、JSON Schema標準の一部ではありません。そのため、`example`から`examples`への移行が推奨されます。🤓 -{* ../../docs_src/schema_extra_example/tutorial003.py hl[21,22,23,24,25,26] *} +詳細はこのページの最後で読めます。 -## ドキュメントのUIの例 +/// + +## `Field`の追加引数 { #field-additional-arguments } + +Pydanticモデルで`Field()`を使う場合、追加の`examples`も宣言できます: + +{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *} + +## JSON Schema内の`examples` - OpenAPI { #examples-in-json-schema-openapi } + +以下のいずれかを使用する場合: + +* `Path()` +* `Query()` +* `Header()` +* `Cookie()` +* `Body()` +* `Form()` +* `File()` + +追加情報を含む`examples`のグループを宣言でき、それらは**OpenAPI**内のそれぞれの**JSON Schemas**に追加されます。 + +### `examples`を使う`Body` { #body-with-examples } + +ここでは、`Body()`で期待されるデータの例を1つ含む`examples`を渡します: + +{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *} + +### ドキュメントUIでの例 { #example-in-the-docs-ui } 上記のいずれの方法でも、`/docs`の中では以下のようになります: - + -## 技術詳細 +### 複数の`examples`を使う`Body` { #body-with-multiple-examples } -`example` と `examples`について... +もちろん、複数の`examples`を渡すこともできます: -JSON Schemaの最新バージョンでは`examples`というフィールドを定義していますが、OpenAPIは`examples`を持たない古いバージョンのJSON Schemaをベースにしています。 +{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *} -そのため、OpenAPIでは同じ目的のために`example`を独自に定義しており(`examples`ではなく`example`として)、それがdocs UI(Swagger UIを使用)で使用されています。 +この場合、examplesはそのボディデータの内部**JSON Schema**の一部になります。 -つまり、`example`はJSON Schemaの一部ではありませんが、OpenAPIの一部であり、それがdocs UIで使用されることになります。 +それでも、執筆時点では、ドキュメントUIの表示を担当するツールであるSwagger UIは、**JSON Schema**内のデータに対して複数の例を表示することをサポートしていません。しかし、回避策については以下を読んでください。 -## その他の情報 +### OpenAPI固有の`examples` { #openapi-specific-examples } -同じように、フロントエンドのユーザーインターフェースなどをカスタマイズするために、各モデルのJSON Schemaに追加される独自の追加情報を追加することができます。 +**JSON Schema**が`examples`をサポートする前から、OpenAPIは同じく`examples`という別のフィールドをサポートしていました。 + +この**OpenAPI固有**の`examples`は、OpenAPI仕様の別のセクションに入ります。各JSON Schemaの中ではなく、**各*path operation*の詳細**に入ります。 + +そしてSwagger UIは、この特定の`examples`フィールドを以前からサポートしています。そのため、これを使って**ドキュメントUIに異なる例を表示**できます。 + +このOpenAPI固有フィールド`examples`の形は**複数の例**(`list`ではなく)を持つ`dict`であり、それぞれに追加情報が含まれ、その追加情報は**OpenAPI**にも追加されます。 + +これはOpenAPIに含まれる各JSON Schemaの中には入らず、外側の、*path operation*に直接入ります。 + +### `openapi_examples`パラメータの使用 { #using-the-openapi-examples-parameter } + +FastAPIでは、以下に対してパラメータ`openapi_examples`を使って、OpenAPI固有の`examples`を宣言できます: + +* `Path()` +* `Query()` +* `Header()` +* `Cookie()` +* `Body()` +* `Form()` +* `File()` + +`dict`のキーは各例を識別し、各値は別の`dict`です。 + +`examples`内の各特定の例`dict`には、次の内容を含められます: + +* `summary`: 例の短い説明。 +* `description`: Markdownテキストを含められる長い説明。 +* `value`: 実際に表示される例(例: `dict`)。 +* `externalValue`: `value`の代替で、例を指すURLです。ただし、`value`ほど多くのツールでサポートされていない可能性があります。 + +次のように使えます: + +{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *} + +### ドキュメントUIのOpenAPI Examples { #openapi-examples-in-the-docs-ui } + +`Body()`に`openapi_examples`を追加すると、`/docs`は次のようになります: + + + +## 技術詳細 { #technical-details } + +/// tip | 豆知識 + +すでに**FastAPI**バージョン**0.99.0以上**を使用している場合、おそらくこれらの詳細は**スキップ**できます。 + +これらは、OpenAPI 3.1.0が利用可能になる前の古いバージョンにより関連します。 + +これは簡単なOpenAPIとJSON Schemaの**歴史の授業**だと考えられます。🤓 + +/// + +/// warning | 注意 + +ここでは、標準である**JSON Schema**と**OpenAPI**についての非常に技術的な詳細を扱います。 + +上のアイデアがすでにうまく動いているなら、それで十分かもしれませんし、おそらくこの詳細は不要です。気軽にスキップしてください。 + +/// + +OpenAPI 3.1.0より前は、OpenAPIは古く改変されたバージョンの**JSON Schema**を使用していました。 + +JSON Schemaには`examples`がなかったため、OpenAPIは自身が改変したバージョンに独自の`example`フィールドを追加しました。 + +OpenAPIは、仕様の他の部分にも`example`と`examples`フィールドを追加しました: + +* `Parameter Object`(仕様内)。FastAPIの以下で使用されました: + * `Path()` + * `Query()` + * `Header()` + * `Cookie()` +* `Request Body Object`。仕様内の`Media Type Object`の`content`フィールド(仕様内)。FastAPIの以下で使用されました: + * `Body()` + * `File()` + * `Form()` + +/// info | 情報 + +この古いOpenAPI固有の`examples`パラメータは、FastAPI `0.103.0`以降は`openapi_examples`になりました。 + +/// + +### JSON Schemaの`examples`フィールド { #json-schemas-examples-field } + +しかしその後、JSON Schemaは新しいバージョンの仕様に`examples`フィールドを追加しました。 + +そして、新しいOpenAPI 3.1.0は、この新しいフィールド`examples`を含む最新バージョン(JSON Schema 2020-12)に基づくようになりました。 + +そして現在、この新しい`examples`フィールドは、古い単一(かつカスタム)の`example`フィールドより優先され、`example`は現在非推奨です。 + +JSON Schemaのこの新しい`examples`フィールドは、OpenAPIの他の場所(上で説明)にあるような追加メタデータを持つdictではなく、**単なる例の`list`**です。 + +/// info | 情報 + +OpenAPI 3.1.0がこのJSON Schemaとの新しいよりシンプルな統合とともにリリースされた後も、しばらくの間、自動ドキュメントを提供するツールであるSwagger UIはOpenAPI 3.1.0をサポートしていませんでした(バージョン5.0.0からサポートされています🎉)。 + +そのため、FastAPI 0.99.0より前のバージョンは、OpenAPI 3.1.0より低いバージョンのOpenAPIをまだ使用していました。 + +/// + +### PydanticとFastAPIの`examples` { #pydantic-and-fastapi-examples } + +Pydanticモデル内で、`schema_extra`または`Field(examples=["something"])`を使って`examples`を追加すると、その例はそのPydanticモデルの**JSON Schema**に追加されます。 + +そしてそのPydanticモデルの**JSON Schema**はAPIの**OpenAPI**に含まれ、ドキュメントUIで使用されます。 + +FastAPI 0.99.0より前のバージョン(0.99.0以上は新しいOpenAPI 3.1.0を使用)では、他のユーティリティ(`Query()`、`Body()`など)で`example`または`examples`を使っても、それらの例はそのデータを説明するJSON Schema(OpenAPI独自版のJSON Schemaでさえ)には追加されず、OpenAPI内の*path operation*宣言に直接追加されていました(JSON Schemaを使用するOpenAPIの部分の外側)。 + +しかし、FastAPI 0.99.0以上ではOpenAPI 3.1.0を使用し、それはJSON Schema 2020-12とSwagger UI 5.0.0以上を使うため、すべてがより一貫し、例はJSON Schemaに含まれます。 + +### Swagger UIとOpenAPI固有の`examples` { #swagger-ui-and-openapi-specific-examples } + +Swagger UIは複数のJSON Schema examplesをサポートしていなかった(2023-08-26時点)ため、ユーザーはドキュメントで複数の例を表示する手段がありませんでした。 + +それを解決するため、FastAPI `0.103.0`は、新しいパラメータ`openapi_examples`で、同じ古い**OpenAPI固有**の`examples`フィールドを宣言するための**サポートを追加**しました。🤓 + +### まとめ { #summary } + +昔は歴史があまり好きではないと言っていました...が、今の私は「技術の歴史」の授業をしています。😅 + +要するに、**FastAPI 0.99.0以上にアップグレード**してください。そうすれば、物事はもっと**シンプルで一貫性があり直感的**になり、これらの歴史的詳細を知る必要もありません。😎 diff --git a/docs/ja/docs/tutorial/security/first-steps.md b/docs/ja/docs/tutorial/security/first-steps.md index 0ce0f929be..76ef04db8d 100644 --- a/docs/ja/docs/tutorial/security/first-steps.md +++ b/docs/ja/docs/tutorial/security/first-steps.md @@ -1,4 +1,4 @@ -# セキュリティ - 最初の一歩 +# セキュリティ - 最初の一歩 { #security-first-steps } あるドメインに、**バックエンド** APIを持っているとしましょう。 @@ -12,25 +12,31 @@ **FastAPI**が提供するツールを使って、セキュリティを制御してみましょう。 -## どう見えるか +## どう見えるか { #how-it-looks } まずはこのコードを使って、どう動くか観察します。その後で、何が起こっているのか理解しましょう。 -## `main.py`を作成 +## `main.py`を作成 { #create-main-py } `main.py`に、下記の例をコピーします: -{* ../../docs_src/security/tutorial001.py *} +{* ../../docs_src/security/tutorial001_an_py39.py *} -## 実行 +## 実行 { #run-it } /// info | 情報 -まず`python-multipart`をインストールします。 +`python-multipart` パッケージは、`pip install "fastapi[standard]"` コマンドを実行すると **FastAPI** と一緒に自動的にインストールされます。 -例えば、`pip install python-multipart`。 +しかし、`pip install fastapi` コマンドを使用する場合、`python-multipart` パッケージはデフォルトでは含まれません。 -これは、**OAuth2**が `ユーザー名` や `パスワード` を送信するために、「フォームデータ」を使うからです。 +手動でインストールするには、[仮想環境](../../virtual-environments.md){.internal-link target=_blank}を作成して有効化し、次のコマンドでインストールしてください: + +```console +$ pip install python-multipart +``` + +これは、**OAuth2**が `username` と `password` を送信するために、「フォームデータ」を使うからです。 /// @@ -39,14 +45,14 @@
```console -$ uvicorn main:app --reload +$ fastapi dev main.py INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ```
-## 確認 +## 確認 { #check-it } 次のインタラクティブなドキュメントにアクセスしてください: http://127.0.0.1:8000/docs。 @@ -62,7 +68,7 @@ $ uvicorn main:app --reload /// -それをクリックすると、`ユーザー名`と`パスワード` (およびその他のオプションフィールド) を入力する小さな認証フォームが表示されます: +それをクリックすると、`username` と `password`(およびその他のオプションフィールド)を入力する小さな認可フォームが表示されます: @@ -80,11 +86,11 @@ $ uvicorn main:app --reload また、同じアプリケーションのデバッグ、チェック、テストのためにも利用できます。 -## `パスワード` フロー +## `password` フロー { #the-password-flow } では、少し話を戻して、どうなっているか理解しましょう。 -`パスワード`の「フロー」は、OAuth2で定義されているセキュリティと認証を扱う方法 (「フロー」) の1つです。 +`password`の「フロー」は、OAuth2で定義されているセキュリティと認証を扱う方法 (「フロー」) の1つです。 OAuth2は、バックエンドやAPIがユーザーを認証するサーバーから独立したものとして設計されていました。 @@ -92,9 +98,9 @@ OAuth2は、バックエンドやAPIがユーザーを認証するサーバー そこで、簡略化した箇所から見直してみましょう: -* ユーザーはフロントエンドで`ユーザー名`と`パスワード`を入力し、`Enter`を押します。 -* フロントエンド (ユーザーのブラウザで実行中) は、`ユーザー名`と`パスワード`をAPIの特定のURL (`tokenUrl="token"`で宣言された) に送信します。 -* APIは`ユーザー名`と`パスワード`をチェックし、「トークン」を返却します (まだ実装していません)。 +* ユーザーはフロントエンドで`username`と`password`を入力し、`Enter`を押します。 +* フロントエンド (ユーザーのブラウザで実行中) は、`username`と`password`をAPIの特定のURL (`tokenUrl="token"`で宣言された) に送信します。 +* APIは`username`と`password`をチェックし、「トークン」を返却します (まだ実装していません)。 * 「トークン」はただの文字列であり、あとでこのユーザーを検証するために使用します。 * 通常、トークンは時間が経つと期限切れになるように設定されています。 * トークンが期限切れの場合は、再度ログインする必要があります。 @@ -106,11 +112,11 @@ OAuth2は、バックエンドやAPIがユーザーを認証するサーバー * したがって、APIで認証するため、HTTPヘッダー`Authorization`に`Bearer`の文字列とトークンを加えた値を送信します。 * トークンに`foobar`が含まれている場合、`Authorization`ヘッダーの内容は次のようになります: `Bearer foobar`。 -## **FastAPI**の`OAuth2PasswordBearer` +## **FastAPI**の`OAuth2PasswordBearer` { #fastapis-oauth2passwordbearer } **FastAPI**は、これらのセキュリティ機能を実装するために、抽象度の異なる複数のツールを提供しています。 -この例では、**Bearer**トークンを使用して**OAuth2**を**パスワード**フローで使用します。これには`OAuth2PasswordBearer`クラスを使用します。 +この例では、**Bearer**トークンを使用して**OAuth2**を**Password**フローで使用します。これには`OAuth2PasswordBearer`クラスを使用します。 /// info | 情報 @@ -124,9 +130,9 @@ OAuth2は、バックエンドやAPIがユーザーを認証するサーバー /// -`OAuth2PasswordBearer` クラスのインスタンスを作成する時に、パラメーター`tokenUrl`を渡します。このパラメーターには、クライアント (ユーザーのブラウザで動作するフロントエンド) がトークンを取得するために`ユーザー名`と`パスワード`を送信するURLを指定します。 +`OAuth2PasswordBearer` クラスのインスタンスを作成する時に、パラメーター`tokenUrl`を渡します。このパラメーターには、クライアント (ユーザーのブラウザで動作するフロントエンド) がトークンを取得するために`username`と`password`を送信するURLを指定します。 -{* ../../docs_src/security/tutorial001.py hl[6] *} +{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *} /// tip | 豆知識 @@ -134,13 +140,13 @@ OAuth2は、バックエンドやAPIがユーザーを認証するサーバー 相対URLを使っているので、APIが`https://example.com/`にある場合、`https://example.com/token`を参照します。しかし、APIが`https://example.com/api/v1/`にある場合は`https://example.com/api/v1/token`を参照することになります。 -相対 URL を使うことは、[プロキシと接続](../../advanced/behind-a-proxy.md){.internal-link target=_blank}のような高度なユースケースでもアプリケーションを動作させ続けるために重要です。 +相対 URL を使うことは、[プロキシの背後](../../advanced/behind-a-proxy.md){.internal-link target=_blank}のような高度なユースケースでもアプリケーションを動作させ続けるために重要です。 /// このパラメーターはエンドポイント/ *path operation*を作成しません。しかし、URL`/token`はクライアントがトークンを取得するために使用するものであると宣言します。この情報は OpenAPI やインタラクティブな API ドキュメントシステムで使われます。 -実際のpath operationもすぐに作ります。 +実際の path operation もすぐに作ります。 /// info | 情報 @@ -160,13 +166,13 @@ oauth2_scheme(some, parameters) そのため、`Depends`と一緒に使うことができます。 -### 使い方 +### 使い方 { #use-it } これで`oauth2_scheme`を`Depends`で依存関係に渡すことができます。 -{* ../../docs_src/security/tutorial001.py hl[10] *} +{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *} -この依存関係は、*path operation function*のパラメーター`token`に代入される`str`を提供します。 +この依存関係は、*path operation 関数*のパラメーター`token`に代入される`str`を提供します。 **FastAPI**は、この依存関係を使用してOpenAPIスキーマ (および自動APIドキュメント) で「セキュリティスキーム」を定義できることを知っています。 @@ -178,13 +184,13 @@ OpenAPIと統合するセキュリティユーティリティ (および自動AP /// -## どのように動作するか +## 何をするか { #what-it-does } -リクエストの中に`Authorization`ヘッダーを探しに行き、その値が`Bearer`と何らかのトークンを含んでいるかどうかをチェックし、そのトークンを`str`として返します。 +リクエストの中に`Authorization`ヘッダーを探しに行き、その値が`Bearer `と何らかのトークンを含んでいるかどうかをチェックし、そのトークンを`str`として返します。 -もし`Authorization`ヘッダーが見つからなかったり、値が`Bearer`トークンを持っていなかったりすると、401 ステータスコードエラー (`UNAUTHORIZED`) で直接応答します。 +もし`Authorization`ヘッダーが見つからなかったり、値が`Bearer `トークンを持っていなかったりすると、401 ステータスコードエラー (`UNAUTHORIZED`) で直接応答します。 -トークンが存在するかどうかをチェックしてエラーを返す必要はありません。関数が実行された場合、そのトークンに`str`が含まれているか確認できます。 +トークンが存在するかどうかをチェックしてエラーを返す必要はありません。関数が実行された場合、そのトークンに`str`が含まれていることを確信できます。 インタラクティブなドキュメントですでに試すことができます: @@ -192,6 +198,6 @@ OpenAPIと統合するセキュリティユーティリティ (および自動AP まだトークンの有効性を検証しているわけではありませんが、これはもう始まっています。 -## まとめ +## まとめ { #recap } つまり、たった3~4行の追加で、すでに何らかの基礎的なセキュリティの形になっています。 diff --git a/docs/ja/docs/tutorial/security/get-current-user.md b/docs/ja/docs/tutorial/security/get-current-user.md index 9fc46c07c5..39b97cca52 100644 --- a/docs/ja/docs/tutorial/security/get-current-user.md +++ b/docs/ja/docs/tutorial/security/get-current-user.md @@ -1,22 +1,22 @@ -# 現在のユーザーの取得 +# 現在のユーザーの取得 { #get-current-user } -一つ前の章では、(依存性注入システムに基づいた)セキュリティシステムは、 *path operation関数* に `str` として `token` を与えていました: +一つ前の章では、(依存性注入システムに基づいた)セキュリティシステムは、 *path operation 関数* に `str` として `token` を与えていました: -{* ../../docs_src/security/tutorial001.py hl[10] *} +{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *} しかし、それはまだそんなに有用ではありません。 現在のユーザーを取得するようにしてみましょう。 -## ユーザーモデルの作成 +## ユーザーモデルの作成 { #create-a-user-model } まずは、Pydanticのユーザーモデルを作成しましょう。 ボディを宣言するのにPydanticを使用するのと同じやり方で、Pydanticを別のどんなところでも使うことができます: -{* ../../docs_src/security/tutorial002.py hl[5,12:16] *} +{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *} -## 依存関係 `get_current_user` を作成 +## 依存関係 `get_current_user` を作成 { #create-a-get-current-user-dependency } 依存関係 `get_current_user` を作ってみましょう。 @@ -24,21 +24,21 @@ `get_current_user` は前に作成した `oauth2_scheme` と同じ依存関係を持ちます。 -以前直接 *path operation* の中でしていたのと同じように、新しい依存関係である `get_current_user` は `str` として `token` を受け取るようになります: +以前直接 *path operation* の中でしていたのと同じように、新しい依存関係である `get_current_user` はサブ依存関係である `oauth2_scheme` から `str` として `token` を受け取るようになります: -{* ../../docs_src/security/tutorial002.py hl[25] *} +{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *} -## ユーザーの取得 +## ユーザーの取得 { #get-the-user } `get_current_user` は作成した(偽物の)ユーティリティ関数を使って、 `str` としてトークンを受け取り、先ほどのPydanticの `User` モデルを返却します: -{* ../../docs_src/security/tutorial002.py hl[19:22,26:27] *} +{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *} -## 現在のユーザーの注入 +## 現在のユーザーの注入 { #inject-the-current-user } ですので、 `get_current_user` に対して同様に *path operation* の中で `Depends` を利用できます。 -{* ../../docs_src/security/tutorial002.py hl[31] *} +{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *} Pydanticモデルの `User` として、 `current_user` の型を宣言することに注意してください。 @@ -54,15 +54,15 @@ Pydanticモデルの `User` として、 `current_user` の型を宣言するこ /// check | 確認 -依存関係システムがこのように設計されているおかげで、 `User` モデルを返却する別の依存関係(別の"dependables")を持つことができます。 +依存関係システムがこのように設計されているおかげで、 `User` モデルを返却する別の依存関係(別の「dependables」)を持つことができます。 同じデータ型を返却する依存関係は一つだけしか持てない、という制約が入ることはないのです。 /// -## 別のモデル +## 別のモデル { #other-models } -これで、*path operation関数* の中で現在のユーザーを直接取得し、`Depends` を使って、 **依存性注入** レベルでセキュリティメカニズムを処理できるようになりました。 +これで、*path operation 関数* の中で現在のユーザーを直接取得し、`Depends` を使って、 **依存性注入** レベルでセキュリティメカニズムを処理できるようになりました。 そして、セキュリティ要件のためにどんなモデルやデータでも利用することができます。(この場合は、 Pydanticモデルの `User`) @@ -76,10 +76,9 @@ Pydanticモデルの `User` として、 `current_user` の型を宣言するこ あなたのアプリケーションに必要なのがどんな種類のモデル、どんな種類のクラス、どんな種類のデータベースであったとしても、 **FastAPI** は依存性注入システムでカバーしてくれます。 +## コードサイズ { #code-size } -## コードサイズ - -この例は冗長に見えるかもしれません。セキュリティとデータモデルユーティリティ関数および *path operations* が同じファイルに混在しているということを覚えておいてください。 +この例は冗長に見えるかもしれません。セキュリティとデータモデルユーティリティ関数および *path operation* が同じファイルに混在しているということを覚えておいてください。 しかし、ここに重要なポイントがあります。 @@ -87,20 +86,20 @@ Pydanticモデルの `User` として、 `current_user` の型を宣言するこ そして、それは好きなだけ複雑にすることができます。それでも、一箇所に、一度だけ書くのです。すべての柔軟性を備えます。 -しかし、同じセキュリティシステムを使って何千ものエンドポイント(*path operations*)を持つことができます。 +しかし、同じセキュリティシステムを使って何千ものエンドポイント(*path operation*)を持つことができます。 そして、それらエンドポイントのすべて(必要な、どの部分でも)がこうした依存関係や、あなたが作成する別の依存関係を再利用する利点を享受できるのです。 -さらに、こうした何千もの *path operations* は、たった3行で表現できるのです: +さらに、こうした何千もの *path operation* は、たった3行で表現できるのです: -{* ../../docs_src/security/tutorial002.py hl[30:32] *} +{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *} -## まとめ +## まとめ { #recap } -これで、 *path operation関数* の中で直接現在のユーザーを取得できるようになりました。 +これで、 *path operation 関数* の中で直接現在のユーザーを取得できるようになりました。 既に半分のところまで来ています。 -あとは、 `username` と `password` を実際にそのユーザーやクライアントに送る、 *path operation* を追加する必要があるだけです。 +あとは、ユーザー/クライアントが実際に `username` と `password` を送信するための *path operation* を追加する必要があるだけです。 次はそれを説明します。 diff --git a/docs/ja/docs/tutorial/security/oauth2-jwt.md b/docs/ja/docs/tutorial/security/oauth2-jwt.md index 599fc7b069..186936f1ba 100644 --- a/docs/ja/docs/tutorial/security/oauth2-jwt.md +++ b/docs/ja/docs/tutorial/security/oauth2-jwt.md @@ -1,4 +1,4 @@ -# パスワード(およびハッシュ化)によるOAuth2、JWTトークンによるBearer +# パスワード(およびハッシュ化)によるOAuth2、JWTトークンによるBearer { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens } これでセキュリティの流れが全てわかったので、JWTトークンと安全なパスワードのハッシュ化を使用して、実際にアプリケーションを安全にしてみましょう。 @@ -6,7 +6,7 @@ 本章では、前章の続きから始めて、コードをアップデートしていきます。 -## JWT について +## JWT について { #about-jwt } JWTとは「JSON Web Tokens」の略称です。 @@ -26,33 +26,31 @@ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4 JWT トークンを使って遊んでみたいという方は、https://jwt.io をチェックしてください。 -## `python-jose` のインストール +## `PyJWT` のインストール { #install-pyjwt } -PythonでJWTトークンの生成と検証を行うために、`python-jose`をインストールする必要があります: +PythonでJWTトークンの生成と検証を行うために、`PyJWT`をインストールする必要があります。 + +[仮想環境](../../virtual-environments.md){.internal-link target=_blank}を作成し、アクティベートしてから、`pyjwt`をインストールしてください。
```console -$ pip install python-jose[cryptography] +$ pip install pyjwt ---> 100% ```
-また、Python-joseだけではなく、暗号を扱うためのパッケージを追加で必要とします。 +/// info | 情報 -ここでは、推奨されているものを使用します:pyca/cryptography。 +RSAやECDSAのようなデジタル署名アルゴリズムを使用する予定がある場合は、cryptographyライブラリの依存関係`pyjwt[crypto]`をインストールしてください。 -/// tip | 豆知識 - -このチュートリアルでは以前、PyJWTを使用していました。 - -しかし、Python-joseは、PyJWTのすべての機能に加えて、後に他のツールと統合して構築する際におそらく必要となる可能性のあるいくつかの追加機能を提供しています。そのため、代わりにPython-joseを使用するように更新されました。 +詳細はPyJWT Installation docsで確認できます。 /// -## パスワードのハッシュ化 +## パスワードのハッシュ化 { #password-hashing } 「ハッシュ化」とは、あるコンテンツ(ここではパスワード)を、規則性のないバイト列(単なる文字列)に変換することです。 @@ -60,26 +58,26 @@ $ pip install python-jose[cryptography] しかし、規則性のないバイト列から元のパスワードに戻すことはできません。 -### パスワードのハッシュ化を使う理由 +### パスワードのハッシュ化を使う理由 { #why-use-password-hashing } データベースが盗まれても、ユーザーの平文のパスワードは盗まれず、ハッシュ値だけが盗まれます。 したがって、泥棒はそのパスワードを別のシステムで使えません(多くのユーザーはどこでも同じパスワードを使用しているため、危険性があります)。 -## `passlib` のインストール +## `pwdlib` のインストール { #install-pwdlib } -PassLib は、パスワードのハッシュを処理するための優れたPythonパッケージです。 +pwdlib は、パスワードのハッシュを処理するための優れたPythonパッケージです。 このパッケージは、多くの安全なハッシュアルゴリズムとユーティリティをサポートします。 -推奨されるアルゴリズムは「Bcrypt」です。 +推奨されるアルゴリズムは「Argon2」です。 -そのため、Bcryptを指定してPassLibをインストールします: +[仮想環境](../../virtual-environments.md){.internal-link target=_blank}を作成し、アクティベートしてから、Argon2付きでpwdlibをインストールしてください。
```console -$ pip install passlib[bcrypt] +$ pip install "pwdlib[argon2]" ---> 100% ``` @@ -88,7 +86,7 @@ $ pip install passlib[bcrypt] /// tip | 豆知識 -`passlib`を使用すると、**Django**や**Flask**のセキュリティプラグインなどで作成されたパスワードを読み取れるように設定できます。 +`pwdlib`を使用すると、**Django**や**Flask**のセキュリティプラグインなどで作成されたパスワードを読み取れるように設定できます。 例えば、Djangoアプリケーションからデータベース内の同じデータをFastAPIアプリケーションと共有できるだけではなく、同じデータベースを使用してDjangoアプリケーションを徐々に移行することもできます。 @@ -96,17 +94,17 @@ $ pip install passlib[bcrypt] /// -## パスワードのハッシュ化と検証 +## パスワードのハッシュ化と検証 { #hash-and-verify-the-passwords } -必要なツールを `passlib`からインポートします。 +必要なツールを `pwdlib`からインポートします。 -PassLib の「context」を作成します。これは、パスワードのハッシュ化と検証に使用されるものです。 +推奨設定でPasswordHashインスタンスを作成します。これは、パスワードのハッシュ化と検証に使用されます。 /// tip | 豆知識 -PassLibのcontextには、検証だけが許された非推奨の古いハッシュアルゴリズムを含む、様々なハッシュアルゴリズムを使用した検証機能もあります。 +pwdlibはbcryptハッシュアルゴリズムもサポートしていますが、レガシーアルゴリズムは含みません。古いハッシュを扱うには、passlibライブラリを使用することが推奨されます。 -例えば、この機能を使用して、別のシステム(Djangoなど)によって生成されたパスワードを読み取って検証し、Bcryptなどの別のアルゴリズムを使用して新しいパスワードをハッシュするといったことができます。 +例えば、この機能を使用して、別のシステム(Djangoなど)によって生成されたパスワードを読み取って検証し、Argon2やBcryptなどの別のアルゴリズムを使用して新しいパスワードをハッシュするといったことができます。 そして、同時にそれらはすべてに互換性があります。 @@ -118,15 +116,15 @@ PassLibのcontextには、検証だけが許された非推奨の古いハッシ さらに、ユーザーを認証して返す関数も作成します。 -{* ../../docs_src/security/tutorial004.py hl[7,48,55:56,59:60,69:75] *} +{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *} /// note | 備考 -新しい(偽の)データベース`fake_users_db`を確認すると、ハッシュ化されたパスワードが次のようになっていることがわかります:`"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"` +新しい(偽の)データベース`fake_users_db`を確認すると、ハッシュ化されたパスワードが次のようになっていることがわかります:`"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`。 /// -## JWTトークンの取り扱い +## JWTトークンの取り扱い { #handle-jwt-tokens } インストールした複数のモジュールをインポートします。 @@ -148,33 +146,33 @@ $ openssl rand -hex 32 JWTトークンの署名に使用するアルゴリズム`"HS256"`を指定した変数`ALGORITHM`を作成します。 -トークンの有効期限を指定した変数`ACCESS_TOKEN_EXPIRE_MINUTES`を作成します。 +トークンの有効期限を指定した変数を作成します。 レスポンスのトークンエンドポイントで使用するPydanticモデルを定義します。 新しいアクセストークンを生成するユーティリティ関数を作成します。 -{* ../../docs_src/security/tutorial004.py hl[6,12:14,28:30,78:86] *} +{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *} -## 依存関係の更新 +## 依存関係の更新 { #update-the-dependencies } `get_current_user`を更新して、先ほどと同じトークンを受け取るようにしますが、今回はJWTトークンを使用します。 -受け取ったトークンを復号して検証し、現在のユーザーを返します。 +受け取ったトークンをデコードして検証し、現在のユーザーを返します。 トークンが無効な場合は、すぐにHTTPエラーを返します。 -{* ../../docs_src/security/tutorial004.py hl[89:106] *} +{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *} -## `/token` パスオペレーションの更新 +## `/token` *path operation* の更新 { #update-the-token-path-operation } トークンの有効期限を表す`timedelta`を作成します。 -JWTアクセストークンを作成し、それを返します。 +実際のJWTアクセストークンを作成し、それを返します。 -{* ../../docs_src/security/tutorial004.py hl[115:130] *} +{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *} -### JWTの"subject" `sub` についての技術的な詳細 +### JWTの「subject」`sub` についての技術的な詳細 { #technical-details-about-the-jwt-subject-sub } JWTの仕様では、トークンのsubjectを表すキー`sub`があるとされています。 @@ -192,13 +190,13 @@ JWTは、ユーザーを識別して、そのユーザーがAPI上で直接操 しかしながら、それらのエンティティのいくつかが同じIDを持つ可能性があります。例えば、`foo`(ユーザー`foo`、車 `foo`、ブログ投稿`foo`)などです。 -IDの衝突を回避するために、ユーザーのJWTトークンを作成するとき、subキーの値にプレフィックスを付けることができます(例えば、`username:`)。したがって、この例では、`sub`の値は次のようになっている可能性があります:`username:johndoe` +IDの衝突を回避するために、ユーザーのJWTトークンを作成するとき、subキーの値にプレフィックスを付けることができます(例えば、`username:`)。したがって、この例では、`sub`の値は次のようになっている可能性があります:`username:johndoe`。 覚えておくべき重要なことは、`sub`キーはアプリケーション全体で一意の識別子を持ち、文字列である必要があるということです。 -## 確認 +## 確認 { #check-it } -サーバーを実行し、ドキュメントに移動します:http://127.0.0.1:8000/docs +サーバーを実行し、ドキュメントに移動します:http://127.0.0.1:8000/docs。 次のようなユーザーインターフェイスが表示されます: @@ -232,17 +230,17 @@ Password: `secret` -開発者ツールを開くと、送信されるデータにはトークンだけが含まれており、パスワードはユーザーを認証してアクセストークンを取得する最初のリクエストでのみ送信され、その後は送信されないことがわかります。 +開発者ツールを開くと、送信されるデータにはトークンだけが含まれており、パスワードはユーザーを認証してアクセストークンを取得する最初のリクエストでのみ送信され、その後は送信されないことがわかります: /// note | 備考 -ヘッダーの`Authorization`には、`Bearer`で始まる値があります。 +ヘッダーの`Authorization`には、`Bearer `で始まる値があります。 /// -## `scopes` を使った高度なユースケース +## `scopes` を使った高度なユースケース { #advanced-usage-with-scopes } OAuth2には、「スコープ」という概念があります。 @@ -252,7 +250,7 @@ OAuth2には、「スコープ」という概念があります。 これらの使用方法や**FastAPI**への統合方法については、**高度なユーザーガイド**で後ほど説明します。 -## まとめ +## まとめ { #recap } ここまでの説明で、OAuth2やJWTなどの規格を使った安全な**FastAPI**アプリケーションを設定することができます。 @@ -266,7 +264,7 @@ OAuth2には、「スコープ」という概念があります。 そのため、プロジェクトに合わせて自由に選択することができます。 -また、**FastAPI**は外部パッケージを統合するために複雑な仕組みを必要としないため、`passlib`や`python-jose`のようなよく整備され広く使われている多くのパッケージを直接使用することができます。 +また、**FastAPI**は外部パッケージを統合するために複雑な仕組みを必要としないため、`pwdlib`や`PyJWT`のようなよく整備され広く使われている多くのパッケージを直接使用することができます。 しかし、柔軟性、堅牢性、セキュリティを損なうことなく、可能な限りプロセスを簡素化するためのツールを提供します。 diff --git a/docs/ja/docs/tutorial/static-files.md b/docs/ja/docs/tutorial/static-files.md index f910d7e36d..c79789494c 100644 --- a/docs/ja/docs/tutorial/static-files.md +++ b/docs/ja/docs/tutorial/static-files.md @@ -1,13 +1,13 @@ -# 静的ファイル +# 静的ファイル { #static-files } `StaticFiles` を使用して、ディレクトリから静的ファイルを自動的に提供できます。 -## `StaticFiles` の使用 +## `StaticFiles` の使用 { #use-staticfiles } * `StaticFiles` をインポート。 -* `StaticFiles()` インスタンスを生成し、特定のパスに「マウント」。 +* `StaticFiles()` インスタンスを特定のパスに「マウント」。 -{* ../../docs_src/static_files/tutorial001.py hl[2,6] *} +{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *} /// note | 技術詳細 @@ -17,15 +17,15 @@ /// -### 「マウント」とは +### 「マウント」とは { #what-is-mounting } 「マウント」とは、特定のパスに完全な「独立した」アプリケーションを追加することを意味します。これにより、すべてのサブパスの処理がなされます。 これは、マウントされたアプリケーションが完全に独立しているため、`APIRouter` とは異なります。メインアプリケーションのOpenAPIとドキュメントには、マウントされたアプリケーションの内容などは含まれません。 -これについて詳しくは、**高度なユーザーガイド** をご覧ください。 +これについて詳しくは、[高度なユーザーガイド](../advanced/index.md){.internal-link target=_blank} をご覧ください。 -## 詳細 +## 詳細 { #details } 最初の `"/static"` は、この「サブアプリケーション」が「マウント」されるサブパスを指します。したがって、`"/static"` から始まるパスはすべてサブアプリケーションによって処理されます。 @@ -33,8 +33,8 @@ `name="static"` は、**FastAPI** が内部で使用できる名前を付けます。 -これらのパラメータはすべて「`静的`」とは異なる場合があり、独自のアプリケーションのニーズと詳細に合わせて調整します。 +これらのパラメータはすべて「`static`」とは異なる場合があり、独自のアプリケーションのニーズと詳細に合わせて調整します。 -## より詳しい情報 +## より詳しい情報 { #more-info } 詳細とオプションについては、Starletteの静的ファイルに関するドキュメントを確認してください。 diff --git a/docs/ja/docs/tutorial/testing.md b/docs/ja/docs/tutorial/testing.md index 4e8ad4f7cc..0ec6250f31 100644 --- a/docs/ja/docs/tutorial/testing.md +++ b/docs/ja/docs/tutorial/testing.md @@ -1,12 +1,24 @@ -# テスト +# テスト { #testing } Starlette のおかげで、**FastAPI** アプリケーションのテストは簡単で楽しいものになっています。 -HTTPX がベースなので、非常に使いやすく直感的です。 +HTTPX がベースで、さらにその設計は Requests をベースにしているため、とても馴染みがあり直感的です。 これを使用すると、**FastAPI** と共に pytest を直接利用できます。 -## `TestClient` を使用 +## `TestClient` を使用 { #using-testclient } + +/// info | 情報 + +`TestClient` を使用するには、まず `httpx` をインストールします。 + +[仮想環境](../virtual-environments.md){.internal-link target=_blank} を作成し、それを有効化してから、例えば以下のようにインストールしてください: + +```console +$ pip install httpx +``` + +/// `TestClient` をインポートします。 @@ -16,9 +28,9 @@ `httpx` と同じ様に `TestClient` オブジェクトを使用します。 -チェックしたい Python の標準的な式と共に、シンプルに `assert` 文を記述します。 +チェックしたい Python の標準的な式と共に、シンプルに `assert` 文を記述します (これも `pytest` の標準です)。 -{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *} +{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *} /// tip | 豆知識 @@ -44,48 +56,81 @@ FastAPIアプリケーションへのリクエストの送信とは別に、テ /// -## テストの分離 +## テストの分離 { #separating-tests } 実際のアプリケーションでは、おそらくテストを別のファイルに保存します。 また、**FastAPI** アプリケーションは、複数のファイル/モジュールなどで構成されている場合もあります。 -### **FastAPI** アプリファイル +### **FastAPI** アプリファイル { #fastapi-app-file } -**FastAPI** アプリに `main.py` ファイルがあるとします: +[Bigger Applications](bigger-applications.md){.internal-link target=_blank} で説明されている、次のようなファイル構成があるとします: -{* ../../docs_src/app_testing/main.py *} +``` +. +├── app +│   ├── __init__.py +│   └── main.py +``` -### テストファイル +ファイル `main.py` に **FastAPI** アプリがあります: -次に、テストを含む `test_main.py` ファイルを作成し、`main` モジュール (`main.py`) から `app` をインポートします: -{* ../../docs_src/app_testing/test_main.py *} +{* ../../docs_src/app_testing/app_a_py39/main.py *} -## テスト: 例の拡張 +### テストファイル { #testing-file } + +次に、テストを含む `test_main.py` ファイルを用意できます。これは同じ Python パッケージ (`__init__.py` ファイルがある同じディレクトリ) に置けます: + +``` hl_lines="5" +. +├── app +│   ├── __init__.py +│   ├── main.py +│   └── test_main.py +``` + +このファイルは同じパッケージ内にあるため、相対インポートを使って `main` モジュール (`main.py`) からオブジェクト `app` をインポートできます: + +{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *} + + +...そして、これまでと同じようにテストコードを書けます。 + +## テスト: 例の拡張 { #testing-extended-example } 次に、この例を拡張し、詳細を追加して、さまざまなパーツをテストする方法を確認しましょう。 +### 拡張版 **FastAPI** アプリファイル { #extended-fastapi-app-file } -### 拡張版 **FastAPI** アプリファイル +先ほどと同じファイル構成で続けます: -**FastAPI** アプリに `main_b.py` ファイルがあるとします。 +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +│   └── test_main.py +``` -そのファイルには、エラーを返す可能性のある `GET` オペレーションがあります。 +ここで、**FastAPI** アプリがある `main.py` ファイルには、他の path operation があります。 -また、いくつかのエラーを返す可能性のある `POST` オペレーションもあります。 +エラーを返す可能性のある `GET` オペレーションがあります。 -これらの *path operation* には `X-Token` ヘッダーが必要です。 +いくつかのエラーを返す可能性のある `POST` オペレーションもあります。 -{* ../../docs_src/app_testing/app_b_py310/main.py *} +両方の *path operation* には `X-Token` ヘッダーが必要です。 -### 拡張版テストファイル +{* ../../docs_src/app_testing/app_b_an_py310/main.py *} -次に、先程のものに拡張版のテストを加えた、`test_main_b.py` を作成します。 +### 拡張版テストファイル { #extended-testing-file } -{* ../../docs_src/app_testing/app_b/test_main.py *} +次に、拡張版のテストで `test_main.py` を更新できます: -リクエストに情報を渡せるクライアントが必要で、その方法がわからない場合はいつでも、`httpx` での実現方法を検索 (Google) できます。 +{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *} + + +リクエストに情報を渡せるクライアントが必要で、その方法がわからない場合はいつでも、`httpx` での実現方法、あるいは HTTPX の設計が Requests の設計をベースにしているため `requests` での実現方法を検索 (Google) できます。 テストでも同じことを行います。 @@ -107,9 +152,11 @@ FastAPIアプリケーションへのリクエストの送信とは別に、テ /// -## 実行 +## 実行 { #run-it } -後は、`pytest` をインストールするだけです: +その後、`pytest` をインストールするだけです。 + +[仮想環境](../virtual-environments.md){.internal-link target=_blank} を作成し、それを有効化してから、例えば以下のようにインストールしてください:
@@ -121,7 +168,7 @@ $ pip install pytest
-ファイルを検知し、自動テストを実行し、結果のレポートを返します。 +ファイルとテストを自動的に検出し、実行して、結果のレポートを返します。 以下でテストを実行します: diff --git a/docs/ja/docs/virtual-environments.md b/docs/ja/docs/virtual-environments.md index 791cf64a84..6723230632 100644 --- a/docs/ja/docs/virtual-environments.md +++ b/docs/ja/docs/virtual-environments.md @@ -1,10 +1,10 @@ -# 仮想環境 +# 仮想環境 { #virtual-environments } Pythonプロジェクトの作業では、**仮想環境**(または類似の仕組み)を使用し、プロジェクトごとにインストールするパッケージを分離するべきでしょう。 /// info | 情報 -もし、仮想環境の概要や作成方法、使用方法について既にご存知なら、このセクションをスキップすることができます。🤓 +もし、仮想環境の概要や作成方法、使用方法について既にご存知なら、このセクションをスキップした方がよいかもしれません。🤓 /// @@ -25,7 +25,7 @@ Pythonプロジェクトの作業では、**仮想環境**(または類似の /// -## プロジェクトの作成 +## プロジェクトの作成 { #create-a-project } まず、プロジェクト用のディレクトリを作成します。 @@ -48,9 +48,9 @@ $ cd awesome-project
-## 仮想環境の作成 +## 仮想環境の作成 { #create-a-virtual-environment } -Pythonプロジェクトでの**初めての**作業を開始する際には、**プロジェクト内**に仮想環境を作成してください。 +Pythonプロジェクトでの**初めての**作業を開始する際には、**プロジェクト内**に仮想環境を作成してください。 /// tip | 豆知識 @@ -72,10 +72,10 @@ $ python -m venv .venv /// details | このコマンドの意味 -- `python` : `python` というプログラムを呼び出します -- `-m` : モジュールをスクリプトとして呼び出します。どのモジュールを呼び出すのか、この次に指定します -- `venv` : 通常Pythonに付随してインストールされる `venv`モジュールを使用します -- `.venv` : 仮想環境を`.venv`という新しいディレクトリに作成します +* `python`: `python` というプログラムを呼び出します +* `-m`: モジュールをスクリプトとして呼び出します。どのモジュールを呼び出すのか、この次に指定します +* `venv`: 通常Pythonに付随してインストールされる `venv`モジュールを使用します +* `.venv`: 仮想環境を`.venv`という新しいディレクトリに作成します /// @@ -111,13 +111,13 @@ $ uv venv /// -## 仮想環境の有効化 +## 仮想環境の有効化 { #activate-the-virtual-environment } 実行されるPythonコマンドやインストールされるパッケージが新しく作成した仮想環境を使用するよう、その仮想環境を有効化しましょう。 /// tip | 豆知識 -そのプロジェクトの作業で**新しいターミナルセッション**を開始する際には、**毎回**有効化が必要です。 +そのプロジェクトの作業で**新しいターミナルセッション**を開始する際には、**毎回**有効化してください。 /// @@ -161,13 +161,13 @@ $ source .venv/Scripts/activate /// tip | 豆知識 -**新しいパッケージ**を仮想環境にインストールするときには、再度**有効化**してください。 +**新しいパッケージ**を仮想環境にインストールするたびに、環境をもう一度**有効化**してください。 こうすることで、そのパッケージがインストールした**ターミナル(CLI)プログラム**を使用する場合に、仮想環境内のものが確実に使われ、グローバル環境にインストールされている別のもの(おそらく必要なものとは異なるバージョン)を誤って使用することを防ぎます。 /// -## 仮想環境が有効であることを確認する +## 仮想環境が有効であることを確認する { #check-the-virtual-environment-is-active } 仮想環境が有効である(前のコマンドが正常に機能した)ことを確認します。 @@ -197,7 +197,7 @@ $ which python
-``` console +```console $ Get-Command python C:\Users\user\code\awesome-project\.venv\Scripts\python @@ -209,7 +209,7 @@ C:\Users\user\code\awesome-project\.venv\Scripts\python //// -## `pip` をアップグレードする +## `pip` をアップグレードする { #upgrade-pip } /// tip | 豆知識 @@ -239,7 +239,27 @@ $ python -m pip install --upgrade pip
-## `.gitignore` を追加する +/// tip | 豆知識 + +ときどき、pip をアップグレードしようとすると **`No module named pip`** エラーが表示されることがあります。 + +その場合は、以下のコマンドで pip をインストールしてアップグレードしてください: + +
+ +```console +$ python -m ensurepip --upgrade + +---> 100% +``` + +
+ +このコマンドは、pip がまだインストールされていなければ pip をインストールし、また、インストールされる pip のバージョンが `ensurepip` で利用可能なもの以上に新しいことも保証します。 + +/// + +## `.gitignore` を追加する { #add-gitignore } **Git**を使用している場合(使用するべきでしょう)、 `.gitignore` ファイルを追加して、 `.venv` 内のあらゆるファイルをGitの管理対象から除外します。 @@ -265,9 +285,9 @@ $ echo "*" > .venv/.gitignore /// details | このコマンドの意味 -- `echo "*"` : ターミナルに `*` というテキストを「表示」しようとします。(次の部分によってその動作が少し変わります) -- `>` : `>` の左側のコマンドがターミナルに表示しようとする内容を、ターミナルには表示せず、 `>` の右側のファイルに書き込みます。 -- `.gitignore` : `*` を書き込むファイル名。 +* `echo "*"`: ターミナルに `*` というテキストを「表示」しようとします。(次の部分によってその動作が少し変わります) +* `>`: `>` の左側のコマンドがターミナルに表示しようとする内容を、ターミナルには表示せず、 `>` の右側のファイルに書き込みます。 +* `.gitignore`: `*` を書き込むファイル名。 ここで、Gitにおける `*` は「すべて」を意味するので、このコマンドによって `.venv` ディレクトリ内のすべてがGitに無視されるようになります。 @@ -279,7 +299,7 @@ $ echo "*" > .venv/.gitignore /// -## パッケージのインストール +## パッケージのインストール { #install-packages } 仮想環境を有効化した後、その中でパッケージをインストールできます。 @@ -291,7 +311,7 @@ $ echo "*" > .venv/.gitignore /// -### パッケージを直接インストールする +### パッケージを直接インストールする { #install-packages-directly } 急いでいて、プロジェクトのパッケージ要件を宣言するファイルを使いたくない場合、パッケージを直接インストールできます。 @@ -330,7 +350,7 @@ $ uv pip install "fastapi[standard]" //// -### `requirements.txt` からインストールする +### `requirements.txt` からインストールする { #install-from-requirements-txt } もし `requirements.txt` があるなら、パッケージのインストールに使用できます。 @@ -373,7 +393,7 @@ pydantic==2.8.0 /// -## プログラムを実行する +## プログラムを実行する { #run-your-program } 仮想環境を有効化した後、プログラムを実行できます。この際、仮想環境内のPythonと、そこにインストールしたパッケージが使用されます。 @@ -387,7 +407,7 @@ Hello World -## エディタの設定 +## エディタの設定 { #configure-your-editor } プロジェクトではおそらくエディタを使用するでしょう。コード補完やインラインエラーの表示ができるように、作成した仮想環境をエディタでも使えるよう設定してください。(多くの場合、自動検出されます) @@ -402,7 +422,7 @@ Hello World /// -## 仮想環境の無効化 +## 仮想環境の無効化 { #deactivate-the-virtual-environment } プロジェクトの作業が終了したら、その仮想環境を**無効化**できます。 @@ -416,9 +436,11 @@ $ deactivate これにより、 `python` コマンドを実行しても、そのプロジェクト用(のパッケージがインストールされた)仮想環境から `python` プログラムを呼び出そうとはしなくなります。 -## 作業準備完了 +## 作業準備完了 { #ready-to-work } + +これで、プロジェクトの作業を始める準備が整いました。 + -ここまでで、プロジェクトの作業を始める準備が整いました。 /// tip | 豆知識 @@ -428,9 +450,9 @@ $ deactivate /// -## なぜ仮想環境? +## なぜ仮想環境? { #why-virtual-environments } -FastAPIを使った作業をするには、 [Python](https://www.python.org/) のインストールが必要です。 +FastAPIを使った作業をするには、Python のインストールが必要です。 それから、FastAPIや、使用したいその他の**パッケージ**を**インストール**する必要があります。 @@ -438,7 +460,7 @@ FastAPIを使った作業をするには、 [Python](https://www.python.org/) ただし、`pip` を直接使用すると、パッケージは**グローバルなPython環境**(OS全体にインストールされたPython環境)にインストールされます。 -### 問題点 +### 問題点 { #the-problem } では、グローバルPython環境にパッケージをインストールすることの問題点は何でしょうか? @@ -521,7 +543,7 @@ Pythonのパッケージでは、**新しいバージョン**で**互換性を また、使用しているOS(Linux、Windows、macOS など)によっては、Pythonがすでにインストールされていることがあります。この場合、特定のバージョンのパッケージが**OSの動作に必要である**ことがあります。グローバル環境にパッケージをインストールすると、OSに付属するプログラムを**壊してしまう**可能性があります。 -## パッケージのインストール先 +## パッケージのインストール先 { #where-are-packages-installed } Pythonをインストールしたとき、ファイルを含んだいくつかのディレクトリが作成されます。 @@ -539,7 +561,7 @@ $ pip install "fastapi[standard]" -FastAPIのコードを含む圧縮ファイルが、通常は [PyPI](https://pypi.org/project/fastapi/) からダウンロードされます。 +FastAPIのコードを含む圧縮ファイルが、通常は PyPI からダウンロードされます。 また、FastAPIが依存する他のパッケージも**ダウンロード**されます。 @@ -547,7 +569,7 @@ FastAPIのコードを含む圧縮ファイルが、通常は [PyPI](https://pyp デフォルトでは、これらのファイルはPythonのインストール時に作成されるディレクトリ、つまり**グローバル環境**に配置されます。 -## 仮想環境とは +## 仮想環境とは { #what-are-virtual-environments } すべてのパッケージをグローバル環境に配置することによって生じる問題の解決策は、作業する**プロジェクトごとの仮想環境**を使用することです。 @@ -572,7 +594,7 @@ flowchart TB stone-project ~~~ azkaban-project ``` -## 仮想環境の有効化とは +## 仮想環境の有効化とは { #what-does-activating-a-virtual-environment-mean } 仮想環境を有効にしたとき、例えば次のコマンドを実行した場合を考えます: @@ -620,7 +642,7 @@ $ source .venv/Scripts/activate /// tip | 豆知識 -`PATH` 変数についての詳細は [環境変数](environment-variables.md#path環境変数){.internal-link target=_blank} を参照してください。 +`PATH` 変数についての詳細は [環境変数](environment-variables.md#path-environment-variable){.internal-link target=_blank} を参照してください。 /// @@ -701,7 +723,7 @@ C:\Users\user\code\awesome-project\.venv\Scripts\python 仮想環境を有効にして変更されることは他にもありますが、これが最も重要な変更のひとつです。 -## 仮想環境の確認 +## 仮想環境の確認 { #checking-a-virtual-environment } 仮想環境が有効かどうか、例えば次のように確認できます。: @@ -753,7 +775,7 @@ LinuxやmacOSでは `which` を、Windows PowerShellでは `Get-Command` を使 /// -## なぜ仮想環境を無効化するのか +## なぜ仮想環境を無効化するのか { #why-deactivate-a-virtual-environment } 例えば、`philosophers-stone` (賢者の石)というプロジェクトで作業をしていて、**その仮想環境を有効にし**、必要なパッケージをインストールしてその環境内で作業を進めているとします。 @@ -786,7 +808,7 @@ Traceback (most recent call last): -しかし、その仮想環境を無効化し、 `prisoner-of-azkaban` (アズカバンの囚人)のための新しい仮想環境を有効にすれば、 `python` を実行したときに `prisoner-of-azkaban` (アズカバンの囚人)の仮想環境の Python が使用されるようになります。 +しかし、その仮想環境を無効化し、 `prisoner-of-askaban` のための新しい仮想環境を有効にすれば、 `python` を実行したときに `prisoner-of-azkaban` (アズカバンの囚人)の仮想環境の Python が使用されるようになります。
@@ -807,7 +829,7 @@ I solemnly swear 🐺
-## 代替手段 +## 代替手段 { #alternatives } これは、あらゆる仕組みを**根本から**学ぶためのシンプルな入門ガイドです。 @@ -824,7 +846,7 @@ I solemnly swear 🐺 * パッケージとそのバージョンの、依存関係を含めた**厳密な**組み合わせを保持し、これによって、本番環境で、開発環境と全く同じようにプロジェクトを実行できる(これは**locking**と呼ばれます) * その他のさまざまな機能 -## まとめ +## まとめ { #conclusion } ここまで読みすべて理解したなら、世間の多くの開発者と比べて、仮想環境について**あなたはより多くのことを知っています**。🤓 diff --git a/docs/ja/llm-prompt.md b/docs/ja/llm-prompt.md index 18909cd595..de26167461 100644 --- a/docs/ja/llm-prompt.md +++ b/docs/ja/llm-prompt.md @@ -30,8 +30,7 @@ Use the following preferred translations when they apply in documentation prose: - request (HTTP): リクエスト - response (HTTP): レスポンス -- path operation: パスオペレーション -- path operation function: パスオペレーション関数 +- path operation: path operation (do not translate) ### `///` admonitions diff --git a/docs/ko/docs/_llm-test.md b/docs/ko/docs/_llm-test.md new file mode 100644 index 0000000000..1b828c663e --- /dev/null +++ b/docs/ko/docs/_llm-test.md @@ -0,0 +1,503 @@ +# LLM 테스트 파일 { #llm-test-file } + +이 문서는 문서를 번역하는 LLM이 `scripts/translate.py`의 `general_prompt`와 `docs/{language code}/llm-prompt.md`의 언어별 프롬프트를 이해하는지 테스트합니다. 언어별 프롬프트는 `general_prompt`에 추가됩니다. + +여기에 추가된 테스트는 언어별 프롬프트를 설계하는 모든 사람이 보게 됩니다. + +사용 방법은 다음과 같습니다: + +* 언어별 프롬프트 `docs/{language code}/llm-prompt.md`를 준비합니다. +* 이 문서를 원하는 대상 언어로 새로 번역합니다(예: `translate.py`의 `translate-page` 명령). 그러면 `docs/{language code}/docs/_llm-test.md` 아래에 번역이 생성됩니다. +* 번역에서 문제가 없는지 확인합니다. +* 필요하다면 언어별 프롬프트, 일반 프롬프트, 또는 영어 문서를 개선합니다. +* 그런 다음 번역에서 남아 있는 문제를 수동으로 수정해 좋은 번역이 되게 합니다. +* 좋은 번역을 둔 상태에서 다시 번역합니다. 이상적인 결과는 LLM이 더 이상 번역에 변경을 만들지 않는 것입니다. 이는 일반 프롬프트와 언어별 프롬프트가 가능한 한 최선이라는 뜻입니다(때때로 몇 가지 seemingly random 변경을 할 수 있는데, 그 이유는 LLM은 결정론적 알고리즘이 아니기 때문입니다). + +테스트: + +## 코드 스니펫 { #code-snippets } + +//// tab | 테스트 + +다음은 코드 스니펫입니다: `foo`. 그리고 이것은 또 다른 코드 스니펫입니다: `bar`. 그리고 또 하나: `baz quux`. + +//// + +//// tab | 정보 + +코드 스니펫의 내용은 그대로 두어야 합니다. + +`scripts/translate.py`의 일반 프롬프트에서 `### Content of code snippets` 섹션을 참고하세요. + +//// + +## 따옴표 { #quotes } + +//// tab | 테스트 + +어제 제 친구가 이렇게 썼습니다: "If you spell incorrectly correctly, you have spelled it incorrectly". 이에 저는 이렇게 답했습니다: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"". + +/// note | 참고 + +LLM은 아마 이것을 잘못 번역할 것입니다. 흥미로운 점은 재번역할 때 고정된 번역을 유지하는지 여부뿐입니다. + +/// + +//// + +//// tab | 정보 + +프롬프트 설계자는 중립 따옴표를 타이포그래피 따옴표로 변환할지 선택할 수 있습니다. 그대로 두어도 괜찮습니다. + +예를 들어 `docs/de/llm-prompt.md`의 `### Quotes` 섹션을 참고하세요. + +//// + +## 코드 스니펫의 따옴표 { #quotes-in-code-snippets } + +//// tab | 테스트 + +`pip install "foo[bar]"` + +코드 스니펫에서 문자열 리터럴의 예: `"this"`, `'that'`. + +코드 스니펫에서 문자열 리터럴의 어려운 예: `f"I like {'oranges' if orange else "apples"}"` + +하드코어: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"` + +//// + +//// tab | 정보 + +... 하지만 코드 스니펫 안의 따옴표는 그대로 유지되어야 합니다. + +//// + +## 코드 블록 { #code-blocks } + +//// tab | 테스트 + +Bash 코드 예시... + +```bash +# 우주에 인사말 출력 +echo "Hello universe" +``` + +...그리고 콘솔 코드 예시... + +```console +$ fastapi run main.py + FastAPI Starting server + Searching for package file structure +``` + +...그리고 또 다른 콘솔 코드 예시... + +```console +// "Code" 디렉터리 생성 +$ mkdir code +// 해당 디렉터리로 이동 +$ cd code +``` + +...그리고 Python 코드 예시... + +```Python +wont_work() # 이건 동작하지 않습니다 😱 +works(foo="bar") # 이건 동작합니다 🎉 +``` + +...이상입니다. + +//// + +//// tab | 정보 + +코드 블록의 코드는(주석을 제외하고) 수정하면 안 됩니다. + +`scripts/translate.py`의 일반 프롬프트에서 `### Content of code blocks` 섹션을 참고하세요. + +//// + +## 탭과 색상 박스 { #tabs-and-colored-boxes } + +//// tab | 테스트 + +/// info | 정보 +일부 텍스트 +/// + +/// note | 참고 +일부 텍스트 +/// + +/// note Technical details | 기술 세부사항 +일부 텍스트 +/// + +/// check | 확인 +일부 텍스트 +/// + +/// tip | 팁 +일부 텍스트 +/// + +/// warning | 경고 +일부 텍스트 +/// + +/// danger | 위험 +일부 텍스트 +/// + +//// + +//// tab | 정보 + +탭과 `Info`/`Note`/`Warning`/등의 블록은 제목 번역을 수직 막대(`|`) 뒤에 추가해야 합니다. + +`scripts/translate.py`의 일반 프롬프트에서 `### Special blocks`와 `### Tab blocks` 섹션을 참고하세요. + +//// + +## 웹 및 내부 링크 { #web-and-internal-links } + +//// tab | 테스트 + +링크 텍스트는 번역되어야 하고, 링크 주소는 변경되지 않아야 합니다: + +* [위의 제목으로 가는 링크](#code-snippets) +* [내부 링크](index.md#installation){.internal-link target=_blank} +* 외부 링크 +* 스타일로 가는 링크 +* 스크립트로 가는 링크 +* 이미지로 가는 링크 + +링크 텍스트는 번역되어야 하고, 링크 주소는 번역 페이지를 가리켜야 합니다: + +* FastAPI 링크 + +//// + +//// tab | 정보 + +링크는 번역되어야 하지만, 주소는 변경되지 않아야 합니다. 예외는 FastAPI 문서 페이지로 향하는 절대 링크이며, 이 경우 번역 페이지로 연결되어야 합니다. + +`scripts/translate.py`의 일반 프롬프트에서 `### Links` 섹션을 참고하세요. + +//// + +## HTML "abbr" 요소 { #html-abbr-elements } + +//// tab | 테스트 + +여기 HTML "abbr" 요소로 감싼 몇 가지가 있습니다(일부는 임의로 만든 것입니다): + +### abbr가 전체 문구를 제공 { #the-abbr-gives-a-full-phrase } + +* GTD +* lt +* XWT +* PSGI + +### abbr가 설명을 제공 { #the-abbr-gives-an-explanation } + +* cluster +* Deep Learning + +### abbr가 전체 문구와 설명을 제공 { #the-abbr-gives-a-full-phrase-and-an-explanation } + +* MDN +* I/O. + +//// + +//// tab | 정보 + +"abbr" 요소의 "title" 속성은 몇 가지 구체적인 지침에 따라 번역됩니다. + +번역에서는(영어 단어를 설명하기 위해) 자체 "abbr" 요소를 추가할 수 있으며, LLM은 이를 제거하면 안 됩니다. + +`scripts/translate.py`의 일반 프롬프트에서 `### HTML abbr elements` 섹션을 참고하세요. + +//// + +## 제목 { #headings } + +//// tab | 테스트 + +### 웹앱 개발하기 - 튜토리얼 { #develop-a-webapp-a-tutorial } + +안녕하세요. + +### 타입 힌트와 -애너테이션 { #type-hints-and-annotations } + +다시 안녕하세요. + +### super- 및 subclasses { #super-and-subclasses } + +다시 안녕하세요. + +//// + +//// tab | 정보 + +제목에 대한 유일한 강한 규칙은, LLM이 중괄호 안의 해시 부분을 변경하지 않아 링크가 깨지지 않게 하는 것입니다. + +`scripts/translate.py`의 일반 프롬프트에서 `### Headings` 섹션을 참고하세요. + +언어별 지침은 예를 들어 `docs/de/llm-prompt.md`의 `### Headings` 섹션을 참고하세요. + +//// + +## 문서에서 사용되는 용어 { #terms-used-in-the-docs } + +//// tab | 테스트 + +* 당신 +* 당신의 + +* 예: (e.g.) +* 등 (etc.) + +* `int`로서의 `foo` +* `str`로서의 `bar` +* `list`로서의 `baz` + +* 튜토리얼 - 사용자 가이드 +* 고급 사용자 가이드 +* SQLModel 문서 +* API 문서 +* 자동 문서 + +* Data Science +* Deep Learning +* Machine Learning +* Dependency Injection +* HTTP Basic authentication +* HTTP Digest +* ISO format +* JSON Schema 표준 +* JSON schema +* schema definition +* Password Flow +* Mobile + +* deprecated +* designed +* invalid +* on the fly +* standard +* default +* case-sensitive +* case-insensitive + +* 애플리케이션을 서빙하다 +* 페이지를 서빙하다 + +* 앱 +* 애플리케이션 + +* 요청 +* 응답 +* 오류 응답 + +* 경로 처리 +* 경로 처리 데코레이터 +* 경로 처리 함수 + +* body +* 요청 body +* 응답 body +* JSON body +* form body +* file body +* 함수 body + +* parameter +* body parameter +* path parameter +* query parameter +* cookie parameter +* header parameter +* form parameter +* function parameter + +* event +* startup event +* 서버 startup +* shutdown event +* lifespan event + +* handler +* event handler +* exception handler +* 처리하다 + +* model +* Pydantic model +* data model +* database model +* form model +* model object + +* class +* base class +* parent class +* subclass +* child class +* sibling class +* class method + +* header +* headers +* authorization header +* `Authorization` header +* forwarded header + +* dependency injection system +* dependency +* dependable +* dependant + +* I/O bound +* CPU bound +* concurrency +* parallelism +* multiprocessing + +* env var +* environment variable +* `PATH` +* `PATH` variable + +* authentication +* authentication provider +* authorization +* authorization form +* authorization provider +* 사용자가 인증한다 +* 시스템이 사용자를 인증한다 + +* CLI +* command line interface + +* server +* client + +* cloud provider +* cloud service + +* development +* development stages + +* dict +* dictionary +* enumeration +* enum +* enum member + +* encoder +* decoder +* encode하다 +* decode하다 + +* exception +* raise하다 + +* expression +* statement + +* frontend +* backend + +* GitHub discussion +* GitHub issue + +* performance +* performance optimization + +* return type +* return value + +* security +* security scheme + +* task +* background task +* task function + +* template +* template engine + +* type annotation +* type hint + +* server worker +* Uvicorn worker +* Gunicorn Worker +* worker process +* worker class +* workload + +* deployment +* deploy하다 + +* SDK +* software development kit + +* `APIRouter` +* `requirements.txt` +* Bearer Token +* breaking change +* bug +* button +* callable +* code +* commit +* context manager +* coroutine +* database session +* disk +* domain +* engine +* fake X +* HTTP GET method +* item +* library +* lifespan +* lock +* middleware +* mobile application +* module +* mounting +* network +* origin +* override +* payload +* processor +* property +* proxy +* pull request +* query +* RAM +* remote machine +* status code +* string +* tag +* web framework +* wildcard +* return하다 +* validate하다 + +//// + +//// tab | 정보 + +이것은 문서에서 보이는 (대부분) 기술 용어의 불완전하고 비규범적인 목록입니다. 프롬프트 설계자가 어떤 용어에 대해 LLM에 추가적인 도움이 필요한지 파악하는 데 유용할 수 있습니다. 예를 들어, 좋은 번역을 계속 덜 좋은 번역으로 되돌릴 때, 또는 언어에서 용어의 활용/변화를 처리하는 데 문제가 있을 때 도움이 됩니다. + +예를 들어 `docs/de/llm-prompt.md`의 `### List of English terms and their preferred German translations` 섹션을 참고하세요. + +//// diff --git a/docs/ko/docs/advanced/additional-responses.md b/docs/ko/docs/advanced/additional-responses.md new file mode 100644 index 0000000000..a6f51f5b93 --- /dev/null +++ b/docs/ko/docs/advanced/additional-responses.md @@ -0,0 +1,247 @@ +# OpenAPI에서 추가 응답 { #additional-responses-in-openapi } + +/// warning | 경고 + +이는 꽤 고급 주제입니다. + +**FastAPI**를 막 시작했다면, 이 내용이 필요 없을 수도 있습니다. + +/// + +추가 상태 코드, 미디어 타입, 설명 등을 포함한 추가 응답을 선언할 수 있습니다. + +이러한 추가 응답은 OpenAPI 스키마에 포함되므로 API 문서에도 표시됩니다. + +하지만 이러한 추가 응답의 경우, 상태 코드와 콘텐츠를 포함하여 `JSONResponse` 같은 `Response`를 직접 반환하도록 반드시 처리해야 합니다. + +## `model`을 사용한 추가 응답 { #additional-response-with-model } + +*경로 처리 데코레이터*에 `responses` 파라미터를 전달할 수 있습니다. + +이는 `dict`를 받습니다. 키는 각 응답의 상태 코드(예: `200`)이고, 값은 각 응답에 대한 정보를 담은 다른 `dict`입니다. + +각 응답 `dict`에는 `response_model`처럼 Pydantic 모델을 담는 `model` 키가 있을 수 있습니다. + +**FastAPI**는 그 모델을 사용해 JSON Schema를 생성하고, OpenAPI의 올바른 위치에 포함합니다. + +예를 들어, 상태 코드 `404`와 Pydantic 모델 `Message`를 사용하는 다른 응답을 선언하려면 다음과 같이 작성할 수 있습니다: + +{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *} + +/// note | 참고 + +`JSONResponse`를 직접 반환해야 한다는 점을 기억하세요. + +/// + +/// info | 정보 + +`model` 키는 OpenAPI의 일부가 아닙니다. + +**FastAPI**는 여기에서 Pydantic 모델을 가져와 JSON Schema를 생성하고 올바른 위치에 넣습니다. + +올바른 위치는 다음과 같습니다: + +* 값으로 또 다른 JSON 객체(`dict`)를 가지는 `content` 키 안에: + * 미디어 타입(예: `application/json`)을 키로 가지며, 값으로 또 다른 JSON 객체를 포함하고: + * `schema` 키가 있고, 그 값이 모델에서 생성된 JSON Schema입니다. 이것이 올바른 위치입니다. + * **FastAPI**는 이를 직접 포함하는 대신, OpenAPI의 다른 위치에 있는 전역 JSON Schemas를 참조하도록 여기에서 reference를 추가합니다. 이렇게 하면 다른 애플리케이션과 클라이언트가 그 JSON Schema를 직접 사용할 수 있고, 더 나은 코드 생성 도구 등을 제공할 수 있습니다. + +/// + +이 *경로 처리*에 대해 OpenAPI에 생성되는 응답은 다음과 같습니다: + +```JSON hl_lines="3-12" +{ + "responses": { + "404": { + "description": "Additional Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Item" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } +} +``` + +스키마는 OpenAPI 스키마 내부의 다른 위치를 참조합니다: + +```JSON hl_lines="4-16" +{ + "components": { + "schemas": { + "Message": { + "title": "Message", + "required": [ + "message" + ], + "type": "object", + "properties": { + "message": { + "title": "Message", + "type": "string" + } + } + }, + "Item": { + "title": "Item", + "required": [ + "id", + "value" + ], + "type": "object", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "value": { + "title": "Value", + "type": "string" + } + } + }, + "ValidationError": { + "title": "ValidationError", + "required": [ + "loc", + "msg", + "type" + ], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "type": "string" + } + }, + "msg": { + "title": "Message", + "type": "string" + }, + "type": { + "title": "Error Type", + "type": "string" + } + } + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + } + } + } + } + } + } +} +``` + +## 주요 응답에 대한 추가 미디어 타입 { #additional-media-types-for-the-main-response } + +같은 `responses` 파라미터를 사용해 동일한 주요 응답에 대해 다른 미디어 타입을 추가할 수도 있습니다. + +예를 들어, *경로 처리*가 JSON 객체(미디어 타입 `application/json`) 또는 PNG 이미지(미디어 타입 `image/png`)를 반환할 수 있다고 선언하기 위해 `image/png`라는 추가 미디어 타입을 추가할 수 있습니다: + +{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *} + +/// note | 참고 + +이미지는 `FileResponse`를 사용해 직접 반환해야 한다는 점에 유의하세요. + +/// + +/// info | 정보 + +`responses` 파라미터에서 다른 미디어 타입을 명시적으로 지정하지 않는 한, FastAPI는 응답이 주요 응답 클래스와 동일한 미디어 타입(기본값 `application/json`)을 가진다고 가정합니다. + +하지만 커스텀 응답 클래스를 지정하면서 미디어 타입을 `None`으로 설정했다면, FastAPI는 연결된 모델이 있는 모든 추가 응답에 대해 `application/json`을 사용합니다. + +/// + +## 정보 결합하기 { #combining-information } + +`response_model`, `status_code`, `responses` 파라미터를 포함해 여러 위치의 응답 정보를 결합할 수도 있습니다. + +기본 상태 코드 `200`(또는 필요하다면 커스텀 코드)을 사용하여 `response_model`을 선언하고, 그와 동일한 응답에 대한 추가 정보를 `responses`에서 OpenAPI 스키마에 직접 선언할 수 있습니다. + +**FastAPI**는 `responses`의 추가 정보를 유지하고, 모델의 JSON Schema와 결합합니다. + +예를 들어, Pydantic 모델을 사용하고 커스텀 `description`을 가진 상태 코드 `404` 응답을 선언할 수 있습니다. + +또한 `response_model`을 사용하는 상태 코드 `200` 응답을 선언하되, 커스텀 `example`을 포함할 수도 있습니다: + +{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *} + +이 모든 내용은 OpenAPI에 결합되어 포함되고, API 문서에 표시됩니다: + + + +## 미리 정의된 응답과 커스텀 응답 결합하기 { #combine-predefined-responses-and-custom-ones } + +여러 *경로 처리*에 적용되는 미리 정의된 응답이 필요할 수도 있지만, 각 *경로 처리*마다 필요한 커스텀 응답과 결합하고 싶을 수도 있습니다. + +그런 경우 Python의 `dict` “unpacking” 기법인 `**dict_to_unpack`을 사용할 수 있습니다: + +```Python +old_dict = { + "old key": "old value", + "second old key": "second old value", +} +new_dict = {**old_dict, "new key": "new value"} +``` + +여기서 `new_dict`는 `old_dict`의 모든 키-값 쌍에 더해 새 키-값 쌍까지 포함합니다: + +```Python +{ + "old key": "old value", + "second old key": "second old value", + "new key": "new value", +} +``` + +이 기법을 사용해 *경로 처리*에서 일부 미리 정의된 응답을 재사용하고, 추가 커스텀 응답과 결합할 수 있습니다. + +예를 들어: + +{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *} + +## OpenAPI 응답에 대한 추가 정보 { #more-information-about-openapi-responses } + +응답에 정확히 무엇을 포함할 수 있는지 보려면, OpenAPI 사양의 다음 섹션을 확인하세요: + +* OpenAPI Responses Object: `Response Object`를 포함합니다. +* OpenAPI Response Object: `responses` 파라미터 안의 각 응답에 이것의 어떤 항목이든 직접 포함할 수 있습니다. `description`, `headers`, `content`(여기에서 서로 다른 미디어 타입과 JSON Schema를 선언합니다), `links` 등을 포함할 수 있습니다. diff --git a/docs/ko/docs/advanced/behind-a-proxy.md b/docs/ko/docs/advanced/behind-a-proxy.md new file mode 100644 index 0000000000..92bddac51a --- /dev/null +++ b/docs/ko/docs/advanced/behind-a-proxy.md @@ -0,0 +1,466 @@ +# 프록시 뒤에서 실행하기 { #behind-a-proxy } + +많은 경우 FastAPI 앱 앞단에 Traefik이나 Nginx 같은 **프록시(proxy)**를 두고 사용합니다. + +이런 프록시는 HTTPS 인증서 처리 등 여러 작업을 담당할 수 있습니다. + +## 프록시 전달 헤더 { #proxy-forwarded-headers } + +애플리케이션 앞단의 **프록시**는 보통 **서버**로 요청을 보내기 전에, 해당 요청이 프록시에 의해 **전달(forwarded)**되었다는 것을 서버가 알 수 있도록 몇몇 헤더를 동적으로 설정합니다. 이를 통해 서버는 도메인을 포함한 원래의 (공개) URL, HTTPS 사용 여부 등 정보를 알 수 있습니다. + +**서버** 프로그램(예: **FastAPI CLI**를 통해 실행되는 **Uvicorn**)은 이런 헤더를 해석할 수 있고, 그 정보를 애플리케이션으로 전달할 수 있습니다. + +하지만 보안상, 서버는 자신이 신뢰할 수 있는 프록시 뒤에 있다는 것을 모르면 해당 헤더를 해석하지 않습니다. + +/// note | 기술 세부사항 + +프록시 헤더는 다음과 같습니다: + +* X-Forwarded-For +* X-Forwarded-Proto +* X-Forwarded-Host + +/// + +### 프록시 전달 헤더 활성화하기 { #enable-proxy-forwarded-headers } + +FastAPI CLI를 *CLI 옵션* `--forwarded-allow-ips`로 실행하고, 전달 헤더를 읽을 수 있도록 신뢰할 IP 주소들을 넘길 수 있습니다. + +`--forwarded-allow-ips="*"`로 설정하면 들어오는 모든 IP를 신뢰합니다. + +**서버**가 신뢰할 수 있는 **프록시** 뒤에 있고 프록시만 서버에 접근한다면, 이는 해당 **프록시**의 IP가 무엇이든 간에 받아들이게 됩니다. + +
+ +```console +$ fastapi run --forwarded-allow-ips="*" + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +### HTTPS에서 리디렉션 { #redirects-with-https } + +예를 들어, *경로 처리* `/items/`를 정의했다고 해봅시다: + +{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *} + +클라이언트가 `/items`로 접근하면, 기본적으로 `/items/`로 리디렉션됩니다. + +하지만 *CLI 옵션* `--forwarded-allow-ips`를 설정하기 전에는 `http://localhost:8000/items/`로 리디렉션될 수 있습니다. + +그런데 애플리케이션이 `https://mysuperapp.com`에 호스팅되어 있고, 리디렉션도 `https://mysuperapp.com/items/`로 되어야 할 수 있습니다. + +이때 `--proxy-headers`를 설정하면 FastAPI가 올바른 위치로 리디렉션할 수 있습니다. 😎 + +``` +https://mysuperapp.com/items/ +``` + +/// tip | 팁 + +HTTPS에 대해 더 알아보려면 가이드 [HTTPS에 대하여](../deployment/https.md){.internal-link target=_blank}를 확인하세요. + +/// + +### 프록시 전달 헤더가 동작하는 방식 { #how-proxy-forwarded-headers-work } + +다음은 **프록시**가 클라이언트와 **애플리케이션 서버** 사이에서 전달 헤더를 추가하는 과정을 시각적으로 나타낸 것입니다: + +```mermaid +sequenceDiagram + participant Client + participant Proxy as Proxy/Load Balancer + participant Server as FastAPI Server + + Client->>Proxy: HTTPS Request
Host: mysuperapp.com
Path: /items + + Note over Proxy: Proxy adds forwarded headers + + Proxy->>Server: HTTP Request
X-Forwarded-For: [client IP]
X-Forwarded-Proto: https
X-Forwarded-Host: mysuperapp.com
Path: /items + + Note over Server: Server interprets headers
(if --forwarded-allow-ips is set) + + Server->>Proxy: HTTP Response
with correct HTTPS URLs + + Proxy->>Client: HTTPS Response +``` + +**프록시**는 원래의 클라이언트 요청을 가로채고, **애플리케이션 서버**로 요청을 전달하기 전에 특수한 *forwarded* 헤더(`X-Forwarded-*`)를 추가합니다. + +이 헤더들은 그렇지 않으면 사라질 수 있는 원래 요청의 정보를 보존합니다: + +* **X-Forwarded-For**: 원래 클라이언트의 IP 주소 +* **X-Forwarded-Proto**: 원래 프로토콜(`https`) +* **X-Forwarded-Host**: 원래 호스트(`mysuperapp.com`) + +**FastAPI CLI**를 `--forwarded-allow-ips`로 설정하면, 이 헤더를 신뢰하고 사용합니다. 예를 들어 리디렉션에서 올바른 URL을 생성하는 데 사용됩니다. + +## 제거된 경로 접두사를 가진 프록시 { #proxy-with-a-stripped-path-prefix } + +애플리케이션에 경로 접두사(prefix)를 추가하는 프록시를 둘 수도 있습니다. + +이런 경우 `root_path`를 사용해 애플리케이션을 구성할 수 있습니다. + +`root_path`는 (FastAPI가 Starlette를 통해 기반으로 하는) ASGI 사양에서 제공하는 메커니즘입니다. + +`root_path`는 이러한 특정 사례를 처리하는 데 사용됩니다. + +또한 서브 애플리케이션을 마운트할 때 내부적으로도 사용됩니다. + +경로 접두사가 제거(stripped)되는 프록시가 있다는 것은, 코드에서는 `/app`에 경로를 선언하지만, 위에 한 겹(프록시)을 추가해 **FastAPI** 애플리케이션을 `/api/v1` 같은 경로 아래에 두는 것을 의미합니다. + +이 경우 원래 경로 `/app`은 실제로 `/api/v1/app`에서 서비스됩니다. + +코드는 모두 `/app`만 있다고 가정하고 작성되어 있는데도 말입니다. + +{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *} + +그리고 프록시는 요청을 앱 서버(아마 FastAPI CLI를 통해 실행되는 Uvicorn)로 전달하기 전에, 동적으로 **경로 접두사**를 **"제거"**합니다. 그래서 애플리케이션은 여전히 `/app`에서 서비스된다고 믿게 되고, 코드 전체를 `/api/v1` 접두사를 포함하도록 수정할 필요가 없어집니다. + +여기까지는 보통 정상적으로 동작합니다. + +하지만 통합 문서 UI(프론트엔드)를 열면, OpenAPI 스키마를 `/api/v1/openapi.json`이 아니라 `/openapi.json`에서 가져오려고 합니다. + +그래서 브라우저에서 실행되는 프론트엔드는 `/openapi.json`에 접근하려고 시도하지만 OpenAPI 스키마를 얻지 못합니다. + +앱에 대해 `/api/v1` 경로 접두사를 가진 프록시가 있으므로, 프론트엔드는 `/api/v1/openapi.json`에서 OpenAPI 스키마를 가져와야 합니다. + +```mermaid +graph LR + +browser("Browser") +proxy["Proxy on http://0.0.0.0:9999/api/v1/app"] +server["Server on http://127.0.0.1:8000/app"] + +browser --> proxy +proxy --> server +``` + +/// tip | 팁 + +IP `0.0.0.0`은 보통 해당 머신/서버에서 사용 가능한 모든 IP에서 프로그램이 리슨한다는 의미로 사용됩니다. + +/// + +문서 UI는 또한 OpenAPI 스키마에서 이 API `server`가 `/api/v1`(프록시 뒤) 위치에 있다고 선언해야 합니다. 예: + +```JSON hl_lines="4-8" +{ + "openapi": "3.1.0", + // More stuff here + "servers": [ + { + "url": "/api/v1" + } + ], + "paths": { + // More stuff here + } +} +``` + +이 예시에서 "Proxy"는 **Traefik** 같은 것이고, 서버는 **Uvicorn**으로 실행되는 FastAPI CLI처럼, FastAPI 애플리케이션을 실행하는 구성일 수 있습니다. + +### `root_path` 제공하기 { #providing-the-root-path } + +이를 달성하려면 다음처럼 커맨드 라인 옵션 `--root-path`를 사용할 수 있습니다: + +
+ +```console +$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +Hypercorn을 사용한다면, Hypercorn에도 `--root-path` 옵션이 있습니다. + +/// note | 기술 세부사항 + +ASGI 사양은 이 사용 사례를 위해 `root_path`를 정의합니다. + +그리고 커맨드 라인 옵션 `--root-path`가 그 `root_path`를 제공합니다. + +/// + +### 현재 `root_path` 확인하기 { #checking-the-current-root-path } + +요청마다 애플리케이션에서 사용 중인 현재 `root_path`를 얻을 수 있는데, 이는 `scope` 딕셔너리(ASGI 사양의 일부)에 포함되어 있습니다. + +여기서는 데모 목적을 위해 메시지에 포함하고 있습니다. + +{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *} + +그 다음 Uvicorn을 다음과 같이 시작하면: + +
+ +```console +$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +응답은 다음과 비슷할 것입니다: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +### FastAPI 앱에서 `root_path` 설정하기 { #setting-the-root-path-in-the-fastapi-app } + +또는 `--root-path` 같은 커맨드 라인 옵션(또는 동등한 방법)을 제공할 수 없는 경우, FastAPI 앱을 생성할 때 `root_path` 파라미터를 설정할 수 있습니다: + +{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *} + +`FastAPI`에 `root_path`를 전달하는 것은 Uvicorn이나 Hypercorn에 커맨드 라인 옵션 `--root-path`를 전달하는 것과 동일합니다. + +### `root_path`에 대하여 { #about-root-path } + +서버(Uvicorn)는 그 `root_path`를 앱에 전달하는 것 외에는 다른 용도로 사용하지 않는다는 점을 기억하세요. + +하지만 브라우저로 http://127.0.0.1:8000/app에 접속하면 정상 응답을 볼 수 있습니다: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +따라서 `http://127.0.0.1:8000/api/v1/app`로 접근될 것이라고 기대하지는 않습니다. + +Uvicorn은 프록시가 `http://127.0.0.1:8000/app`에서 Uvicorn에 접근할 것을 기대하고, 그 위에 `/api/v1` 접두사를 추가하는 것은 프록시의 책임입니다. + +## 제거된 경로 접두사를 가진 프록시에 대하여 { #about-proxies-with-a-stripped-path-prefix } + +경로 접두사가 제거되는 프록시는 구성 방법 중 하나일 뿐이라는 점을 기억하세요. + +많은 경우 기본값은 프록시가 경로 접두사를 제거하지 않는 방식일 것입니다. + +그런 경우(경로 접두사를 제거하지 않는 경우) 프록시는 `https://myawesomeapp.com` 같은 곳에서 리슨하고, 브라우저가 `https://myawesomeapp.com/api/v1/app`로 접근하면, 서버(예: Uvicorn)가 `http://127.0.0.1:8000`에서 리슨하고 있을 때 프록시(경로 접두사를 제거하지 않는)는 동일한 경로로 Uvicorn에 접근합니다: `http://127.0.0.1:8000/api/v1/app`. + +## Traefik으로 로컬 테스트하기 { #testing-locally-with-traefik } + +Traefik을 사용하면, 경로 접두사가 제거되는 구성을 로컬에서 쉽게 실험할 수 있습니다. + +Traefik 다운로드는 단일 바이너리이며, 압축 파일을 풀고 터미널에서 바로 실행할 수 있습니다. + +그 다음 다음 내용을 가진 `traefik.toml` 파일을 생성하세요: + +```TOML hl_lines="3" +[entryPoints] + [entryPoints.http] + address = ":9999" + +[providers] + [providers.file] + filename = "routes.toml" +``` + +이는 Traefik이 9999 포트에서 리슨하고, 다른 파일 `routes.toml`을 사용하도록 지시합니다. + +/// tip | 팁 + +표준 HTTP 포트 80 대신 9999 포트를 사용해서, 관리자(`sudo`) 권한으로 실행하지 않아도 되게 했습니다. + +/// + +이제 다른 파일 `routes.toml`을 생성하세요: + +```TOML hl_lines="5 12 20" +[http] + [http.middlewares] + + [http.middlewares.api-stripprefix.stripPrefix] + prefixes = ["/api/v1"] + + [http.routers] + + [http.routers.app-http] + entryPoints = ["http"] + service = "app" + rule = "PathPrefix(`/api/v1`)" + middlewares = ["api-stripprefix"] + + [http.services] + + [http.services.app] + [http.services.app.loadBalancer] + [[http.services.app.loadBalancer.servers]] + url = "http://127.0.0.1:8000" +``` + +이 파일은 Traefik이 경로 접두사 `/api/v1`을 사용하도록 설정합니다. + +그리고 Traefik은 요청을 `http://127.0.0.1:8000`에서 실행 중인 Uvicorn으로 전달합니다. + +이제 Traefik을 시작하세요: + +
+ +```console +$ ./traefik --configFile=traefik.toml + +INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml +``` + +
+ +그리고 `--root-path` 옵션을 사용해 앱을 시작하세요: + +
+ +```console +$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +### 응답 확인하기 { #check-the-responses } + +이제 Uvicorn의 포트로 된 URL인 http://127.0.0.1:8000/app로 접속하면 정상 응답을 볼 수 있습니다: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +/// tip | 팁 + +`http://127.0.0.1:8000/app`로 접근했는데도 `/api/v1`의 `root_path`가 표시되는 것에 주의하세요. 이는 옵션 `--root-path`에서 가져온 값입니다. + +/// + +이제 Traefik의 포트가 포함되고 경로 접두사가 포함된 URL http://127.0.0.1:9999/api/v1/app을 여세요. + +동일한 응답을 얻습니다: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +하지만 이번에는 프록시가 제공한 접두사 경로 `/api/v1`이 포함된 URL에서의 응답입니다. + +물론 여기서의 아이디어는 모두가 프록시를 통해 앱에 접근한다는 것이므로, `/api/v1` 경로 접두사가 있는 버전이 "올바른" 접근입니다. + +그리고 경로 접두사가 없는 버전(`http://127.0.0.1:8000/app`)은 Uvicorn이 직접 제공하는 것이며, 오직 _프록시_(Traefik)가 접근하기 위한 용도입니다. + +이는 프록시(Traefik)가 경로 접두사를 어떻게 사용하는지, 그리고 서버(Uvicorn)가 옵션 `--root-path`로부터의 `root_path`를 어떻게 사용하는지를 보여줍니다. + +### 문서 UI 확인하기 { #check-the-docs-ui } + +하지만 재미있는 부분은 여기입니다. ✨ + +앱에 접근하는 "공식" 방법은 우리가 정의한 경로 접두사를 가진 프록시를 통해서입니다. 따라서 기대하는 대로, URL에 경로 접두사가 없는 상태에서 Uvicorn이 직접 제공하는 docs UI를 시도하면, 프록시를 통해 접근된다고 가정하고 있기 때문에 동작하지 않습니다. + +http://127.0.0.1:8000/docs에서 확인할 수 있습니다: + + + +하지만 프록시(포트 `9999`)를 사용해 "공식" URL인 `/api/v1/docs`에서 docs UI에 접근하면, 올바르게 동작합니다! 🎉 + +http://127.0.0.1:9999/api/v1/docs에서 확인할 수 있습니다: + + + +원하던 그대로입니다. ✔️ + +이는 FastAPI가 이 `root_path`를 사용해, OpenAPI에서 기본 `server`를 `root_path`가 제공한 URL로 생성하기 때문입니다. + +## 추가 서버 { #additional-servers } + +/// warning | 경고 + +이는 더 고급 사용 사례입니다. 건너뛰어도 괜찮습니다. + +/// + +기본적으로 **FastAPI**는 OpenAPI 스키마에서 `root_path`의 URL로 `server`를 생성합니다. + +하지만 예를 들어 동일한 docs UI가 스테이징과 프로덕션 환경 모두와 상호작용하도록 하려면, 다른 대안 `servers`를 제공할 수도 있습니다. + +사용자 정의 `servers` 리스트를 전달했고 `root_path`(API가 프록시 뒤에 있기 때문)가 있다면, **FastAPI**는 리스트의 맨 앞에 이 `root_path`를 가진 "server"를 삽입합니다. + +예: + +{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *} + +다음과 같은 OpenAPI 스키마를 생성합니다: + +```JSON hl_lines="5-7" +{ + "openapi": "3.1.0", + // More stuff here + "servers": [ + { + "url": "/api/v1" + }, + { + "url": "https://stag.example.com", + "description": "Staging environment" + }, + { + "url": "https://prod.example.com", + "description": "Production environment" + } + ], + "paths": { + // More stuff here + } +} +``` + +/// tip | 팁 + +`root_path`에서 가져온 값인 `/api/v1`의 `url` 값을 가진, 자동 생성된 server에 주목하세요. + +/// + +http://127.0.0.1:9999/api/v1/docs의 docs UI에서는 다음처럼 보입니다: + + + +/// tip | 팁 + +docs UI는 선택한 server와 상호작용합니다. + +/// + +/// note | 기술 세부사항 + +OpenAPI 사양에서 `servers` 속성은 선택 사항입니다. + +`servers` 파라미터를 지정하지 않고 `root_path`가 `/`와 같다면, 생성된 OpenAPI 스키마의 `servers` 속성은 기본적으로 완전히 생략되며, 이는 `url` 값이 `/`인 단일 server와 동등합니다. + +/// + +### `root_path`에서 자동 server 비활성화하기 { #disable-automatic-server-from-root-path } + +**FastAPI**가 `root_path`를 사용한 자동 server를 포함하지 않게 하려면, `root_path_in_servers=False` 파라미터를 사용할 수 있습니다: + +{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *} + +그러면 OpenAPI 스키마에 포함되지 않습니다. + +## 서브 애플리케이션 마운트하기 { #mounting-a-sub-application } + +프록시에서 `root_path`를 사용하면서도, [서브 애플리케이션 - 마운트](sub-applications.md){.internal-link target=_blank}에 설명된 것처럼 서브 애플리케이션을 마운트해야 한다면, 기대하는 대로 일반적으로 수행할 수 있습니다. + +FastAPI가 내부적으로 `root_path`를 똑똑하게 사용하므로, 그냥 동작합니다. ✨ diff --git a/docs/ko/docs/advanced/dataclasses.md b/docs/ko/docs/advanced/dataclasses.md new file mode 100644 index 0000000000..92ad5545b3 --- /dev/null +++ b/docs/ko/docs/advanced/dataclasses.md @@ -0,0 +1,95 @@ +# Dataclasses 사용하기 { #using-dataclasses } + +FastAPI는 **Pydantic** 위에 구축되어 있으며, 지금까지는 Pydantic 모델을 사용해 요청과 응답을 선언하는 방법을 보여드렸습니다. + +하지만 FastAPI는 `dataclasses`도 같은 방식으로 사용하는 것을 지원합니다: + +{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *} + +이는 **Pydantic** 덕분에 여전히 지원되는데, Pydantic이 `dataclasses`에 대한 내부 지원을 제공하기 때문입니다. + +따라서 위 코드처럼 Pydantic을 명시적으로 사용하지 않더라도, FastAPI는 Pydantic을 사용해 표준 dataclasses를 Pydantic의 dataclasses 변형으로 변환합니다. + +그리고 물론 다음과 같은 기능도 동일하게 지원합니다: + +* 데이터 검증 +* 데이터 직렬화 +* 데이터 문서화 등 + +이는 Pydantic 모델을 사용할 때와 같은 방식으로 동작합니다. 그리고 실제로도 내부적으로는 Pydantic을 사용해 같은 방식으로 구현됩니다. + +/// info | 정보 + +dataclasses는 Pydantic 모델이 할 수 있는 모든 것을 할 수는 없다는 점을 기억하세요. + +그래서 여전히 Pydantic 모델을 사용해야 할 수도 있습니다. + +하지만 이미 여러 dataclasses를 가지고 있다면, 이것은 FastAPI로 웹 API를 구동하는 데 그것들을 활용할 수 있는 좋은 방법입니다. 🤓 + +/// + +## `response_model`에서 Dataclasses 사용하기 { #dataclasses-in-response-model } + +`response_model` 매개변수에서도 `dataclasses`를 사용할 수 있습니다: + +{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *} + +dataclass는 자동으로 Pydantic dataclass로 변환됩니다. + +이렇게 하면 해당 스키마가 API docs 사용자 인터페이스에 표시됩니다: + + + +## 중첩 데이터 구조에서 Dataclasses 사용하기 { #dataclasses-in-nested-data-structures } + +`dataclasses`를 다른 타입 애너테이션과 조합해 중첩 데이터 구조를 만들 수도 있습니다. + +일부 경우에는 Pydantic 버전의 `dataclasses`를 사용해야 할 수도 있습니다. 예를 들어 자동 생성된 API 문서에서 오류가 발생하는 경우입니다. + +그런 경우 표준 `dataclasses`를 드롭인 대체재인 `pydantic.dataclasses`로 간단히 바꾸면 됩니다: + +{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *} + +1. 표준 `dataclasses`에서 `field`를 계속 임포트합니다. + +2. `pydantic.dataclasses`는 `dataclasses`의 드롭인 대체재입니다. + +3. `Author` dataclass에는 `Item` dataclasses의 리스트가 포함됩니다. + +4. `Author` dataclass가 `response_model` 매개변수로 사용됩니다. + +5. 요청 본문으로 dataclasses와 함께 다른 표준 타입 애너테이션을 사용할 수 있습니다. + + 이 경우에는 `Item` dataclasses의 리스트입니다. + +6. 여기서는 dataclasses 리스트인 `items`를 포함하는 딕셔너리를 반환합니다. + + FastAPI는 여전히 데이터를 JSON으로 serializing할 수 있습니다. + +7. 여기서 `response_model`은 `Author` dataclasses 리스트에 대한 타입 애너테이션을 사용합니다. + + 다시 말해, `dataclasses`를 표준 타입 애너테이션과 조합할 수 있습니다. + +8. 이 *경로 처리 함수*는 `async def` 대신 일반 `def`를 사용하고 있다는 점에 주목하세요. + + 언제나처럼 FastAPI에서는 필요에 따라 `def`와 `async def`를 조합해 사용할 수 있습니다. + + 어떤 것을 언제 사용해야 하는지 다시 확인하고 싶다면, [`async`와 `await`](../async.md#in-a-hurry){.internal-link target=_blank} 문서의 _"급하신가요?"_ 섹션을 확인하세요. + +9. 이 *경로 처리 함수*는 dataclasses를(물론 반환할 수도 있지만) 반환하지 않고, 내부 데이터를 담은 딕셔너리들의 리스트를 반환합니다. + + FastAPI는 `response_model` 매개변수(dataclasses 포함)를 사용해 응답을 변환합니다. + +`dataclasses`는 다른 타입 애너테이션과 매우 다양한 조합으로 결합해 복잡한 데이터 구조를 구성할 수 있습니다. + +더 구체적인 내용은 위 코드 내 애너테이션 팁을 확인하세요. + +## 더 알아보기 { #learn-more } + +`dataclasses`를 다른 Pydantic 모델과 조합하거나, 이를 상속하거나, 여러분의 모델에 포함하는 등의 작업도 할 수 있습니다. + +자세한 내용은 dataclasses에 관한 Pydantic 문서를 참고하세요. + +## 버전 { #version } + +이 기능은 FastAPI `0.67.0` 버전부터 사용할 수 있습니다. 🔖 diff --git a/docs/ko/docs/advanced/generate-clients.md b/docs/ko/docs/advanced/generate-clients.md new file mode 100644 index 0000000000..1def3efe12 --- /dev/null +++ b/docs/ko/docs/advanced/generate-clients.md @@ -0,0 +1,208 @@ +# SDK 생성하기 { #generating-sdks } + +**FastAPI**는 **OpenAPI** 사양을 기반으로 하므로, FastAPI의 API는 많은 도구가 이해할 수 있는 표준 형식으로 설명할 수 있습니다. + +덕분에 여러 언어용 클라이언트 라이브러리(**SDKs**), 최신 **문서**, 그리고 코드와 동기화된 **테스트** 또는 **자동화 워크플로**를 쉽게 생성할 수 있습니다. + +이 가이드에서는 FastAPI 백엔드용 **TypeScript SDK**를 생성하는 방법을 배웁니다. + +## 오픈 소스 SDK 생성기 { #open-source-sdk-generators } + +다양하게 활용할 수 있는 옵션으로 OpenAPI Generator가 있으며, **다양한 프로그래밍 언어**를 지원하고 OpenAPI 사양으로부터 SDK를 생성할 수 있습니다. + +**TypeScript 클라이언트**의 경우 Hey API는 TypeScript 생태계에 최적화된 경험을 제공하는 목적에 맞게 설계된 솔루션입니다. + +더 많은 SDK 생성기는 OpenAPI.Tools에서 확인할 수 있습니다. + +/// tip | 팁 + +FastAPI는 **OpenAPI 3.1** 사양을 자동으로 생성하므로, 사용하는 도구는 이 버전을 지원해야 합니다. + +/// + +## FastAPI 스폰서의 SDK 생성기 { #sdk-generators-from-fastapi-sponsors } + +이 섹션에서는 FastAPI를 후원하는 회사들이 제공하는 **벤처 투자 기반** 및 **기업 지원** 솔루션을 소개합니다. 이 제품들은 고품질로 생성된 SDK에 더해 **추가 기능**과 **통합**을 제공합니다. + +✨ [**FastAPI 후원하기**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨를 통해, 이 회사들은 프레임워크와 그 **생태계**가 건강하고 **지속 가능**하게 유지되도록 돕습니다. + +또한 이들의 후원은 FastAPI **커뮤니티**(여러분)에 대한 강한 헌신을 보여주며, **좋은 서비스**를 제공하는 것뿐 아니라, 견고하고 활발한 프레임워크인 FastAPI를 지원하는 데에도 관심이 있음을 나타냅니다. 🙇 + +예를 들어 다음을 사용해 볼 수 있습니다: + +* Speakeasy +* Stainless +* liblab + +이 중 일부는 오픈 소스이거나 무료 티어를 제공하므로, 비용 부담 없이 사용해 볼 수 있습니다. 다른 상용 SDK 생성기도 있으며 온라인에서 찾을 수 있습니다. 🤓 + +## TypeScript SDK 만들기 { #create-a-typescript-sdk } + +간단한 FastAPI 애플리케이션으로 시작해 보겠습니다: + +{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *} + +*path operation*에서 요청 페이로드와 응답 페이로드에 사용하는 모델을 `Item`, `ResponseMessage` 모델로 정의하고 있다는 점에 주목하세요. + +### API 문서 { #api-docs } + +`/docs`로 이동하면, 요청으로 보낼 데이터와 응답으로 받을 데이터에 대한 **스키마(schemas)**가 있는 것을 볼 수 있습니다: + + + +이 스키마는 앱에서 모델로 선언되었기 때문에 볼 수 있습니다. + +그 정보는 앱의 **OpenAPI 스키마**에서 사용할 수 있고, 이후 API 문서에 표시됩니다. + +OpenAPI에 포함된 모델의 동일한 정보가 **클라이언트 코드 생성**에 사용될 수 있습니다. + +### Hey API { #hey-api } + +모델이 포함된 FastAPI 앱이 준비되면, Hey API를 사용해 TypeScript 클라이언트를 생성할 수 있습니다. 가장 빠른 방법은 npx를 사용하는 것입니다. + +```sh +npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client +``` + +이 명령은 `./src/client`에 TypeScript SDK를 생성합니다. + +`@hey-api/openapi-ts` 설치 방법생성된 결과물은 해당 웹사이트에서 확인할 수 있습니다. + +### SDK 사용하기 { #using-the-sdk } + +이제 클라이언트 코드를 import해서 사용할 수 있습니다. 아래처럼 사용할 수 있으며, 메서드에 대한 자동 완성이 제공되는 것을 확인할 수 있습니다: + + + +보낼 페이로드에 대해서도 자동 완성이 제공됩니다: + + + +/// tip | 팁 + +`name`과 `price`에 대한 자동 완성은 FastAPI 애플리케이션에서 `Item` 모델에 정의된 내용입니다. + +/// + +전송하는 데이터에 대해 인라인 오류도 표시됩니다: + + + +응답 객체도 자동 완성을 제공합니다: + + + +## 태그가 있는 FastAPI 앱 { #fastapi-app-with-tags } + +대부분의 경우 FastAPI 앱은 더 커지고, 서로 다른 *path operations* 그룹을 분리하기 위해 태그를 사용하게 될 가능성이 큽니다. + +예를 들어 **items** 섹션과 **users** 섹션이 있고, 이를 태그로 분리할 수 있습니다: + +{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *} + +### 태그로 TypeScript 클라이언트 생성하기 { #generate-a-typescript-client-with-tags } + +태그를 사용하는 FastAPI 앱에 대해 클라이언트를 생성하면, 일반적으로 생성된 클라이언트 코드도 태그를 기준으로 분리됩니다. + +이렇게 하면 클라이언트 코드에서 항목들이 올바르게 정렬되고 그룹화됩니다: + + + +이 경우 다음이 있습니다: + +* `ItemsService` +* `UsersService` + +### 클라이언트 메서드 이름 { #client-method-names } + +현재 `createItemItemsPost` 같은 생성된 메서드 이름은 그다지 깔끔하지 않습니다: + +```TypeScript +ItemsService.createItemItemsPost({name: "Plumbus", price: 5}) +``` + +...이는 클라이언트 생성기가 각 *path operation*에 대해 OpenAPI 내부의 **operation ID**를 사용하기 때문입니다. + +OpenAPI는 모든 *path operations* 전체에서 operation ID가 각각 유일해야 한다고 요구합니다. 그래서 FastAPI는 operation ID가 유일하도록 **함수 이름**, **경로**, **HTTP method/operation**을 조합해 operation ID를 생성합니다. + +하지만 다음에서 이를 개선하는 방법을 보여드리겠습니다. 🤓 + +## 커스텀 Operation ID와 더 나은 메서드 이름 { #custom-operation-ids-and-better-method-names } + +클라이언트에서 **더 단순한 메서드 이름**을 갖도록, operation ID가 **생성되는 방식**을 **수정**할 수 있습니다. + +이 경우 operation ID가 다른 방식으로도 **유일**하도록 보장해야 합니다. + +예를 들어 각 *path operation*이 태그를 갖도록 한 다음, **태그**와 *path operation* **이름**(함수 이름)을 기반으로 operation ID를 생성할 수 있습니다. + +### 유일 ID 생성 함수 커스터마이징 { #custom-generate-unique-id-function } + +FastAPI는 각 *path operation*에 대해 **유일 ID**를 사용하며, 이는 **operation ID** 및 요청/응답에 필요한 커스텀 모델 이름에도 사용됩니다. + +이 함수를 커스터마이징할 수 있습니다. 이 함수는 `APIRoute`를 받아 문자열을 반환합니다. + +예를 들어 아래에서는 첫 번째 태그(대부분 태그는 하나만 있을 것입니다)와 *path operation* 이름(함수 이름)을 사용합니다. + +그 다음 이 커스텀 함수를 `generate_unique_id_function` 매개변수로 **FastAPI**에 전달할 수 있습니다: + +{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *} + +### 커스텀 Operation ID로 TypeScript 클라이언트 생성하기 { #generate-a-typescript-client-with-custom-operation-ids } + +이제 클라이언트를 다시 생성하면, 개선된 메서드 이름을 확인할 수 있습니다: + + + +보시다시피, 이제 메서드 이름은 태그 다음에 함수 이름이 오며, URL 경로와 HTTP operation의 정보는 포함하지 않습니다. + +### 클라이언트 생성기를 위한 OpenAPI 사양 전처리 { #preprocess-the-openapi-specification-for-the-client-generator } + +생성된 코드에는 여전히 일부 **중복 정보**가 있습니다. + +`ItemsService`(태그에서 가져옴)에 이미 **items**가 포함되어 있어 이 메서드가 items와 관련되어 있음을 알 수 있지만, 메서드 이름에도 태그 이름이 접두사로 붙어 있습니다. 😕 + +OpenAPI 전반에서는 operation ID가 **유일**하다는 것을 보장하기 위해 이 방식을 유지하고 싶을 수 있습니다. + +하지만 생성된 클라이언트에서는, 클라이언트를 생성하기 직전에 OpenAPI operation ID를 **수정**해서 메서드 이름을 더 보기 좋고 **깔끔하게** 만들 수 있습니다. + +OpenAPI JSON을 `openapi.json` 파일로 다운로드한 뒤, 아래와 같은 스크립트로 **접두사 태그를 제거**할 수 있습니다: + +{* ../../docs_src/generate_clients/tutorial004_py39.py *} + +//// tab | Node.js + +```Javascript +{!> ../../docs_src/generate_clients/tutorial004.js!} +``` + +//// + +이렇게 하면 operation ID가 `items-get_items` 같은 형태에서 `get_items`로 변경되어, 클라이언트 생성기가 더 단순한 메서드 이름을 생성할 수 있습니다. + +### 전처리된 OpenAPI로 TypeScript 클라이언트 생성하기 { #generate-a-typescript-client-with-the-preprocessed-openapi } + +이제 최종 결과가 `openapi.json` 파일에 있으므로, 입력 위치를 업데이트해야 합니다: + +```sh +npx @hey-api/openapi-ts -i ./openapi.json -o src/client +``` + +새 클라이언트를 생성한 후에는 **깔끔한 메서드 이름**을 가지면서도, **자동 완성**, **인라인 오류** 등은 그대로 제공됩니다: + + + +## 장점 { #benefits } + +자동으로 생성된 클라이언트를 사용하면 다음에 대해 **자동 완성**을 받을 수 있습니다: + +* 메서드 +* 본문(body)의 요청 페이로드, 쿼리 파라미터 등 +* 응답 페이로드 + +또한 모든 것에 대해 **인라인 오류**도 확인할 수 있습니다. + +그리고 백엔드 코드를 업데이트한 뒤 프론트엔드를 **재생성(regenerate)**하면, 새 *path operations*가 메서드로 추가되고 기존 것은 제거되며, 그 밖의 변경 사항도 생성된 코드에 반영됩니다. 🤓 + +이는 무언가 변경되면 그 변경이 클라이언트 코드에도 자동으로 **반영**된다는 뜻입니다. 또한 클라이언트를 **빌드(build)**하면 사용된 데이터가 **불일치(mismatch)**할 경우 오류가 발생합니다. + +따라서 운영 환경에서 최종 사용자에게 오류가 노출된 뒤 문제를 추적하는 대신, 개발 사이클 초기에 **많은 오류를 매우 빨리 감지**할 수 있습니다. ✨ diff --git a/docs/ko/docs/advanced/middleware.md b/docs/ko/docs/advanced/middleware.md new file mode 100644 index 0000000000..be2c972a6b --- /dev/null +++ b/docs/ko/docs/advanced/middleware.md @@ -0,0 +1,97 @@ +# 고급 Middleware { #advanced-middleware } + +메인 튜토리얼에서 애플리케이션에 [커스텀 Middleware](../tutorial/middleware.md){.internal-link target=_blank}를 추가하는 방법을 읽었습니다. + +그리고 [`CORSMiddleware`로 CORS 처리하기](../tutorial/cors.md){.internal-link target=_blank}도 읽었습니다. + +이 섹션에서는 다른 middleware들을 사용하는 방법을 살펴보겠습니다. + +## ASGI middleware 추가하기 { #adding-asgi-middlewares } + +**FastAPI**는 Starlette를 기반으로 하고 ASGI 사양을 구현하므로, 어떤 ASGI middleware든 사용할 수 있습니다. + +ASGI 사양을 따르기만 하면, FastAPI나 Starlette를 위해 만들어진 middleware가 아니어도 동작합니다. + +일반적으로 ASGI middleware는 첫 번째 인자로 ASGI 앱을 받도록 기대하는 클래스입니다. + +그래서 서드파티 ASGI middleware 문서에서는 아마 다음과 같이 하라고 안내할 것입니다: + +```Python +from unicorn import UnicornMiddleware + +app = SomeASGIApp() + +new_app = UnicornMiddleware(app, some_config="rainbow") +``` + +하지만 FastAPI(정확히는 Starlette)는 더 간단한 방법을 제공하며, 이를 통해 내부 middleware가 서버 오류를 처리하고 커스텀 예외 핸들러가 올바르게 동작하도록 보장합니다. + +이를 위해(그리고 CORS 예제에서처럼) `app.add_middleware()`를 사용합니다. + +```Python +from fastapi import FastAPI +from unicorn import UnicornMiddleware + +app = FastAPI() + +app.add_middleware(UnicornMiddleware, some_config="rainbow") +``` + +`app.add_middleware()`는 첫 번째 인자로 middleware 클래스를 받고, 그 뒤에는 middleware에 전달할 추가 인자들을 받습니다. + +## 통합 middleware { #integrated-middlewares } + +**FastAPI**에는 일반적인 사용 사례를 위한 여러 middleware가 포함되어 있습니다. 다음에서 이를 사용하는 방법을 살펴보겠습니다. + +/// note | 기술 세부사항 + +다음 예제에서는 `from starlette.middleware.something import SomethingMiddleware`를 사용해도 됩니다. + +**FastAPI**는 개발자 편의를 위해 `fastapi.middleware`에 여러 middleware를 제공하지만, 사용 가능한 대부분의 middleware는 Starlette에서 직접 제공됩니다. + +/// + +## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware } + +들어오는 모든 요청이 `https` 또는 `wss`여야 하도록 강제합니다. + +`http` 또는 `ws`로 들어오는 모든 요청은 대신 보안 스킴으로 리디렉션됩니다. + +{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *} + +## `TrustedHostMiddleware` { #trustedhostmiddleware } + +HTTP Host Header 공격을 방어하기 위해, 들어오는 모든 요청에 올바르게 설정된 `Host` 헤더가 있어야 하도록 강제합니다. + +{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *} + +다음 인자들을 지원합니다: + +* `allowed_hosts` - 호스트명으로 허용할 도메인 이름 목록입니다. `*.example.com` 같은 와일드카드 도메인으로 서브도메인을 매칭하는 것도 지원합니다. 어떤 호스트명이든 허용하려면 `allowed_hosts=["*"]`를 사용하거나 middleware를 생략하세요. +* `www_redirect` - True로 설정하면, 허용된 호스트의 non-www 버전으로 들어오는 요청을 www 버전으로 리디렉션합니다. 기본값은 `True`입니다. + +들어오는 요청이 올바르게 검증되지 않으면 `400` 응답이 전송됩니다. + +## `GZipMiddleware` { #gzipmiddleware } + +`Accept-Encoding` 헤더에 `"gzip"`이 포함된 어떤 요청이든 GZip 응답을 처리합니다. + +이 middleware는 일반 응답과 스트리밍 응답을 모두 처리합니다. + +{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *} + +다음 인자들을 지원합니다: + +* `minimum_size` - 바이트 단위로 지정한 최소 크기보다 작은 응답은 GZip으로 압축하지 않습니다. 기본값은 `500`입니다. +* `compresslevel` - GZip 압축 중에 사용됩니다. 1부터 9까지의 정수입니다. 기본값은 `9`입니다. 값이 낮을수록 압축은 더 빠르지만 파일 크기는 더 커지고, 값이 높을수록 압축은 더 느리지만 파일 크기는 더 작아집니다. + +## 다른 middleware { #other-middlewares } + +다른 ASGI middleware도 많이 있습니다. + +예를 들어: + +* Uvicorn의 `ProxyHeadersMiddleware` +* MessagePack + +사용 가능한 다른 middleware를 보려면 Starlette의 Middleware 문서ASGI Awesome List를 확인하세요. diff --git a/docs/ko/docs/advanced/openapi-callbacks.md b/docs/ko/docs/advanced/openapi-callbacks.md new file mode 100644 index 0000000000..e4bdea9d6c --- /dev/null +++ b/docs/ko/docs/advanced/openapi-callbacks.md @@ -0,0 +1,186 @@ +# OpenAPI 콜백 { #openapi-callbacks } + +다른 사람이 만든 *external API*(아마도 당신의 API를 *사용*할 동일한 개발자)가 요청을 트리거하도록 만드는 *경로 처리*를 가진 API를 만들 수 있습니다. + +당신의 API 앱이 *external API*를 호출할 때 일어나는 과정을 "callback"이라고 합니다. 외부 개발자가 작성한 소프트웨어가 당신의 API로 요청을 보낸 다음, 당신의 API가 다시 *external API*로 요청을 보내 *되돌려 호출*하기 때문입니다(아마도 같은 개발자가 만든 API일 것입니다). + +이 경우, 그 *external API*가 어떤 형태여야 하는지 문서화하고 싶을 수 있습니다. 어떤 *경로 처리*를 가져야 하는지, 어떤 body를 기대하는지, 어떤 응답을 반환해야 하는지 등입니다. + +## 콜백이 있는 앱 { #an-app-with-callbacks } + +예시로 확인해 보겠습니다. + +청구서를 생성할 수 있는 앱을 개발한다고 가정해 보세요. + +이 청구서는 `id`, `title`(선택 사항), `customer`, `total`을 갖습니다. + +당신의 API 사용자(외부 개발자)는 POST 요청으로 당신의 API에서 청구서를 생성합니다. + +그 다음 당신의 API는(가정해 보면): + +* 청구서를 외부 개발자의 고객에게 전송합니다. +* 돈을 수금합니다. +* API 사용자(외부 개발자)의 API로 다시 알림을 보냅니다. + * 이는 (당신의 API에서) 그 외부 개발자가 제공하는 어떤 *external API*로 POST 요청을 보내는 방식으로 수행됩니다(이것이 "callback"입니다). + +## 일반적인 **FastAPI** 앱 { #the-normal-fastapi-app } + +먼저 콜백을 추가하기 전, 일반적인 API 앱이 어떻게 생겼는지 보겠습니다. + +`Invoice` body를 받는 *경로 처리*와, 콜백을 위한 URL을 담는 쿼리 파라미터 `callback_url`이 있을 것입니다. + +이 부분은 꽤 일반적이며, 대부분의 코드는 이미 익숙할 것입니다: + +{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *} + +/// tip | 팁 + +`callback_url` 쿼리 파라미터는 Pydantic의 Url 타입을 사용합니다. + +/// + +유일하게 새로운 것은 *경로 처리 데코레이터*의 인자로 `callbacks=invoices_callback_router.routes`가 들어간다는 점입니다. 이것이 무엇인지 다음에서 보겠습니다. + +## 콜백 문서화하기 { #documenting-the-callback } + +실제 콜백 코드는 당신의 API 앱에 크게 의존합니다. + +그리고 앱마다 많이 달라질 수 있습니다. + +다음처럼 한두 줄의 코드일 수도 있습니다: + +```Python +callback_url = "https://example.com/api/v1/invoices/events/" +httpx.post(callback_url, json={"description": "Invoice paid", "paid": True}) +``` + +하지만 콜백에서 가장 중요한 부분은, 당신의 API 사용자(외부 개발자)가 콜백 요청 body로 *당신의 API*가 보낼 데이터 등에 맞춰 *external API*를 올바르게 구현하도록 보장하는 것입니다. + +그래서 다음으로 할 일은, *당신의 API*에서 보내는 콜백을 받기 위해 그 *external API*가 어떤 형태여야 하는지 문서화하는 코드를 추가하는 것입니다. + +그 문서는 당신의 API에서 `/docs`의 Swagger UI에 표시되며, 외부 개발자들이 *external API*를 어떻게 만들어야 하는지 알 수 있게 해줍니다. + +이 예시는 콜백 자체(한 줄 코드로도 될 수 있음)를 구현하지 않고, 문서화 부분만 구현합니다. + +/// tip | 팁 + +실제 콜백은 단지 HTTP 요청입니다. + +콜백을 직접 구현할 때는 HTTPXRequests 같은 것을 사용할 수 있습니다. + +/// + +## 콜백 문서화 코드 작성하기 { #write-the-callback-documentation-code } + +이 코드는 앱에서 실행되지 않습니다. 그 *external API*가 어떤 형태여야 하는지 *문서화*하는 데만 필요합니다. + +하지만 **FastAPI**로 API의 자동 문서를 쉽게 생성하는 방법은 이미 알고 있습니다. + +따라서 그와 같은 지식을 사용해 *external API*가 어떻게 생겨야 하는지 문서화할 것입니다... 즉 외부 API가 구현해야 하는 *경로 처리(들)*(당신의 API가 호출할 것들)을 만들어서 말입니다. + +/// tip | 팁 + +콜백을 문서화하는 코드를 작성할 때는, 자신이 그 *외부 개발자*라고 상상하는 것이 유용할 수 있습니다. 그리고 지금은 *당신의 API*가 아니라 *external API*를 구현하고 있다고 생각해 보세요. + +이 관점(외부 개발자의 관점)을 잠시 채택하면, 그 *external API*를 위해 파라미터, body용 Pydantic 모델, 응답 등을 어디에 두어야 하는지가 더 명확하게 느껴질 수 있습니다. + +/// + +### 콜백 `APIRouter` 생성하기 { #create-a-callback-apirouter } + +먼저 하나 이상의 콜백을 담을 새 `APIRouter`를 만듭니다. + +{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *} + +### 콜백 *경로 처리* 생성하기 { #create-the-callback-path-operation } + +콜백 *경로 처리*를 만들려면 위에서 만든 동일한 `APIRouter`를 사용합니다. + +일반적인 FastAPI *경로 처리*처럼 보일 것입니다: + +* 아마도 받아야 할 body 선언이 있을 것입니다(예: `body: InvoiceEvent`). +* 그리고 반환해야 할 응답 선언도 있을 수 있습니다(예: `response_model=InvoiceEventReceived`). + +{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *} + +일반적인 *경로 처리*와의 주요 차이점은 2가지입니다: + +* 실제 코드를 가질 필요가 없습니다. 당신의 앱은 이 코드를 절대 호출하지 않기 때문입니다. 이는 *external API*를 문서화하는 데만 사용됩니다. 따라서 함수는 그냥 `pass`만 있어도 됩니다. +* *path*에는 OpenAPI 3 expression(자세한 내용은 아래 참고)이 포함될 수 있으며, 이를 통해 *당신의 API*로 보내진 원래 요청의 파라미터와 일부 값을 변수로 사용할 수 있습니다. + +### 콜백 경로 표현식 { #the-callback-path-expression } + +콜백 *path*는 *당신의 API*로 보내진 원래 요청의 일부를 포함할 수 있는 OpenAPI 3 expression을 가질 수 있습니다. + +이 경우, 다음 `str`입니다: + +```Python +"{$callback_url}/invoices/{$request.body.id}" +``` + +따라서 당신의 API 사용자(외부 개발자)가 *당신의 API*로 다음 요청을 보내고: + +``` +https://yourapi.com/invoices/?callback_url=https://www.external.org/events +``` + +JSON body가 다음과 같다면: + +```JSON +{ + "id": "2expen51ve", + "customer": "Mr. Richie Rich", + "total": "9999" +} +``` + +그러면 *당신의 API*는 청구서를 처리하고, 나중에 어느 시점에서 `callback_url`(즉 *external API*)로 콜백 요청을 보냅니다: + +``` +https://www.external.org/events/invoices/2expen51ve +``` + +그리고 다음과 같은 JSON body를 포함할 것입니다: + +```JSON +{ + "description": "Payment celebration", + "paid": true +} +``` + +또한 그 *external API*로부터 다음과 같은 JSON body 응답을 기대합니다: + +```JSON +{ + "ok": true +} +``` + +/// tip | 팁 + +콜백 URL에는 `callback_url` 쿼리 파라미터로 받은 URL(`https://www.external.org/events`)뿐 아니라, JSON body 안의 청구서 `id`(`2expen51ve`)도 함께 사용된다는 점에 주목하세요. + +/// + +### 콜백 라우터 추가하기 { #add-the-callback-router } + +이 시점에서, 위에서 만든 콜백 라우터 안에 *콜백 경로 처리(들)*(즉 *external developer*가 *external API*에 구현해야 하는 것들)을 준비했습니다. + +이제 *당신의 API 경로 처리 데코레이터*에서 `callbacks` 파라미터를 사용해, 그 콜백 라우터의 `.routes` 속성(실제로는 routes/*경로 처리*의 `list`)을 전달합니다: + +{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *} + +/// tip | 팁 + +`callback=`에 라우터 자체(`invoices_callback_router`)를 넘기는 것이 아니라, `invoices_callback_router.routes`처럼 `.routes` 속성을 넘긴다는 점에 주목하세요. + +/// + +### 문서 확인하기 { #check-the-docs } + +이제 앱을 실행하고 http://127.0.0.1:8000/docs로 이동하세요. + +*경로 처리*에 대해 "Callbacks" 섹션을 포함한 문서가 표시되며, *external API*가 어떤 형태여야 하는지 확인할 수 있습니다: + + diff --git a/docs/ko/docs/advanced/openapi-webhooks.md b/docs/ko/docs/advanced/openapi-webhooks.md new file mode 100644 index 0000000000..89cacf7b78 --- /dev/null +++ b/docs/ko/docs/advanced/openapi-webhooks.md @@ -0,0 +1,55 @@ +# OpenAPI Webhooks { #openapi-webhooks } + +앱이 어떤 데이터와 함께 (요청을 보내서) *사용자의* 앱을 호출할 수 있고, 보통 어떤 **이벤트**를 **알리기** 위해 그렇게 할 수 있다는 것을 API **사용자**에게 알려야 하는 경우가 있습니다. + +이는 사용자가 여러분의 API로 요청을 보내는 일반적인 과정 대신, **여러분의 API**(또는 앱)가 **사용자의 시스템**(사용자의 API, 사용자의 앱)으로 **요청을 보낼 수 있다**는 의미입니다. + +이를 보통 **webhook**이라고 합니다. + +## Webhooks 단계 { #webhooks-steps } + +일반적인 과정은, 여러분이 코드에서 보낼 메시지, 즉 **요청 본문(body)**이 무엇인지 **정의**하는 것입니다. + +또한 여러분의 앱이 어떤 **시점**에 그 요청(또는 이벤트)을 보낼지도 어떤 방식으로든 정의합니다. + +그리고 **사용자**는 (예: 어딘가의 웹 대시보드에서) 여러분의 앱이 그 요청을 보내야 할 **URL**을 어떤 방식으로든 정의합니다. + +webhook의 URL을 등록하는 방법과 실제로 그 요청을 보내는 코드에 대한 모든 **로직**은 여러분에게 달려 있습니다. **여러분의 코드**에서 원하는 방식으로 작성하면 됩니다. + +## **FastAPI**와 OpenAPI로 webhooks 문서화하기 { #documenting-webhooks-with-fastapi-and-openapi } + +**FastAPI**에서는 OpenAPI를 사용해, 이러한 webhook의 이름, 여러분의 앱이 보낼 수 있는 HTTP 작업 타입(예: `POST`, `PUT` 등), 그리고 여러분의 앱이 보낼 요청 **본문(body)**을 정의할 수 있습니다. + +이렇게 하면 사용자가 여러분의 **webhook** 요청을 받기 위해 **자신들의 API를 구현**하기가 훨씬 쉬워지고, 경우에 따라서는 자신의 API 코드 일부를 자동 생성할 수도 있습니다. + +/// info | 정보 + +Webhooks는 OpenAPI 3.1.0 이상에서 사용할 수 있으며, FastAPI `0.99.0` 이상에서 지원됩니다. + +/// + +## webhooks가 있는 앱 { #an-app-with-webhooks } + +**FastAPI** 애플리케이션을 만들면, *경로 처리*를 정의하는 것과 같은 방식으로(예: `@app.webhooks.post()`), *webhooks*를 정의하는 데 사용할 수 있는 `webhooks` 속성이 있습니다. + +{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *} + +여러분이 정의한 webhook은 **OpenAPI** 스키마와 자동 **docs UI**에 포함됩니다. + +/// info | 정보 + +`app.webhooks` 객체는 실제로 `APIRouter`일 뿐이며, 여러 파일로 앱을 구조화할 때 사용하는 것과 동일한 타입입니다. + +/// + +webhook에서는 실제로(`/items/` 같은) *경로(path)*를 선언하지 않는다는 점에 유의하세요. 그곳에 전달하는 텍스트는 webhook의 **식별자**(이벤트 이름)일 뿐입니다. 예를 들어 `@app.webhooks.post("new-subscription")`에서 webhook 이름은 `new-subscription`입니다. + +이는 **사용자**가 webhook 요청을 받고 싶은 실제 **URL 경로**를 다른 방식(예: 웹 대시보드)으로 정의할 것이라고 기대하기 때문입니다. + +### 문서 확인하기 { #check-the-docs } + +이제 앱을 실행하고 http://127.0.0.1:8000/docs로 이동하세요. + +문서에 일반적인 *경로 처리*가 보이고, 이제는 일부 **webhooks**도 함께 보일 것입니다: + + diff --git a/docs/ko/docs/advanced/path-operation-advanced-configuration.md b/docs/ko/docs/advanced/path-operation-advanced-configuration.md new file mode 100644 index 0000000000..f20fa6d263 --- /dev/null +++ b/docs/ko/docs/advanced/path-operation-advanced-configuration.md @@ -0,0 +1,172 @@ +# 경로 처리 고급 구성 { #path-operation-advanced-configuration } + +## OpenAPI operationId { #openapi-operationid } + +/// warning | 경고 + +OpenAPI “전문가”가 아니라면, 아마 이 내용은 필요하지 않을 것입니다. + +/// + +매개변수 `operation_id`를 사용해 *경로 처리*에 사용할 OpenAPI `operationId`를 설정할 수 있습니다. + +각 작업마다 고유하도록 보장해야 합니다. + +{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *} + +### *경로 처리 함수* 이름을 operationId로 사용하기 { #using-the-path-operation-function-name-as-the-operationid } + +API의 함수 이름을 `operationId`로 사용하고 싶다면, 모든 API를 순회하면서 `APIRoute.name`을 사용해 각 *경로 처리*의 `operation_id`를 덮어쓸 수 있습니다. + +모든 *경로 처리*를 추가한 뒤에 수행해야 합니다. + +{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *} + +/// tip | 팁 + +`app.openapi()`를 수동으로 호출한다면, 그 전에 `operationId`들을 업데이트해야 합니다. + +/// + +/// warning | 경고 + +이렇게 할 경우, 각 *경로 처리 함수*의 이름이 고유하도록 보장해야 합니다. + +서로 다른 모듈(파이썬 파일)에 있어도 마찬가지입니다. + +/// + +## OpenAPI에서 제외하기 { #exclude-from-openapi } + +생성된 OpenAPI 스키마(따라서 자동 문서화 시스템)에서 특정 *경로 처리*를 제외하려면, `include_in_schema` 매개변수를 `False`로 설정하세요: + +{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *} + +## docstring에서 고급 설명 가져오기 { #advanced-description-from-docstring } + +OpenAPI에 사용할 *경로 처리 함수*의 docstring 줄 수를 제한할 수 있습니다. + +`\f`(이스케이프된 "form feed" 문자)를 추가하면 **FastAPI**는 이 지점에서 OpenAPI에 사용할 출력 내용을 잘라냅니다. + +문서에는 표시되지 않지만, Sphinx 같은 다른 도구는 나머지 부분을 사용할 수 있습니다. + +{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *} + +## 추가 응답 { #additional-responses } + +*경로 처리*에 대해 `response_model`과 `status_code`를 선언하는 방법을 이미 보셨을 것입니다. + +이는 *경로 처리*의 기본 응답에 대한 메타데이터를 정의합니다. + +모델, 상태 코드 등과 함께 추가 응답도 선언할 수 있습니다. + +이에 대한 문서의 전체 장이 있으니, [OpenAPI의 추가 응답](additional-responses.md){.internal-link target=_blank}에서 읽어보세요. + +## OpenAPI Extra { #openapi-extra } + +애플리케이션에서 *경로 처리*를 선언하면, **FastAPI**는 OpenAPI 스키마에 포함될 해당 *경로 처리*의 관련 메타데이터를 자동으로 생성합니다. + +/// note | 기술 세부사항 + +OpenAPI 명세에서는 이를 Operation Object라고 부릅니다. + +/// + +여기에는 *경로 처리*에 대한 모든 정보가 있으며, 자동 문서를 생성하는 데 사용됩니다. + +`tags`, `parameters`, `requestBody`, `responses` 등이 포함됩니다. + +이 *경로 처리* 전용 OpenAPI 스키마는 보통 **FastAPI**가 자동으로 생성하지만, 확장할 수도 있습니다. + +/// tip | 팁 + +이는 저수준 확장 지점입니다. + +추가 응답만 선언하면 된다면, 더 편리한 방법은 [OpenAPI의 추가 응답](additional-responses.md){.internal-link target=_blank}을 사용하는 것입니다. + +/// + +`openapi_extra` 매개변수를 사용해 *경로 처리*의 OpenAPI 스키마를 확장할 수 있습니다. + +### OpenAPI 확장 { #openapi-extensions } + +예를 들어 `openapi_extra`는 [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] *} + +자동 API 문서를 열면, 해당 특정 *경로 처리*의 하단에 확장이 표시됩니다. + + + +또한 API의 `/openapi.json`에서 결과 OpenAPI를 보면, 특정 *경로 처리*의 일부로 확장이 포함된 것도 확인할 수 있습니다: + +```JSON hl_lines="22" +{ + "openapi": "3.1.0", + "info": { + "title": "FastAPI", + "version": "0.1.0" + }, + "paths": { + "/items/": { + "get": { + "summary": "Read Items", + "operationId": "read_items_items__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "x-aperture-labs-portal": "blue" + } + } + } +} +``` + +### 사용자 정의 OpenAPI *경로 처리* 스키마 { #custom-openapi-path-operation-schema } + +`openapi_extra`의 딕셔너리는 *경로 처리*에 대해 자동으로 생성된 OpenAPI 스키마와 깊게 병합됩니다. + +따라서 자동 생성된 스키마에 추가 데이터를 더할 수 있습니다. + +예를 들어 Pydantic과 함께 FastAPI의 자동 기능을 사용하지 않고, 자체 코드로 요청을 읽고 검증하기로 결정할 수도 있지만, OpenAPI 스키마에는 여전히 그 요청을 정의하고 싶을 수 있습니다. + +그럴 때 `openapi_extra`를 사용할 수 있습니다: + +{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *} + +이 예시에서는 어떤 Pydantic 모델도 선언하지 않았습니다. 사실 요청 바디는 JSON으로 parsed되지도 않고, `bytes`로 직접 읽습니다. 그리고 함수 `magic_data_reader()`가 어떤 방식으로든 이를 파싱하는 역할을 담당합니다. + +그럼에도 불구하고, 요청 바디에 대해 기대하는 스키마를 선언할 수 있습니다. + +### 사용자 정의 OpenAPI 콘텐츠 타입 { #custom-openapi-content-type } + +같은 트릭을 사용하면, Pydantic 모델을 이용해 JSON Schema를 정의하고 이를 *경로 처리*의 사용자 정의 OpenAPI 스키마 섹션에 포함시킬 수 있습니다. + +요청의 데이터 타입이 JSON이 아니더라도 이렇게 할 수 있습니다. + +예를 들어 이 애플리케이션에서는 Pydantic 모델에서 JSON Schema를 추출하는 FastAPI의 통합 기능도, JSON에 대한 자동 검증도 사용하지 않습니다. 실제로 요청 콘텐츠 타입을 JSON이 아니라 YAML로 선언합니다: + +{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *} + +그럼에도 기본 통합 기능을 사용하지 않더라도, YAML로 받고자 하는 데이터에 대한 JSON Schema를 수동으로 생성하기 위해 Pydantic 모델을 여전히 사용합니다. + +그 다음 요청을 직접 사용하고, 바디를 `bytes`로 추출합니다. 이는 FastAPI가 요청 페이로드를 JSON으로 파싱하려고 시도조차 하지 않는다는 뜻입니다. + +그리고 코드에서 YAML 콘텐츠를 직접 파싱한 뒤, 다시 같은 Pydantic 모델을 사용해 YAML 콘텐츠를 검증합니다: + +{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *} + +/// tip | 팁 + +여기서는 같은 Pydantic 모델을 재사용합니다. + +하지만 마찬가지로, 다른 방식으로 검증할 수도 있습니다. + +/// diff --git a/docs/ko/docs/advanced/security/http-basic-auth.md b/docs/ko/docs/advanced/security/http-basic-auth.md new file mode 100644 index 0000000000..611aad7956 --- /dev/null +++ b/docs/ko/docs/advanced/security/http-basic-auth.md @@ -0,0 +1,107 @@ +# HTTP Basic Auth { #http-basic-auth } + +가장 단순한 경우에는 HTTP Basic Auth를 사용할 수 있습니다. + +HTTP Basic Auth에서는 애플리케이션이 사용자명과 비밀번호가 들어 있는 헤더를 기대합니다. + +이를 받지 못하면 HTTP 401 "Unauthorized" 오류를 반환합니다. + +그리고 값이 `Basic`이고 선택적으로 `realm` 파라미터를 포함하는 `WWW-Authenticate` 헤더를 반환합니다. + +이는 브라우저가 사용자명과 비밀번호를 입력하는 통합 프롬프트를 표시하도록 알려줍니다. + +그다음 사용자명과 비밀번호를 입력하면, 브라우저가 자동으로 해당 값을 헤더에 담아 전송합니다. + +## 간단한 HTTP Basic Auth { #simple-http-basic-auth } + +* `HTTPBasic`과 `HTTPBasicCredentials`를 임포트합니다. +* `HTTPBasic`을 사용해 "`security` scheme"을 생성합니다. +* *경로 처리*에서 dependency로 해당 `security`를 사용합니다. +* `HTTPBasicCredentials` 타입의 객체를 반환합니다: + * 전송된 `username`과 `password`를 포함합니다. + +{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *} + +처음으로 URL을 열어보면(또는 문서에서 "Execute" 버튼을 클릭하면) 브라우저가 사용자명과 비밀번호를 물어봅니다: + + + +## 사용자명 확인하기 { #check-the-username } + +더 완전한 예시입니다. + +dependency를 사용해 사용자명과 비밀번호가 올바른지 확인하세요. + +이를 위해 Python 표준 모듈 `secrets`를 사용해 사용자명과 비밀번호를 확인합니다. + +`secrets.compare_digest()`는 `bytes` 또는 ASCII 문자(영어에서 사용하는 문자)만 포함한 `str`을 받아야 합니다. 즉, `Sebastián`의 `á` 같은 문자가 있으면 동작하지 않습니다. + +이를 처리하기 위해 먼저 `username`과 `password`를 UTF-8로 인코딩해서 `bytes`로 변환합니다. + +그런 다음 `secrets.compare_digest()`를 사용해 `credentials.username`이 `"stanleyjobson"`이고 `credentials.password`가 `"swordfish"`인지 확실히 확인할 수 있습니다. + +{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *} + +이는 다음과 비슷합니다: + +```Python +if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"): + # Return some error + ... +``` + +하지만 `secrets.compare_digest()`를 사용하면 "timing attacks"라고 불리는 한 유형의 공격에 대해 안전해집니다. + +### Timing Attacks { #timing-attacks } + +그렇다면 "timing attack"이란 무엇일까요? + +공격자들이 사용자명과 비밀번호를 추측하려고 한다고 가정해봅시다. + +그리고 사용자명 `johndoe`, 비밀번호 `love123`으로 요청을 보냅니다. + +그러면 애플리케이션의 Python 코드는 대략 다음과 같을 것입니다: + +```Python +if "johndoe" == "stanleyjobson" and "love123" == "swordfish": + ... +``` + +하지만 Python이 `johndoe`의 첫 글자 `j`를 `stanleyjobson`의 첫 글자 `s`와 비교하는 순간, 두 문자열이 같지 않다는 것을 이미 알게 되어 `False`를 반환합니다. 이는 “나머지 글자들을 비교하느라 계산을 더 낭비할 필요가 없다”고 판단하기 때문입니다. 그리고 애플리케이션은 "Incorrect username or password"라고 말합니다. + +그런데 공격자들이 사용자명을 `stanleyjobsox`, 비밀번호를 `love123`으로 다시 시도합니다. + +그러면 애플리케이션 코드는 다음과 비슷하게 동작합니다: + +```Python +if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish": + ... +``` + +Python은 두 문자열이 같지 않다는 것을 알아차리기 전까지 `stanleyjobsox`와 `stanleyjobson` 양쪽의 `stanleyjobso` 전체를 비교해야 합니다. 그래서 "Incorrect username or password"라고 응답하기까지 추가로 몇 마이크로초가 더 걸릴 것입니다. + +#### 응답 시간은 공격자에게 도움이 됩니다 { #the-time-to-answer-helps-the-attackers } + +이 시점에서 서버가 "Incorrect username or password" 응답을 보내는 데 몇 마이크로초 더 걸렸다는 것을 알아채면, 공격자들은 _무언가_ 맞았다는 것(초기 몇 글자가 맞았다는 것)을 알게 됩니다. + +그리고 `johndoe`보다는 `stanleyjobsox`에 더 가까운 값을 시도해야 한다는 것을 알고 다시 시도할 수 있습니다. + +#### "전문적인" 공격 { #a-professional-attack } + +물론 공격자들은 이런 작업을 손으로 하지 않습니다. 보통 초당 수천~수백만 번 테스트할 수 있는 프로그램을 작성할 것이고, 한 번에 정답 글자 하나씩 추가로 얻어낼 수 있습니다. + +그렇게 하면 몇 분 또는 몇 시간 만에, 응답에 걸린 시간만을 이용해(우리 애플리케이션의 “도움”을 받아) 올바른 사용자명과 비밀번호를 추측할 수 있게 됩니다. + +#### `secrets.compare_digest()`로 해결하기 { #fix-it-with-secrets-compare-digest } + +하지만 우리 코드는 실제로 `secrets.compare_digest()`를 사용하고 있습니다. + +요약하면, `stanleyjobsox`와 `stanleyjobson`을 비교하는 데 걸리는 시간은 `johndoe`와 `stanleyjobson`을 비교하는 데 걸리는 시간과 같아집니다. 비밀번호도 마찬가지입니다. + +이렇게 애플리케이션 코드에서 `secrets.compare_digest()`를 사용하면, 이러한 범위의 보안 공격 전반에 대해 안전해집니다. + +### 오류 반환하기 { #return-the-error } + +자격 증명이 올바르지 않다고 판단되면, 상태 코드 401(자격 증명이 제공되지 않았을 때와 동일)을 사용하는 `HTTPException`을 반환하고 브라우저가 로그인 프롬프트를 다시 표시하도록 `WWW-Authenticate` 헤더를 추가하세요: + +{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *} diff --git a/docs/ko/docs/advanced/security/index.md b/docs/ko/docs/advanced/security/index.md new file mode 100644 index 0000000000..4c7abfacc7 --- /dev/null +++ b/docs/ko/docs/advanced/security/index.md @@ -0,0 +1,19 @@ +# 고급 보안 { #advanced-security } + +## 추가 기능 { #additional-features } + +[튜토리얼 - 사용자 가이드: 보안](../../tutorial/security/index.md){.internal-link target=_blank}에서 다룬 내용 외에도, 보안을 처리하기 위한 몇 가지 추가 기능이 있습니다. + +/// tip | 팁 + +다음 섹션들은 **반드시 "고급"이라고 할 수는 없습니다**. + +그리고 사용 사례에 따라, 그중 하나에 해결책이 있을 수도 있습니다. + +/// + +## 먼저 튜토리얼 읽기 { #read-the-tutorial-first } + +다음 섹션은 주요 [튜토리얼 - 사용자 가이드: 보안](../../tutorial/security/index.md){.internal-link target=_blank}을 이미 읽었다고 가정합니다. + +모두 동일한 개념을 기반으로 하지만, 몇 가지 추가 기능을 사용할 수 있게 해줍니다. diff --git a/docs/ko/docs/advanced/security/oauth2-scopes.md b/docs/ko/docs/advanced/security/oauth2-scopes.md new file mode 100644 index 0000000000..0f90f92ae9 --- /dev/null +++ b/docs/ko/docs/advanced/security/oauth2-scopes.md @@ -0,0 +1,274 @@ +# OAuth2 스코프 { #oauth2-scopes } + +**FastAPI**에서 OAuth2 스코프를 직접 사용할 수 있으며, 자연스럽게 동작하도록 통합되어 있습니다. + +이를 통해 OAuth2 표준을 따르는 더 세밀한 권한 시스템을 OpenAPI 애플리케이션(및 API 문서)에 통합할 수 있습니다. + +스코프를 사용하는 OAuth2는 Facebook, Google, GitHub, Microsoft, X(Twitter) 등 많은 대형 인증 제공자가 사용하는 메커니즘입니다. 이들은 이를 통해 사용자와 애플리케이션에 특정 권한을 제공합니다. + +Facebook, Google, GitHub, Microsoft, X(Twitter)로 “로그인”할 때마다, 해당 애플리케이션은 스코프가 있는 OAuth2를 사용하고 있습니다. + +이 섹션에서는 **FastAPI** 애플리케이션에서 동일한 “스코프가 있는 OAuth2”로 인증(Authentication)과 인가(Authorization)를 관리하는 방법을 확인합니다. + +/// warning | 경고 + +이 섹션은 다소 고급 내용입니다. 이제 막 시작했다면 건너뛰어도 됩니다. + +OAuth2 스코프가 반드시 필요한 것은 아니며, 인증과 인가는 원하는 방식으로 처리할 수 있습니다. + +하지만 스코프가 있는 OAuth2는 (OpenAPI와 함께) API 및 API 문서에 깔끔하게 통합될 수 있습니다. + +그럼에도 불구하고, 해당 스코프(또는 그 밖의 어떤 보안/인가 요구사항이든)는 코드에서 필요에 맞게 직접 강제해야 합니다. + +많은 경우 스코프가 있는 OAuth2는 과한 선택일 수 있습니다. + +하지만 필요하다고 알고 있거나 궁금하다면 계속 읽어보세요. + +/// + +## OAuth2 스코프와 OpenAPI { #oauth2-scopes-and-openapi } + +OAuth2 사양은 “스코프(scopes)”를 공백으로 구분된 문자열 목록으로 정의합니다. + +각 문자열의 내용은 어떤 형식이든 될 수 있지만, 공백을 포함하면 안 됩니다. + +이 스코프들은 “권한”을 나타냅니다. + +OpenAPI(예: API 문서)에서는 “security schemes”를 정의할 수 있습니다. + +이 security scheme 중 하나가 OAuth2를 사용한다면, 스코프도 선언하고 사용할 수 있습니다. + +각 “스코프”는 (공백 없는) 문자열일 뿐입니다. + +보통 다음과 같이 특정 보안 권한을 선언하는 데 사용합니다: + +* `users:read` 또는 `users:write` 는 흔한 예시입니다. +* `instagram_basic` 는 Facebook/Instagram에서 사용합니다. +* `https://www.googleapis.com/auth/drive` 는 Google에서 사용합니다. + +/// info | 정보 + +OAuth2에서 “스코프”는 필요한 특정 권한을 선언하는 문자열일 뿐입니다. + +`:` 같은 다른 문자가 있거나 URL이어도 상관없습니다. + +그런 세부사항은 구현에 따라 달라집니다. + +OAuth2 입장에서는 그저 문자열입니다. + +/// + +## 전체 개요 { #global-view } + +먼저, 메인 **튜토리얼 - 사용자 가이드**의 [비밀번호(및 해싱)를 사용하는 OAuth2, JWT 토큰을 사용하는 Bearer](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank} 예제에서 어떤 부분이 바뀌는지 빠르게 살펴보겠습니다. 이제 OAuth2 스코프를 사용합니다: + +{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *} + +이제 변경 사항을 단계별로 살펴보겠습니다. + +## OAuth2 보안 스킴 { #oauth2-security-scheme } + +첫 번째 변경 사항은 이제 사용 가능한 스코프 2개(`me`, `items`)로 OAuth2 보안 스킴을 선언한다는 점입니다. + +`scopes` 매개변수는 각 스코프를 키로 하고, 설명을 값으로 하는 `dict`를 받습니다: + +{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *} + +이제 스코프를 선언했기 때문에, 로그인/인가할 때 API 문서에 스코프가 표시됩니다. + +그리고 접근을 허용할 스코프를 선택할 수 있게 됩니다: `me`와 `items`. + +이는 Facebook, Google, GitHub 등으로 로그인하면서 권한을 부여할 때 사용되는 것과 동일한 메커니즘입니다: + + + +## 스코프를 포함한 JWT 토큰 { #jwt-token-with-scopes } + +이제 토큰 *경로 처리*를 수정해, 요청된 스코프를 반환하도록 합니다. + +여전히 동일한 `OAuth2PasswordRequestForm`을 사용합니다. 여기에는 요청에서 받은 각 스코프를 담는 `scopes` 속성이 있으며, 타입은 `str`의 `list`입니다. + +그리고 JWT 토큰의 일부로 스코프를 반환합니다. + +/// danger | 위험 + +단순화를 위해, 여기서는 요청으로 받은 스코프를 그대로 토큰에 추가하고 있습니다. + +하지만 실제 애플리케이션에서는 보안을 위해, 사용자가 실제로 가질 수 있는 스코프만(또는 미리 정의한 것만) 추가하도록 반드시 확인해야 합니다. + +/// + +{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *} + +## *경로 처리*와 의존성에서 스코프 선언하기 { #declare-scopes-in-path-operations-and-dependencies } + +이제 `/users/me/items/`에 대한 *경로 처리*가 스코프 `items`를 요구한다고 선언합니다. + +이를 위해 `fastapi`에서 `Security`를 import하여 사용합니다. + +`Security`는 (`Depends`처럼) 의존성을 선언하는 데 사용할 수 있지만, `Security`는 스코프(문자열) 목록을 받는 `scopes` 매개변수도 받습니다. + +이 경우, 의존성 함수 `get_current_active_user`를 `Security`에 전달합니다(`Depends`로 할 때와 같은 방식). + +하지만 스코프 `list`도 함께 전달합니다. 여기서는 스코프 하나만: `items`(더 많을 수도 있습니다). + +또한 의존성 함수 `get_current_active_user`는 `Depends`뿐 아니라 `Security`로도 하위 의존성을 선언할 수 있습니다. 자체 하위 의존성 함수(`get_current_user`)와 추가 스코프 요구사항을 선언합니다. + +이 경우에는 스코프 `me`를 요구합니다(여러 스코프를 요구할 수도 있습니다). + +/// note | 참고 + +반드시 서로 다른 곳에 서로 다른 스코프를 추가해야 하는 것은 아닙니다. + +여기서는 **FastAPI**가 서로 다른 레벨에서 선언된 스코프를 어떻게 처리하는지 보여주기 위해 이렇게 합니다. + +/// + +{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *} + +/// info | 기술 세부사항 + +`Security`는 실제로 `Depends`의 서브클래스이며, 나중에 보게 될 추가 매개변수 하나만 더 있습니다. + +하지만 `Depends` 대신 `Security`를 사용하면, **FastAPI**는 보안 스코프를 선언할 수 있음을 알고 내부적으로 이를 사용하며, OpenAPI로 API를 문서화할 수 있습니다. + +하지만 `fastapi`에서 `Query`, `Path`, `Depends`, `Security` 등을 import할 때, 이것들은 실제로 특수한 클래스를 반환하는 함수입니다. + +/// + +## `SecurityScopes` 사용하기 { #use-securityscopes } + +이제 의존성 `get_current_user`를 업데이트합니다. + +이는 위의 의존성들이 사용하는 것입니다. + +여기에서 앞서 만든 동일한 OAuth2 스킴을 의존성으로 선언하여 사용합니다: `oauth2_scheme`. + +이 의존성 함수 자체에는 스코프 요구사항이 없기 때문에, `oauth2_scheme`와 함께 `Depends`를 사용할 수 있습니다. 보안 스코프를 지정할 필요가 없을 때는 `Security`를 쓸 필요가 없습니다. + +또한 `fastapi.security`에서 import한 `SecurityScopes` 타입의 특별한 매개변수를 선언합니다. + +이 `SecurityScopes` 클래스는 `Request`와 비슷합니다(`Request`는 요청 객체를 직접 얻기 위해 사용했습니다). + +{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *} + +## `scopes` 사용하기 { #use-the-scopes } + +매개변수 `security_scopes`의 타입은 `SecurityScopes`입니다. + +여기에는 `scopes` 속성이 있으며, 자기 자신과 이 함수를 하위 의존성으로 사용하는 모든 의존성이 요구하는 스코프 전체를 담은 `list`를 가집니다. 즉, 모든 “dependants”... 다소 헷갈릴 수 있는데, 아래에서 다시 설명합니다. + +`security_scopes` 객체(`SecurityScopes` 클래스)에는 또한 `scope_str` 속성이 있는데, 공백으로 구분된 단일 문자열로 스코프들을 담고 있습니다(이를 사용할 것입니다). + +나중에 여러 지점에서 재사용(`raise`)할 수 있는 `HTTPException`을 생성합니다. + +이 예외에는 필요한 스코프(있다면)를 공백으로 구분된 문자열(`scope_str`)로 포함합니다. 그리고 그 스코프 문자열을 `WWW-Authenticate` 헤더에 넣습니다(이는 사양의 일부입니다). + +{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *} + +## `username`과 데이터 형태 검증하기 { #verify-the-username-and-data-shape } + +`username`을 얻었는지 확인하고, 스코프를 추출합니다. + +그런 다음 Pydantic 모델로 데이터를 검증합니다(`ValidationError` 예외를 잡습니다). JWT 토큰을 읽거나 Pydantic으로 데이터를 검증하는 과정에서 오류가 나면, 앞에서 만든 `HTTPException`을 raise합니다. + +이를 위해 Pydantic 모델 `TokenData`에 새 속성 `scopes`를 추가합니다. + +Pydantic으로 데이터를 검증하면, 예를 들어 스코프가 정확히 `str`의 `list`이고 `username`이 `str`인지 등을 보장할 수 있습니다. + +예를 들어 `dict`나 다른 형태라면, 나중에 애플리케이션이 어느 시점에 깨지면서 보안 위험이 될 수 있습니다. + +또한 해당 username을 가진 사용자가 있는지 확인하고, 없다면 앞에서 만든 동일한 예외를 raise합니다. + +{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *} + +## `scopes` 검증하기 { #verify-the-scopes } + +이제 이 의존성과 모든 dependant( *경로 처리* 포함)가 요구하는 모든 스코프가, 받은 토큰의 스코프에 포함되어 있는지 확인합니다. 그렇지 않으면 `HTTPException`을 raise합니다. + +이를 위해, 모든 스코프를 `str`로 담고 있는 `security_scopes.scopes`를 사용합니다. + +{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *} + +## 의존성 트리와 스코프 { #dependency-tree-and-scopes } + +이 의존성 트리와 스코프를 다시 살펴보겠습니다. + +`get_current_active_user` 의존성은 `get_current_user`를 하위 의존성으로 가지므로, `get_current_active_user`에서 선언된 스코프 `"me"`는 `get_current_user`에 전달되는 `security_scopes.scopes`의 요구 스코프 목록에 포함됩니다. + +*경로 처리* 자체도 스코프 `"items"`를 선언하므로, 이것 또한 `get_current_user`에 전달되는 `security_scopes.scopes` 목록에 포함됩니다. + +의존성과 스코프의 계층 구조는 다음과 같습니다: + +* *경로 처리* `read_own_items`는: + * 의존성과 함께 요구 스코프 `["items"]`를 가집니다: + * `get_current_active_user`: + * 의존성 함수 `get_current_active_user`는: + * 의존성과 함께 요구 스코프 `["me"]`를 가집니다: + * `get_current_user`: + * 의존성 함수 `get_current_user`는: + * 자체적으로는 요구 스코프가 없습니다. + * `oauth2_scheme`를 사용하는 의존성이 있습니다. + * `SecurityScopes` 타입의 `security_scopes` 매개변수가 있습니다: + * 이 `security_scopes` 매개변수는 위에서 선언된 모든 스코프를 담은 `list`인 `scopes` 속성을 가지므로: + * *경로 처리* `read_own_items`의 경우 `security_scopes.scopes`에는 `["me", "items"]`가 들어갑니다. + * *경로 처리* `read_users_me`의 경우 `security_scopes.scopes`에는 `["me"]`가 들어갑니다. 이는 의존성 `get_current_active_user`에서 선언되기 때문입니다. + * *경로 처리* `read_system_status`의 경우 `security_scopes.scopes`에는 `[]`(없음)가 들어갑니다. `scopes`가 있는 `Security`를 선언하지 않았고, 그 의존성인 `get_current_user`도 `scopes`를 선언하지 않았기 때문입니다. + +/// tip | 팁 + +여기서 중요한 “마법 같은” 점은 `get_current_user`가 각 *경로 처리*마다 검사해야 하는 `scopes` 목록이 달라진다는 것입니다. + +이는 특정 *경로 처리*에 대한 의존성 트리에서, 각 *경로 처리*와 각 의존성에 선언된 `scopes`에 따라 달라집니다. + +/// + +## `SecurityScopes`에 대한 추가 설명 { #more-details-about-securityscopes } + +`SecurityScopes`는 어느 지점에서든, 그리고 여러 곳에서 사용할 수 있으며, “루트” 의존성에만 있어야 하는 것은 아닙니다. + +`SecurityScopes`는 **해당 특정** *경로 처리*와 **해당 특정** 의존성 트리에 대해, 현재 `Security` 의존성과 모든 dependant에 선언된 보안 스코프를 항상 갖고 있습니다. + +`SecurityScopes`에는 dependant가 선언한 모든 스코프가 포함되므로, 중앙의 의존성 함수에서 토큰이 필요한 스코프를 가지고 있는지 검증한 다음, 서로 다른 *경로 처리*에서 서로 다른 스코프 요구사항을 선언할 수 있습니다. + +이들은 각 *경로 처리*마다 독립적으로 검사됩니다. + +## 확인하기 { #check-it } + +API 문서를 열면, 인증하고 인가할 스코프를 지정할 수 있습니다. + + + +어떤 스코프도 선택하지 않으면 “인증”은 되지만, `/users/me/` 또는 `/users/me/items/`에 접근하려고 하면 권한이 충분하지 않다는 오류가 발생합니다. `/status/`에는 여전히 접근할 수 있습니다. + +그리고 스코프 `me`는 선택했지만 스코프 `items`는 선택하지 않았다면, `/users/me/`에는 접근할 수 있지만 `/users/me/items/`에는 접근할 수 없습니다. + +이는 사용자가 애플리케이션에 얼마나 많은 권한을 부여했는지에 따라, 제3자 애플리케이션이 사용자로부터 제공받은 토큰으로 이 *경로 처리*들 중 하나에 접근하려고 할 때 발생하는 상황과 같습니다. + +## 제3자 통합에 대해 { #about-third-party-integrations } + +이 예제에서는 OAuth2 “password” 플로우를 사용하고 있습니다. + +이는 보통 자체 프론트엔드가 있는 우리 애플리케이션에 로그인할 때 적합합니다. + +우리가 이를 통제하므로 `username`과 `password`를 받는 것을 신뢰할 수 있기 때문입니다. + +하지만 다른 사람들이 연결할 OAuth2 애플리케이션(즉, Facebook, Google, GitHub 등과 동등한 인증 제공자를 만들고 있다면)을 구축한다면, 다른 플로우 중 하나를 사용해야 합니다. + +가장 흔한 것은 implicit 플로우입니다. + +가장 안전한 것은 code 플로우이지만, 더 많은 단계가 필요해 구현이 더 복잡합니다. 복잡하기 때문에 많은 제공자는 implicit 플로우를 권장하게 됩니다. + +/// note | 참고 + +인증 제공자마다 자신들의 브랜드의 일부로 만들기 위해, 각 플로우를 서로 다른 방식으로 이름 붙이는 경우가 흔합니다. + +하지만 결국, 동일한 OAuth2 표준을 구현하고 있는 것입니다. + +/// + +**FastAPI**는 `fastapi.security.oauth2`에 이러한 모든 OAuth2 인증 플로우를 위한 유틸리티를 포함합니다. + +## 데코레이터 `dependencies`에서의 `Security` { #security-in-decorator-dependencies } + +[경로 처리 데코레이터의 의존성](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}에서 설명한 것처럼 데코레이터의 `dependencies` 매개변수에 `Depends`의 `list`를 정의할 수 있는 것과 같은 방식으로, 거기에서 `scopes`와 함께 `Security`를 사용할 수도 있습니다. diff --git a/docs/ko/docs/advanced/settings.md b/docs/ko/docs/advanced/settings.md new file mode 100644 index 0000000000..6fa7c644cc --- /dev/null +++ b/docs/ko/docs/advanced/settings.md @@ -0,0 +1,302 @@ +# 설정과 환경 변수 { #settings-and-environment-variables } + +많은 경우 애플리케이션에는 외부 설정이나 구성(예: secret key, 데이터베이스 자격 증명, 이메일 서비스 자격 증명 등)이 필요할 수 있습니다. + +이러한 설정 대부분은 데이터베이스 URL처럼 변동 가능(변경될 수 있음)합니다. 그리고 많은 설정은 secret처럼 민감할 수 있습니다. + +이 때문에 보통 애플리케이션이 읽어들이는 환경 변수로 이를 제공하는 것이 일반적입니다. + +/// tip | 팁 + +환경 변수를 이해하려면 [환경 변수](../environment-variables.md){.internal-link target=_blank}를 읽어보세요. + +/// + +## 타입과 검증 { #types-and-validation } + +이 환경 변수들은 Python 외부에 있으며 다른 프로그램 및 시스템의 나머지 부분(그리고 Linux, Windows, macOS 같은 서로 다른 운영체제와도)과 호환되어야 하므로, 텍스트 문자열만 다룰 수 있습니다. + +즉, Python에서 환경 변수로부터 읽어온 어떤 값이든 `str`이 되며, 다른 타입으로의 변환이나 검증은 코드에서 수행해야 합니다. + +## Pydantic `Settings` { #pydantic-settings } + +다행히 Pydantic은 Pydantic: Settings management를 통해 환경 변수에서 오는 이러한 설정을 처리할 수 있는 훌륭한 유틸리티를 제공합니다. + +### `pydantic-settings` 설치하기 { #install-pydantic-settings } + +먼저 [가상 환경](../virtual-environments.md){.internal-link target=_blank}을 만들고 활성화한 다음, `pydantic-settings` 패키지를 설치하세요: + +
+ +```console +$ pip install pydantic-settings +---> 100% +``` + +
+ +또는 다음처럼 `all` extras를 설치하면 함께 포함됩니다: + +
+ +```console +$ pip install "fastapi[all]" +---> 100% +``` + +
+ +### `Settings` 객체 만들기 { #create-the-settings-object } + +Pydantic에서 `BaseSettings`를 import하고, Pydantic 모델과 매우 비슷하게 서브클래스를 만드세요. + +Pydantic 모델과 같은 방식으로, 타입 어노테이션(그리고 필요하다면 기본값)과 함께 클래스 속성을 선언합니다. + +다양한 데이터 타입, `Field()`로 추가 검증 등 Pydantic 모델에서 사용하는 동일한 검증 기능과 도구를 모두 사용할 수 있습니다. + +{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *} + +/// tip | 팁 + +빠르게 복사/붙여넣기할 예시가 필요하다면, 이 예시는 사용하지 말고 아래의 마지막 예시를 사용하세요. + +/// + +그 다음, 해당 `Settings` 클래스의 인스턴스(여기서는 `settings` 객체)를 생성하면 Pydantic이 대소문자를 구분하지 않고 환경 변수를 읽습니다. 따라서 대문자 변수 `APP_NAME`도 `app_name` 속성에 대해 읽힙니다. + +이후 데이터를 변환하고 검증합니다. 그래서 그 `settings` 객체를 사용할 때는 선언한 타입의 데이터를 갖게 됩니다(예: `items_per_user`는 `int`가 됩니다). + +### `settings` 사용하기 { #use-the-settings } + +이제 애플리케이션에서 새 `settings` 객체를 사용할 수 있습니다: + +{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *} + +### 서버 실행하기 { #run-the-server } + +다음으로 환경 변수를 통해 구성을 전달하면서 서버를 실행합니다. 예를 들어 다음처럼 `ADMIN_EMAIL`과 `APP_NAME`을 설정할 수 있습니다: + +
+ +```console +$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +/// tip | 팁 + +하나의 명령에 여러 env var를 설정하려면 공백으로 구분하고, 모두 명령 앞에 두세요. + +/// + +그러면 `admin_email` 설정은 `"deadpool@example.com"`으로 설정됩니다. + +`app_name`은 `"ChimichangApp"`이 됩니다. + +그리고 `items_per_user`는 기본값 `50`을 유지합니다. + +## 다른 모듈의 설정 { #settings-in-another-module } + +[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}에서 본 것처럼, 설정을 다른 모듈 파일에 넣을 수도 있습니다. + +예를 들어 `config.py` 파일을 다음처럼 만들 수 있습니다: + +{* ../../docs_src/settings/app01_py39/config.py *} + +그리고 `main.py` 파일에서 이를 사용합니다: + +{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *} + +/// tip | 팁 + +[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}에서 본 것처럼 `__init__.py` 파일도 필요합니다. + +/// + +## 의존성에서 설정 사용하기 { #settings-in-a-dependency } + +어떤 경우에는 어디서나 사용되는 전역 `settings` 객체를 두는 대신, 의존성에서 설정을 제공하는 것이 유용할 수 있습니다. + +이는 특히 테스트 중에 유용할 수 있는데, 사용자 정의 설정으로 의존성을 override하기가 매우 쉽기 때문입니다. + +### config 파일 { #the-config-file } + +이전 예시에서 이어서, `config.py` 파일은 다음과 같을 수 있습니다: + +{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *} + +이제는 기본 인스턴스 `settings = Settings()`를 생성하지 않는다는 점에 유의하세요. + +### 메인 앱 파일 { #the-main-app-file } + +이제 새로운 `config.Settings()`를 반환하는 의존성을 생성합니다. + +{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *} + +/// tip | 팁 + +`@lru_cache`는 조금 뒤에 다룹니다. + +지금은 `get_settings()`가 일반 함수라고 가정해도 됩니다. + +/// + +그 다음 *경로 처리 함수*에서 이를 의존성으로 요구하고, 필요한 어디서든 사용할 수 있습니다. + +{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *} + +### 설정과 테스트 { #settings-and-testing } + +그 다음, `get_settings`에 대한 의존성 override를 만들어 테스트 중에 다른 설정 객체를 제공하기가 매우 쉬워집니다: + +{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *} + +의존성 override에서는 새 `Settings` 객체를 생성할 때 `admin_email`의 새 값을 설정하고, 그 새 객체를 반환합니다. + +그 다음 그것이 사용되는지 테스트할 수 있습니다. + +## `.env` 파일 읽기 { #reading-a-env-file } + +많이 바뀔 수 있는 설정이 많고, 서로 다른 환경에서 사용한다면, 이를 파일에 넣어 환경 변수인 것처럼 읽는 것이 유용할 수 있습니다. + +이 관행은 충분히 흔해서 이름도 있는데, 이러한 환경 변수들은 보통 `.env` 파일에 두며, 그 파일을 "dotenv"라고 부릅니다. + +/// tip | 팁 + +점(`.`)으로 시작하는 파일은 Linux, macOS 같은 Unix 계열 시스템에서 숨김 파일입니다. + +하지만 dotenv 파일이 꼭 그 정확한 파일명을 가져야 하는 것은 아닙니다. + +/// + +Pydantic은 외부 라이브러리를 사용해 이런 유형의 파일에서 읽는 기능을 지원합니다. 자세한 내용은 Pydantic Settings: Dotenv (.env) support를 참고하세요. + +/// tip | 팁 + +이를 사용하려면 `pip install python-dotenv`가 필요합니다. + +/// + +### `.env` 파일 { #the-env-file } + +다음과 같은 `.env` 파일을 둘 수 있습니다: + +```bash +ADMIN_EMAIL="deadpool@example.com" +APP_NAME="ChimichangApp" +``` + +### `.env`에서 설정 읽기 { #read-settings-from-env } + +그리고 `config.py`를 다음처럼 업데이트합니다: + +{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *} + +/// tip | 팁 + +`model_config` 속성은 Pydantic 설정을 위한 것입니다. 자세한 내용은 Pydantic: Concepts: Configuration을 참고하세요. + +/// + +여기서는 Pydantic `Settings` 클래스 안에 config `env_file`을 정의하고, 사용하려는 dotenv 파일의 파일명을 값으로 설정합니다. + +### `lru_cache`로 `Settings`를 한 번만 생성하기 { #creating-the-settings-only-once-with-lru-cache } + +디스크에서 파일을 읽는 것은 보통 비용이 큰(느린) 작업이므로, 각 요청마다 읽기보다는 한 번만 수행하고 동일한 settings 객체를 재사용하는 것이 좋습니다. + +하지만 매번 다음을 수행하면: + +```Python +Settings() +``` + +새 `Settings` 객체가 생성되고, 생성 시점에 `.env` 파일을 다시 읽게 됩니다. + +의존성 함수가 단순히 다음과 같다면: + +```Python +def get_settings(): + return Settings() +``` + +요청마다 객체를 생성하게 되고, 요청마다 `.env` 파일을 읽게 됩니다. ⚠️ + +하지만 위에 `@lru_cache` 데코레이터를 사용하고 있으므로, `Settings` 객체는 최초 호출 시 딱 한 번만 생성됩니다. ✔️ + +{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *} + +그 다음 요청들에서 의존성으로 `get_settings()`가 다시 호출될 때마다, `get_settings()`의 내부 코드를 실행해서 새 `Settings` 객체를 만드는 대신, 첫 호출에서 반환된 동일한 객체를 계속 반환합니다. + +#### `lru_cache` Technical Details { #lru-cache-technical-details } + +`@lru_cache`는 데코레이션한 함수가 매번 다시 계산하는 대신, 첫 번째에 반환된 동일한 값을 반환하도록 함수를 수정합니다(즉, 매번 함수 코드를 실행하지 않습니다). + +따라서 아래의 함수는 인자 조합마다 한 번씩 실행됩니다. 그리고 각각의 인자 조합에 대해 반환된 값은, 함수가 정확히 같은 인자 조합으로 호출될 때마다 반복해서 사용됩니다. + +예를 들어 다음 함수가 있다면: + +```Python +@lru_cache +def say_hi(name: str, salutation: str = "Ms."): + return f"Hello {salutation} {name}" +``` + +프로그램은 다음과 같이 실행될 수 있습니다: + +```mermaid +sequenceDiagram + +participant code as Code +participant function as say_hi() +participant execute as Execute function + + rect rgba(0, 255, 0, .1) + code ->> function: say_hi(name="Camila") + function ->> execute: execute function code + execute ->> code: return the result + end + + rect rgba(0, 255, 255, .1) + code ->> function: say_hi(name="Camila") + function ->> code: return stored result + end + + rect rgba(0, 255, 0, .1) + code ->> function: say_hi(name="Rick") + function ->> execute: execute function code + execute ->> code: return the result + end + + rect rgba(0, 255, 0, .1) + code ->> function: say_hi(name="Rick", salutation="Mr.") + function ->> execute: execute function code + execute ->> code: return the result + end + + rect rgba(0, 255, 255, .1) + code ->> function: say_hi(name="Rick") + function ->> code: return stored result + end + + rect rgba(0, 255, 255, .1) + code ->> function: say_hi(name="Camila") + function ->> code: return stored result + end +``` + +우리의 의존성 `get_settings()`의 경우, 함수가 어떤 인자도 받지 않으므로 항상 같은 값을 반환합니다. + +이렇게 하면 거의 전역 변수처럼 동작합니다. 하지만 의존성 함수를 사용하므로 테스트를 위해 쉽게 override할 수 있습니다. + +`@lru_cache`는 Python 표준 라이브러리의 `functools`에 포함되어 있으며, 자세한 내용은 `@lru_cache`에 대한 Python 문서에서 확인할 수 있습니다. + +## 정리 { #recap } + +Pydantic Settings를 사용하면 Pydantic 모델의 모든 강력한 기능을 활용해 애플리케이션의 설정 또는 구성을 처리할 수 있습니다. + +* 의존성을 사용하면 테스트를 단순화할 수 있습니다. +* `.env` 파일을 사용할 수 있습니다. +* `@lru_cache`를 사용하면 각 요청마다 dotenv 파일을 반복해서 읽는 것을 피하면서도, 테스트 중에는 이를 override할 수 있습니다. diff --git a/docs/ko/docs/alternatives.md b/docs/ko/docs/alternatives.md new file mode 100644 index 0000000000..d8c2df2d8d --- /dev/null +++ b/docs/ko/docs/alternatives.md @@ -0,0 +1,485 @@ +# 대안, 영감, 비교 { #alternatives-inspiration-and-comparisons } + +**FastAPI**에 영감을 준 것들, 대안과의 비교, 그리고 그로부터 무엇을 배웠는지에 대한 내용입니다. + +## 소개 { #intro } + +다른 사람들의 이전 작업이 없었다면 **FastAPI**는 존재하지 않았을 것입니다. + +그 전에 만들어진 많은 도구들이 **FastAPI**의 탄생에 영감을 주었습니다. + +저는 여러 해 동안 새로운 framework를 만드는 것을 피하고 있었습니다. 먼저 **FastAPI**가 다루는 모든 기능을 여러 서로 다른 framework, plug-in, 도구를 사용해 해결해 보려고 했습니다. + +하지만 어느 시점에는, 이전 도구들의 가장 좋은 아이디어를 가져와 가능한 최선의 방식으로 조합하고, 이전에는 존재하지 않았던 언어 기능(Python 3.6+ type hints)을 활용해 이 모든 기능을 제공하는 무언가를 만드는 것 외에는 다른 선택지가 없었습니다. + +## 이전 도구들 { #previous-tools } + +### Django { #django } + +가장 인기 있는 Python framework이며 널리 신뢰받고 있습니다. Instagram 같은 시스템을 만드는 데 사용됩니다. + +상대적으로 관계형 데이터베이스(예: MySQL 또는 PostgreSQL)와 강하게 결합되어 있어서, NoSQL 데이터베이스(예: Couchbase, MongoDB, Cassandra 등)를 주 저장 엔진으로 사용하는 것은 그리 쉽지 않습니다. + +백엔드에서 HTML을 생성하기 위해 만들어졌지, 현대적인 프런트엔드(예: React, Vue.js, Angular)나 다른 시스템(예: IoT 기기)에서 사용되는 API를 만들기 위해 설계된 것은 아닙니다. + +### Django REST Framework { #django-rest-framework } + +Django REST framework는 Django를 기반으로 Web API를 구축하기 위한 유연한 toolkit으로 만들어졌고, Django의 API 기능을 개선하기 위한 목적이었습니다. + +Mozilla, Red Hat, Eventbrite를 포함해 많은 회사에서 사용합니다. + +**자동 API 문서화**의 초기 사례 중 하나였고, 이것이 특히 **FastAPI**를 "찾게 된" 첫 아이디어 중 하나였습니다. + +/// note | 참고 + +Django REST Framework는 Tom Christie가 만들었습니다. **FastAPI**의 기반이 되는 Starlette와 Uvicorn을 만든 사람과 동일합니다. + +/// + +/// check | **FastAPI**에 영감을 준 것 + +자동 API 문서화 웹 사용자 인터페이스를 제공하기. + +/// + +### Flask { #flask } + +Flask는 "microframework"로, Django에 기본으로 포함된 데이터베이스 통합이나 여러 기능들을 포함하지 않습니다. + +이 단순함과 유연성 덕분에 NoSQL 데이터베이스를 주 데이터 저장 시스템으로 사용하는 같은 작업이 가능합니다. + +매우 단순하기 때문에 비교적 직관적으로 배울 수 있지만, 문서가 어떤 지점에서는 다소 기술적으로 깊어지기도 합니다. + +또한 데이터베이스, 사용자 관리, 혹은 Django에 미리 구축되어 있는 다양한 기능들이 꼭 필요하지 않은 다른 애플리케이션에도 흔히 사용됩니다. 물론 이런 기능들 중 다수는 plug-in으로 추가할 수 있습니다. + +이런 구성요소의 분리와, 필요한 것만 정확히 덧붙여 확장할 수 있는 "microframework"라는 점은 제가 유지하고 싶었던 핵심 특성이었습니다. + +Flask의 단순함을 고려하면 API를 구축하는 데 잘 맞는 것처럼 보였습니다. 다음으로 찾고자 했던 것은 Flask용 "Django REST Framework"였습니다. + +/// check | **FastAPI**에 영감을 준 것 + +micro-framework가 되기. 필요한 도구와 구성요소를 쉽게 조합할 수 있도록 하기. + +단순하고 사용하기 쉬운 routing 시스템을 갖기. + +/// + +### Requests { #requests } + +**FastAPI**는 실제로 **Requests**의 대안이 아닙니다. 둘의 범위는 매우 다릅니다. + +실제로 FastAPI 애플리케이션 *내부에서* Requests를 사용하는 경우도 흔합니다. + +그럼에도 FastAPI는 Requests로부터 꽤 많은 영감을 얻었습니다. + +**Requests**는 (클라이언트로서) API와 *상호작용*하기 위한 라이브러리이고, **FastAPI**는 (서버로서) API를 *구축*하기 위한 라이브러리입니다. + +대략 말하면 서로 반대편에 있으며, 서로를 보완합니다. + +Requests는 매우 단순하고 직관적인 설계를 가졌고, 합리적인 기본값을 바탕으로 사용하기가 아주 쉽습니다. 동시에 매우 강력하고 커스터마이징도 가능합니다. + +그래서 공식 웹사이트에서 말하듯이: + +> Requests is one of the most downloaded Python packages of all time + +사용 방법은 매우 간단합니다. 예를 들어 `GET` 요청을 하려면 다음처럼 작성합니다: + +```Python +response = requests.get("http://example.com/some/url") +``` + +이에 대응하는 FastAPI의 API *경로 처리*는 다음과 같이 보일 수 있습니다: + +```Python hl_lines="1" +@app.get("/some/url") +def read_url(): + return {"message": "Hello World"} +``` + +`requests.get(...)`와 `@app.get(...)`의 유사성을 확인해 보세요. + +/// check | **FastAPI**에 영감을 준 것 + +* 단순하고 직관적인 API를 갖기. +* HTTP method 이름(operations)을 직접, 직관적이고 명확한 방식으로 사용하기. +* 합리적인 기본값을 제공하되, 강력한 커스터마이징을 가능하게 하기. + +/// + +### Swagger / OpenAPI { #swagger-openapi } + +제가 Django REST Framework에서 가장 원했던 주요 기능은 자동 API 문서화였습니다. + +그 후 JSON(또는 JSON의 확장인 YAML)을 사용해 API를 문서화하는 표준인 Swagger가 있다는 것을 알게 되었습니다. + +그리고 Swagger API를 위한 웹 사용자 인터페이스도 이미 만들어져 있었습니다. 그래서 API에 대한 Swagger 문서를 생성할 수 있다면, 이 웹 사용자 인터페이스를 자동으로 사용할 수 있게 됩니다. + +어느 시점에 Swagger는 Linux Foundation으로 넘어가 OpenAPI로 이름이 바뀌었습니다. + +그래서 2.0 버전을 이야기할 때는 "Swagger"라고 말하는 것이 일반적이고, 3+ 버전은 "OpenAPI"라고 말하는 것이 일반적입니다. + +/// check | **FastAPI**에 영감을 준 것 + +커스텀 schema 대신, API 사양을 위한 열린 표준을 채택하고 사용하기. + +또한 표준 기반의 사용자 인터페이스 도구를 통합하기: + +* Swagger UI +* ReDoc + +이 두 가지는 꽤 대중적이고 안정적이기 때문에 선택되었습니다. 하지만 간단히 검색해보면 OpenAPI를 위한 대안 UI가 수십 가지나 있다는 것을 알 수 있습니다(**FastAPI**와 함께 사용할 수 있습니다). + +/// + +### Flask REST framework들 { #flask-rest-frameworks } + +Flask REST framework는 여러 개가 있지만, 시간을 들여 조사해 본 결과, 상당수가 중단되었거나 방치되어 있었고, 해결되지 않은 여러 이슈 때문에 적합하지 않은 경우가 많았습니다. + +### Marshmallow { #marshmallow } + +API 시스템에 필요한 주요 기능 중 하나는 데이터 "serialization"입니다. 이는 코드(Python)에서 데이터를 가져와 네트워크로 전송할 수 있는 형태로 변환하는 것을 의미합니다. 예를 들어 데이터베이스의 데이터를 담은 객체를 JSON 객체로 변환하거나, `datetime` 객체를 문자열로 변환하는 등의 작업입니다. + +API에 또 하나 크게 필요한 기능은 데이터 검증입니다. 특정 파라미터를 기준으로 데이터가 유효한지 확인하는 것입니다. 예를 들어 어떤 필드가 `int`인지, 임의의 문자열이 아닌지 확인하는 식입니다. 이는 특히 들어오는 데이터에 유용합니다. + +데이터 검증 시스템이 없다면, 모든 검사를 코드에서 수동으로 해야 합니다. + +이런 기능들을 제공하기 위해 Marshmallow가 만들어졌습니다. 훌륭한 라이브러리이며, 저도 이전에 많이 사용했습니다. + +하지만 Python type hints가 존재하기 전에 만들어졌습니다. 그래서 각 schema를 정의하려면 Marshmallow가 제공하는 특정 유틸리티와 클래스를 사용해야 합니다. + +/// check | **FastAPI**에 영감을 준 것 + +데이터 타입과 검증을 제공하는 "schema"를 코드로 정의하고, 이를 자동으로 활용하기. + +/// + +### Webargs { #webargs } + +API에 필요한 또 다른 큰 기능은 들어오는 요청에서 데이터를 parsing하는 것입니다. + +Webargs는 Flask를 포함한 여러 framework 위에서 이를 제공하기 위해 만들어진 도구입니다. + +내부적으로 Marshmallow를 사용해 데이터 검증을 수행합니다. 그리고 같은 개발자들이 만들었습니다. + +아주 훌륭한 도구이며, 저도 **FastAPI**를 만들기 전에 많이 사용했습니다. + +/// info | 정보 + +Webargs는 Marshmallow와 같은 개발자들이 만들었습니다. + +/// + +/// check | **FastAPI**에 영감을 준 것 + +들어오는 요청 데이터의 자동 검증을 갖기. + +/// + +### APISpec { #apispec } + +Marshmallow와 Webargs는 plug-in 형태로 검증, parsing, serialization을 제공합니다. + +하지만 문서화는 여전히 부족했습니다. 그래서 APISpec이 만들어졌습니다. + +이는 여러 framework를 위한 plug-in이며(Starlette용 plug-in도 있습니다). + +작동 방식은, 각 route를 처리하는 함수의 docstring 안에 YAML 형식으로 schema 정의를 작성하고, + +그로부터 OpenAPI schema를 생성합니다. + +Flask, Starlette, Responder 등에서 이런 방식으로 동작합니다. + +하지만 다시, Python 문자열 내부(큰 YAML)에서 micro-syntax를 다루어야 한다는 문제가 있습니다. + +에디터가 이를 크게 도와주지 못합니다. 또한 파라미터나 Marshmallow schema를 수정해놓고 YAML docstring도 같이 수정하는 것을 잊어버리면, 생성된 schema는 오래된 상태가 됩니다. + +/// info | 정보 + +APISpec은 Marshmallow와 같은 개발자들이 만들었습니다. + +/// + +/// check | **FastAPI**에 영감을 준 것 + +API를 위한 열린 표준인 OpenAPI를 지원하기. + +/// + +### Flask-apispec { #flask-apispec } + +Flask plug-in으로, Webargs, Marshmallow, APISpec을 묶어줍니다. + +Webargs와 Marshmallow의 정보를 사용해 APISpec으로 OpenAPI schema를 자동 생성합니다. + +훌륭한 도구인데도 과소평가되어 있습니다. 다른 많은 Flask plug-in보다 훨씬 더 유명해져야 합니다. 문서가 너무 간결하고 추상적이라서 그럴 수도 있습니다. + +이 도구는 Python docstring 내부에 YAML(또 다른 문법)을 작성해야 하는 문제를 해결했습니다. + +Flask + Flask-apispec + Marshmallow + Webargs 조합은 **FastAPI**를 만들기 전까지 제가 가장 좋아하던 백엔드 stack이었습니다. + +이를 사용하면서 여러 Flask full-stack generator가 만들어졌습니다. 이것들이 지금까지 저(그리고 여러 외부 팀)가 사용해 온 주요 stack입니다: + +* https://github.com/tiangolo/full-stack +* https://github.com/tiangolo/full-stack-flask-couchbase +* https://github.com/tiangolo/full-stack-flask-couchdb + +그리고 이 동일한 full-stack generator들이 [**FastAPI** Project Generators](project-generation.md){.internal-link target=_blank}의 기반이 되었습니다. + +/// info | 정보 + +Flask-apispec은 Marshmallow와 같은 개발자들이 만들었습니다. + +/// + +/// check | **FastAPI**에 영감을 준 것 + +serialization과 validation을 정의하는 동일한 코드로부터 OpenAPI schema를 자동 생성하기. + +/// + +### NestJS (그리고 Angular) { #nestjs-and-angular } + +이건 Python도 아닙니다. NestJS는 Angular에서 영감을 받은 JavaScript(TypeScript) NodeJS framework입니다. + +Flask-apispec으로 할 수 있는 것과 어느 정도 비슷한 것을 달성합니다. + +Angular 2에서 영감을 받은 의존성 주입 시스템이 통합되어 있습니다. 제가 아는 다른 의존성 주입 시스템들처럼 "injectable"을 사전에 등록해야 하므로, 장황함과 코드 반복이 늘어납니다. + +파라미터가 TypeScript 타입(Python type hints와 유사함)으로 설명되기 때문에 에디터 지원은 꽤 좋습니다. + +하지만 TypeScript 데이터는 JavaScript로 컴파일된 뒤에는 보존되지 않기 때문에, 타입에 의존해 검증, serialization, 문서화를 동시에 정의할 수 없습니다. 이 점과 일부 설계 결정 때문에, 검증/serialization/자동 schema 생성을 하려면 여러 곳에 decorator를 추가해야 하며, 결과적으로 매우 장황해집니다. + +중첩 모델을 잘 처리하지 못합니다. 즉, 요청의 JSON body가 내부 필드를 가진 JSON 객체이고 그 내부 필드들이 다시 중첩된 JSON 객체인 경우, 제대로 문서화하고 검증할 수 없습니다. + +/// check | **FastAPI**에 영감을 준 것 + +Python 타입을 사용해 뛰어난 에디터 지원을 제공하기. + +강력한 의존성 주입 시스템을 갖추기. 코드 반복을 최소화하는 방법을 찾기. + +/// + +### Sanic { #sanic } + +`asyncio` 기반의 매우 빠른 Python framework 중 초기 사례였습니다. Flask와 매우 유사하게 만들어졌습니다. + +/// note | 기술 세부사항 + +기본 Python `asyncio` 루프 대신 `uvloop`를 사용했습니다. 이것이 매우 빠르게 만든 요인입니다. + +이는 Uvicorn과 Starlette에 명확히 영감을 주었고, 현재 공개 benchmark에서는 이 둘이 Sanic보다 더 빠릅니다. + +/// + +/// check | **FastAPI**에 영감을 준 것 + +미친 성능을 낼 수 있는 방법을 찾기. + +그래서 **FastAPI**는 Starlette를 기반으로 합니다. Starlette는 사용 가능한 framework 중 가장 빠르기 때문입니다(서드파티 benchmark로 테스트됨). + +/// + +### Falcon { #falcon } + +Falcon은 또 다른 고성능 Python framework로, 최소한으로 설계되었고 Hug 같은 다른 framework의 기반으로 동작하도록 만들어졌습니다. + +함수가 두 개의 파라미터(하나는 "request", 하나는 "response")를 받도록 설계되어 있습니다. 그런 다음 request에서 일부를 "읽고", response에 일부를 "작성"합니다. 이 설계 때문에, 표준 Python type hints를 함수 파라미터로 사용해 요청 파라미터와 body를 선언하는 것이 불가능합니다. + +따라서 데이터 검증, serialization, 문서화는 자동으로 되지 않고 코드로 해야 합니다. 또는 Hug처럼 Falcon 위에 framework를 얹어 구현해야 합니다. request 객체 하나와 response 객체 하나를 파라미터로 받는 Falcon의 설계에서 영감을 받은 다른 framework에서도 같은 구분이 나타납니다. + +/// check | **FastAPI**에 영감을 준 것 + +훌륭한 성능을 얻는 방법을 찾기. + +Hug(= Falcon 기반)과 함께, 함수에서 `response` 파라미터를 선언하도록 **FastAPI**에 영감을 주었습니다. + +다만 FastAPI에서는 선택 사항이며, 주로 헤더, 쿠키, 그리고 대체 status code를 설정하는 데 사용됩니다. + +/// + +### Molten { #molten } + +**FastAPI**를 만들기 시작한 초기 단계에서 Molten을 알게 되었고, 꽤 비슷한 아이디어를 갖고 있었습니다: + +* Python type hints 기반 +* 이 타입으로부터 검증과 문서화 생성 +* 의존성 주입 시스템 + +Pydantic 같은 서드파티 라이브러리를 사용해 데이터 검증/serialization/문서화를 하지 않고 자체 구현을 사용합니다. 그래서 이런 데이터 타입 정의를 쉽게 재사용하기는 어렵습니다. + +조금 더 장황한 설정이 필요합니다. 또한 WSGI(ASGI가 아니라) 기반이므로, Uvicorn, Starlette, Sanic 같은 도구가 제공하는 고성능을 활용하도록 설계되지 않았습니다. + +의존성 주입 시스템은 의존성을 사전에 등록해야 하고, 선언된 타입을 기반으로 의존성을 해결합니다. 따라서 특정 타입을 제공하는 "component"를 두 개 이상 선언할 수 없습니다. + +Route는 한 곳에서 선언하고, 다른 곳에 선언된 함수를 사용합니다(엔드포인트를 처리하는 함수 바로 위에 둘 수 있는 decorator를 사용하는 대신). 이는 Flask(및 Starlette)보다는 Django 방식에 가깝습니다. 코드에서 상대적으로 강하게 결합된 것들을 분리해 놓습니다. + +/// check | **FastAPI**에 영감을 준 것 + +모델 속성의 "default" 값으로 데이터 타입에 대한 추가 검증을 정의하기. 이는 에디터 지원을 개선하며, 이전에는 Pydantic에 없었습니다. + +이것은 실제로 Pydantic의 일부를 업데이트하여 같은 검증 선언 스타일을 지원하도록 하는 데 영감을 주었습니다(이 기능은 이제 Pydantic에 이미 포함되어 있습니다). + +/// + +### Hug { #hug } + +Hug는 Python type hints를 사용해 API 파라미터 타입을 선언하는 기능을 구현한 초기 framework 중 하나였습니다. 이는 다른 도구들도 같은 방식을 하도록 영감을 준 훌륭한 아이디어였습니다. + +표준 Python 타입 대신 커스텀 타입을 선언에 사용했지만, 여전히 큰 진전이었습니다. + +또한 전체 API를 JSON으로 선언하는 커스텀 schema를 생성한 초기 framework 중 하나였습니다. + +OpenAPI나 JSON Schema 같은 표준을 기반으로 하지 않았기 때문에 Swagger UI 같은 다른 도구와 통합하는 것은 직관적이지 않았습니다. 하지만 역시 매우 혁신적인 아이디어였습니다. + +흥미롭고 흔치 않은 기능이 하나 있습니다. 같은 framework로 API뿐 아니라 CLI도 만들 수 있습니다. + +동기식 Python 웹 framework의 이전 표준(WSGI) 기반이어서 Websockets와 다른 것들을 처리할 수는 없지만, 성능은 여전히 높습니다. + +/// info | 정보 + +Hug는 Timothy Crosley가 만들었습니다. Python 파일에서 import를 자동으로 정렬하는 훌륭한 도구인 `isort`의 제작자이기도 합니다. + +/// + +/// check | **FastAPI**에 영감을 준 아이디어들 + +Hug는 APIStar의 일부에 영감을 주었고, 저는 APIStar와 함께 Hug를 가장 유망한 도구 중 하나로 보았습니다. + +Hug는 Python type hints로 파라미터를 선언하고, API를 정의하는 schema를 자동으로 생성하도록 **FastAPI**에 영감을 주었습니다. + +Hug는 헤더와 쿠키를 설정하기 위해 함수에 `response` 파라미터를 선언하도록 **FastAPI**에 영감을 주었습니다. + +/// + +### APIStar (<= 0.5) { #apistar-0-5 } + +**FastAPI**를 만들기로 결정하기 직전에 **APIStar** 서버를 발견했습니다. 찾고 있던 거의 모든 것을 갖추고 있었고 설계도 훌륭했습니다. + +NestJS와 Molten보다 앞서, Python type hints를 사용해 파라미터와 요청을 선언하는 framework 구현을 제가 처음 본 사례들 중 하나였습니다. Hug와 거의 같은 시기에 발견했습니다. 하지만 APIStar는 OpenAPI 표준을 사용했습니다. + +여러 위치에서 동일한 type hints를 기반으로 자동 데이터 검증, 데이터 serialization, OpenAPI schema 생성을 제공했습니다. + +Body schema 정의는 Pydantic처럼 동일한 Python type hints를 사용하지는 않았고 Marshmallow와 조금 더 비슷해서 에디터 지원은 그만큼 좋지 않았지만, 그래도 APIStar는 당시 사용할 수 있는 최선의 선택지였습니다. + +당시 최고의 성능 benchmark를 가졌습니다(Starlette에 의해서만 추월됨). + +처음에는 자동 API 문서화 웹 UI가 없었지만, Swagger UI를 추가할 수 있다는 것을 알고 있었습니다. + +의존성 주입 시스템도 있었습니다. 위에서 언급한 다른 도구들처럼 component의 사전 등록이 필요했지만, 여전히 훌륭한 기능이었습니다. + +보안 통합이 없어서 전체 프로젝트에서 사용해 볼 수는 없었습니다. 그래서 Flask-apispec 기반 full-stack generator로 갖추고 있던 모든 기능을 대체할 수 없었습니다. 그 기능을 추가하는 pull request를 만드는 것이 제 백로그에 있었습니다. + +하지만 이후 프로젝트의 초점이 바뀌었습니다. + +더 이상 API web framework가 아니게 되었는데, 제작자가 Starlette에 집중해야 했기 때문입니다. + +이제 APIStar는 web framework가 아니라 OpenAPI 사양을 검증하기 위한 도구 모음입니다. + +/// info | 정보 + +APIStar는 Tom Christie가 만들었습니다. 다음을 만든 사람과 동일합니다: + +* Django REST Framework +* Starlette(**FastAPI**의 기반) +* Uvicorn(Starlette와 **FastAPI**에서 사용) + +/// + +/// check | **FastAPI**에 영감을 준 것 + +존재하게 만들기. + +동일한 Python 타입으로 여러 가지(데이터 검증, serialization, 문서화)를 선언하면서 동시에 뛰어난 에디터 지원을 제공한다는 아이디어는 제가 매우 훌륭하다고 생각했습니다. + +그리고 오랫동안 비슷한 framework를 찾아 여러 대안을 테스트한 끝에, APIStar가 그때 이용 가능한 최선의 선택지였습니다. + +그 후 APIStar 서버가 더는 존재하지 않게 되고 Starlette가 만들어졌는데, 이는 그런 시스템을 위한 더 새롭고 더 나은 기반이었습니다. 이것이 **FastAPI**를 만들게 된 최종 영감이었습니다. + +저는 **FastAPI**를 APIStar의 "정신적 후계자"로 생각합니다. 동시에, 이 모든 이전 도구들에서 배운 것들을 바탕으로 기능, typing 시스템, 그리고 다른 부분들을 개선하고 확장했습니다. + +/// + +## **FastAPI**가 사용하는 것 { #used-by-fastapi } + +### Pydantic { #pydantic } + +Pydantic은 Python type hints를 기반으로 데이터 검증, serialization, 문서화(JSON Schema 사용)를 정의하는 라이브러리입니다. + +그 덕분에 매우 직관적입니다. + +Marshmallow와 비교할 수 있습니다. 다만 benchmark에서 Marshmallow보다 빠릅니다. 그리고 동일한 Python type hints를 기반으로 하므로 에디터 지원도 훌륭합니다. + +/// check | **FastAPI**가 이를 사용하는 목적 + +모든 데이터 검증, 데이터 serialization, 자동 모델 문서화(JSON Schema 기반)를 처리하기. + +그 다음 **FastAPI**는 그 JSON Schema 데이터를 가져와 OpenAPI에 포함시키며, 그 외에도 여러 작업을 수행합니다. + +/// + +### Starlette { #starlette } + +Starlette는 경량 ASGI framework/toolkit으로, 고성능 asyncio 서비스를 만들기에 이상적입니다. + +매우 단순하고 직관적입니다. 쉽게 확장할 수 있도록 설계되었고, 모듈식 component를 갖습니다. + +다음이 포함됩니다: + +* 정말 인상적인 성능. +* WebSocket 지원. +* 프로세스 내 백그라운드 작업. +* 시작 및 종료 이벤트. +* HTTPX 기반의 테스트 클라이언트. +* CORS, GZip, Static Files, Streaming responses. +* 세션 및 쿠키 지원. +* 100% 테스트 커버리지. +* 100% 타입 주석이 달린 코드베이스. +* 소수의 필수 의존성. + +Starlette는 현재 테스트된 Python framework 중 가장 빠릅니다. 단, framework가 아니라 서버인 Uvicorn이 더 빠릅니다. + +Starlette는 웹 microframework의 기본 기능을 모두 제공합니다. + +하지만 자동 데이터 검증, serialization, 문서화는 제공하지 않습니다. + +그것이 **FastAPI**가 위에 추가하는 핵심 중 하나이며, 모두 Python type hints(Pydantic 사용)를 기반으로 합니다. 여기에 더해 의존성 주입 시스템, 보안 유틸리티, OpenAPI schema 생성 등도 포함됩니다. + +/// note | 기술 세부사항 + +ASGI는 Django 코어 팀 멤버들이 개발 중인 새로운 "표준"입니다. 아직 "Python 표준"(PEP)은 아니지만, 그 방향으로 진행 중입니다. + +그럼에도 이미 여러 도구에서 "표준"으로 사용되고 있습니다. 이는 상호운용성을 크게 개선합니다. 예를 들어 Uvicorn을 다른 ASGI 서버(예: Daphne 또는 Hypercorn)로 교체할 수도 있고, `python-socketio` 같은 ASGI 호환 도구를 추가할 수도 있습니다. + +/// + +/// check | **FastAPI**가 이를 사용하는 목적 + +핵심 웹 부분을 모두 처리하기. 그 위에 기능을 추가하기. + +`FastAPI` 클래스 자체는 `Starlette` 클래스를 직접 상속합니다. + +따라서 Starlette로 할 수 있는 모든 것은 기본적으로 **FastAPI**로도 직접 할 수 있습니다. 즉, **FastAPI**는 사실상 Starlette에 강력한 기능을 더한 것입니다. + +/// + +### Uvicorn { #uvicorn } + +Uvicorn은 uvloop과 httptools로 구축된 초고속 ASGI 서버입니다. + +web framework가 아니라 서버입니다. 예를 들어 경로 기반 routing을 위한 도구는 제공하지 않습니다. 그런 것은 Starlette(또는 **FastAPI**) 같은 framework가 위에서 제공합니다. + +Starlette와 **FastAPI**에서 권장하는 서버입니다. + +/// check | **FastAPI**가 이를 권장하는 방식 + +**FastAPI** 애플리케이션을 실행하기 위한 주요 웹 서버. + +또한 `--workers` 커맨드라인 옵션을 사용하면 비동기 멀티프로세스 서버로 실행할 수도 있습니다. + +자세한 내용은 [배포](deployment/index.md){.internal-link target=_blank} 섹션을 확인하세요. + +/// + +## 벤치마크와 속도 { #benchmarks-and-speed } + +Uvicorn, Starlette, FastAPI 사이의 차이를 이해하고 비교하려면 [벤치마크](benchmarks.md){.internal-link target=_blank} 섹션을 확인하세요. diff --git a/docs/ko/docs/async.md b/docs/ko/docs/async.md index ec503d5406..36f1ca6bf1 100644 --- a/docs/ko/docs/async.md +++ b/docs/ko/docs/async.md @@ -1,18 +1,18 @@ -# 동시성과 async / await +# 동시성과 async / await { #concurrency-and-async-await } -*경로 작동 함수*에서의 `async def` 문법에 대한 세부사항과 비동기 코드, 동시성 및 병렬성에 대한 배경 +*경로 처리 함수*에서의 `async def` 문법에 대한 세부사항과 비동기 코드, 동시성 및 병렬성에 대한 배경 -## 바쁘신 경우 +## 바쁘신가요? { #in-a-hurry } -요약 +TL;DR: -다음과 같이 `await`를 사용해 호출하는 제3의 라이브러리를 사용하는 경우: +다음과 같이 `await`를 사용해 호출하라고 안내하는 제3자 라이브러리를 사용하는 경우: ```Python results = await some_library() ``` -다음처럼 *경로 작동 함수*를 `async def`를 사용해 선언하십시오: +다음처럼 *경로 처리 함수*를 `async def`를 사용해 선언하십시오: ```Python hl_lines="2" @app.get('/') @@ -29,7 +29,7 @@ async def read_results(): --- -데이터베이스, API, 파일시스템 등과 의사소통하는 제3의 라이브러리를 사용하고, 그것이 `await`를 지원하지 않는 경우(현재 거의 모든 데이터베이스 라이브러리가 그러합니다), *경로 작동 함수*를 일반적인 `def`를 사용해 선언하십시오: +데이터베이스, API, 파일시스템 등과 의사소통하는 제3자 라이브러리를 사용하고, 그것이 `await` 사용을 지원하지 않는 경우(현재 대부분의 데이터베이스 라이브러리가 그러합니다), *경로 처리 함수*를 일반적인 `def`를 사용해 선언하십시오: ```Python hl_lines="2" @app.get('/') @@ -40,23 +40,23 @@ def results(): --- -만약 당신의 응용프로그램이 (어째서인지) 다른 무엇과 의사소통하고 그것이 응답하기를 기다릴 필요가 없다면 `async def`를 사용하십시오. +만약 여러분의 애플리케이션이 (어째서인지) 다른 어떤 것과도 통신하고 그 응답을 기다릴 필요가 없다면, 내부에서 `await`를 사용할 필요가 없더라도 `async def`를 사용하세요. --- -모르겠다면, 그냥 `def`를 사용하십시오. +잘 모르겠다면, 일반적인 `def`를 사용하세요. --- -**참고**: *경로 작동 함수*에서 필요한만큼 `def`와 `async def`를 혼용할 수 있고, 가장 알맞은 것을 선택해서 정의할 수 있습니다. FastAPI가 자체적으로 알맞은 작업을 수행할 것입니다. +**참고**: *경로 처리 함수*에서 필요한 만큼 `def`와 `async def`를 혼용할 수 있으며, 각각에 대해 가장 알맞은 옵션을 선택해 정의하면 됩니다. FastAPI가 올바르게 처리합니다. -어찌되었든, 상기 어떠한 경우라도, FastAPI는 여전히 비동기적으로 작동하고 매우 빠릅니다. +어쨌든 위의 어떤 경우에서도 FastAPI는 여전히 비동기적으로 동작하며 매우 빠릅니다. -그러나 상기 작업을 수행함으로써 어느 정도의 성능 최적화가 가능합니다. +하지만 위의 단계를 따르면, 몇 가지 성능 최적화를 할 수 있습니다. -## 기술적 세부사항 +## 기술적 세부사항 { #technical-details } -최신 파이썬 버전은 `async`와 `await` 문법과 함께 **“코루틴”**이라고 하는 것을 사용하는 **“비동기 코드”**를 지원합니다. +최신 파이썬 버전은 **“코루틴”**이라고 하는 것을 사용하는 **“비동기 코드”**를 **`async` 및 `await`** 문법과 함께 지원합니다. 아래 섹션들에서 해당 문장을 부분별로 살펴보겠습니다: @@ -64,251 +64,283 @@ def results(): * **`async`와 `await`** * **코루틴** -## 비동기 코드 +## 비동기 코드 { #asynchronous-code } -비동기 코드란 언어 💬 가 코드의 어느 한 부분에서, 컴퓨터 / 프로그램🤖에게 *다른 무언가*가 어딘가에서 끝날 때까지 기다려야한다고 말하는 방식입니다. *다른 무언가*가 “느린-파일" 📝 이라고 불린다고 가정해봅시다. +비동기 코드는 언어 💬 가 코드의 어느 한 부분에서 컴퓨터/프로그램 🤖 에게, 어느 시점에는 어딘가에서 *다른 무언가*가 끝날 때까지 기다려야 한다고 말할 수 있는 방법이 있다는 의미입니다. 그 *다른 무언가*를 "slow-file" 📝 이라고 해보겠습니다. -따라서 “느린-파일” 📝이 끝날때까지 컴퓨터는 다른 작업을 수행할 수 있습니다. +따라서 그 시간 동안 컴퓨터는 "slow-file" 📝 이 끝나는 동안 다른 작업을 하러 갈 수 있습니다. -그 다음 컴퓨터 / 프로그램 🤖 은 다시 기다리고 있기 때문에 기회가 있을 때마다 다시 돌아오거나, 혹은 당시에 수행해야하는 작업들이 완료될 때마다 다시 돌아옵니다. 그리고 그것 🤖 은 기다리고 있던 작업 중 어느 것이 이미 완료되었는지, 그것 🤖 이 해야하는 모든 작업을 수행하면서 확인합니다. +그 다음 컴퓨터/프로그램 🤖 은 다시 기다리는 중이기 때문에 기회가 있을 때마다 돌아오거나, 혹은 그 시점에 해야 할 작업을 모두 끝낼 때마다 돌아옵니다. 그리고 기다리던 작업 중 이미 끝난 것이 있는지 확인하면서, 해야 했던 작업을 수행합니다. -다음으로, 그것 🤖 은 완료할 첫번째 작업에 착수하고(우리의 "느린-파일" 📝 이라고 가정합시다) 그에 대해 수행해야하는 작업을 계속합니다. +다음으로, 완료된 첫 번째 작업(우리의 "slow-file" 📝 이라고 해보겠습니다)을 가져와서, 그에 대해 해야 했던 작업을 계속합니다. -"다른 무언가를 기다리는 것"은 일반적으로 비교적 "느린" (프로세서와 RAM 메모리 속도에 비해) I/O 작업을 의미합니다. 예를 들면 다음의 것들을 기다리는 것입니다: +이 "다른 무언가를 기다리는 것"은 일반적으로 프로세서와 RAM 메모리 속도에 비해 상대적으로 "느린" I/O 작업을 의미합니다. 예를 들어 다음을 기다리는 것입니다: -* 네트워크를 통해 클라이언트로부터 전송되는 데이터 -* 네트워크를 통해 클라이언트가 수신할, 당신의 프로그램으로부터 전송되는 데이터 -* 시스템이 읽고 프로그램에 전달할 디스크 내의 파일 내용 -* 당신의 프로그램이 시스템에 전달하는, 디스크에 작성될 내용 +* 네트워크를 통해 클라이언트가 데이터를 보내는 것 +* 네트워크를 통해 클라이언트가 여러분의 프로그램이 보낸 데이터를 받는 것 +* 시스템이 디스크의 파일 내용을 읽어서 프로그램에 전달하는 것 +* 프로그램이 시스템에 전달한 내용을 디스크에 쓰는 것 * 원격 API 작업 -* 완료될 데이터베이스 작업 -* 결과를 반환하는 데이터베이스 쿼리 -* 기타 +* 데이터베이스 작업이 완료되는 것 +* 데이터베이스 쿼리가 결과를 반환하는 것 +* 기타 등등 -수행 시간의 대부분이 I/O 작업을 기다리는데에 소요되기 때문에, "I/O에 묶인" 작업이라고 불립니다. +실행 시간의 대부분이 I/O 작업을 기다리는 데 소비되기 때문에, 이를 "I/O bound" 작업이라고 부릅니다. -이것은 "비동기"라고 불리는데 컴퓨터 / 프로그램이 작업 결과를 가지고 일을 수행할 수 있도록, 느린 작업에 "동기화"되어 아무것도 하지 않으면서 작업이 완료될 정확한 시점만을 기다릴 필요가 없기 때문입니다. +이것은 컴퓨터/프로그램이 느린 작업과 "동기화"되어, 아무것도 하지 않은 채 그 작업이 끝나는 정확한 시점만 기다렸다가 결과를 가져와 일을 계속할 필요가 없기 때문에 "비동기"라고 불립니다. -이 대신에, "비동기" 시스템에서는, 작업은 일단 완료되면, 컴퓨터 / 프로그램이 수행하고 있는 일을 완료하고 이후 다시 돌아와서 그것의 결과를 받아 이를 사용해 작업을 지속할 때까지 잠시 (몇 마이크로초) 대기할 수 있습니다. +대신 "비동기" 시스템에서는, 작업이 끝나면 컴퓨터/프로그램이 하러 갔던 일을 마칠 때까지 잠시(몇 마이크로초) 줄에서 기다렸다가, 다시 돌아와 결과를 받아 이를 사용해 작업을 계속할 수 있습니다. -"동기"("비동기"의 반대)는 컴퓨터 / 프로그램이 상이한 작업들간 전환을 하기 전에 그것이 대기를 동반하게 될지라도 모든 순서를 따르기 때문에 "순차"라는 용어로도 흔히 불립니다. +"동기"(“비동기”의 반대)는 보통 "순차"라는 용어로도 불리는데, 컴퓨터/프로그램이 다른 작업으로 전환하기 전에 모든 단계를 순서대로 따르기 때문이며, 그 단계들에 기다림이 포함되어 있더라도 마찬가지입니다. -### 동시성과 버거 +### 동시성과 햄버거 { #concurrency-and-burgers } -위에서 설명한 **비동기** 코드에 대한 개념은 종종 **"동시성"**이라고도 불립니다. 이것은 **"병렬성"**과는 다릅니다. +위에서 설명한 **비동기** 코드에 대한 개념은 때때로 **"동시성"**이라고도 불립니다. 이는 **"병렬성"**과는 다릅니다. -**동시성**과 **병렬성**은 모두 "동시에 일어나는 서로 다른 일들"과 관련이 있습니다. +**동시성**과 **병렬성**은 모두 "대략 같은 시간에 일어나는 서로 다른 일들"과 관련이 있습니다. 하지만 *동시성*과 *병렬성*의 세부적인 개념에는 꽤 차이가 있습니다. -차이를 확인하기 위해, 다음의 버거에 대한 이야기를 상상해보십시오: +차이를 보기 위해, 다음의 햄버거 이야기를 상상해보세요: -### 동시 버거 +### 동시 햄버거 { #concurrent-burgers } -당신은 짝사랑 상대 😍 와 패스트푸드 🍔 를 먹으러 갔습니다. 당신은 점원 💁 이 당신 앞에 있는 사람들의 주문을 받을 동안 줄을 서서 기다리고 있습니다. +여러분은 짝사랑 상대와 패스트푸드를 먹으러 갔고, 점원이 여러분 앞 사람들의 주문을 받는 동안 줄을 서서 기다립니다. 😍 -이제 당신의 순서가 되어서, 당신은 당신과 짝사랑 상대 😍 를 위한 두 개의 고급스러운 버거 🍔 를 주문합니다. + -당신이 돈을 냅니다 💸. +이제 여러분 차례가 되어, 여러분과 짝사랑 상대를 위해 매우 고급스러운 햄버거 2개를 주문합니다. 🍔🍔 -점원 💁 은 주방 👨‍🍳 에 요리를 하라고 전달하고, 따라서 그들은 당신의 버거 🍔 를 준비해야한다는 사실을 알게됩니다(그들이 지금은 당신 앞 고객들의 주문을 준비하고 있을지라도 말입니다). + -점원 💁 은 당신의 순서가 적힌 번호표를 줍니다. +점원은 주방의 요리사에게 무언가를 말해, (지금은 앞선 손님들의 주문을 준비하고 있더라도) 여러분의 햄버거를 준비해야 한다는 것을 알게 합니다. -기다리는 동안, 당신은 짝사랑 상대 😍 와 함께 테이블을 고르고, 자리에 앉아 오랫동안 (당신이 주문한 버거는 꽤나 고급스럽기 때문에 준비하는데 시간이 조금 걸립니다 ✨🍔✨) 대화를 나눕니다. + -짝사랑 상대 😍 와 테이블에 앉아서 버거 🍔 를 기다리는 동안, 그 사람 😍 이 얼마나 멋지고, 사랑스럽고, 똑똑한지 감탄하며 시간을 보냅니다 ✨😍✨. +여러분이 돈을 냅니다. 💸 -짝사랑 상대 😍 와 기다리면서 얘기하는 동안, 때때로, 당신은 당신의 차례가 되었는지 보기 위해 카운터의 번호를 확인합니다. +점원은 여러분 차례 번호를 줍니다. -그러다 어느 순간, 당신의 차례가 됩니다. 카운터에 가서, 버거 🍔 를 받고, 테이블로 다시 돌아옵니다. + -당신과 짝사랑 상대 😍 는 버거 🍔 를 먹으며 좋은 시간을 보냅니다 ✨. +기다리는 동안, 여러분은 짝사랑 상대와 함께 자리를 고르고 앉아 오랫동안 대화를 나눕니다(여러분의 햄버거는 매우 고급스럽기 때문에 준비하는 데 시간이 좀 걸립니다). + +짝사랑 상대와 테이블에 앉아 햄버거를 기다리는 동안, 그 사람이 얼마나 멋지고 귀엽고 똑똑한지 감탄하며 시간을 보낼 수 있습니다 ✨😍✨. + + + +기다리며 대화하는 동안, 때때로 여러분은 카운터에 표시되는 번호를 확인해 여러분 차례인지 봅니다. + +그러다 어느 순간 마침내 여러분 차례가 됩니다. 여러분은 카운터에 가서 햄버거를 받고, 테이블로 돌아옵니다. + + + +여러분과 짝사랑 상대는 햄버거를 먹으며 좋은 시간을 보냅니다. ✨ + + + +/// info | 정보 + +아름다운 일러스트: Ketrina Thompson. 🎨 + +/// --- -당신이 이 이야기에서 컴퓨터 / 프로그램 🤖 이라고 상상해보십시오. +이 이야기에서 여러분이 컴퓨터/프로그램 🤖 이라고 상상해보세요. -줄을 서서 기다리는 동안, 당신은 아무것도 하지 않고 😴 당신의 차례를 기다리며, 어떠한 "생산적인" 일도 하지 않습니다. 하지만 점원 💁 이 (음식을 준비하지는 않고) 주문을 받기만 하기 때문에 줄이 빨리 줄어들어서 괜찮습니다. +줄을 서 있는 동안, 여러분은 그냥 쉬고 😴, 차례를 기다리며, 그다지 "생산적인" 일을 하지 않습니다. 하지만 점원은 주문만 받지(음식을 준비하진 않기) 때문에 줄이 빠르게 줄어들어 괜찮습니다. -그다음, 당신이 차례가 오면, 당신은 실제로 "생산적인" 일 🤓 을 합니다. 당신은 메뉴를 보고, 무엇을 먹을지 결정하고, 짝사랑 상대 😍 의 선택을 묻고, 돈을 내고 💸 , 맞는 카드를 냈는지 확인하고, 비용이 제대로 지불되었는지 확인하고, 주문이 제대로 들어갔는지 확인을 하는 작업 등등을 수행합니다. +그 다음 여러분 차례가 되면, 여러분은 실제로 "생산적인" 일을 합니다. 메뉴를 처리하고, 무엇을 먹을지 결정하고, 짝사랑 상대의 선택을 확인하고, 결제하고, 올바른 현금이나 카드를 냈는지 확인하고, 정확히 청구되었는지 확인하고, 주문에 올바른 항목들이 들어갔는지 확인하는 등등을 합니다. -하지만 이후에는, 버거 🍔 를 아직 받지 못했음에도, 버거가 준비될 때까지 기다려야 🕙 하기 때문에 점원 💁 과의 작업은 "일시정지" ⏸ 상태입니다. +하지만 그 다음에는, 아직 햄버거를 받지 못했더라도, 햄버거가 준비될 때까지 기다려야 🕙 하므로 점원과의 작업은 "일시정지" ⏸ 상태입니다. -하지만 번호표를 받고 카운터에서 나와 테이블에 앉으면, 당신은 짝사랑 상대 😍 와 그 "작업" ⏯ 🤓 에 번갈아가며 🔀 집중합니다. 그러면 당신은 다시 짝사랑 상대 😍 에게 작업을 거는 매우 "생산적인" 일 🤓 을 합니다. +하지만 번호를 들고 카운터에서 벗어나 테이블에 앉으면, 여러분은 짝사랑 상대에게 관심을 전환 🔀 하고, 그에 대한 "작업" ⏯ 🤓 을 할 수 있습니다. 그러면 여러분은 다시 짝사랑 상대에게 작업을 거는 매우 "생산적인" 일을 하게 됩니다 😍. -점원 💁 이 카운터 화면에 당신의 번호를 표시함으로써 "버거 🍔 가 준비되었습니다"라고 해도, 당신은 즉시 뛰쳐나가지는 않을 것입니다. 당신은 당신의 번호를 갖고있고, 다른 사람들은 그들의 번호를 갖고있기 때문에, 아무도 당신의 버거 🍔 를 훔쳐가지 않는다는 사실을 알기 때문입니다. +그 다음 점원 💁 이 카운터 화면에 여러분 번호를 띄워 "햄버거를 만들었어요"라고 말하지만, 표시된 번호가 여러분 차례로 바뀌었다고 해서 즉시 미친 듯이 뛰어가지는 않습니다. 여러분은 여러분 번호를 갖고 있고, 다른 사람들은 그들의 번호를 갖고 있으니, 아무도 여러분 햄버거를 훔쳐갈 수 없다는 것을 알기 때문입니다. -그래서 당신은 짝사랑 상대 😍 가 이야기를 끝낼 때까지 기다린 후 (현재 작업 완료 ⏯ / 진행 중인 작업 처리 🤓 ), 정중하게 미소짓고 버거를 가지러 가겠다고 말합니다 ⏸. +그래서 여러분은 짝사랑 상대가 이야기를 끝낼 때까지 기다린 다음(현재 작업 ⏯ / 처리 중인 작업 🤓 을 끝내고), 부드럽게 미소 지으며 햄버거를 가지러 가겠다고 말합니다 ⏸. -그다음 당신은 카운터에 가서 🔀 , 초기 작업을 이제 완료하고 ⏯ , 버거 🍔 를 받고, 감사하다고 말하고 테이블로 가져옵니다. 이로써 카운터와의 상호작용 단계 / 작업이 종료됩니다 ⏹. +그 다음 여러분은 카운터로 가서 🔀, 이제 끝난 초기 작업 ⏯ 으로 돌아와 햄버거를 받고, 감사 인사를 하고, 테이블로 가져옵니다. 이로써 카운터와 상호작용하는 그 단계/작업이 끝납니다 ⏹. 그리고 이는 새로운 작업인 "햄버거 먹기" 🔀 ⏯ 를 만들지만, 이전 작업인 "햄버거 받기"는 끝났습니다 ⏹. -이전 작업인 "버거 받기"가 종료되면 ⏹ "버거 먹기"라는 새로운 작업이 생성됩니다 🔀 ⏯. +### 병렬 햄버거 { #parallel-burgers } -### 병렬 버거 +이제 이것이 "동시 햄버거"가 아니라 "병렬 햄버거"라고 상상해봅시다. -이제 "동시 버거"가 아닌 "병렬 버거"를 상상해보십시오. +여러분은 짝사랑 상대와 함께 병렬 패스트푸드를 먹으러 갑니다. -당신은 짝사랑 상대 😍 와 함께 병렬 패스트푸드 🍔 를 먹으러 갔습니다. +여러분은 여러 명(예: 8명)의 점원이 동시에 요리사이기도 하여 여러분 앞 사람들의 주문을 받는 동안 줄을 서 있습니다. -당신은 여러명(8명이라고 가정합니다)의 점원이 당신 앞 사람들의 주문을 받으며 동시에 요리 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳 도 하는 동안 줄을 서서 기다립니다. +여러분 앞의 모든 사람들은, 8명의 점원 각각이 다음 주문을 받기 전에 바로 햄버거를 준비하러 가기 때문에, 카운터를 떠나지 않고 햄버거가 준비될 때까지 기다립니다. -당신 앞 모든 사람들이 버거가 준비될 때까지 카운터에서 떠나지 않고 기다립니다 🕙 . 왜냐하면 8명의 직원들이 다음 주문을 받기 전에 버거를 준비하러 가기 때문입니다. + -마침내 당신의 차례가 왔고, 당신은 당신과 짝사랑 상대 😍 를 위한 두 개의 고급스러운 버거 🍔 를 주문합니다. +마침내 여러분 차례가 되어, 여러분과 짝사랑 상대를 위해 매우 고급스러운 햄버거 2개를 주문합니다. -당신이 비용을 지불합니다 💸 . +여러분이 돈을 냅니다 💸. -점원이 주방에 갑니다 👨‍🍳 . + -당신은 번호표가 없기 때문에 누구도 당신의 버거 🍔 를 대신 가져갈 수 없도록 카운터에 서서 기다립니다 🕙 . +점원은 주방으로 갑니다. -당신과 짝사랑 상대 😍 는 다른 사람이 새치기해서 버거를 가져가지 못하게 하느라 바쁘기 때문에 🕙 , 짝사랑 상대에게 주의를 기울일 수 없습니다 😞 . +여러분은 번호표가 없으므로, 다른 사람이 여러분보다 먼저 햄버거를 가져가지 못하도록 카운터 앞에 서서 기다립니다 🕙. -이것은 "동기" 작업이고, 당신은 점원/요리사 👨‍🍳 와 "동기화" 되었습니다. 당신은 기다리고 🕙 , 점원/요리사 👨‍🍳 가 버거 🍔 준비를 완료한 후 당신에게 주거나, 누군가가 그것을 가져가는 그 순간에 그 곳에 있어야합니다. + -카운터 앞에서 오랫동안 기다린 후에 🕙 , 점원/요리사 👨‍🍳 가 당신의 버거 🍔 를 가지고 돌아옵니다. +여러분과 짝사랑 상대는 햄버거가 나오면 다른 사람이 끼어들어 가져가지 못하게 하느라 바쁘기 때문에, 짝사랑 상대에게 집중할 수 없습니다. 😞 -당신은 버거를 받고 짝사랑 상대와 함께 테이블로 돌아옵니다. +이것은 "동기" 작업이며, 여러분은 점원/요리사 👨‍🍳 와 "동기화"되어 있습니다. 점원/요리사 👨‍🍳 가 햄버거를 완성해 여러분에게 주는 정확한 순간에 그 자리에 있어야 하므로, 여러분은 기다려야 🕙 하고, 그렇지 않으면 다른 사람이 가져갈 수도 있습니다. -단지 먹기만 하다가, 다 먹었습니다 🍔 ⏹. + -카운터 앞에서 기다리면서 🕙 너무 많은 시간을 허비했기 때문에 대화를 하거나 작업을 걸 시간이 거의 없었습니다 😞 . +그러다 점원/요리사 👨‍🍳 가 카운터 앞에서 오랫동안 기다린 🕙 끝에 마침내 햄버거를 가지고 돌아옵니다. + + + +여러분은 햄버거를 받아 짝사랑 상대와 테이블로 갑니다. + +그냥 먹고, 끝입니다. ⏹ + + + +대부분의 시간을 카운터 앞에서 기다리는 데 🕙 썼기 때문에, 대화하거나 작업을 걸 시간은 많지 않았습니다. 😞 + +/// info | 정보 + +아름다운 일러스트: Ketrina Thompson. 🎨 + +/// --- -이 병렬 버거 시나리오에서, 당신은 기다리고 🕙 , 오랜 시간동안 "카운터에서 기다리는" 🕙 데에 주의를 기울이는 ⏯ 두 개의 프로세서(당신과 짝사랑 상대😍)를 가진 컴퓨터 / 프로그램 🤖 입니다. +이 병렬 햄버거 시나리오에서, 여러분은 두 개의 프로세서(여러분과 짝사랑 상대)를 가진 컴퓨터/프로그램 🤖 이며, 둘 다 기다리고 🕙 오랫동안 "카운터에서 기다리기" 🕙 에 주의를 ⏯ 기울입니다. -패스트푸드점에는 8개의 프로세서(점원/요리사) 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳 가 있습니다. 동시 버거는 단 두 개(한 명의 직원과 한 명의 요리사) 💁 👨‍🍳 만을 가지고 있었습니다. +패스트푸드점에는 8개의 프로세서(점원/요리사)가 있습니다. 동시 햄버거 가게는 2개(점원 1명, 요리사 1명)만 있었을 것입니다. -하지만 여전히, 병렬 버거 예시가 최선은 아닙니다 😞 . +하지만 여전히 최종 경험은 그다지 좋지 않습니다. 😞 --- -이 예시는 버거🍔 이야기와 결이 같습니다. +이것이 햄버거의 병렬 버전에 해당하는 이야기입니다. 🍔 -더 "현실적인" 예시로, 은행을 상상해보십시오. +좀 더 "현실적인" 예시로, 은행을 상상해보세요. -최근까지, 대다수의 은행에는 다수의 은행원들 👨‍💼👨‍💼👨‍💼👨‍💼 과 긴 줄 🕙🕙🕙🕙🕙🕙🕙🕙 이 있습니다. +최근까지 대부분의 은행에는 여러 은행원 👨‍💼👨‍💼👨‍💼👨‍💼 과 긴 줄 🕙🕙🕙🕙🕙🕙🕙🕙 이 있었습니다. -모든 은행원들은 한 명 한 명의 고객들을 차례로 상대합니다 👨‍💼⏯ . +모든 은행원이 한 고객씩 순서대로 모든 일을 처리합니다 👨‍💼⏯. -그리고 당신은 오랫동안 줄에서 기다려야하고 🕙 , 그렇지 않으면 당신의 차례를 잃게 됩니다. +그리고 여러분은 오랫동안 줄에서 기다려야 🕙 하며, 그렇지 않으면 차례를 잃습니다. -아마 당신은 은행 🏦 심부름에 짝사랑 상대 😍 를 데려가고 싶지는 않을 것입니다. +아마 은행 🏦 업무를 보러 갈 때 짝사랑 상대 😍 를 데려가고 싶지는 않을 것입니다. -### 버거 예시의 결론 +### 햄버거 예시의 결론 { #burger-conclusion } -"짝사랑 상대와의 패스트푸드점 버거" 시나리오에서, 오랜 기다림 🕙 이 있기 때문에 동시 시스템 ⏸🔀⏯ 을 사용하는 것이 더 합리적입니다. +"짝사랑 상대와의 패스트푸드점 햄버거" 시나리오에서는 기다림 🕙 이 많기 때문에, 동시 시스템 ⏸🔀⏯ 을 사용하는 것이 훨씬 더 합리적입니다. -대다수의 웹 응용프로그램의 경우가 그러합니다. +대부분의 웹 애플리케이션이 그렇습니다. -매우 많은 수의 유저가 있지만, 서버는 그들의 요청을 전송하기 위해 그닥-좋지-않은 연결을 기다려야 합니다 🕙 . +매우 많은 사용자들이 있고, 서버는 그들의 좋지 않은 연결을 통해 요청이 전송되기를 기다립니다 🕙. -그리고 응답이 돌아올 때까지 다시 기다려야 합니다 🕙 . +그리고 응답이 돌아오기를 다시 기다립니다 🕙. -이 "기다림" 🕙 은 마이크로초 단위이지만, 모두 더해지면, 결국에는 매우 긴 대기시간이 됩니다. +이 "기다림" 🕙 은 마이크로초 단위로 측정되지만, 모두 합치면 결국 꽤 많은 대기 시간이 됩니다. -따라서 웹 API를 위해 비동기 ⏸🔀⏯ 코드를 사용하는 것이 합리적입니다. +그래서 웹 API에는 비동기 ⏸🔀⏯ 코드를 사용하는 것이 매우 합리적입니다. -대부분의 존재하는 유명한 파이썬 프레임워크 (Flask와 Django 등)은 새로운 비동기 기능들이 파이썬에 존재하기 전에 만들어졌습니다. 그래서, 그들의 배포 방식은 병렬 실행과 새로운 기능만큼 강력하지는 않은 예전 버전의 비동기 실행을 지원합니다. +이러한 종류의 비동기성은 NodeJS가 인기 있는 이유(비록 NodeJS가 병렬은 아니지만)이자, 프로그래밍 언어로서 Go의 강점입니다. -비동기 웹 파이썬(ASGI)에 대한 주요 명세가 웹소켓을 지원하기 위해 Django에서 개발 되었음에도 그렇습니다. +그리고 이것이 **FastAPI**로 얻는 것과 같은 수준의 성능입니다. -이러한 종류의 비동기성은 (NodeJS는 병렬적이지 않음에도) NodeJS가 사랑받는 이유이고, 프로그래밍 언어로서의 Go의 강점입니다. +또한 병렬성과 비동기성을 동시에 사용할 수 있으므로, 대부분의 테스트된 NodeJS 프레임워크보다 더 높은 성능을 얻고, C에 더 가까운 컴파일 언어인 Go와 동등한 성능을 얻을 수 있습니다 (모두 Starlette 덕분입니다). -그리고 **FastAPI**를 사용함으로써 동일한 성능을 낼 수 있습니다. +### 동시성이 병렬성보다 더 나은가요? { #is-concurrency-better-than-parallelism } -또한 병렬성과 비동기성을 동시에 사용할 수 있기 때문에, 대부분의 테스트가 완료된 NodeJS 프레임워크보다 더 높은 성능을 얻고 C에 더 가까운 컴파일 언어인 Go와 동등한 성능을 얻을 수 있습니다(모두 Starlette 덕분입니다). +아니요! 그게 이 이야기의 교훈은 아닙니다. -### 동시성이 병렬성보다 더 나은가? +동시성은 병렬성과 다릅니다. 그리고 많은 기다림이 포함되는 **특정한** 시나리오에서는 더 낫습니다. 그 때문에 웹 애플리케이션 개발에서는 일반적으로 병렬성보다 훨씬 더 낫습니다. 하지만 모든 것에 해당하진 않습니다. -그렇지 않습니다! 그것이 이야기의 교훈은 아닙니다. +그래서 균형을 맞추기 위해, 다음의 짧은 이야기를 상상해보세요: -동시성은 병렬성과 다릅니다. 그리고 그것은 많은 대기를 필요로하는 **특정한** 시나리오에서는 더 낫습니다. 이로 인해, 웹 응용프로그램 개발에서 동시성이 병렬성보다 일반적으로 훨씬 낫습니다. 하지만 모든 경우에 그런 것은 아닙니다. - -따라서, 균형을 맞추기 위해, 다음의 짧은 이야기를 상상해보십시오: - -> 당신은 크고, 더러운 집을 청소해야합니다. +> 여러분은 크고 더러운 집을 청소해야 합니다. *네, 이게 전부입니다*. --- -어디에도 대기 🕙 는 없고, 집안 곳곳에서 해야하는 많은 작업들만 있습니다. +어디에도 기다림 🕙 은 없고, 집의 여러 장소에서 해야 할 일이 많을 뿐입니다. -버거 예시처럼 처음에는 거실, 그 다음은 부엌과 같은 식으로 순서를 정할 수도 있으나, 무엇도 기다리지 🕙 않고 계속해서 청소 작업만 수행하기 때문에, 순서는 아무런 영향을 미치지 않습니다. +햄버거 예시처럼 거실부터, 그 다음은 부엌처럼 순서를 정할 수도 있지만, 어떤 것도 기다리지 🕙 않고 계속 청소만 하기 때문에, 순서는 아무런 영향을 주지 않습니다. -순서가 있든 없든 동일한 시간이 소요될 것이고(동시성) 동일한 양의 작업을 하게 될 것입니다. +순서가 있든 없든(동시성) 끝내는 데 걸리는 시간은 같고, 같은 양의 일을 하게 됩니다. -하지만 이 경우에서, 8명의 전(前)-점원/요리사이면서-현(現)-청소부 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳 를 고용할 수 있고, 그들 각자(그리고 당신)가 집의 한 부분씩 맡아 청소를 한다면, 당신은 **병렬적**으로 작업을 수행할 수 있고, 조금의 도움이 있다면, 훨씬 더 빨리 끝낼 수 있습니다. +하지만 이 경우, 전(前) 점원/요리사이자 현(現) 청소부가 된 8명을 데려올 수 있고, 각자(그리고 여러분)가 집의 구역을 하나씩 맡아 청소한다면, 추가 도움과 함께 모든 일을 **병렬**로 수행하여 훨씬 더 빨리 끝낼 수 있습니다. -이 시나리오에서, (당신을 포함한) 각각의 청소부들은 프로세서가 될 것이고, 각자의 역할을 수행합니다. +이 시나리오에서 (여러분을 포함한) 각 청소부는 프로세서가 되어, 맡은 일을 수행합니다. -실행 시간의 대부분이 대기가 아닌 실제 작업에 소요되고, 컴퓨터에서 작업은 CPU에서 이루어지므로, 이러한 문제를 "CPU에 묶였"다고 합니다. +그리고 실행 시간의 대부분이 기다림이 아니라 실제 작업에 쓰이고, 컴퓨터에서 작업은 CPU가 수행하므로, 이런 문제를 "CPU bound"라고 부릅니다. --- -CPU에 묶인 연산에 관한 흔한 예시는 복잡한 수학 처리를 필요로 하는 경우입니다. +CPU bound 작업의 흔한 예시는 복잡한 수학 처리가 필요한 것들입니다. 예를 들어: -* **오디오** 또는 **이미지** 처리. -* **컴퓨터 비전**: 하나의 이미지는 수백개의 픽셀로 구성되어있고, 각 픽셀은 3개의 값 / 색을 갖고 있으며, 일반적으로 해당 픽셀들에 대해 동시에 무언가를 계산해야하는 처리. -* **머신러닝**: 일반적으로 많은 "행렬"과 "벡터" 곱셈이 필요합니다. 거대한 스프레드 시트에 수들이 있고 그 수들을 동시에 곱해야 한다고 생각해보십시오. -* **딥러닝**: 머신러닝의 하위영역으로, 동일한 예시가 적용됩니다. 단지 이 경우에는 하나의 스프레드 시트에 곱해야할 수들이 있는 것이 아니라, 거대한 세트의 스프레드 시트들이 있고, 많은 경우에, 이 모델들을 만들고 사용하기 위해 특수한 프로세서를 사용합니다. +* **오디오** 또는 **이미지** 처리 +* **컴퓨터 비전**: 이미지는 수백만 개의 픽셀로 구성되며, 각 픽셀은 3개의 값/색을 갖습니다. 보통 그 픽셀들에 대해 동시에 무언가를 계산해야 합니다. +* **머신러닝**: 보통 많은 "matrix"와 "vector" 곱셈이 필요합니다. 숫자가 있는 거대한 스프레드시트를 생각하고, 그 모든 수를 동시에 곱한다고 생각해보세요. +* **딥러닝**: 머신러닝의 하위 분야이므로 동일하게 적용됩니다. 다만 곱해야 할 숫자가 있는 스프레드시트가 하나가 아니라, 아주 큰 집합이며, 많은 경우 그 모델을 만들고/또는 사용하기 위해 특별한 프로세서를 사용합니다. -### 동시성 + 병렬성: 웹 + 머신러닝 +### 동시성 + 병렬성: 웹 + 머신러닝 { #concurrency-parallelism-web-machine-learning } -**FastAPI**를 사용하면 웹 개발에서는 매우 흔한 동시성의 이점을 (NodeJS의 주된 매력만큼) 얻을 수 있습니다. +**FastAPI**를 사용하면 웹 개발에서 매우 흔한 동시성의 이점을( NodeJS의 주요 매력과 같은) 얻을 수 있습니다. -뿐만 아니라 머신러닝 시스템과 같이 **CPU에 묶인** 작업을 위해 병렬성과 멀티프로세싱(다수의 프로세스를 병렬적으로 동작시키는 것)을 이용하는 것도 가능합니다. +또한 머신러닝 시스템처럼 **CPU bound** 워크로드에 대해 병렬성과 멀티프로세싱(여러 프로세스를 병렬로 실행)을 활용할 수도 있습니다. -파이썬이 **데이터 사이언스**, 머신러닝과 특히 딥러닝에 의 주된 언어라는 간단한 사실에 더해서, 이것은 FastAPI를 데이터 사이언스 / 머신러닝 웹 API와 응용프로그램에 (다른 것들보다) 좋은 선택지가 되게 합니다. +이것은 파이썬이 **데이터 사이언스**, 머신러닝, 특히 딥러닝의 주요 언어라는 단순한 사실과 더해져, FastAPI를 데이터 사이언스/머신러닝 웹 API 및 애플리케이션(그 외에도 많은 것들)에 매우 잘 맞는 선택으로 만들어 줍니다. -배포시 병렬을 어떻게 가능하게 하는지 알고싶다면, [배포](deployment/index.md){.internal-link target=_blank}문서를 참고하십시오. +프로덕션에서 이 병렬성을 어떻게 달성하는지 보려면 [배포](deployment/index.md){.internal-link target=_blank} 섹션을 참고하세요. -## `async`와 `await` +## `async`와 `await` { #async-and-await } -최신 파이썬 버전에는 비동기 코드를 정의하는 매우 직관적인 방법이 있습니다. 이는 이것을 평범한 "순차적" 코드로 보이게 하고, 적절한 순간에 당신을 위해 "대기"합니다. +최신 파이썬 버전에는 비동기 코드를 정의하는 매우 직관적인 방법이 있습니다. 이 방법은 이를 평범한 "순차" 코드처럼 보이게 하고, 적절한 순간에 여러분을 위해 "기다림"을 수행합니다. -연산이 결과를 전달하기 전에 대기를 해야하고 새로운 파이썬 기능들을 지원한다면, 이렇게 코드를 작성할 수 있습니다: +결과를 주기 전에 기다림이 필요한 작업이 있고, 이러한 새로운 파이썬 기능을 지원한다면, 다음과 같이 작성할 수 있습니다: ```Python burgers = await get_burgers(2) ``` -여기서 핵심은 `await`입니다. 이것은 파이썬에게 `burgers` 결과를 저장하기 이전에 `get_burgers(2)`의 작업이 완료되기를 🕙 기다리라고 ⏸ 말합니다. 이로 인해, 파이썬은 그동안 (다른 요청을 받는 것과 같은) 다른 작업을 수행해도 된다는 것을 🔀 ⏯ 알게될 것입니다. +여기서 핵심은 `await`입니다. 이는 파이썬에게 `get_burgers(2)`가 그 일을 끝낼 때까지 🕙 기다리도록 ⏸ 말하고, 그 결과를 `burgers`에 저장하기 전에 완료되기를 기다리라고 합니다. 이를 통해 파이썬은 그동안(예: 다른 요청을 받는 것처럼) 다른 일을 하러 갈 수 있다는 것 🔀 ⏯ 을 알게 됩니다. -`await`가 동작하기 위해, 이것은 비동기를 지원하는 함수 내부에 있어야 합니다. 이를 위해서 함수를 `async def`를 사용해 정의하기만 하면 됩니다: +`await`가 동작하려면, 이 비동기성을 지원하는 함수 내부에 있어야 합니다. 그러려면 `async def`로 선언하기만 하면 됩니다: ```Python hl_lines="1" async def get_burgers(number: int): - # Do some asynchronous stuff to create the burgers + # 햄버거를 만들기 위한 비동기 처리를 수행 return burgers ``` -...`def`를 사용하는 대신: +...`def` 대신: ```Python hl_lines="2" -# This is not asynchronous +# 비동기가 아닙니다 def get_sequential_burgers(number: int): - # Do some sequential stuff to create the burgers + # 햄버거를 만들기 위한 순차 처리를 수행 return burgers ``` -`async def`를 사용하면, 파이썬은 해당 함수 내에서 `await` 표현에 주의해야한다는 사실과, 해당 함수의 실행을 "일시정지"⏸하고 다시 돌아오기 전까지 다른 작업을 수행🔀할 수 있다는 것을 알게됩니다. +`async def`를 사용하면, 파이썬은 그 함수 내부에서 `await` 표현식에 주의해야 하며, 그 함수의 실행을 "일시정지" ⏸ 하고 다시 돌아오기 전에 다른 일을 하러 갈 수 있다는 것 🔀 을 알게 됩니다. -`async def`f 함수를 호출하고자 할 때, "대기"해야합니다. 따라서, 아래는 동작하지 않습니다. +`async def` 함수를 호출하고자 할 때는, 그 함수를 "await" 해야 합니다. 따라서 아래는 동작하지 않습니다: ```Python -# This won't work, because get_burgers was defined with: async def +# 동작하지 않습니다. get_burgers는 async def로 정의되었습니다 burgers = get_burgers(2) ``` --- -따라서, `await`f를 사용해서 호출할 수 있는 라이브러리를 사용한다면, 다음과 같이 `async def`를 사용하는 *경로 작동 함수*를 생성해야 합니다: +따라서, `await`로 호출할 수 있다고 말하는 라이브러리를 사용한다면, 다음과 같이 그것을 사용하는 *경로 처리 함수*를 `async def`로 만들어야 합니다: ```Python hl_lines="2-3" @app.get('/burgers') @@ -317,94 +349,96 @@ async def read_burgers(): return burgers ``` -### 더 세부적인 기술적 사항 +### 더 세부적인 기술적 사항 { #more-technical-details } -`await`가 `async def`를 사용하는 함수 내부에서만 사용이 가능하다는 것을 눈치채셨을 것입니다. +`await`는 `async def`로 정의된 함수 내부에서만 사용할 수 있다는 것을 눈치채셨을 것입니다. -하지만 동시에, `async def`로 정의된 함수들은 "대기"되어야만 합니다. 따라서, `async def`를 사용한 함수들은 역시 `async def`를 사용한 함수 내부에서만 호출될 수 있습니다. +하지만 동시에, `async def`로 정의된 함수는 "await" 되어야 합니다. 따라서 `async def`를 가진 함수는 `async def`로 정의된 함수 내부에서만 호출될 수 있습니다. -그렇다면 닭이 먼저냐, 달걀이 먼저냐, 첫 `async` 함수를 어떻게 호출할 수 있겠습니까? +그렇다면, 닭이 먼저냐 달걀이 먼저냐처럼, 첫 번째 `async` 함수는 어떻게 호출할 수 있을까요? -**FastAPI**를 사용해 작업한다면 이것을 걱정하지 않아도 됩니다. 왜냐하면 그 "첫" 함수는 당신의 *경로 작동 함수*가 될 것이고, FastAPI는 어떻게 올바르게 처리할지 알고있기 때문입니다. +**FastAPI**로 작업한다면 걱정할 필요가 없습니다. 그 "첫" 함수는 여러분의 *경로 처리 함수*가 될 것이고, FastAPI는 올바르게 처리하는 방법을 알고 있기 때문입니다. -하지만 FastAPI를 사용하지 않고 `async` / `await`를 사용하고 싶다면, 이 역시 가능합니다. +하지만 FastAPI 없이 `async` / `await`를 사용하고 싶다면, 그것도 가능합니다. -### 당신만의 비동기 코드 작성하기 +### 여러분만의 async 코드 작성하기 { #write-your-own-async-code } -Starlette(그리고 FastAPI)는 AnyIO를 기반으로 하고있고, 따라서 파이썬 표준 라이브러리인 asyncioTrio와 호환됩니다. +Starlette(그리고 **FastAPI**)는 AnyIO를 기반으로 하고 있으며, 파이썬 표준 라이브러리 asyncioTrio 모두와 호환됩니다. -특히, 코드에서 고급 패턴이 필요한 고급 동시성을 사용하는 경우 직접적으로 AnyIO를 사용할 수 있습니다. +특히, 코드에서 더 고급 패턴이 필요한 고급 동시성 사용 사례에서는 직접 AnyIO를 사용할 수 있습니다. -FastAPI를 사용하지 않더라도, 높은 호환성 및 AnyIO의 이점(예: *구조화된 동시성*)을 취하기 위해 AnyIO를 사용해 비동기 응용프로그램을 작성할 수 있습니다. +그리고 FastAPI를 사용하지 않더라도, 높은 호환성을 확보하고 그 이점(예: *structured concurrency*)을 얻기 위해 AnyIO로 여러분만의 async 애플리케이션을 작성할 수도 있습니다. -### 비동기 코드의 다른 형태 +저는 AnyIO 위에 얇은 레이어로 또 다른 라이브러리를 만들었는데, 타입 어노테이션을 조금 개선하고 더 나은 **자동완성**, **인라인 오류** 등을 얻기 위한 것입니다. 또한 **이해**하고 **여러분만의 async 코드**를 작성하도록 돕는 친절한 소개와 튜토리얼도 제공합니다: Asyncer. 특히 **async 코드와 일반**(blocking/동기) 코드를 **결합**해야 한다면 아주 유용합니다. -파이썬에서 `async`와 `await`를 사용하게 된 것은 비교적 최근의 일입니다. +### 비동기 코드의 다른 형태 { #other-forms-of-asynchronous-code } -하지만 이로 인해 비동기 코드 작업이 훨씬 간단해졌습니다. +`async`와 `await`를 사용하는 이 스타일은 언어에서 비교적 최근에 추가되었습니다. -같은 (또는 거의 유사한) 문법은 최신 버전의 자바스크립트(브라우저와 NodeJS)에도 추가되었습니다. +하지만 비동기 코드를 다루는 일을 훨씬 더 쉽게 만들어 줍니다. -하지만 그 이전에, 비동기 코드를 처리하는 것은 꽤 복잡하고 어려운 일이었습니다. +거의 동일한 문법이 최근 브라우저와 NodeJS의 최신 JavaScript에도 포함되었습니다. -파이썬의 예전 버전이라면, 스레드 또는 Gevent를 사용할 수 있을 것입니다. 하지만 코드를 이해하고, 디버깅하고, 이에 대해 생각하는게 훨씬 복잡합니다. +하지만 그 이전에는 비동기 코드를 처리하는 것이 훨씬 더 복잡하고 어려웠습니다. -예전 버전의 NodeJS / 브라우저 자바스크립트라면, "콜백 함수"를 사용했을 것입니다. 그리고 이로 인해 "콜백 지옥"에 빠지게 될 수 있습니다. +이전 버전의 파이썬에서는 스레드 또는 Gevent를 사용할 수 있었을 것입니다. 하지만 코드를 이해하고, 디버깅하고, 이에 대해 생각하는 것이 훨씬 더 복잡합니다. -## 코루틴 +이전 버전의 NodeJS/브라우저 JavaScript에서는 "callback"을 사용했을 것입니다. 이는 "callback hell"로 이어집니다. -**코루틴**은 `async def` 함수가 반환하는 것을 칭하는 매우 고급스러운 용어일 뿐입니다. 파이썬은 그것이 시작되고 어느 시점에서 완료되지만 내부에 `await`가 있을 때마다 내부적으로 일시정지⏸될 수도 있는 함수와 유사한 것이라는 사실을 알고있습니다. +## 코루틴 { #coroutines } -그러나 `async` 및 `await`와 함께 비동기 코드를 사용하는 이 모든 기능들은 "코루틴"으로 간단히 요약됩니다. 이것은 Go의 주된 핵심 기능인 "고루틴"에 견줄 수 있습니다. +**코루틴**은 `async def` 함수가 반환하는 것에 대한 매우 고급스러운 용어일 뿐입니다. 파이썬은 그것이 함수와 비슷한 무언가로서 시작할 수 있고, 어느 시점에 끝나지만, 내부에 `await`가 있을 때마다 내부적으로도 일시정지 ⏸ 될 수 있다는 것을 알고 있습니다. -## 결론 +하지만 `async` 및 `await`와 함께 비동기 코드를 사용하는 이 모든 기능은 종종 "코루틴"을 사용한다고 요약됩니다. 이는 Go의 주요 핵심 기능인 "Goroutines"에 비견됩니다. -상기 문장을 다시 한 번 봅시다: +## 결론 { #conclusion } -> 최신 파이썬 버전은 **`async` 및 `await`** 문법과 함께 **“코루틴”**이라고 하는 것을 사용하는 **“비동기 코드”**를 지원합니다. +위의 같은 문장을 다시 봅시다: -이제 이 말을 조금 더 이해할 수 있을 것입니다. ✨ +> 최신 파이썬 버전은 **“코루틴”**이라고 하는 것을 사용하는 **“비동기 코드”**를 **`async` 및 `await`** 문법과 함께 지원합니다. -이것이 (Starlette을 통해) FastAPI를 강하게 하면서 그것이 인상적인 성능을 낼 수 있게 합니다. +이제 더 이해가 될 것입니다. ✨ -## 매우 세부적인 기술적 사항 +이 모든 것이 FastAPI(Starlette을 통해)를 구동하고, 인상적인 성능을 내게 하는 원동력입니다. + +## 매우 세부적인 기술적 사항 { #very-technical-details } /// warning | 경고 -이 부분은 넘어가도 됩니다. +이 부분은 아마 건너뛰어도 됩니다. 이것들은 **FastAPI**가 내부적으로 어떻게 동작하는지에 대한 매우 세부적인 기술사항입니다. -만약 기술적 지식(코루틴, 스레드, 블록킹 등)이 있고 FastAPI가 어떻게 `async def` vs `def`를 다루는지 궁금하다면, 계속하십시오. +(코루틴, 스레드, 블로킹 등) 같은 기술 지식이 꽤 있고 FastAPI가 `async def`와 일반 `def`를 어떻게 처리하는지 궁금하다면, 계속 읽어보세요. /// -### 경로 작동 함수 +### 경로 처리 함수 { #path-operation-functions } -경로 작동 함수를 `async def` 대신 일반적인 `def`로 선언하는 경우, (서버를 차단하는 것처럼) 그것을 직접 호출하는 대신 대기중인 외부 스레드풀에서 실행됩니다. +*경로 처리 함수*를 `async def` 대신 일반적인 `def`로 선언하면, (서버를 블로킹할 수 있으므로 직접 호출하는 대신) 외부 스레드풀에서 실행되고 그 결과를 await 합니다. -만약 상기에 묘사된대로 동작하지 않는 비동기 프로그램을 사용해왔고 약간의 성능 향상 (약 100 나노초)을 위해 `def`를 사용해서 계산만을 위한 사소한 *경로 작동 함수*를 정의해왔다면, **FastAPI**는 이와는 반대라는 것에 주의하십시오. 이러한 경우에, *경로 작동 함수*가 블로킹 I/O를 수행하는 코드를 사용하지 않는 한 `async def`를 사용하는 편이 더 낫습니다. +위에서 설명한 방식으로 동작하지 않는 다른 async 프레임워크를 사용해본 적이 있고, 아주 작은 성능 향상(약 100 나노초)을 위해 계산만 하는 사소한 *경로 처리 함수*를 일반 `def`로 정의하곤 했다면, **FastAPI**에서는 그 효과가 정반대가 될 수 있다는 점에 유의하세요. 이런 경우에는 *경로 처리 함수*에서 블로킹 I/O 를 수행하는 코드를 사용하지 않는 한 `async def`를 사용하는 편이 더 낫습니다. -하지만 두 경우 모두, FastAPI가 당신이 전에 사용하던 프레임워크보다 [더 빠를](index.md#_11){.internal-link target=_blank} (최소한 비견될) 확률이 높습니다. +그럼에도 두 경우 모두, **FastAPI**는 이전에 사용하던 프레임워크보다 [여전히 더 빠를](index.md#performance){.internal-link target=_blank} 가능성이 높습니다(또는 최소한 비슷합니다). -### 의존성 +### 의존성 { #dependencies } -의존성에도 동일하게 적용됩니다. 의존성이 `async def`가 아닌 표준 `def` 함수라면, 외부 스레드풀에서 실행됩니다. +[의존성](tutorial/dependencies/index.md){.internal-link target=_blank}에도 동일하게 적용됩니다. 의존성이 `async def` 대신 표준 `def` 함수라면, 외부 스레드풀에서 실행됩니다. -### 하위-의존성 +### 하위 의존성 { #sub-dependencies } -함수 정의시 매개변수로 서로를 필요로하는 다수의 의존성과 하위-의존성을 가질 수 있고, 그 중 일부는 `async def`로, 다른 일부는 일반적인 `def`로 생성되었을 수 있습니다. 이것은 여전히 잘 동작하고, 일반적인 `def`로 생성된 것들은 "대기"되는 대신에 (스레드풀로부터) 외부 스레드에서 호출됩니다. +서로를 필요로 하는 여러 의존성과 [하위 의존성](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank}을 함수 정의의 매개변수로 가질 수 있으며, 그중 일부는 `async def`로, 다른 일부는 일반 `def`로 생성되었을 수 있습니다. 그래도 정상 동작하며, 일반 `def`로 생성된 것들은 "await"되는 대신 (스레드풀에서) 외부 스레드에서 호출됩니다. -### 다른 유틸리티 함수 +### 다른 유틸리티 함수 { #other-utility-functions } -직접 호출되는 다른 모든 유틸리티 함수는 일반적인 `def`나 `async def`로 생성될 수 있고 FastAPI는 이를 호출하는 방식에 영향을 미치지 않습니다. +직접 호출하는 다른 모든 유틸리티 함수는 일반 `def`나 `async def`로 생성될 수 있으며, FastAPI는 호출 방식에 영향을 주지 않습니다. -이것은 FastAPI가 당신을 위해 호출하는 함수와는 반대입니다: *경로 작동 함수*와 의존성 +이는 FastAPI가 여러분을 위해 호출하는 함수(즉, *경로 처리 함수*와 의존성)와 대비됩니다. -만약 당신의 유틸리티 함수가 `def`를 사용한 일반적인 함수라면, 스레드풀에서가 아니라 직접 호출(당신이 코드에 작성한 대로)될 것이고, `async def`로 생성된 함수라면 코드에서 호출할 때 그 함수를 `await` 해야 합니다. +유틸리티 함수가 `def`로 만든 일반 함수라면, 스레드풀이 아니라 직접(코드에 작성한 대로) 호출됩니다. 그리고 `async def`로 생성된 함수라면, 코드에서 호출할 때 그 함수를 `await` 해야 합니다. --- -다시 말하지만, 이것은 당신이 이것에 대해 찾고있던 경우에 한해 유용할 매우 세부적인 기술사항입니다. +다시 말하지만, 이것들은 아마도 이를 찾고 있었던 경우에 유용한 매우 세부적인 기술사항입니다. -그렇지 않은 경우, 상기의 가이드라인만으로도 충분할 것입니다: [바쁘신 경우](#_1). +그렇지 않다면, 위 섹션의 가이드라인이면 충분합니다: 바쁘신가요?. diff --git a/docs/ko/docs/deployment/concepts.md b/docs/ko/docs/deployment/concepts.md new file mode 100644 index 0000000000..dd7edd1bae --- /dev/null +++ b/docs/ko/docs/deployment/concepts.md @@ -0,0 +1,321 @@ +# 배포 개념 { #deployments-concepts } + +**FastAPI** 애플리케이션(사실 어떤 종류의 웹 API든)을 배포할 때는, 여러분이 신경 써야 할 여러 개념이 있습니다. 그리고 이 개념들을 활용하면 **애플리케이션을 배포하기 위한 가장 적절한 방법**을 찾을 수 있습니다. + +중요한 개념 몇 가지는 다음과 같습니다: + +* 보안 - HTTPS +* 시작 시 실행 +* 재시작 +* 복제(실행 중인 프로세스 수) +* 메모리 +* 시작 전 사전 단계 + +이것들이 **배포**에 어떤 영향을 주는지 살펴보겠습니다. + +결국 최종 목표는 **API 클라이언트에 서비스를 제공**할 때 **보안**을 보장하고, **중단을 피하며**, **컴퓨팅 리소스**(예: 원격 서버/가상 머신)를 가능한 한 효율적으로 사용하는 것입니다. 🚀 + +여기서 이 **개념들**을 조금 더 설명하겠습니다. 그러면 서로 매우 다른 환경, 심지어 아직 존재하지 않는 **미래**의 환경에서도 API를 어떻게 배포할지 결정하는 데 필요한 **직관**을 얻을 수 있을 것입니다. + +이 개념들을 고려하면, 여러분은 **자신의 API**를 배포하기 위한 최선의 방법을 **평가하고 설계**할 수 있습니다. + +다음 장들에서는 FastAPI 애플리케이션을 배포하기 위한 더 **구체적인 레시피**를 제공하겠습니다. + +하지만 지금은, 이 중요한 **개념적 아이디어**들을 확인해 봅시다. 이 개념들은 다른 어떤 종류의 웹 API에도 동일하게 적용됩니다. 💡 + +## 보안 - HTTPS { #security-https } + +[이전 HTTPS 장](https.md){.internal-link target=_blank}에서 HTTPS가 API에 암호화를 제공하는 방식에 대해 배웠습니다. + +또한 HTTPS는 일반적으로 애플리케이션 서버 바깥의 **외부** 컴포넌트인 **TLS Termination Proxy**가 제공한다는 것도 확인했습니다. + +그리고 **HTTPS 인증서 갱신**을 담당하는 무언가가 필요합니다. 같은 컴포넌트가 그 역할을 할 수도 있고, 다른 무언가가 담당할 수도 있습니다. + +### HTTPS를 위한 도구 예시 { #example-tools-for-https } + +TLS Termination Proxy로 사용할 수 있는 도구는 예를 들어 다음과 같습니다: + +* Traefik + * 인증서 갱신을 자동으로 처리 ✨ +* Caddy + * 인증서 갱신을 자동으로 처리 ✨ +* Nginx + * 인증서 갱신을 위해 Certbot 같은 외부 컴포넌트 사용 +* HAProxy + * 인증서 갱신을 위해 Certbot 같은 외부 컴포넌트 사용 +* Nginx 같은 Ingress Controller를 사용하는 Kubernetes + * 인증서 갱신을 위해 cert-manager 같은 외부 컴포넌트 사용 +* 클라우드 제공자가 서비스 일부로 내부적으로 처리(아래를 읽어보세요 👇) + +또 다른 선택지는 HTTPS 설정을 포함해 더 많은 일을 대신해주는 **클라우드 서비스**를 사용하는 것입니다. 제약이 있거나 비용이 더 들 수도 있습니다. 하지만 그 경우에는 TLS Termination Proxy를 직접 설정할 필요가 없습니다. + +다음 장에서 구체적인 예시를 보여드리겠습니다. + +--- + +다음으로 고려할 개념들은 실제로 여러분의 API를 실행하는 프로그램(예: Uvicorn)과 관련된 내용입니다. + +## 프로그램과 프로세스 { #program-and-process } + +실행 중인 "**프로세스**"에 대해 많이 이야기하게 될 텐데, 이 말이 무엇을 의미하는지, 그리고 "**프로그램**"이라는 단어와 무엇이 다른지 명확히 해두는 것이 유용합니다. + +### 프로그램이란 { #what-is-a-program } + +**프로그램**이라는 단어는 보통 여러 가지를 가리키는 데 사용됩니다: + +* 여러분이 작성하는 **코드**, 즉 **Python 파일**들 +* 운영체제에서 **실행**할 수 있는 **파일**, 예: `python`, `python.exe`, `uvicorn` +* 운영체제에서 **실행 중**인 특정 프로그램으로, CPU를 사용하고 메모리에 내용을 저장합니다. 이것을 **프로세스**라고도 합니다. + +### 프로세스란 { #what-is-a-process } + +**프로세스**라는 단어는 보통 더 구체적으로, 운영체제에서 실행 중인 것(위 마지막 항목처럼)만을 가리키는 데 사용됩니다: + +* 운영체제에서 **실행 중**인 특정 프로그램 + * 파일이나 코드를 의미하는 것이 아니라, 운영체제가 **실제로 실행**하고 관리하는 대상을 **구체적으로** 의미합니다. +* 어떤 프로그램이든 어떤 코드든, **실행**될 때만 무언가를 **할 수 있습니다**. 즉, **프로세스가 실행 중**일 때입니다. +* 프로세스는 여러분이, 혹은 운영체제가 **종료**(또는 “kill”)할 수 있습니다. 그러면 실행이 멈추고, 더 이상 **아무것도 할 수 없습니다**. +* 컴퓨터에서 실행 중인 각 애플리케이션 뒤에는 프로세스가 있습니다. 실행 중인 프로그램, 각 창 등도 마찬가지입니다. 그리고 컴퓨터가 켜져 있는 동안 보통 많은 프로세스가 **동시에** 실행됩니다. +* **같은 프로그램**의 **여러 프로세스**가 동시에 실행될 수도 있습니다. + +운영체제의 “작업 관리자(task manager)”나 “시스템 모니터(system monitor)”(또는 비슷한 도구)를 확인해 보면, 이런 프로세스가 많이 실행 중인 것을 볼 수 있습니다. + +또 예를 들어, 같은 브라우저 프로그램(Firefox, Chrome, Edge 등)을 실행하는 프로세스가 여러 개 있는 것도 보일 가능성이 큽니다. 보통 탭마다 하나의 프로세스를 실행하고, 그 외에도 추가 프로세스 몇 개가 더 있습니다. + + + +--- + +이제 **프로세스**와 **프로그램**의 차이를 알았으니, 배포에 대한 이야기를 계속해 보겠습니다. + +## 시작 시 실행 { #running-on-startup } + +대부분의 경우 웹 API를 만들면, 클라이언트가 언제나 접근할 수 있도록 **항상 실행**되고 중단되지 않기를 원합니다. 물론 특정 상황에서만 실행하고 싶은 특별한 이유가 있을 수는 있지만, 대부분은 지속적으로 실행되며 **사용 가능**한 상태이기를 원합니다. + +### 원격 서버에서 { #in-a-remote-server } + +원격 서버(클라우드 서버, 가상 머신 등)를 설정할 때, 가장 단순한 방법은 로컬 개발 때처럼 수동으로 `fastapi run`(Uvicorn을 사용합니다)이나 비슷한 명령을 실행하는 것입니다. + +이 방식은 동작하고, **개발 중에는** 유용합니다. + +하지만 서버에 대한 연결이 끊기면, 실행 중인 **프로세스**도 아마 종료될 것입니다. + +또 서버가 재시작되면(예: 업데이트 이후, 혹은 클라우드 제공자의 마이그레이션 이후) 여러분은 아마 **알아차리지 못할** 겁니다. 그 결과, 프로세스를 수동으로 다시 시작해야 한다는 사실도 모르게 됩니다. 그러면 API는 그냥 죽은 상태로 남습니다. 😱 + +### 시작 시 자동 실행 { #run-automatically-on-startup } + +일반적으로 서버 프로그램(예: Uvicorn)은 서버가 시작될 때 자동으로 시작되고, **사람의 개입** 없이도 FastAPI 앱을 실행하는 프로세스가 항상 실행 중이도록(예: FastAPI 앱을 실행하는 Uvicorn) 구성하고 싶을 것입니다. + +### 별도의 프로그램 { #separate-program } + +이를 위해 보통 애플리케이션이 시작 시 실행되도록 보장하는 **별도의 프로그램**을 둡니다. 그리고 많은 경우, 데이터베이스 같은 다른 컴포넌트나 애플리케이션도 함께 실행되도록 보장합니다. + +### 시작 시 실행을 위한 도구 예시 { #example-tools-to-run-at-startup } + +이 역할을 할 수 있는 도구 예시는 다음과 같습니다: + +* Docker +* Kubernetes +* Docker Compose +* Swarm Mode의 Docker +* Systemd +* Supervisor +* 클라우드 제공자가 서비스 일부로 내부적으로 처리 +* 기타... + +다음 장에서 더 구체적인 예시를 제공하겠습니다. + +## 재시작 { #restarts } + +애플리케이션이 시작 시 실행되도록 보장하는 것과 비슷하게, 장애가 발생했을 때 **재시작**되도록 보장하고 싶을 것입니다. + +### 우리는 실수합니다 { #we-make-mistakes } + +사람은 언제나 **실수**합니다. 소프트웨어에는 거의 *항상* 여기저기에 숨은 **버그**가 있습니다. 🐛 + +그리고 개발자는 버그를 발견하고 새로운 기능을 구현하면서 코드를 계속 개선합니다(새로운 버그도 추가할 수 있겠죠 😅). + +### 작은 오류는 자동으로 처리됨 { #small-errors-automatically-handled } + +FastAPI로 웹 API를 만들 때 코드에 오류가 있으면, FastAPI는 보통 그 오류를 발생시킨 단일 요청 안에만 문제를 가둡니다. 🛡 + +클라이언트는 해당 요청에 대해 **500 Internal Server Error**를 받지만, 애플리케이션은 완전히 크래시하지 않고 다음 요청부터는 계속 동작합니다. + +### 더 큰 오류 - 크래시 { #bigger-errors-crashes } + +그럼에도 불구하고, 우리가 작성한 코드가 **전체 애플리케이션을 크래시**시켜 Uvicorn과 Python 자체가 종료되는 경우가 있을 수 있습니다. 💥 + +그래도 한 군데 오류 때문에 애플리케이션이 죽은 채로 남아 있기를 바라지는 않을 것입니다. 망가진 경로 처리를 제외한 나머지 *경로 처리*라도 **계속 실행**되기를 원할 가능성이 큽니다. + +### 크래시 후 재시작 { #restart-after-crash } + +하지만 실행 중인 **프로세스**가 크래시하는 정말 심각한 오류의 경우에는, 적어도 몇 번은 프로세스를 **재시작**하도록 담당하는 외부 컴포넌트가 필요합니다... + +/// tip | 팁 + +...다만 애플리케이션 전체가 **즉시 계속 크래시**한다면, 무한히 재시작하는 것은 아마 의미가 없을 것입니다. 그런 경우에는 개발 중에, 또는 최소한 배포 직후에 알아차릴 가능성이 큽니다. + +그러니 여기서는, 특정한 경우에만 전체가 크래시할 수 있고 **미래**에도 그럴 수 있으며, 그래도 재시작하는 것이 의미 있는 주요 사례에 집중해 봅시다. + +/// + +애플리케이션을 재시작하는 역할은 **외부 컴포넌트**가 맡는 편이 보통 좋습니다. 그 시점에는 Uvicorn과 Python을 포함한 애플리케이션이 이미 크래시했기 때문에, 같은 앱의 같은 코드 안에서 이를 해결할 방법이 없기 때문입니다. + +### 자동 재시작을 위한 도구 예시 { #example-tools-to-restart-automatically } + +대부분의 경우 **시작 시 실행**에 사용한 도구가 자동 **재시작**도 함께 처리합니다. + +예를 들어 다음이 가능합니다: + +* Docker +* Kubernetes +* Docker Compose +* Swarm Mode의 Docker +* Systemd +* Supervisor +* 클라우드 제공자가 서비스 일부로 내부적으로 처리 +* 기타... + +## 복제 - 프로세스와 메모리 { #replication-processes-and-memory } + +FastAPI 애플리케이션은 Uvicorn을 실행하는 `fastapi` 명령 같은 서버 프로그램을 사용하면, **하나의 프로세스**로 실행하더라도 여러 클라이언트를 동시에 처리할 수 있습니다. + +하지만 많은 경우, 여러 워커 프로세스를 동시에 실행하고 싶을 것입니다. + +### 여러 프로세스 - 워커 { #multiple-processes-workers } + +단일 프로세스가 처리할 수 있는 것보다 클라이언트가 더 많고(예: 가상 머신이 그리 크지 않을 때), 서버 CPU에 **여러 코어**가 있다면, 같은 애플리케이션을 실행하는 **여러 프로세스**를 동시에 띄우고 요청을 분산시킬 수 있습니다. + +같은 API 프로그램을 **여러 프로세스**로 실행할 때, 이 프로세스들을 보통 **workers**라고 부릅니다. + +### 워커 프로세스와 포트 { #worker-processes-and-ports } + +[HTTPS에 대한 문서](https.md){.internal-link target=_blank}에서, 서버에서 하나의 포트와 IP 주소 조합에는 하나의 프로세스만 리스닝할 수 있다는 것을 기억하시나요? + +이것은 여전히 사실입니다. + +따라서 **여러 프로세스**를 동시에 실행하려면, 먼저 **포트에서 리스닝하는 단일 프로세스**가 있어야 하고, 그 프로세스가 어떤 방식으로든 각 워커 프로세스로 통신을 전달해야 합니다. + +### 프로세스당 메모리 { #memory-per-process } + +이제 프로그램이 메모리에 무언가를 로드한다고 해봅시다. 예를 들어 머신러닝 모델을 변수에 올리거나 큰 파일 내용을 변수에 올리는 경우입니다. 이런 것들은 서버의 **메모리(RAM)**를 어느 정도 사용합니다. + +그리고 여러 프로세스는 보통 **메모리를 공유하지 않습니다**. 즉, 각 실행 중인 프로세스는 자체 변수와 메모리를 갖습니다. 코드에서 메모리를 많이 사용한다면, **각 프로세스**가 그만큼의 메모리를 사용하게 됩니다. + +### 서버 메모리 { #server-memory } + +예를 들어 코드가 크기 **1 GB**의 머신러닝 모델을 로드한다고 해봅시다. API를 프로세스 하나로 실행하면 RAM을 최소 1GB 사용합니다. 그리고 **4개 프로세스**(워커 4개)를 시작하면 각각 1GB RAM을 사용합니다. 즉 총 **4 GB RAM**을 사용합니다. + +그런데 원격 서버나 가상 머신의 RAM이 3GB뿐이라면, 4GB를 넘게 로드하려고 할 때 문제가 생깁니다. 🚨 + +### 여러 프로세스 - 예시 { #multiple-processes-an-example } + +이 예시에서는 **Manager Process**가 두 개의 **Worker Processes**를 시작하고 제어합니다. + +이 Manager Process는 아마 IP의 **포트**에서 리스닝하는 역할을 합니다. 그리고 모든 통신을 워커 프로세스로 전달합니다. + +워커 프로세스들이 실제로 애플리케이션을 실행하며, **요청**을 받아 **응답**을 반환하는 주요 연산을 수행하고, RAM에 변수로 로드한 모든 내용을 담습니다. + + + +그리고 물론 같은 머신에는 애플리케이션 외에도 **다른 프로세스**들이 실행 중일 가능성이 큽니다. + +흥미로운 점은 각 프로세스의 **CPU 사용률**은 시간에 따라 크게 **변동**할 수 있지만, **메모리(RAM)**는 보통 대체로 **안정적**으로 유지된다는 것입니다. + +매번 비슷한 양의 연산을 수행하는 API이고 클라이언트가 많다면, **CPU 사용률**도 (급격히 오르내리기보다는) *안정적일* 가능성이 큽니다. + +### 복제 도구와 전략 예시 { #examples-of-replication-tools-and-strategies } + +이를 달성하는 접근 방식은 여러 가지가 있을 수 있으며, 다음 장들에서 Docker와 컨테이너를 설명할 때 구체적인 전략을 더 알려드리겠습니다. + +고려해야 할 주요 제약은 **공개 IP**의 **포트**를 처리하는 **단일** 컴포넌트가 있어야 한다는 점입니다. 그리고 그 컴포넌트는 복제된 **프로세스/워커**로 통신을 **전달**할 방법이 있어야 합니다. + +가능한 조합과 전략 몇 가지는 다음과 같습니다: + +* `--workers` 옵션을 사용한 **Uvicorn** + * 하나의 Uvicorn **프로세스 매니저**가 **IP**와 **포트**에서 리스닝하고, **여러 Uvicorn 워커 프로세스**를 시작합니다. +* **Kubernetes** 및 기타 분산 **컨테이너 시스템** + * **Kubernetes** 레이어의 무언가가 **IP**와 **포트**에서 리스닝합니다. 그리고 **여러 컨테이너**를 두어 복제하며, 각 컨테이너에는 **하나의 Uvicorn 프로세스**가 실행됩니다. +* 이를 대신 처리해주는 **클라우드 서비스** + * 클라우드 서비스가 **복제를 대신 처리**해줄 가능성이 큽니다. 실행할 **프로세스**나 사용할 **컨테이너 이미지**를 정의하게 해줄 수도 있지만, 어떤 경우든 대개 **단일 Uvicorn 프로세스**를 기준으로 하고, 클라우드 서비스가 이를 복제하는 역할을 맡습니다. + +/// tip | 팁 + +**컨테이너**, Docker, Kubernetes에 대한 일부 내용이 아직은 잘 이해되지 않아도 괜찮습니다. + +다음 장에서 컨테이너 이미지, Docker, Kubernetes 등을 더 설명하겠습니다: [컨테이너에서 FastAPI - Docker](docker.md){.internal-link target=_blank}. + +/// + +## 시작 전 사전 단계 { #previous-steps-before-starting } + +애플리케이션을 **시작하기 전에** 어떤 단계를 수행하고 싶은 경우가 많습니다. + +예를 들어 **데이터베이스 마이그레이션**을 실행하고 싶을 수 있습니다. + +하지만 대부분의 경우, 이런 단계는 **한 번만** 수행하고 싶을 것입니다. + +그래서 애플리케이션을 시작하기 전에 그 **사전 단계**를 수행할 **단일 프로세스**를 두고 싶을 것입니다. + +또한 이후에 애플리케이션 자체를 **여러 프로세스**(여러 워커)로 시작하더라도, 사전 단계를 수행하는 프로세스는 *반드시* 하나만 실행되도록 해야 합니다. 만약 사전 단계를 **여러 프로세스**가 수행하면, **병렬로** 실행하면서 작업이 **중복**될 수 있습니다. 그리고 데이터베이스 마이그레이션처럼 민감한 작업이라면 서로 충돌을 일으킬 수 있습니다. + +물론 사전 단계를 여러 번 실행해도 문제가 없는 경우도 있습니다. 그런 경우에는 처리하기가 훨씬 쉽습니다. + +/// tip | 팁 + +또한 설정에 따라, 어떤 경우에는 애플리케이션을 시작하기 전에 **사전 단계가 전혀 필요 없을** 수도 있다는 점을 기억하세요. + +그런 경우에는 이런 것들을 전혀 걱정할 필요가 없습니다. 🤷 + +/// + +### 사전 단계 전략 예시 { #examples-of-previous-steps-strategies } + +이는 여러분이 **시스템을 배포하는 방식**에 크게 좌우되며, 프로그램을 시작하는 방식, 재시작 처리 방식 등과도 연결되어 있을 가능성이 큽니다. + +가능한 아이디어는 다음과 같습니다: + +* 앱 컨테이너보다 먼저 실행되는 Kubernetes의 “Init Container” +* 사전 단계를 실행한 다음 애플리케이션을 시작하는 bash 스크립트 + * 이 bash 스크립트를 시작/재시작하고, 오류를 감지하는 등의 방법도 여전히 필요합니다. + +/// tip | 팁 + +컨테이너로 이를 처리하는 더 구체적인 예시는 다음 장에서 제공하겠습니다: [컨테이너에서 FastAPI - Docker](docker.md){.internal-link target=_blank}. + +/// + +## 리소스 활용 { #resource-utilization } + +서버는 여러분이 프로그램으로 소비하거나 **활용(utilize)**할 수 있는 **리소스**입니다. CPU의 계산 시간과 사용 가능한 RAM 메모리가 대표적입니다. + +시스템 리소스를 얼마나 소비/활용하고 싶으신가요? “많지 않게”라고 생각하기 쉽지만, 실제로는 **크래시하지 않는 선에서 가능한 한 많이** 사용하고 싶을 가능성이 큽니다. + +서버 3대를 비용을 내고 쓰고 있는데 RAM과 CPU를 조금만 사용한다면, 아마 **돈을 낭비**하고 💸, **서버 전력도 낭비**하고 🌎, 기타 등등이 될 수 있습니다. + +그 경우에는 서버를 2대만 두고, 각 서버의 리소스(CPU, 메모리, 디스크, 네트워크 대역폭 등)를 더 높은 비율로 사용하는 것이 더 나을 수 있습니다. + +반대로 서버 2대를 두고 CPU와 RAM을 **100%** 사용하고 있다면, 어느 시점에 프로세스 하나가 더 많은 메모리를 요청하게 되고, 서버는 디스크를 “메모리”처럼 사용해야 할 수도 있습니다(수천 배 느릴 수 있습니다). 또는 심지어 **크래시**할 수도 있습니다. 혹은 어떤 프로세스가 계산을 해야 하는데 CPU가 다시 비워질 때까지 기다려야 할 수도 있습니다. + +이 경우에는 **서버 한 대를 추가**로 확보하고 일부 프로세스를 그쪽에서 실행해, 모두가 **충분한 RAM과 CPU 시간**을 갖도록 하는 편이 더 낫습니다. + +또 어떤 이유로 API 사용량이 **급증(spike)**할 가능성도 있습니다. 바이럴이 되었거나, 다른 서비스나 봇이 사용하기 시작했을 수도 있습니다. 그런 경우를 대비해 추가 리소스를 확보해두고 싶을 수 있습니다. + +리소스 활용률 목표로 **임의의 수치**를 정할 수 있습니다. 예를 들어 **50%에서 90% 사이**처럼요. 요점은, 이런 것들이 배포를 조정할 때 측정하고 튜닝하는 주요 지표가 될 가능성이 크다는 것입니다. + +`htop` 같은 간단한 도구로 서버의 CPU와 RAM 사용량, 또는 각 프로세스별 사용량을 볼 수 있습니다. 혹은 서버 여러 대에 분산될 수도 있는 더 복잡한 모니터링 도구를 사용할 수도 있습니다. + +## 요약 { #recap } + +여기까지 애플리케이션 배포 방식을 결정할 때 염두에 두어야 할 주요 개념들을 읽었습니다: + +* 보안 - HTTPS +* 시작 시 실행 +* 재시작 +* 복제(실행 중인 프로세스 수) +* 메모리 +* 시작 전 사전 단계 + +이 아이디어들을 이해하고 적용하는 방법을 알면, 배포를 구성하고 조정할 때 필요한 직관을 얻는 데 도움이 될 것입니다. 🤓 + +다음 섹션에서는 따라 할 수 있는 가능한 전략의 더 구체적인 예시를 제공하겠습니다. 🚀 diff --git a/docs/ko/docs/deployment/fastapicloud.md b/docs/ko/docs/deployment/fastapicloud.md new file mode 100644 index 0000000000..9a830b1579 --- /dev/null +++ b/docs/ko/docs/deployment/fastapicloud.md @@ -0,0 +1,65 @@ +# FastAPI Cloud { #fastapi-cloud } + +**한 번의 명령**으로 FastAPI 앱을 FastAPI Cloud에 배포할 수 있습니다. 아직이라면 대기자 명단에 등록해 보세요. 🚀 + +## 로그인하기 { #login } + +먼저 **FastAPI Cloud** 계정이 이미 있는지 확인하세요(대기자 명단에서 초대해 드렸을 거예요 😉). + +그다음 로그인합니다: + +
+ +```console +$ fastapi login + +You are logged in to FastAPI Cloud 🚀 +``` + +
+ +## 배포하기 { #deploy } + +이제 **한 번의 명령**으로 앱을 배포합니다: + +
+ +```console +$ fastapi deploy + +Deploying to FastAPI Cloud... + +✅ Deployment successful! + +🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev +``` + +
+ +이게 전부입니다! 이제 해당 URL에서 앱에 접근할 수 있습니다. ✨ + +## FastAPI Cloud 소개 { #about-fastapi-cloud } + +**FastAPI Cloud**는 **FastAPI**를 만든 동일한 저자와 팀이 구축했습니다. + +최소한의 노력으로 API를 **구축**, **배포**, **접근**하는 과정을 간소화합니다. + +FastAPI로 앱을 만들 때의 동일한 **개발자 경험**을, 클라우드에 **배포**할 때도 제공합니다. 🎉 + +또한 앱을 배포할 때 보통 필요한 대부분의 것들도 처리해 줍니다. 예를 들면: + +* HTTPS +* 요청을 기반으로 자동 스케일링하는 복제(Replication) +* 등 + +FastAPI Cloud는 *FastAPI and friends* 오픈 소스 프로젝트의 주요 스폰서이자 자금 지원 제공자입니다. ✨ + +## 다른 클라우드 제공업체에 배포하기 { #deploy-to-other-cloud-providers } + +FastAPI는 오픈 소스이며 표준을 기반으로 합니다. 원하는 어떤 클라우드 제공업체에도 FastAPI 앱을 배포할 수 있습니다. + +해당 클라우드 제공업체의 가이드를 따라 FastAPI 앱을 배포하세요. 🤓 + +## 자체 서버에 배포하기 { #deploy-your-own-server } + +또한 이 **Deployment** 가이드에서 이후에 모든 세부사항을 알려드릴 거예요. 그래서 무슨 일이 일어나고 있는지, 무엇이 필요하며, 본인의 서버를 포함해 직접 FastAPI 앱을 어떻게 배포하는지까지 이해할 수 있게 될 것입니다. 🤓 diff --git a/docs/ko/docs/deployment/https.md b/docs/ko/docs/deployment/https.md new file mode 100644 index 0000000000..888ec61592 --- /dev/null +++ b/docs/ko/docs/deployment/https.md @@ -0,0 +1,231 @@ +# HTTPS 알아보기 { #about-https } + +HTTPS는 그냥 “켜져 있거나” 아니면 “꺼져 있는” 것이라고 생각하기 쉽습니다. + +하지만 실제로는 훨씬 더 복잡합니다. + +/// tip | 팁 + +바쁘거나 별로 신경 쓰고 싶지 않다면, 다음 섹션에서 다양한 기법으로 모든 것을 설정하는 단계별 안내를 계속 보세요. + +/// + +소비자 관점에서 **HTTPS의 기본을 배우려면** https://howhttps.works/를 확인하세요. + +이제 **개발자 관점**에서 HTTPS를 생각할 때 염두에 두어야 할 여러 가지가 있습니다: + +* HTTPS를 사용하려면, **서버**가 **제3자**가 발급한 **"인증서(certificates)"**를 **보유**해야 합니다. + * 이 인증서는 실제로 제3자가 “생성”해 주는 것이고, 서버가 만드는 것이 아니라 제3자로부터 **발급/획득**하는 것입니다. +* 인증서에는 **유효 기간**이 있습니다. + * 즉, **만료**됩니다. + * 그리고 나면 제3자로부터 다시 **갱신**해서 **재발급/재획득**해야 합니다. +* 연결의 암호화는 **TCP 레벨**에서 일어납니다. + * 이는 **HTTP보다 한 계층 아래**입니다. + * 따라서 **인증서와 암호화** 처리는 **HTTP 이전**에 수행됩니다. +* **TCP는 "도메인"을 모릅니다.** IP 주소만 압니다. + * 어떤 **특정 도메인**을 요청했는지에 대한 정보는 **HTTP 데이터**에 들어 있습니다. +* **HTTPS 인증서**는 특정 **도메인**을 “인증”하지만, 프로토콜과 암호화는 TCP 레벨에서 일어나며, 어떤 도메인을 다루는지 **알기 전에** 처리됩니다. +* **기본적으로** 이는 IP 주소 하나당 **HTTPS 인증서 하나만** 둘 수 있다는 뜻입니다. + * 서버가 아무리 크든, 그 위에 올린 각 애플리케이션이 아무리 작든 상관없습니다. + * 하지만 이에 대한 **해결책**이 있습니다. +* **TLS** 프로토콜(HTTP 이전, TCP 레벨에서 암호화를 처리하는 것)에 대한 **확장** 중에 **SNI**라는 것이 있습니다. + * 이 SNI 확장을 사용하면, 단일 서버(**단일 IP 주소**)에서 **여러 HTTPS 인증서**를 사용하고 **여러 HTTPS 도메인/애플리케이션**을 제공할 수 있습니다. + * 이를 위해서는 서버에서 **공개 IP 주소**로 리스닝하는 **하나의** 컴포넌트(프로그램)가 서버에 있는 **모든 HTTPS 인증서**에 접근할 수 있어야 합니다. +* 보안 연결을 얻은 **이후에도**, 통신 프로토콜 자체는 **여전히 HTTP**입니다. + * **HTTP 프로토콜**로 전송되더라도, 내용은 **암호화**되어 있습니다. + +일반적으로 서버(머신, 호스트 등)에는 **프로그램/HTTP 서버 하나**를 실행해 **HTTPS 관련 부분 전체**를 관리하게 합니다: **암호화된 HTTPS 요청**을 받고, 복호화된 **HTTP 요청**을 같은 서버에서 실행 중인 실제 HTTP 애플리케이션(이 경우 **FastAPI** 애플리케이션)으로 전달하고, 애플리케이션의 **HTTP 응답**을 받아 적절한 **HTTPS 인증서**로 **암호화**한 뒤 **HTTPS**로 클라이언트에 다시 보내는 역할입니다. 이런 서버를 흔히 **TLS Termination Proxy**라고 부릅니다. + +TLS Termination Proxy로 사용할 수 있는 옵션은 다음과 같습니다: + +* Traefik (인증서 갱신도 처리 가능) +* Caddy (인증서 갱신도 처리 가능) +* Nginx +* HAProxy + +## Let's Encrypt { #lets-encrypt } + +Let's Encrypt 이전에는 이러한 **HTTPS 인증서**가 신뢰할 수 있는 제3자에 의해 판매되었습니다. + +인증서를 획득하는 과정은 번거롭고, 꽤 많은 서류 작업이 필요했으며, 인증서도 상당히 비쌌습니다. + +하지만 그 후 **Let's Encrypt**가 만들어졌습니다. + +이는 Linux Foundation의 프로젝트입니다. 표준 암호학적 보안을 모두 사용하는 **HTTPS 인증서**를 **무료로**, 자동화된 방식으로 제공합니다. 이 인증서들은 수명이 짧고(약 3개월) 그래서 유효 기간이 짧은 만큼 **실제로 보안이 더 좋아지기도** 합니다. + +도메인은 안전하게 검증되며 인증서는 자동으로 생성됩니다. 또한 이로 인해 인증서 갱신도 자동화할 수 있습니다. + +목표는 인증서의 발급과 갱신을 자동화하여 **무료로, 영구히, 안전한 HTTPS**를 사용할 수 있게 하는 것입니다. + +## 개발자를 위한 HTTPS { #https-for-developers } + +개발자에게 중요한 개념들을 중심으로, HTTPS API가 단계별로 어떻게 보일 수 있는지 예시를 들어 보겠습니다. + +### 도메인 이름 { #domain-name } + +아마도 시작은 **도메인 이름**을 **획득**하는 것일 겁니다. 그 다음 DNS 서버(아마 같은 클라우드 제공업체)에서 이를 설정합니다. + +대개 클라우드 서버(가상 머신) 같은 것을 사용하게 되고, 거기에는 fixed **공개 IP 주소**가 있습니다. + +DNS 서버(들)에서 **도메인**이 서버의 **공개 IP 주소**를 가리키도록 레코드(“`A record`”)를 설정합니다. + +보통은 처음 한 번, 모든 것을 설정할 때만 이 작업을 합니다. + +/// tip | 팁 + +도메인 이름 부분은 HTTPS보다 훨씬 이전 단계지만, 모든 것이 도메인과 IP 주소에 의존하므로 여기서 언급할 가치가 있습니다. + +/// + +### DNS { #dns } + +이제 실제 HTTPS 부분에 집중해 보겠습니다. + +먼저 브라우저는 **DNS 서버**에 질의하여, 여기서는 `someapp.example.com`이라는 **도메인에 대한 IP**가 무엇인지 확인합니다. + +DNS 서버는 브라우저에게 특정 **IP 주소**를 사용하라고 알려줍니다. 이는 DNS 서버에 설정해 둔, 서버가 사용하는 공개 IP 주소입니다. + + + +### TLS 핸드셰이크 시작 { #tls-handshake-start } + +그 다음 브라우저는 **포트 443**(HTTPS 포트)에서 해당 IP 주소와 통신합니다. + +통신의 첫 부분은 클라이언트와 서버 사이의 연결을 설정하고, 사용할 암호화 키 등을 결정하는 과정입니다. + + + +클라이언트와 서버가 TLS 연결을 설정하기 위해 상호작용하는 이 과정을 **TLS 핸드셰이크**라고 합니다. + +### SNI 확장을 사용하는 TLS { #tls-with-sni-extension } + +서버에서는 특정 **IP 주소**의 특정 **포트**에서 **하나의 프로세스만** 리스닝할 수 있습니다. 같은 IP 주소에서 다른 포트로 리스닝하는 프로세스는 있을 수 있지만, IP 주소와 포트 조합마다 하나만 가능합니다. + +TLS(HTTPS)는 기본적으로 특정 포트 `443`을 사용합니다. 따라서 우리가 필요한 포트는 이것입니다. + +이 포트에서 하나의 프로세스만 리스닝할 수 있으므로, 그 역할을 하는 프로세스는 **TLS Termination Proxy**가 됩니다. + +TLS Termination Proxy는 하나 이상의 **TLS 인증서**(HTTPS 인증서)에 접근할 수 있습니다. + +앞에서 설명한 **SNI 확장**을 사용해, TLS Termination Proxy는 이 연결에 사용할 수 있는 TLS(HTTPS) 인증서들 중에서 클라이언트가 기대하는 도메인과 일치하는 것을 확인해 선택합니다. + +이 경우에는 `someapp.example.com`에 대한 인증서를 사용합니다. + + + +클라이언트는 이미 해당 TLS 인증서를 생성한 주체(여기서는 Let's Encrypt이지만, 이는 뒤에서 다시 보겠습니다)를 **신뢰**하므로, 인증서가 유효한지 **검증**할 수 있습니다. + +그 다음 인증서를 사용해 클라이언트와 TLS Termination Proxy는 나머지 **TCP 통신**을 어떻게 **암호화할지 결정**합니다. 이로써 **TLS 핸드셰이크** 단계가 완료됩니다. + +이후 클라이언트와 서버는 TLS가 제공하는 **암호화된 TCP 연결**을 갖게 됩니다. 그리고 그 연결을 사용해 실제 **HTTP 통신**을 시작할 수 있습니다. + +이것이 바로 **HTTPS**입니다. 순수(암호화되지 않은) TCP 연결 대신 **안전한 TLS 연결** 안에서 **HTTP**를 그대로 사용하는 것입니다. + +/// tip | 팁 + +통신의 암호화는 HTTP 레벨이 아니라 **TCP 레벨**에서 일어난다는 점에 주의하세요. + +/// + +### HTTPS 요청 { #https-request } + +이제 클라이언트와 서버(구체적으로는 브라우저와 TLS Termination Proxy)가 **암호화된 TCP 연결**을 갖게 되었으니 **HTTP 통신**을 시작할 수 있습니다. + +따라서 클라이언트는 **HTTPS 요청**을 보냅니다. 이는 암호화된 TLS 연결을 통해 전달되는 HTTP 요청일 뿐입니다. + + + +### 요청 복호화 { #decrypt-the-request } + +TLS Termination Proxy는 합의된 암호화를 사용해 **요청을 복호화**하고, 애플리케이션을 실행 중인 프로세스(예: FastAPI 애플리케이션을 실행하는 Uvicorn 프로세스)에 **일반(복호화된) HTTP 요청**을 전달합니다. + + + +### HTTP 응답 { #http-response } + +애플리케이션은 요청을 처리하고 **일반(암호화되지 않은) HTTP 응답**을 TLS Termination Proxy로 보냅니다. + + + +### HTTPS 응답 { #https-response } + +그 다음 TLS Termination Proxy는 이전에 합의한 암호화( `someapp.example.com` 인증서로 시작된 것)를 사용해 **응답을 암호화**하고, 브라우저로 다시 보냅니다. + +이후 브라우저는 응답이 유효한지, 올바른 암호화 키로 암호화되었는지 등을 확인합니다. 그런 다음 **응답을 복호화**하고 처리합니다. + + + +클라이언트(브라우저)는 앞서 **HTTPS 인증서**로 합의한 암호화를 사용하고 있으므로, 해당 응답이 올바른 서버에서 왔다는 것을 알 수 있습니다. + +### 여러 애플리케이션 { #multiple-applications } + +같은 서버(또는 여러 서버)에는 예를 들어 다른 API 프로그램이나 데이터베이스처럼 **여러 애플리케이션**이 있을 수 있습니다. + +특정 IP와 포트 조합은 하나의 프로세스만 처리할 수 있지만(예시에서는 TLS Termination Proxy), 다른 애플리케이션/프로세스도 **공개 IP와 포트 조합**을 동일하게 쓰려고만 하지 않는다면 서버에서 함께 실행될 수 있습니다. + + + +이렇게 하면 TLS Termination Proxy가 **여러 도메인**에 대한 HTTPS와 인증서를 **여러 애플리케이션**에 대해 처리하고, 각 경우에 맞는 애플리케이션으로 요청을 전달할 수 있습니다. + +### 인증서 갱신 { #certificate-renewal } + +미래의 어느 시점에는 각 인증서가 **만료**됩니다(획득 후 약 3개월). + +그 다음에는 또 다른 프로그램(경우에 따라 별도 프로그램일 수도 있고, 경우에 따라 같은 TLS Termination Proxy일 수도 있습니다)이 Let's Encrypt와 통신하여 인증서를 갱신합니다. + + + +**TLS 인증서**는 IP 주소가 아니라 **도메인 이름**과 **연결**되어 있습니다. + +따라서 인증서를 갱신하려면, 갱신 프로그램이 권한 기관(Let's Encrypt)에게 해당 도메인을 실제로 **“소유”하고 제어하고 있음**을 **증명**해야 합니다. + +이를 위해, 그리고 다양한 애플리케이션 요구를 수용하기 위해 여러 방법이 있습니다. 널리 쓰이는 방법은 다음과 같습니다: + +* **일부 DNS 레코드 수정**. + * 이를 위해서는 갱신 프로그램이 DNS 제공업체의 API를 지원해야 하므로, 사용하는 DNS 제공업체에 따라 가능할 수도, 아닐 수도 있습니다. +* 도메인과 연결된 공개 IP 주소에서 **서버로 실행**(적어도 인증서 발급 과정 동안). + * 앞에서 말했듯 특정 IP와 포트에서는 하나의 프로세스만 리스닝할 수 있습니다. + * 이것이 동일한 TLS Termination Proxy가 인증서 갱신 과정까지 처리할 때 매우 유용한 이유 중 하나입니다. + * 그렇지 않으면 TLS Termination Proxy를 잠시 중지하고, 갱신 프로그램을 시작해 인증서를 획득한 다음, TLS Termination Proxy에 인증서를 설정하고, 다시 TLS Termination Proxy를 재시작해야 할 수도 있습니다. 이는 TLS Termination Proxy가 꺼져 있는 동안 앱(들)을 사용할 수 없으므로 이상적이지 않습니다. + +앱을 계속 제공하면서 이 갱신 과정을 처리할 수 있는 것은, 애플리케이션 서버(예: Uvicorn)에서 TLS 인증서를 직접 쓰는 대신 TLS Termination Proxy로 HTTPS를 처리하는 **별도의 시스템**을 두고 싶어지는 주요 이유 중 하나입니다. + +## 프록시 전달 헤더 { #proxy-forwarded-headers } + +프록시를 사용해 HTTPS를 처리할 때, **애플리케이션 서버**(예: FastAPI CLI를 통한 Uvicorn)는 HTTPS 과정에 대해 아무것도 알지 못하고 **TLS Termination Proxy**와는 일반 HTTP로 통신합니다. + +이 **프록시**는 보통 요청을 **애플리케이션 서버**에 전달하기 전에, 요청이 프록시에 의해 **전달(forwarded)**되고 있음을 애플리케이션 서버가 알 수 있도록 일부 HTTP 헤더를 즉석에서 설정합니다. + +/// note | 기술 세부사항 + +프록시 헤더는 다음과 같습니다: + +* X-Forwarded-For +* X-Forwarded-Proto +* X-Forwarded-Host + +/// + +그럼에도 불구하고 **애플리케이션 서버**는 자신이 신뢰할 수 있는 **프록시** 뒤에 있다는 것을 모르므로, 기본적으로는 그 헤더들을 신뢰하지 않습니다. + +하지만 **애플리케이션 서버**가 **프록시**가 보낸 *forwarded* 헤더를 신뢰하도록 설정할 수 있습니다. FastAPI CLI를 사용하고 있다면, *CLI Option* `--forwarded-allow-ips`를 사용해 어떤 IP에서 온 *forwarded* 헤더를 신뢰할지 지정할 수 있습니다. + +예를 들어 **애플리케이션 서버**가 신뢰하는 **프록시**로부터만 통신을 받는다면, `--forwarded-allow-ips="*"`로 설정해 들어오는 모든 IP를 신뢰하게 할 수 있습니다. 어차피 **프록시**가 사용하는 IP에서만 요청을 받게 될 것이기 때문입니다. + +이렇게 하면 애플리케이션은 자신이 사용하는 공개 URL이 무엇인지, HTTPS를 사용하는지, 도메인이 무엇인지 등을 알 수 있습니다. + +예를 들어 리다이렉트를 올바르게 처리하는 데 유용합니다. + +/// tip | 팁 + +이에 대해서는 [프록시 뒤에서 실행하기 - 프록시 전달 헤더 활성화](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank} 문서에서 더 알아볼 수 있습니다. + +/// + +## 요약 { #recap } + +**HTTPS**는 매우 중요하며, 대부분의 경우 상당히 **핵심적**입니다. 개발자가 HTTPS와 관련해 해야 하는 노력의 대부분은 결국 **이 개념들을 이해**하고 그것들이 어떻게 동작하는지 파악하는 것입니다. + +하지만 **개발자를 위한 HTTPS**의 기본 정보를 알고 나면, 여러 도구를 쉽게 조합하고 설정하여 모든 것을 간단하게 관리할 수 있습니다. + +다음 장들에서는 **FastAPI** 애플리케이션을 위한 **HTTPS** 설정 방법을 여러 구체적인 예시로 보여드리겠습니다. 🔒 diff --git a/docs/ko/docs/deployment/manually.md b/docs/ko/docs/deployment/manually.md new file mode 100644 index 0000000000..e85dd02a32 --- /dev/null +++ b/docs/ko/docs/deployment/manually.md @@ -0,0 +1,157 @@ +# 서버를 수동으로 실행하기 { #run-a-server-manually } + +## `fastapi run` 명령 사용하기 { #use-the-fastapi-run-command } + +요약하면, `fastapi run`을 사용해 FastAPI 애플리케이션을 서비스하세요: + +
+ +```console +$ fastapi run main.py + + FastAPI Starting production server 🚀 + + Searching for package file structure from directories + with __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with + the following code: + + from main import app + + app Using import string: main:app + + server Server started at http://0.0.0.0:8000 + server Documentation at http://0.0.0.0:8000/docs + + Logs: + + INFO Started server process [2306215] + INFO Waiting for application startup. + INFO Application startup complete. + INFO Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C + to quit) +``` + +
+ +대부분의 경우에는 이것으로 동작합니다. 😎 + +예를 들어 이 명령은 컨테이너나 서버 등에서 **FastAPI** 앱을 시작할 때 사용할 수 있습니다. + +## ASGI 서버 { #asgi-servers } + +이제 조금 더 자세히 살펴보겠습니다. + +FastAPI는 ASGI라고 불리는, Python 웹 프레임워크와 서버를 만들기 위한 표준을 사용합니다. FastAPI는 ASGI 웹 프레임워크입니다. + +원격 서버 머신에서 **FastAPI** 애플리케이션(또는 다른 ASGI 애플리케이션)을 실행하기 위해 필요한 핵심 요소는 **Uvicorn** 같은 ASGI 서버 프로그램입니다. `fastapi` 명령에는 기본으로 이것이 포함되어 있습니다. + +다음을 포함해 여러 대안이 있습니다: + +* Uvicorn: 고성능 ASGI 서버. +* Hypercorn: HTTP/2 및 Trio 등 여러 기능과 호환되는 ASGI 서버. +* Daphne: Django Channels를 위해 만들어진 ASGI 서버. +* Granian: Python 애플리케이션을 위한 Rust HTTP 서버. +* NGINX Unit: NGINX Unit은 가볍고 다용도로 사용할 수 있는 웹 애플리케이션 런타임입니다. + +## 서버 머신과 서버 프로그램 { #server-machine-and-server-program } + +이름에 관해 기억해 둘 작은 디테일이 있습니다. 💡 + +"**server**"라는 단어는 보통 원격/클라우드 컴퓨터(물리 또는 가상 머신)와, 그 머신에서 실행 중인 프로그램(예: Uvicorn) 둘 다를 가리키는 데 사용됩니다. + +일반적으로 "server"를 읽을 때, 이 두 가지 중 하나를 의미할 수 있다는 점을 기억하세요. + +원격 머신을 가리킬 때는 **server**라고 부르는 것이 일반적이지만, **machine**, **VM**(virtual machine), **node**라고 부르기도 합니다. 이것들은 보통 Linux를 실행하는 원격 머신의 한 형태를 뜻하며, 그곳에서 프로그램을 실행합니다. + +## 서버 프로그램 설치하기 { #install-the-server-program } + +FastAPI를 설치하면 프로덕션 서버인 Uvicorn이 함께 설치되며, `fastapi run` 명령으로 시작할 수 있습니다. + +하지만 ASGI 서버를 수동으로 설치할 수도 있습니다. + +[가상 환경](../virtual-environments.md){.internal-link target=_blank}을 만들고 활성화한 다음, 서버 애플리케이션을 설치하세요. + +예를 들어 Uvicorn을 설치하려면: + +
+ +```console +$ pip install "uvicorn[standard]" + +---> 100% +``` + +
+ +다른 어떤 ASGI 서버 프로그램도 비슷한 과정이 적용됩니다. + +/// tip | 팁 + +`standard`를 추가하면 Uvicorn이 권장되는 추가 의존성 몇 가지를 설치하고 사용합니다. + +여기에는 `asyncio`를 고성능으로 대체할 수 있는 드롭인 대체재인 `uvloop`가 포함되며, 큰 동시성 성능 향상을 제공합니다. + +`pip install "fastapi[standard]"` 같은 방식으로 FastAPI를 설치하면 `uvicorn[standard]`도 함께 설치됩니다. + +/// + +## 서버 프로그램 실행하기 { #run-the-server-program } + +ASGI 서버를 수동으로 설치했다면, 보통 FastAPI 애플리케이션을 임포트하기 위해 특별한 형식의 import string을 전달해야 합니다: + +
+ +```console +$ uvicorn main:app --host 0.0.0.0 --port 80 + +INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit) +``` + +
+ +/// note | 참고 + +`uvicorn main:app` 명령은 다음을 가리킵니다: + +* `main`: 파일 `main.py`(Python "module"). +* `app`: `main.py` 안에서 `app = FastAPI()` 라인으로 생성된 객체. + +이는 다음과 동일합니다: + +```Python +from main import app +``` + +/// + +각 ASGI 서버 프로그램의 대안도 비슷한 명령을 갖고 있으며, 자세한 내용은 각자의 문서를 참고하세요. + +/// warning | 경고 + +Uvicorn과 다른 서버는 개발 중에 유용한 `--reload` 옵션을 지원합니다. + +`--reload` 옵션은 훨씬 더 많은 리소스를 소비하고, 더 불안정합니다. + +**개발** 중에는 큰 도움이 되지만, **프로덕션**에서는 사용하지 **말아야** 합니다. + +/// + +## 배포 개념 { #deployment-concepts } + +이 예제들은 서버 프로그램(예: Uvicorn)을 실행하여 **단일 프로세스**를 시작하고, 사전에 정한 포트(예: `80`)에서 모든 IP(`0.0.0.0`)로 들어오는 요청을 받도록 합니다. + +이것이 기본 아이디어입니다. 하지만 보통은 다음과 같은 추가 사항들도 처리해야 합니다: + +* 보안 - HTTPS +* 시작 시 자동 실행 +* 재시작 +* 복제(실행 중인 프로세스 수) +* 메모리 +* 시작 전 선행 단계 + +다음 장들에서 이 각각의 개념을 어떻게 생각해야 하는지, 그리고 이를 다루기 위한 전략의 구체적인 예시를 더 알려드리겠습니다. 🚀 diff --git a/docs/ko/docs/fastapi-cli.md b/docs/ko/docs/fastapi-cli.md index a1160c71fc..0d87ce3219 100644 --- a/docs/ko/docs/fastapi-cli.md +++ b/docs/ko/docs/fastapi-cli.md @@ -1,83 +1,75 @@ -# FastAPI CLI +# FastAPI CLI { #fastapi-cli } -**FastAPI CLI**는 FastAPI 애플리케이션을 실행하고, 프로젝트를 관리하는 등 다양한 작업을 수행할 수 있는 커맨드 라인 프로그램입니다. +**FastAPI CLI**는 FastAPI 애플리케이션을 서빙하고, FastAPI 프로젝트를 관리하는 등 다양한 작업에 사용할 수 있는 커맨드 라인 프로그램입니다. -FastAPI를 설치할 때 (예: `pip install "fastapi[standard]"` 명령어를 사용할 경우), `fastapi-cli`라는 패키지가 포함됩니다. 이 패키지는 터미널에서 사용할 수 있는 `fastapi` 명령어를 제공합니다. +FastAPI를 설치할 때(예: `pip install "fastapi[standard]"`), `fastapi-cli`라는 패키지가 포함되며, 이 패키지는 터미널에서 `fastapi` 명령어를 제공합니다. -개발용으로 FastAPI 애플리케이션을 실행하려면 다음과 같이 `fastapi dev` 명령어를 사용할 수 있습니다: +개발용으로 FastAPI 애플리케이션을 실행하려면 `fastapi dev` 명령어를 사용할 수 있습니다:
```console -$ fastapi dev main.py -INFO Using path main.py -INFO Resolved absolute path /home/user/code/awesomeapp/main.py -INFO Searching for package file structure from directories with __init__.py files -INFO Importing from /home/user/code/awesomeapp +$ fastapi dev main.py - ╭─ Python module file ─╮ - │ │ - │ 🐍 main.py │ - │ │ - ╰──────────────────────╯ + FastAPI Starting development server 🚀 -INFO Importing module main -INFO Found importable FastAPI app + Searching for package file structure from directories with + __init__.py files + Importing from /home/user/code/awesomeapp - ╭─ Importable FastAPI app ─╮ - │ │ - │ from main import app │ - │ │ - ╰──────────────────────────╯ + module 🐍 main.py -INFO Using import string main:app + code Importing the FastAPI app object from the module with the + following code: - ╭────────── 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 - │ │ - ╰─────────────────────────────────────────────────────╯ + from main import app -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 [2265862] using WatchFiles -INFO: Started server process [2265873] -INFO: Waiting for application startup. -INFO: Application startup complete. + app Using import string: main:app + + server Server started at http://127.0.0.1:8000 + server Documentation at http://127.0.0.1:8000/docs + + tip Running in development mode, for production use: + fastapi run + + Logs: + + 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 [383138] using WatchFiles + INFO Started server process [383153] + INFO Waiting for application startup. + INFO Application startup complete. ```
-`fastapi`라고 불리는 명령어 프로그램은 **FastAPI CLI**입니다. +`fastapi`라고 불리는 커맨드 라인 프로그램은 **FastAPI CLI**입니다. -FastAPI CLI는 Python 프로그램의 경로(예: `main.py`)를 인수로 받아, `FastAPI` 인스턴스(일반적으로 `app`으로 명명)를 자동으로 감지하고 올바른 임포트 과정을 결정한 후 이를 실행합니다. +FastAPI CLI는 Python 프로그램의 경로(예: `main.py`)를 받아 `FastAPI` 인스턴스(일반적으로 `app`으로 이름을 붙임)를 자동으로 감지하고, 올바른 임포트 과정을 결정한 다음 서빙합니다. -프로덕션 환경에서는 `fastapi run` 명령어를 사용합니다. 🚀 +프로덕션에서는 대신 `fastapi run`을 사용합니다. 🚀 -내부적으로, **FastAPI CLI**는 고성능의, 프로덕션에 적합한, ASGI 서버인 Uvicorn을 사용합니다. 😎 +내부적으로 **FastAPI CLI**는 고성능의, 프로덕션에 적합한 ASGI 서버인 Uvicorn을 사용합니다. 😎 -## `fastapi dev` +## `fastapi dev` { #fastapi-dev } -`fastapi dev` 명령을 실행하면 개발 모드가 시작됩니다. +`fastapi dev`를 실행하면 개발 모드가 시작됩니다. -기본적으로 **자동 재시작(auto-reload)** 기능이 활성화되어, 코드에 변경이 생기면 서버를 자동으로 다시 시작합니다. 하지만 이 기능은 리소스를 많이 사용하며, 비활성화했을 때보다 안정성이 떨어질 수 있습니다. 따라서 개발 환경에서만 사용하는 것이 좋습니다. 또한, 서버는 컴퓨터가 자체적으로 통신할 수 있는 IP 주소(`localhost`)인 `127.0.0.1`에서 연결을 대기합니다. +기본적으로 **auto-reload**가 활성화되어 코드에 변경이 생기면 서버를 자동으로 다시 로드합니다. 이는 리소스를 많이 사용하며, 비활성화했을 때보다 안정성이 떨어질 수 있습니다. 개발 환경에서만 사용해야 합니다. 또한 컴퓨터가 자신과만 통신하기 위한(`localhost`) IP인 `127.0.0.1`에서 연결을 대기합니다. -## `fastapi run` +## `fastapi run` { #fastapi-run } -`fastapi run` 명령을 실행하면 기본적으로 프로덕션 모드로 FastAPI가 시작됩니다. +`fastapi run`을 실행하면 기본적으로 프로덕션 모드로 FastAPI가 시작됩니다. -기본적으로 **자동 재시작(auto-reload)** 기능이 비활성화되어 있습니다. 또한, 사용 가능한 모든 IP 주소인 `0.0.0.0`에서 연결을 대기하므로 해당 컴퓨터와 통신할 수 있는 모든 사람이 공개적으로 액세스할 수 있습니다. 이는 일반적으로 컨테이너와 같은 프로덕션 환경에서 실행하는 방법입니다. +기본적으로 **auto-reload**는 비활성화되어 있습니다. 또한 사용 가능한 모든 IP 주소를 의미하는 `0.0.0.0`에서 연결을 대기하므로, 해당 컴퓨터와 통신할 수 있는 누구에게나 공개적으로 접근 가능해집니다. 보통 프로덕션에서는 이렇게 실행하며, 예를 들어 컨테이너에서 이런 방식으로 실행합니다. -애플리케이션을 배포하는 방식에 따라 다르지만, 대부분 "종료 프록시(termination proxy)"를 활용해 HTTPS를 처리하는 것이 좋습니다. 배포 서비스 제공자가 이 작업을 대신 처리해줄 수도 있고, 직접 설정해야 할 수도 있습니다. +대부분의 경우 위에 "termination proxy"를 두고 HTTPS를 처리하게(그리고 처리해야) 됩니다. 이는 애플리케이션을 배포하는 방식에 따라 달라지며, 제공자가 이 작업을 대신 처리해줄 수도 있고 직접 설정해야 할 수도 있습니다. -/// tip +/// tip | 팁 -자세한 내용은 [deployment documentation](deployment/index.md){.internal-link target=\_blank}에서 확인할 수 있습니다. +자세한 내용은 [배포 문서](deployment/index.md){.internal-link target=_blank}에서 확인할 수 있습니다. /// diff --git a/docs/ko/docs/features.md b/docs/ko/docs/features.md index dfbf479998..17cc9289f7 100644 --- a/docs/ko/docs/features.md +++ b/docs/ko/docs/features.md @@ -1,43 +1,43 @@ -# 기능 +# 기능 { #features } -## FastAPI의 기능 +## FastAPI의 기능 { #fastapi-features } **FastAPI**는 다음과 같은 기능을 제공합니다: -### 개방형 표준을 기반으로 +### 개방형 표준을 기반으로 { #based-on-open-standards } -* 경로작동, 매개변수, 본문 요청, 보안 그 외의 선언을 포함한 API 생성을 위한 OpenAPI -* JSON Schema (OpenAPI 자체가 JSON Schema를 기반으로 하고 있습니다)를 사용한 자동 데이터 모델 문서화. -* 단순히 떠올려서 덧붙인 기능이 아닙니다. 세심한 검토를 거친 후, 이러한 표준을 기반으로 설계되었습니다. -* 이는 또한 다양한 언어로 자동적인 **클라이언트 코드 생성**을 사용할 수 있게 지원합니다. +* OpenAPI: path operations, 매개변수, 요청 본문, 보안 등의 선언을 포함하여 API를 생성합니다. +* JSON Schema를 사용한 자동 데이터 모델 문서화(OpenAPI 자체가 JSON Schema를 기반으로 하기 때문입니다). +* 단순히 떠올려서 덧붙인 레이어가 아니라, 세심한 검토를 거친 뒤 이러한 표준을 중심으로 설계되었습니다. +* 이는 또한 다양한 언어로 자동 **클라이언트 코드 생성**을 사용할 수 있게 해줍니다. -### 문서 자동화 +### 문서 자동화 { #automatic-docs } -대화형 API 문서와 웹 탐색 유저 인터페이스를 제공합니다. 프레임워크가 OpenAPI를 기반으로 하기에, 2가지 옵션이 기본적으로 들어간 여러 옵션이 존재합니다. +대화형 API 문서와 탐색용 웹 사용자 인터페이스를 제공합니다. 프레임워크가 OpenAPI를 기반으로 하기에 여러 옵션이 있으며, 기본으로 2가지가 포함됩니다. -* 대화형 탐색 Swagger UI를 이용해, 브라우저에서 바로 여러분의 API를 호출하거나 테스트할 수 있습니다. +* 대화형 탐색이 가능한 Swagger UI로 브라우저에서 직접 API를 호출하고 테스트할 수 있습니다. ![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) -* ReDoc을 이용해 API 문서화를 대체할 수 있습니다. +* ReDoc을 이용한 대체 API 문서화. ![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) -### 그저 현대 파이썬 +### 그저 현대 파이썬 { #just-modern-python } -(Pydantic 덕분에) FastAPI는 표준 **파이썬 3.6 타입** 선언에 기반하고 있습니다. 새로 배울 문법이 없습니다. 그저 표준적인 현대 파이썬입니다. +( Pydantic 덕분에) 모든 것이 표준 **Python 타입** 선언을 기반으로 합니다. 새로 배울 문법이 없습니다. 그저 표준적인 현대 파이썬입니다. -만약 여러분이 파이썬 타입을 어떻게 사용하는지에 대한 2분 정도의 복습이 필요하다면 (비록 여러분이 FastAPI를 사용하지 않는다 하더라도), 다음의 짧은 자습서를 확인하세요: [파이썬 타입](python-types.md){.internal-link target=\_blank}. +Python 타입을 어떻게 사용하는지 2분 정도 복습이 필요하다면(FastAPI를 사용하지 않더라도), 다음의 짧은 자습서를 확인하세요: [Python 타입](python-types.md){.internal-link target=_blank}. -여러분은 타입을 이용한 표준 파이썬을 다음과 같이 적을 수 있습니다: +여러분은 타입이 있는 표준 Python을 다음과 같이 작성합니다: ```Python from datetime import date from pydantic import BaseModel -# 변수를 str로 선언 -# 그 후 함수 안에서 편집기 지원을 받으세요 +# 변수를 str로 선언합니다 +# 그리고 함수 내부에서 편집기 지원을 받습니다 def main(user_id: str): return user_id @@ -49,7 +49,7 @@ class User(BaseModel): joined: date ``` -위의 코드는 다음과 같이 사용될 수 있습니다: +그 다음 다음과 같이 사용할 수 있습니다: ```Python my_user: User = User(id=3, name="John Doe", joined="2018-07-19") @@ -65,23 +65,23 @@ my_second_user: User = User(**second_user_data) /// info | 정보 -`**second_user_data`가 뜻하는 것: +`**second_user_data`는 다음을 의미합니다: -`second_user_data` 딕셔너리의 키와 값을 키-값 인자로서 바로 넘겨줍니다. 다음과 동일합니다: `User(id=4, name="Mary", joined="2018-11-30")` +`second_user_data` `dict`의 키와 값을 키-값 인자로서 바로 넘겨주는 것으로, 다음과 동일합니다: `User(id=4, name="Mary", joined="2018-11-30")` /// -### 편집기 지원 +### 편집기 지원 { #editor-support } -모든 프레임워크는 사용하기 쉽고 직관적으로 설계되었으며, 좋은 개발 경험을 보장하기 위해 개발을 시작하기도 전에 모든 결정들은 여러 편집기에서 테스트됩니다. +프레임워크 전체는 사용하기 쉽고 직관적으로 설계되었으며, 최고의 개발 경험을 보장하기 위해 개발을 시작하기도 전에 모든 결정은 여러 편집기에서 테스트되었습니다. -최근 파이썬 개발자 설문조사에서 "자동 완성"이 가장 많이 사용되는 기능이라는 것이 밝혀졌습니다. +Python 개발자 설문조사에서 가장 많이 사용되는 기능 중 하나가 "자동 완성"이라는 점이 분명합니다. -**FastAPI** 프레임워크의 모든 부분은 이를 충족하기 위해 설계되었습니다. 자동완성은 어느 곳에서나 작동합니다. +**FastAPI** 프레임워크 전체는 이를 만족하기 위해 만들어졌습니다. 자동 완성은 어디서나 작동합니다. -여러분은 문서로 다시 돌아올 일이 거의 없을 겁니다. +문서로 다시 돌아올 일은 거의 없을 것입니다. -다음은 편집기가 어떻게 여러분을 도와주는지 보여줍니다: +편집기가 여러분을 어떻게 도와줄 수 있는지 살펴보세요: * Visual Studio Code에서: @@ -91,111 +91,111 @@ my_second_user: User = User(**second_user_data) ![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png) -여러분이 이전에 불가능하다고 고려했던 코드도 완성할 수 있을 겁니다. 예를 들어, 요청에서 전달되는 (중첩될 수도 있는)JSON 본문 내부에 있는 `price` 키입니다. +이전에 불가능하다고 생각했을 코드에서도 자동 완성을 받을 수 있습니다. 예를 들어, 요청에서 전달되는(중첩될 수도 있는) JSON 본문 내부의 `price` 키 같은 경우입니다. -잘못된 키 이름을 적을 일도, 문서를 왔다 갔다할 일도 없으며, 혹은 마지막으로 `username` 또는 `user_name`을 사용했는지 찾기 위해 위 아래로 스크롤할 일도 없습니다. +더 이상 잘못된 키 이름을 입력하거나, 문서 사이를 왔다 갔다 하거나, `username`을 썼는지 `user_name`을 썼는지 찾으려고 위아래로 스크롤할 필요가 없습니다. -### 토막 정보 +### 간결함 { #short } -어느 곳에서나 선택적 구성이 가능한 모든 것에 합리적인 기본값이 설정되어 있습니다. 모든 매개변수는 여러분이 필요하거나, 원하는 API를 정의하기 위해 미세하게 조정할 수 있습니다. +선택적 구성을 어디서나 할 수 있도록 하면서도, 모든 것에 합리적인 **기본값**이 설정되어 있습니다. 모든 매개변수는 필요한 작업을 하거나 필요한 API를 정의하기 위해 미세하게 조정할 수 있습니다. -하지만 기본적으로 모든 것이 "그냥 작동합니다". +하지만 기본적으로 모든 것이 **"그냥 작동합니다"**. -### 검증 +### 검증 { #validation } -* 다음을 포함한, 대부분의 (혹은 모든?) 파이썬 **데이터 타입** 검증할 수 있습니다: +* 다음을 포함해 대부분(혹은 전부?)의 Python **데이터 타입**에 대한 검증: * JSON 객체 (`dict`). * 아이템 타입을 정의하는 JSON 배열 (`list`). - * 최소 길이와 최대 길이를 정의하는 문자열 (`str`) 필드. - * 최솟값과 최댓값을 가지는 숫자 (`int`, `float`), 그 외. + * 최소/최대 길이를 정의하는 문자열(`str`) 필드. + * 최소/최대 값을 가지는 숫자(`int`, `float`) 등. -* 다음과 같이 더욱 이색적인 타입에 대해 검증할 수 있습니다: +* 다음과 같은 좀 더 이색적인 타입에 대한 검증: * URL. - * 이메일. + * Email. * UUID. - * ...다른 것들. + * ...그 외. -모든 검증은 견고하면서 잘 확립된 **Pydantic**에 의해 처리됩니다. +모든 검증은 잘 확립되어 있고 견고한 **Pydantic**이 처리합니다. -### 보안과 인증 +### 보안과 인증 { #security-and-authentication } -보안과 인증이 통합되어 있습니다. 데이터베이스나 데이터 모델과의 타협없이 사용할 수 있습니다. +보안과 인증이 통합되어 있습니다. 데이터베이스나 데이터 모델과 타협하지 않습니다. -다음을 포함하는, 모든 보안 스키마가 OpenAPI에 정의되어 있습니다. +다음을 포함해 OpenAPI에 정의된 모든 보안 스키마: * HTTP Basic. -* **OAuth2** (**JWT tokens** 또한 포함). [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=\_blank}에 있는 자습서를 확인해 보세요. +* **OAuth2**(**JWT tokens** 또한 포함). [JWT를 사용한 OAuth2](tutorial/security/oauth2-jwt.md){.internal-link target=_blank} 자습서를 확인해 보세요. * 다음에 들어 있는 API 키: * 헤더. - * 매개변수. - * 쿠키 및 그 외. + * 쿼리 매개변수. + * 쿠키 등. -추가적으로 (**세션 쿠키**를 포함한) 모든 보안 기능은 Starlette에 있습니다. +추가로 Starlette의 모든 보안 기능(**세션 쿠키** 포함)도 제공합니다. -모두 재사용할 수 있는 도구와 컴포넌트로 만들어져 있어 여러분의 시스템, 데이터 저장소, 관계형 및 NoSQL 데이터베이스 등과 쉽게 통합할 수 있습니다. +모두 재사용 가능한 도구와 컴포넌트로 만들어져 있어, 여러분의 시스템, 데이터 저장소, 관계형 및 NoSQL 데이터베이스 등과 쉽게 통합할 수 있습니다. -### 의존성 주입 +### 의존성 주입 { #dependency-injection } -FastAPI는 사용하기 매우 간편하지만, 엄청난 의존성 주입시스템을 포함하고 있습니다. +FastAPI는 사용하기 매우 쉽지만, 매우 강력한 Dependency Injection 시스템을 포함하고 있습니다. -* 의존성은 의존성을 가질수도 있어, 이를 통해 의존성의 계층이나 **의존성의 "그래프"**를 형성합니다. -* 모든 것이 프레임워크에 의해 **자동적으로 처리됩니다**. -* 모든 의존성은 요청에서 데이터를 요구하여 자동 문서화와 **경로 작동 제약을 강화할 수 있습니다**. -* 의존성에서 정의된 _경로 작동_ 매개변수에 대해서도 **자동 검증**이 이루어 집니다. -* 복잡한 사용자의 인증 시스템, **데이터베이스 연결**, 등등을 지원합니다. -* 데이터베이스, 프론트엔드 등과 관련되어 **타협하지 않아도 됩니다**. 하지만 그 모든 것과 쉽게 통합이 가능합니다. +* 의존성도 의존성을 가질 수 있어, 의존성의 계층 또는 **의존성의 "그래프"**를 생성합니다. +* 모든 것이 프레임워크에 의해 **자동으로 처리됩니다**. +* 모든 의존성은 요청에서 데이터를 요구할 수 있으며, **경로 처리** 제약과 자동 문서화를 강화할 수 있습니다. +* 의존성에 정의된 *경로 처리* 매개변수에 대해서도 **자동 검증**을 합니다. +* 복잡한 사용자 인증 시스템, **데이터베이스 연결** 등을 지원합니다. +* 데이터베이스, 프론트엔드 등과 **타협하지 않습니다**. 하지만 모두와 쉽게 통합할 수 있습니다. -### 제한 없는 "플러그인" +### 제한 없는 "플러그인" { #unlimited-plug-ins } -또는 다른 방법으로, 그것들을 사용할 필요 없이 필요한 코드만 임포트할 수 있습니다. +또 다른 방식으로는, 그것들이 필요 없습니다. 필요한 코드를 임포트해서 사용하면 됩니다. -어느 통합도 (의존성과 함께) 사용하기 쉽게 설계되어 있어, *경로 작동*에 사용된 것과 동일한 구조와 문법을 사용하여 2줄의 코드로 여러분의 어플리케이션에 사용할 "플러그인"을 만들 수 있습니다. +어떤 통합이든(의존성과 함께) 사용하기 매우 간단하도록 설계되어 있어, *경로 처리*에 사용된 것과 동일한 구조와 문법을 사용해 2줄의 코드로 애플리케이션용 "플러그인"을 만들 수 있습니다. -### 테스트 결과 +### 테스트됨 { #tested } -* 100% 테스트 범위. -* 100% 타입이 명시된 코드 베이스. -* 상용 어플리케이션에서의 사용. +* 100% test coverage. +* 100% type annotated 코드 베이스. +* 프로덕션 애플리케이션에서 사용됩니다. -## Starlette 기능 +## Starlette 기능 { #starlette-features } -**FastAPI**는 Starlette를 기반으로 구축되었으며, 이와 완전히 호환됩니다. 따라서, 여러분이 보유하고 있는 어떤 추가적인 Starlette 코드도 작동할 것입니다. +**FastAPI**는 Starlette와 완전히 호환되며(또한 이를 기반으로 합니다). 따라서 추가로 가지고 있는 Starlette 코드도 모두 동작합니다. -`FastAPI`는 실제로 `Starlette`의 하위 클래스입니다. 그래서, 여러분이 이미 Starlette을 알고 있거나 사용하고 있으면, 대부분의 기능이 같은 방식으로 작동할 것입니다. +`FastAPI`는 실제로 `Starlette`의 하위 클래스입니다. 그래서 Starlette을 이미 알고 있거나 사용하고 있다면, 대부분의 기능이 같은 방식으로 동작할 것입니다. -**FastAPI**를 사용하면 여러분은 **Starlette**의 기능 대부분을 얻게 될 것입니다(FastAPI가 단순히 Starlette를 강화했기 때문입니다): +**FastAPI**를 사용하면 **Starlette**의 모든 기능을 얻게 됩니다(FastAPI는 Starlette에 강력한 기능을 더한 것입니다): -* 아주 인상적인 성능. 이는 **NodeJS**와 **Go**와 동등하게 사용 가능한 가장 빠른 파이썬 프레임워크 중 하나입니다. +* 정말 인상적인 성능. **NodeJS**와 **Go**에 버금가는, 사용 가능한 가장 빠른 Python 프레임워크 중 하나입니다. * **WebSocket** 지원. -* 프로세스 내의 백그라운드 작업. -* 시작과 종료 이벤트. +* 프로세스 내 백그라운드 작업. +* 시작 및 종료 이벤트. * HTTPX 기반 테스트 클라이언트. * **CORS**, GZip, 정적 파일, 스트리밍 응답. * **세션과 쿠키** 지원. -* 100% 테스트 범위. -* 100% 타입이 명시된 코드 베이스. +* 100% test coverage. +* 100% type annotated codebase. -## Pydantic 기능 +## Pydantic 기능 { #pydantic-features } -**FastAPI**는 Pydantic을 기반으로 하며 Pydantic과 완벽하게 호환됩니다. 그래서 어느 추가적인 Pydantic 코드를 여러분이 가지고 있든 작동할 것입니다. +**FastAPI**는 Pydantic과 완벽하게 호환되며(또한 이를 기반으로 합니다). 따라서 추가로 가지고 있는 Pydantic 코드도 모두 동작합니다. -Pydantic을 기반으로 하는, 데이터베이스를 위한 ORM, ODM을 포함한 외부 라이브러리를 포함합니다. +데이터베이스를 위한 ORM, ODM과 같은, Pydantic을 기반으로 하는 외부 라이브러리도 포함합니다. -이는 모든 것이 자동으로 검증되기 때문에, 많은 경우에서 요청을 통해 얻은 동일한 객체를, **직접 데이터베이스로** 넘겨줄 수 있습니다. +이는 모든 것이 자동으로 검증되기 때문에, 많은 경우 요청에서 얻은 동일한 객체를 **직접 데이터베이스로** 넘겨줄 수 있다는 의미이기도 합니다. -반대로도 마찬가지이며, 많은 경우에서 여러분은 **직접 클라이언트로** 그저 객체를 넘겨줄 수 있습니다. +반대로도 마찬가지이며, 많은 경우 데이터베이스에서 얻은 객체를 **직접 클라이언트로** 그대로 넘겨줄 수 있습니다. -**FastAPI**를 사용하면 (모든 데이터 처리를 위해 FastAPI가 Pydantic을 기반으로 하기 있기에) **Pydantic**의 모든 기능을 얻게 됩니다: +**FastAPI**를 사용하면(모든 데이터 처리를 위해 FastAPI가 Pydantic을 기반으로 하기에) **Pydantic**의 모든 기능을 얻게 됩니다: -* **어렵지 않은 언어**: - * 새로운 스키마 정의 마이크로 언어를 배우지 않아도 됩니다. - * 여러분이 파이썬 타입을 안다면, 여러분은 Pydantic을 어떻게 사용하는지 아는 겁니다. -* 여러분의 **IDE/린터/뇌**와 잘 어울립니다: - * Pydantic 데이터 구조는 단순 여러분이 정의한 클래스의 인스턴스이기 때문에, 자동 완성, 린팅, mypy 그리고 여러분의 직관까지 여러분의 검증된 데이터와 올바르게 작동합니다. +* **No brainfuck**: + * 새로운 스키마 정의 마이크로 언어를 배울 필요가 없습니다. + * Python 타입을 알고 있다면 Pydantic 사용법도 알고 있는 것입니다. +* 여러분의 **IDE/linter/뇌**와 잘 어울립니다: + * pydantic 데이터 구조는 여러분이 정의한 클래스 인스턴스일 뿐이므로, 자동 완성, 린팅, mypy, 그리고 직관까지도 검증된 데이터와 함께 제대로 작동합니다. * **복잡한 구조**를 검증합니다: - * 계층적인 Pydantic 모델, 파이썬 `typing`의 `List`와 `Dict`, 그 외를 사용합니다. - * 그리고 검증자는 복잡한 데이터 스키마를 명확하고 쉽게 정의 및 확인하며 JSON 스키마로 문서화합니다. - * 여러분은 깊게 **중첩된 JSON** 객체를 가질 수 있으며, 이 객체 모두 검증하고 설명을 붙일 수 있습니다. -* **확장 가능성**: - * Pydantic은 사용자 정의 데이터 타입을 정의할 수 있게 하거나, 검증자 데코레이터가 붙은 모델의 메소드를 사용하여 검증을 확장할 수 있습니다. -* 100% 테스트 범위. + * 계층적인 Pydantic 모델, Python `typing`의 `List`와 `Dict` 등을 사용합니다. + * 그리고 validator는 복잡한 데이터 스키마를 명확하고 쉽게 정의하고, 검사하고, JSON Schema로 문서화할 수 있게 해줍니다. + * 깊게 **중첩된 JSON** 객체를 가질 수 있으며, 이를 모두 검증하고 주석을 달 수 있습니다. +* **확장 가능**: + * Pydantic은 사용자 정의 데이터 타입을 정의할 수 있게 하거나, validator decorator가 붙은 모델 메서드로 검증을 확장할 수 있습니다. +* 100% test coverage. diff --git a/docs/ko/docs/help-fastapi.md b/docs/ko/docs/help-fastapi.md index b65ef959cb..a4abbe7afa 100644 --- a/docs/ko/docs/help-fastapi.md +++ b/docs/ko/docs/help-fastapi.md @@ -1,4 +1,4 @@ -# FastAPI 지원 - 도움 받기 +# FastAPI 지원 - 도움 받기 { #help-fastapi-get-help } **FastAPI** 가 마음에 드시나요? @@ -10,9 +10,9 @@ FastAPI, 다른 사용자, 개발자를 응원하고 싶으신가요? 또한 도움을 받을 수 있는 방법도 몇 가지 있습니다. -## 뉴스레터 구독 +## 뉴스레터 구독 { #subscribe-to-the-newsletter } -[**FastAPI and friends** 뉴스레터](newsletter.md){.internal-link target=\_blank}를 구독하여 최신 정보를 유지할 수 있습니다: +(자주 발송되지 않는) [**FastAPI and friends** 뉴스레터](newsletter.md){.internal-link target=_blank}를 구독하여 최신 정보를 유지할 수 있습니다: * FastAPI and friends에 대한 뉴스 🚀 * 가이드 📝 @@ -20,65 +20,65 @@ FastAPI, 다른 사용자, 개발자를 응원하고 싶으신가요? * 획기적인 변화 🚨 * 팁과 요령 ✅ -## 트위터에서 FastAPI 팔로우하기 +## X(Twitter)에서 FastAPI 팔로우하기 { #follow-fastapi-on-x-twitter } **X (Twitter)**의 @fastapi를 팔로우하여 **FastAPI** 에 대한 최신 뉴스를 얻을 수 있습니다. 🐦 -## Star **FastAPI** in GitHub +## GitHub에서 **FastAPI**에 Star 주기 { #star-fastapi-in-github } GitHub에서 FastAPI에 "star"를 붙일 수 있습니다 (오른쪽 상단의 star 버튼을 클릭): https://github.com/fastapi/fastapi. ⭐️ 스타를 늘림으로써, 다른 사용자들이 좀 더 쉽게 찾을 수 있고, 많은 사람들에게 유용한 것임을 나타낼 수 있습니다. -## GitHub 저장소에서 릴리즈 확인 +## 릴리즈 확인을 위해 GitHub 저장소 보기 { #watch-the-github-repository-for-releases } -GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/fastapi/fastapi. 👀 +GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 "watch" 버튼을 클릭): https://github.com/fastapi/fastapi. 👀 -여기서 "Releases only"을 선택할 수 있습니다. +여기서 "Releases only"를 선택할 수 있습니다. -이렇게하면, **FastAPI** 의 버그 수정 및 새로운 기능의 구현 등의 새로운 자료 (최신 버전)이 있을 때마다 (이메일) 통지를 받을 수 있습니다. +이렇게하면, **FastAPI** 의 버그 수정 및 새로운 기능의 구현 등의 새로운 릴리즈(새 버전)가 있을 때마다 (이메일) 통지를 받을 수 있습니다. -## 개발자와의 연결 +## 개발자와의 연결 { #connect-with-the-author } -개발자(Sebastián Ramírez / `tiangolo`)와 연락을 취할 수 있습니다. +개발자(작성자)인 저(Sebastián Ramírez / `tiangolo`)와 연락을 취할 수 있습니다. 여러분은 할 수 있습니다: -* **GitHub**에서 팔로우하기.. - * 당신에게 도움이 될 저의 다른 오픈소스 프로젝트를 확인하십시오. +* **GitHub**에서 팔로우하기. + * 여러분에게 도움이 될 저의 다른 오픈소스 프로젝트를 확인하십시오. * 새로운 오픈소스 프로젝트를 만들었을 때 확인하려면 팔로우 하십시오. * **X (Twitter)** 또는 Mastodon에서 팔로우하기. * FastAPI의 사용 용도를 알려주세요 (그것을 듣는 것을 좋아합니다). * 발표나 새로운 툴 출시 소식을 받아보십시오. - * **X (Twitter)**의 @fastapi를 팔로우 (별도 계정에서) 할 수 있습니다. -* **LinkedIn**에서 팔로우하기.. - * 새로운 툴의 발표나 출시 소식을 받아보십시오. (단, X (Twitter)를 더 자주 사용합니다 🤷‍♂). + * X(Twitter)에서 @fastapi를 팔로우 (별도 계정에서) 할 수 있습니다. +* **LinkedIn**에서 팔로우하기. + * 새로운 툴의 발표나 출시 소식을 받아보십시오 (단, X (Twitter)를 더 자주 사용합니다 🤷‍♂). * **Dev.to** 또는 **Medium**에서 제가 작성한 내용을 읽어 보십시오 (또는 팔로우). - * 다른 기사나 아이디어들을 읽고, 제가 만들어왔던 툴에 대해서도 읽으십시오. - * 새로운 기사를 읽기 위해 팔로우 하십시오. + * 다른 아이디어와 기사들을 읽고, 제가 만들어왔던 툴에 대해서도 읽으십시오. + * 새로운 내용을 게시할 때 읽기 위해 팔로우 하십시오. -## **FastAPI**에 대한 트윗 +## **FastAPI**에 대해 트윗하기 { #tweet-about-fastapi } -**FastAPI**에 대해 트윗 하고 FastAPI가 마음에 드는 이유를 알려주세요. 🎉 +**FastAPI**에 대해 트윗 하고 저와 다른 사람들에게 FastAPI가 마음에 드는 이유를 알려주세요. 🎉 **FastAPI**가 어떻게 사용되고 있는지, 어떤 점이 마음에 들었는지, 어떤 프로젝트/회사에서 사용하고 있는지 등에 대해 듣고 싶습니다. -## FastAPI에 투표하기 +## FastAPI에 투표하기 { #vote-for-fastapi } * Slant에서 **FastAPI** 에 대해 투표하십시오. * AlternativeTo에서 **FastAPI** 에 대해 투표하십시오. -* StackShare에서 **FastAPI** 에 대해 투표하십시오. +* StackShare에서 **FastAPI**를 사용한다고 표시하세요. -## GitHub의 이슈로 다른사람 돕기 +## GitHub에서 질문으로 다른 사람 돕기 { #help-others-with-questions-in-github } 다른 사람들의 질문에 도움을 줄 수 있습니다: -* GitHub 디스커션 -* GitHub 이슈 +* GitHub Discussions +* GitHub Issues 많은 경우, 여러분은 이미 그 질문에 대한 답을 알고 있을 수도 있습니다. 🤓 -만약 많은 사람들의 문제를 도와준다면, 공식적인 [FastAPI 전문가](fastapi-people.md#fastapi-experts){.internal-link target=\_blank} 가 될 것입니다. 🎉 +만약 많은 사람들의 질문을 도와준다면, 공식적인 [FastAPI 전문가](fastapi-people.md#fastapi-experts){.internal-link target=_blank}가 될 것입니다. 🎉 가장 중요한 점은: 친절하려고 노력하는 것입니다. 사람들은 좌절감을 안고 오며, 많은 경우 최선의 방식으로 질문하지 않을 수도 있습니다. 하지만 최대한 친절하게 대하려고 노력하세요. 🤗 @@ -86,183 +86,170 @@ GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버 --- -다른 사람들의 질문 (디스커션 또는 이슈에서) 해결을 도울 수 있는 방법은 다음과 같습니다. +다른 사람들의 질문(디스커션 또는 이슈에서) 해결을 도울 수 있는 방법은 다음과 같습니다. -### 질문 이해하기 +### 질문 이해하기 { #understand-the-question } * 질문하는 사람이 가진 **목적**과 사용 사례를 이해할 수 있는지 확인하세요. -* 질문 (대부분은 질문입니다)이 **명확**한지 확인하세요. +* 그런 다음 질문(대부분은 질문입니다)이 **명확**한지 확인하세요. -* 많은 경우, 사용자가 가정한 해결책에 대한 질문을 하지만, 더 **좋은** 해결책이 있을 수 있습니다. 문제와 사용 사례를 더 잘 이해하면 더 나은 **대안적인 해결책**을 제안할 수 있습니다. +* 많은 경우 사용자가 상상한 해결책에 대한 질문을 하지만, 더 **좋은** 해결책이 있을 수 있습니다. 문제와 사용 사례를 더 잘 이해하면 더 나은 **대안적인 해결책**을 제안할 수 있습니다. * 질문을 이해할 수 없다면, 더 **자세한 정보**를 요청하세요. -### 문제 재현하기 +### 문제 재현하기 { #reproduce-the-problem } -대부분의 경우, 질문은 질문자의 **원본 코드**와 관련이 있습니다. +대부분의 경우 그리고 대부분의 질문에서는 질문자의 **원본 코드**와 관련된 내용이 있습니다. 많은 경우, 코드의 일부만 복사해서 올리지만, 그것만으로는 **문제를 재현**하기에 충분하지 않습니다. -* 질문자에게 최소한의 재현 가능한 예제를 제공해달라고 요청하세요. 이렇게 하면 코드를 **복사-붙여넣기**하여 직접 실행하고, 동일한 오류나 동작을 확인하거나 사용 사례를 더 잘 이해할 수 있습니다. +* 질문자에게 최소한의 재현 가능한 예제를 제공해달라고 요청할 수 있습니다. 이렇게 하면 코드를 **복사-붙여넣기**하여 로컬에서 실행하고, 질문자가 보고 있는 것과 동일한 오류나 동작을 확인하거나 사용 사례를 더 잘 이해할 수 있습니다. -* 너그러운 마음이 든다면, 문제 설명만을 기반으로 직접 **예제를 만들어**볼 수도 있습니다. 하지만, 이는 시간이 많이 걸릴 수 있으므로, 먼저 질문을 명확히 해달라고 요청하는 것이 좋습니다. +* 너그러운 마음이 든다면, 문제 설명만을 기반으로 직접 **예제를 만들어**볼 수도 있습니다. 다만 이는 시간이 많이 걸릴 수 있으므로, 먼저 문제를 명확히 해달라고 요청하는 것이 더 나을 수 있다는 점을 기억하세요. -### 해결책 제안하기 +### 해결책 제안하기 { #suggest-solutions } * 질문을 충분히 이해한 후에는 가능한 **답변**을 제공할 수 있습니다. -* 많은 경우, 질문자의 **근본적인 문제나 사용 사례**를 이해하는 것이 중요합니다. 그들이 시도하는 방법보다 더 나은 해결책이 있을 수 있기 때문입니다. +* 많은 경우, 질문자의 **근본적인 문제나 사용 사례**를 이해하는 것이 더 좋습니다. 그들이 시도하는 방법보다 더 나은 해결책이 있을 수 있기 때문입니다. -### 해결 요청하기 +### 종료 요청하기 { #ask-to-close } -질문자가 답변을 확인하고 나면, 당신이 문제를 해결했을 가능성이 높습니다. 축하합니다, **당신은 영웅입니다**! 🦸 +질문자가 답변을 하면, 여러분이 문제를 해결했을 가능성이 높습니다. 축하합니다, **여러분은 영웅입니다**! 🦸 * 이제 문제를 해결했다면, 질문자에게 다음을 요청할 수 있습니다. - * GitHub 디스커션에서: 댓글을 **답변**으로 표시하도록 요청하세요. - * GitHub 이슈에서: 이슈를 **닫아달라고** 요청하세요. + * GitHub Discussions에서: 댓글을 **답변**으로 표시하도록 요청하세요. + * GitHub Issues에서: 이슈를 **닫아달라고** 요청하세요. -## GitHub 저장소 보기 +## GitHub 저장소 보기 { #watch-the-github-repository } -GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/fastapi/fastapi. 👀 +GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 "watch" 버튼을 클릭): https://github.com/fastapi/fastapi. 👀 -"Releases only" 대신 "Watching"을 선택하면, 새로운 이슈나 질문이 생성될 때 알림을 받을 수 있습니다. 또한, 특정하게 새로운 이슈, 디스커션, PR 등만 알림 받도록 설정할 수도 있습니다. +"Releases only" 대신 "Watching"을 선택하면 누군가가 새 이슈나 질문을 만들 때 알림을 받게 됩니다. 또한 새 이슈, 디스커션, PR 등만 알림을 받도록 지정할 수도 있습니다. -그런 다음 이런 이슈들을 해결 할 수 있도록 도움을 줄 수 있습니다. +그런 다음 이런 질문들을 해결하도록 도와줄 수 있습니다. -## 이슈 생성하기 +## 질문하기 { #ask-questions } -GitHub 저장소에 새로운 이슈 생성을 할 수 있습니다, 예를들면 다음과 같습니다: +GitHub 저장소에서 새 질문을 생성할 수 있습니다. 예를 들면 다음과 같습니다: * **질문**을 하거나 **문제**에 대해 질문합니다. * 새로운 **기능**을 제안 합니다. -**참고**: 만약 이슈를 생성한다면, 저는 여러분에게 다른 사람들을 도와달라고 부탁할 것입니다. 😉 +**참고**: 만약 이렇게 한다면, 저는 여러분에게 다른 사람들도 도와달라고 요청할 것입니다. 😉 -## Pull Requests 리뷰하기 +## Pull Request 리뷰하기 { #review-pull-requests } -다른 사람들의 pull request를 리뷰하는 데 도움을 줄 수 있습니다. +다른 사람들이 만든 pull request를 리뷰하는 데 저를 도와줄 수 있습니다. 다시 한번 말하지만, 최대한 친절하게 리뷰해 주세요. 🤗 --- -Pull Rrquest를 리뷰할 때 고려해야 할 사항과 방법은 다음과 같습니다: +Pull request를 리뷰할 때 고려해야 할 사항과 방법은 다음과 같습니다: -### 문제 이해하기 +### 문제 이해하기 { #understand-the-problem } -* 먼저, 해당 pull request가 해결하려는 **문제를 이해하는지** 확인하세요. GitHub 디스커션 또는 이슈에서 더 긴 논의가 있었을 수도 있습니다. +* 먼저, 해당 pull request가 해결하려는 **문제를 이해하는지** 확인하세요. GitHub Discussion 또는 이슈에서 더 긴 논의가 있었을 수도 있습니다. -* Pull request가 필요하지 않을 가능성도 있습니다. **다른 방식**으로 문제를 해결할 수 있다면, 그 방법을 제안하거나 질문할 수 있습니다. +* Pull request가 실제로 필요하지 않을 가능성도 큽니다. 문제가 **다른 방식**으로 해결될 수 있기 때문입니다. 그런 경우 그 방법을 제안하거나 질문할 수 있습니다. -### 스타일에 너무 신경 쓰지 않기 +### 스타일에 너무 신경 쓰지 않기 { #dont-worry-about-style } -* 커밋 메시지 스타일 같은 것에 너무 신경 쓰지 않아도 됩니다. 저는 직접 커밋을 수정하여 squash and merge를 수행할 것입니다. +* 커밋 메시지 스타일 같은 것에 너무 신경 쓰지 마세요. 저는 커밋을 수동으로 조정해서 squash and merge를 할 것입니다. * 코드 스타일 규칙도 걱정할 필요 없습니다. 이미 자동화된 도구들이 이를 검사하고 있습니다. -스타일이나 일관성 관련 요청이 필요한 경우, 제가 직접 요청하거나 필요한 변경 사항을 추가 커밋으로 수정할 것입니다. +그리고 다른 스타일이나 일관성 관련 필요 사항이 있다면, 제가 직접 요청하거나 필요한 변경 사항을 위에 커밋으로 추가할 것입니다. -### 코드 확인하기 +### 코드 확인하기 { #check-the-code } -* 코드를 읽고, **논리적으로 타당**한지 확인한 후 로컬에서 실행하여 문제가 해결되는지 확인하세요. +* 코드를 확인하고 읽어서 말이 되는지 보고, **로컬에서 실행**해 실제로 문제가 해결되는지 확인하세요. -* 그런 다음, 확인했다고 **댓글**을 남겨 주세요. 그래야 제가 검토했음을 알 수 있습니다. +* 그런 다음 그렇게 했다고 **댓글**로 남겨 주세요. 그래야 제가 정말로 확인했음을 알 수 있습니다. -/// info +/// info | 정보 불행히도, 제가 단순히 여러 개의 승인만으로 PR을 신뢰할 수는 없습니다. -3개, 5개 이상의 승인이 달린 PR이 실제로는 깨져 있거나, 버그가 있거나, 주장하는 문제를 해결하지 못하는 경우가 여러 번 있었습니다. 😅 +여러 번, 설명이 그럴듯해서인지 3개, 5개 이상의 승인이 달린 PR이 있었지만, 제가 확인해보면 실제로는 깨져 있거나, 버그가 있거나, 주장하는 문제를 해결하지 못하는 경우가 있었습니다. 😅 따라서, 정말로 코드를 읽고 실행한 뒤, 댓글로 확인 내용을 남겨 주는 것이 매우 중요합니다. 🤓 /// -* PR을 더 단순하게 만들 수 있다면 그렇게 요청할 수 있지만, 너무 까다로울 필요는 없습니다. 주관적인 견해가 많이 있을 수 있기 때문입니다 (그리고 저도 제 견해가 있을 거예요 🙈). 따라서 핵심적인 부분에 집중하는 것이 좋습니다. +* PR을 더 단순하게 만들 수 있다면 그렇게 요청할 수 있지만, 너무 까다로울 필요는 없습니다. 주관적인 견해가 많이 있을 수 있기 때문입니다(그리고 저도 제 견해가 있을 거예요 🙈). 따라서 핵심적인 부분에 집중하는 것이 좋습니다. -### 테스트 +### 테스트 { #tests } * PR에 **테스트**가 포함되어 있는지 확인하는 데 도움을 주세요. -* PR을 적용하기 전에 테스트가 **실패**하는지 확인하세요. 🚨 +* PR 전에는 테스트가 **실패**하는지 확인하세요. 🚨 -* PR을 적용한 후 테스트가 **통과**하는지 확인하세요. ✅ +* 그런 다음 PR 후에는 테스트가 **통과**하는지 확인하세요. ✅ -* 많은 PR에는 테스트가 없습니다. 테스트를 추가하도록 **상기**시켜줄 수도 있고, 직접 테스트를 **제안**할 수도 있습니다. 이는 시간이 많이 소요되는 부분 중 하나이며, 그 부분을 많이 도와줄 수 있습니다. +* 많은 PR에는 테스트가 없습니다. 테스트를 추가하도록 **상기**시켜줄 수도 있고, 직접 테스트를 **제안**할 수도 있습니다. 이는 시간이 가장 많이 드는 것들 중 하나이며, 그 부분을 많이 도와줄 수 있습니다. * 그리고 시도한 내용을 댓글로 남겨주세요. 그러면 제가 확인했다는 걸 알 수 있습니다. 🤓 -## Pull Request를 만드십시오 +## Pull Request 만들기 { #create-a-pull-request } -Pull Requests를 이용하여 소스코드에 [컨트리뷰트](contributing.md){.internal-link target=\_blank} 할 수 있습니다. 예를 들면 다음과 같습니다: +Pull Requests를 이용하여 소스 코드에 [기여](contributing.md){.internal-link target=_blank}할 수 있습니다. 예를 들면 다음과 같습니다: * 문서에서 발견한 오타를 수정할 때. -* FastAPI 관련 문서, 비디오 또는 팟캐스트를 작성했거나 발견하여 이 파일을 편집하여 공유할 때. +* FastAPI에 대한 글, 비디오, 팟캐스트를 작성했거나 발견했다면 이 파일을 편집하여 공유할 때. * 해당 섹션의 시작 부분에 링크를 추가해야 합니다. -* 당신의 언어로 [문서 번역하는데](contributing.md#translations){.internal-link target=\_blank} 기여할 때. +* 여러분의 언어로 [문서 번역에](contributing.md#translations){.internal-link target=_blank} 도움을 줄 때. * 다른 사람이 작성한 번역을 검토하는 것도 도울 수 있습니다. -* 새로운 문서의 섹션을 제안할 때. -* 기존 문제/버그를 수정할 때. +* 새로운 문서 섹션을 제안할 때. +* 기존 이슈/버그를 수정할 때. * 테스트를 반드시 추가해야 합니다. -* 새로운 feature를 추가할 때. +* 새로운 기능을 추가할 때. * 테스트를 반드시 추가해야 합니다. - * 관련 문서가 필요하다면 반드시 추가해야 합니다. + * 관련 문서가 있다면 반드시 추가해야 합니다. -## FastAPI 유지 관리에 도움 주기 +## FastAPI 유지 관리 돕기 { #help-maintain-fastapi } -**FastAPI**의 유지 관리를 도와주세요! 🤓 +**FastAPI** 유지를 도와주세요! 🤓 -할 일이 많고, 그 중 대부분은 **여러분**이 할 수 있습니다. +할 일이 많고, 그중 대부분은 **여러분**이 할 수 있습니다. 지금 할 수 있는 주요 작업은: -* [GitHub에서 다른 사람들의 질문에 도움 주기](#github_1){.internal-link target=_blank} (위의 섹션을 참조하세요). -* [Pull Request 리뷰하기](#pull-requests){.internal-link target=_blank} (위의 섹션을 참조하세요). +* [GitHub에서 질문으로 다른 사람 돕기](#help-others-with-questions-in-github){.internal-link target=_blank} (위의 섹션을 참조하세요). +* [Pull Request 리뷰하기](#review-pull-requests){.internal-link target=_blank} (위의 섹션을 참조하세요). -이 두 작업이 **가장 많은 시간을 소모**하는 일입니다. 그것이 FastAPI 유지 관리의 주요 작업입니다. +이 두 작업이 **가장 많은 시간을 소모**합니다. 이것이 FastAPI를 유지 관리하는 주요 작업입니다. -이 작업을 도와주신다면, **FastAPI 유지 관리에 도움을 주는 것**이며 그것이 **더 빠르고 더 잘 발전하는 것**을 보장하는 것입니다. 🚀 +이 작업을 도와주신다면, **FastAPI 유지를 돕는 것**이며 FastAPI가 **더 빠르고 더 잘 발전하는 것**을 보장하는 것입니다. 🚀 -## 채팅에 참여하십시오 +## 채팅에 참여하기 { #join-the-chat } -👥 디스코드 채팅 서버 👥 에 가입하고 FastAPI 커뮤니티에서 다른 사람들과 어울리세요. +👥 Discord 채팅 서버 👥 에 참여해서 FastAPI 커뮤니티의 다른 사람들과 어울리세요. -/// tip +/// tip | 팁 -질문이 있는 경우, GitHub 디스커션 에서 질문하십시오, [FastAPI Experts](fastapi-people.md#fastapi-experts){.internal-link target=_blank} 의 도움을 받을 가능성이 높습니다. +질문은 GitHub Discussions에서 하세요. [FastAPI Experts](fastapi-people.md#fastapi-experts){.internal-link target=_blank}로부터 도움을 받을 가능성이 훨씬 높습니다. -다른 일반적인 대화에서만 채팅을 사용하십시오. +채팅은 다른 일반적인 대화를 위해서만 사용하세요. /// -### 질문을 위해 채팅을 사용하지 마십시오 +### 질문을 위해 채팅을 사용하지 마세요 { #dont-use-the-chat-for-questions } -채팅은 더 많은 "자유로운 대화"를 허용하기 때문에, 너무 일반적인 질문이나 대답하기 어려운 질문을 쉽게 질문을 할 수 있으므로, 답변을 받지 못할 수 있습니다. +채팅은 더 많은 "자유로운 대화"를 허용하기 때문에, 너무 일반적인 질문이나 답하기 어려운 질문을 쉽게 할 수 있어 답변을 받지 못할 수도 있다는 점을 기억하세요. -GitHub 이슈에서의 템플릿은 올바른 질문을 작성하도록 안내하여 더 쉽게 좋은 답변을 얻거나 질문하기 전에 스스로 문제를 해결할 수도 있습니다. 그리고 GitHub에서는 시간이 조금 걸리더라도 항상 모든 것에 답할 수 있습니다. 채팅 시스템에서는 개인적으로 그렇게 할 수 없습니다. 😅 +GitHub에서는 템플릿이 올바른 질문을 작성하도록 안내하여 더 쉽게 좋은 답변을 얻거나, 질문하기 전에 스스로 문제를 해결할 수도 있습니다. 그리고 GitHub에서는 시간이 조금 걸리더라도 제가 항상 모든 것에 답하도록 보장할 수 있습니다. 채팅 시스템에서는 제가 개인적으로 그렇게 할 수 없습니다. 😅 -채팅 시스템에서의 대화 또한 GitHub에서 처럼 쉽게 검색할 수 없기 때문에 대화 중에 질문과 답변이 손실될 수 있습니다. 그리고 GitHub 이슈에 있는 것만 [FastAPI Expert](fastapi-people.md#fastapi-experts){.internal-link target=_blank}가 되는 것으로 간주되므로, GitHub 이슈에서 더 많은 관심을 받을 것입니다. +채팅 시스템에서의 대화 또한 GitHub만큼 쉽게 검색할 수 없기 때문에, 질문과 답변이 대화 속에서 사라질 수 있습니다. 그리고 GitHub에 있는 것만 [FastAPI Expert](fastapi-people.md#fastapi-experts){.internal-link target=_blank}가 되는 것으로 인정되므로, GitHub에서 더 많은 관심을 받게 될 가능성이 큽니다. -반면, 채팅 시스템에는 수천 명의 사용자가 있기 때문에, 거의 항상 대화 상대를 찾을 가능성이 높습니다. 😄 +반면, 채팅 시스템에는 수천 명의 사용자가 있으므로, 거의 항상 대화 상대를 찾을 가능성이 높습니다. 😄 -## 개발자 스폰서가 되십시오 +## 개발자 스폰서 되기 { #sponsor-the-author } -GitHub 스폰서 를 통해 개발자를 경제적으로 지원할 수 있습니다. - -감사하다는 말로 커피를 ☕️ 한잔 사줄 수 있습니다. 😄 - -또한 FastAPI의 실버 또는 골드 스폰서가 될 수 있습니다. 🏅🎉 - -## FastAPI를 강화하는 도구의 스폰서가 되십시오 - -문서에서 보았듯이, FastAPI는 Starlette과 Pydantic 라는 거인의 어깨에 타고 있습니다. - -다음의 스폰서가 될 수 있습니다 - -* Samuel Colvin (Pydantic) -* Encode (Starlette, Uvicorn) +여러분의 **제품/회사**가 **FastAPI**에 의존하거나 관련되어 있고, FastAPI 사용자를 대상으로 알리고 싶다면 GitHub sponsors를 통해 개발자(저)를 스폰서할 수 있습니다. 티어에 따라 문서에 배지 같은 추가 혜택을 받을 수도 있습니다. 🎁 --- diff --git a/docs/ko/docs/history-design-future.md b/docs/ko/docs/history-design-future.md index 98f01d70d3..d972001215 100644 --- a/docs/ko/docs/history-design-future.md +++ b/docs/ko/docs/history-design-future.md @@ -1,81 +1,79 @@ -# 역사, 디자인 그리고 미래 +# 역사, 디자인 그리고 미래 { #history-design-and-future } -어느 날, [한 FastAPI 사용자](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920)가 이렇게 물었습니다: +얼마 전, 한 **FastAPI** 사용자가 이렇게 물었습니다: -> 이 프로젝트의 역사를 알려 주실 수 있나요? 몇 주 만에 멋진 결과를 낸 것 같아요. [...] +> 이 프로젝트의 역사는 무엇인가요? 몇 주 만에 아무 데서도 갑자기 나타나 엄청나게 좋아진 것처럼 보이네요 [...] 여기서 그 역사에 대해 간단히 설명하겠습니다. ---- +## 대안 { #alternatives } -## 대안 +저는 여러 해 동안 복잡한 요구사항(머신러닝, 분산 시스템, 비동기 작업, NoSQL 데이터베이스 등)을 가진 API를 만들면서 여러 개발 팀을 이끌어 왔습니다. -저는 여러 해 동안 머신러닝, 분산 시스템, 비동기 작업, NoSQL 데이터베이스 같은 복잡한 요구사항을 가진 API를 개발하며 여러 팀을 이끌어 왔습니다. +그 과정에서 많은 대안을 조사하고, 테스트하고, 사용해야 했습니다. -이 과정에서 많은 대안을 조사하고, 테스트하며, 사용해야 했습니다. **FastAPI**의 역사는 그 이전에 나왔던 여러 도구의 역사와 밀접하게 연관되어 있습니다. +**FastAPI**의 역사는 상당 부분 그 이전에 있던 도구들의 역사입니다. [대안](alternatives.md){.internal-link target=_blank} 섹션에서 언급된 것처럼: -> **FastAPI**는 이전에 나왔던 많은 도구들의 노력 없이는 존재하지 않았을 것입니다. -> -> 이전에 개발된 여러 도구들이 이 프로젝트에 영감을 주었습니다. -> -> 저는 오랫동안 새로운 프레임워크를 만드는 것을 피하고자 했습니다. 처음에는 **FastAPI**가 제공하는 기능들을 다양한 프레임워크와 플러그인, 도구들을 조합해 해결하려 했습니다. -> -> 하지만 결국에는 이 모든 기능을 통합하는 도구가 필요해졌습니다. 이전 도구들로부터 최고의 아이디어들을 모으고, 이를 최적의 방식으로 조합해야만 했습니다. 이는 :term:Python 3.6+ 타입 힌트 와 같은, 이전에는 사용할 수 없었던 언어 기능이 가능했기 때문입니다. +
---- +**FastAPI**는 다른 사람들이 이전에 해온 작업이 없었다면 존재하지 않았을 것입니다. -## 조사 +그 전에 만들어진 많은 도구들이 이것의 탄생에 영감을 주었습니다. -여러 대안을 사용해 보며 다양한 도구에서 배운 점들을 모아 저와 개발팀에게 가장 적합한 방식을 찾았습니다. +저는 여러 해 동안 새로운 프레임워크를 만드는 것을 피하고 있었습니다. 처음에는 **FastAPI**가 다루는 모든 기능을 여러 다른 프레임워크, 플러그인, 도구들을 사용해 해결하려고 했습니다. -예를 들어, 표준 :term:Python 타입 힌트 에 기반하는 것이 이상적이라는 점이 명확했습니다. +하지만 어느 시점에는, 이전 도구들의 최고의 아이디어를 가져와 가능한 한 최선의 방식으로 조합하고, 이전에는 존재하지 않았던 언어 기능(Python 3.6+ type hints)을 사용해 이 모든 기능을 제공하는 무언가를 만드는 것 외에는 다른 선택지가 없었습니다. -또한, 이미 존재하는 표준을 활용하는 것이 가장 좋은 접근법이라 판단했습니다. +
-그래서 **FastAPI**의 코드를 작성하기 전에 몇 달 동안 OpenAPI, JSON Schema, OAuth2 명세를 연구하며 이들의 관계와 겹치는 부분, 차이점을 이해했습니다. +## 조사 { #investigation } ---- +이전의 모든 대안을 사용해 보면서, 각 도구로부터 배울 기회를 얻었고, 아이디어를 가져와 제가 일해온 개발 팀들과 저 자신에게 가장 적합하다고 찾은 방식으로 조합할 수 있었습니다. -## 디자인 +예를 들어, 이상적으로는 표준 Python 타입 힌트에 기반해야 한다는 점이 분명했습니다. -그 후, **FastAPI** 사용자가 될 개발자로서 사용하고 싶은 개발자 "API"를 디자인했습니다. +또한, 가장 좋은 접근법은 이미 존재하는 표준을 사용하는 것이었습니다. -[Python Developer Survey](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools)에 따르면 약 80%의 Python 개발자가 PyCharm, VS Code, Jedi 기반 편집기 등에서 개발합니다. 이 과정에서 여러 아이디어를 테스트했습니다. +그래서 **FastAPI**의 코딩을 시작하기도 전에, OpenAPI, JSON Schema, OAuth2 등과 같은 명세를 몇 달 동안 공부했습니다. 이들의 관계, 겹치는 부분, 차이점을 이해하기 위해서였습니다. -대부분의 다른 편집기도 유사하게 동작하기 때문에, **FastAPI**의 이점은 거의 모든 편집기에서 누릴 수 있습니다. +## 디자인 { #design } -이 과정을 통해 코드 중복을 최소화하고, 모든 곳에서 자동 완성, 타입 검사, 에러 확인 기능이 제공되는 최적의 방식을 찾아냈습니다. +그 다음에는 (FastAPI를 사용하는 개발자로서) 사용자로서 갖고 싶었던 개발자 "API"를 디자인하는 데 시간을 썼습니다. -이 모든 것은 개발자들에게 최고의 개발 경험을 제공하기 위해 설계되었습니다. +가장 인기 있는 Python 편집기들: PyCharm, VS Code, Jedi 기반 편집기에서 여러 아이디어를 테스트했습니다. ---- +약 80%의 사용자를 포함하는 최근 Python Developer Survey에 따르면 그렇습니다. -## 필요조건 +즉, **FastAPI**는 Python 개발자의 80%가 사용하는 편집기들로 특별히 테스트되었습니다. 그리고 대부분의 다른 편집기도 유사하게 동작하는 경향이 있으므로, 그 모든 이점은 사실상 모든 편집기에서 동작해야 합니다. -여러 대안을 테스트한 후, [Pydantic](https://docs.pydantic.dev/)을 사용하기로 결정했습니다. +그렇게 해서 코드 중복을 가능한 한 많이 줄이고, 어디서나 자동 완성, 타입 및 에러 검사 등을 제공하는 최선의 방법을 찾을 수 있었습니다. -이후 저는 **Pydantic**이 JSON Schema와 완벽히 호환되도록 개선하고, 다양한 제약 조건 선언을 지원하며, 여러 편집기에서의 자동 완성과 타입 검사 기능을 향상하기 위해 기여했습니다. +모든 개발자에게 최고의 개발 경험을 제공하는 방식으로 말입니다. -또한, 또 다른 주요 필요조건이었던 [Starlette](https://www.starlette.dev/)에도 기여했습니다. +## 필요조건 { #requirements } ---- +여러 대안을 테스트한 후, 장점 때문에 **Pydantic**을 사용하기로 결정했습니다. -## 개발 +그 후, JSON Schema를 완전히 준수하도록 하고, 제약 조건 선언을 정의하는 다양한 방식을 지원하며, 여러 편집기에서의 테스트를 바탕으로 편집기 지원(타입 검사, 자동 완성)을 개선하기 위해 기여했습니다. -**FastAPI**를 개발하기 시작할 즈음에는 대부분의 준비가 이미 완료된 상태였습니다. 설계가 정의되었고, 필요조건과 도구가 준비되었으며, 표준과 명세에 대한 지식도 충분했습니다. +개발 과정에서, 또 다른 핵심 필요조건인 **Starlette**에도 기여했습니다. ---- +## 개발 { #development } -## 미래 +**FastAPI** 자체를 만들기 시작했을 때쯤에는, 대부분의 조각들이 이미 갖춰져 있었고, 디자인은 정의되어 있었으며, 필요조건과 도구는 준비되어 있었고, 표준과 명세에 대한 지식도 명확하고 최신 상태였습니다. -현시점에서 **FastAPI**가 많은 사람들에게 유용하다는 것이 명백해졌습니다. +## 미래 { #future } -여러 용도에 더 적합한 도구로서 기존 대안보다 선호되고 있습니다. -이미 많은 개발자와 팀들이 **FastAPI**에 의존해 프로젝트를 진행 중입니다 (저와 제 팀도 마찬가지입니다). +이 시점에는, **FastAPI**가 그 아이디어와 함께 많은 사람들에게 유용하다는 것이 이미 분명합니다. -하지만 여전히 개선해야 할 점과 추가할 기능들이 많이 남아 있습니다. +많은 사용 사례에 더 잘 맞기 때문에 이전 대안들보다 선택되고 있습니다. + +많은 개발자와 팀이 이미 자신의 프로젝트를 위해 **FastAPI**에 의존하고 있습니다(저와 제 팀도 포함해서요). + +하지만 여전히, 앞으로 나올 개선 사항과 기능들이 많이 있습니다. + +**FastAPI**의 미래는 밝습니다. -**FastAPI**는 밝은 미래로 나아가고 있습니다. 그리고 [여러분의 도움](help-fastapi.md){.internal-link target=_blank}은 큰 힘이 됩니다. diff --git a/docs/ko/docs/how-to/authentication-error-status-code.md b/docs/ko/docs/how-to/authentication-error-status-code.md new file mode 100644 index 0000000000..47120cae68 --- /dev/null +++ b/docs/ko/docs/how-to/authentication-error-status-code.md @@ -0,0 +1,17 @@ +# 이전 403 인증 오류 상태 코드 사용하기 { #use-old-403-authentication-error-status-codes } + +FastAPI 버전 `0.122.0` 이전에는, 통합 보안 유틸리티가 인증 실패 후 클라이언트에 오류를 반환할 때 HTTP 상태 코드 `403 Forbidden`을 사용했습니다. + +FastAPI 버전 `0.122.0`부터는 더 적절한 HTTP 상태 코드 `401 Unauthorized`를 사용하며, HTTP 명세인 RFC 7235, RFC 9110를 따라 응답에 합리적인 `WWW-Authenticate` 헤더를 반환합니다. + +하지만 어떤 이유로든 클라이언트가 이전 동작에 의존하고 있다면, 보안 클래스에서 `make_not_authenticated_error` 메서드를 오버라이드하여 이전 동작으로 되돌릴 수 있습니다. + +예를 들어, 기본값인 `401 Unauthorized` 오류 대신 `403 Forbidden` 오류를 반환하는 `HTTPBearer`의 서브클래스를 만들 수 있습니다: + +{* ../../docs_src/authentication_error_status_code/tutorial001_an_py39.py hl[9:13] *} + +/// tip | 팁 + +함수는 예외를 `raise`하는 것이 아니라 예외 인스턴스를 `return`한다는 점에 유의하세요. 예외를 발생시키는(`raise`) 작업은 내부 코드의 나머지 부분에서 수행됩니다. + +/// diff --git a/docs/ko/docs/how-to/custom-docs-ui-assets.md b/docs/ko/docs/how-to/custom-docs-ui-assets.md new file mode 100644 index 0000000000..d6383c29cb --- /dev/null +++ b/docs/ko/docs/how-to/custom-docs-ui-assets.md @@ -0,0 +1,185 @@ +# 커스텀 Docs UI 정적 에셋(자체 호스팅) { #custom-docs-ui-static-assets-self-hosting } + +API 문서는 **Swagger UI**와 **ReDoc**을 사용하며, 각각 JavaScript와 CSS 파일이 필요합니다. + +기본적으로 이러한 파일은 CDN에서 제공됩니다. + +하지만 이를 커스터마이징할 수 있으며, 특정 CDN을 지정하거나 파일을 직접 제공할 수도 있습니다. + +## JavaScript와 CSS용 커스텀 CDN { #custom-cdn-for-javascript-and-css } + +예를 들어 다른 CDN을 사용하고 싶다고 해봅시다. 예를 들면 `https://unpkg.com/`을 사용하려는 경우입니다. + +이는 예를 들어 특정 국가에서 일부 URL을 제한하는 경우에 유용할 수 있습니다. + +### 자동 문서 비활성화하기 { #disable-the-automatic-docs } + +첫 번째 단계는 자동 문서를 비활성화하는 것입니다. 기본적으로 자동 문서는 기본 CDN을 사용하기 때문입니다. + +비활성화하려면 `FastAPI` 앱을 생성할 때 해당 URL을 `None`으로 설정하세요: + +{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *} + +### 커스텀 문서 포함하기 { #include-the-custom-docs } + +이제 커스텀 문서를 위한 *경로 처리*를 만들 수 있습니다. + +FastAPI 내부 함수를 재사용해 문서용 HTML 페이지를 생성하고, 필요한 인자를 전달할 수 있습니다: + +* `openapi_url`: 문서 HTML 페이지가 API의 OpenAPI 스키마를 가져올 수 있는 URL입니다. 여기서는 `app.openapi_url` 속성을 사용할 수 있습니다. +* `title`: API의 제목입니다. +* `oauth2_redirect_url`: 기본값을 사용하려면 여기서 `app.swagger_ui_oauth2_redirect_url`을 사용할 수 있습니다. +* `swagger_js_url`: Swagger UI 문서의 HTML이 **JavaScript** 파일을 가져올 수 있는 URL입니다. 커스텀 CDN URL입니다. +* `swagger_css_url`: Swagger UI 문서의 HTML이 **CSS** 파일을 가져올 수 있는 URL입니다. 커스텀 CDN URL입니다. + +ReDoc도 마찬가지입니다... + +{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *} + +/// tip | 팁 + +`swagger_ui_redirect`에 대한 *경로 처리*는 OAuth2를 사용할 때 도움이 되는 헬퍼입니다. + +API를 OAuth2 provider와 통합하면 인증을 수행한 뒤 획득한 자격 증명으로 API 문서로 다시 돌아올 수 있습니다. 그리고 실제 OAuth2 인증을 사용해 API와 상호작용할 수 있습니다. + +Swagger UI가 이 과정을 백그라운드에서 처리해 주지만, 이를 위해 이 "redirect" 헬퍼가 필요합니다. + +/// + +### 테스트용 *경로 처리* 만들기 { #create-a-path-operation-to-test-it } + +이제 모든 것이 제대로 동작하는지 테스트할 수 있도록 *경로 처리*를 하나 만드세요: + +{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *} + +### 테스트하기 { #test-it } + +이제 http://127.0.0.1:8000/docs에서 문서에 접속한 뒤 페이지를 새로고침하면, 새 CDN에서 에셋을 불러오는 것을 확인할 수 있습니다. + +## 문서용 JavaScript와 CSS 자체 호스팅하기 { #self-hosting-javascript-and-css-for-docs } + +JavaScript와 CSS를 자체 호스팅하는 것은 예를 들어, 오프라인 상태이거나 외부 인터넷에 접근할 수 없는 환경, 또는 로컬 네트워크에서도 앱이 계속 동작해야 할 때 유용할 수 있습니다. + +여기서는 동일한 FastAPI 앱에서 해당 파일을 직접 제공하고, 문서가 이를 사용하도록 설정하는 방법을 살펴봅니다. + +### 프로젝트 파일 구조 { #project-file-structure } + +프로젝트 파일 구조가 다음과 같다고 해봅시다: + +``` +. +├── app +│ ├── __init__.py +│ ├── main.py +``` + +이제 해당 정적 파일을 저장할 디렉터리를 만드세요. + +새 파일 구조는 다음과 같을 수 있습니다: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +└── static/ +``` + +### 파일 다운로드하기 { #download-the-files } + +문서에 필요한 정적 파일을 다운로드해서 `static/` 디렉터리에 넣으세요. + +각 링크를 우클릭한 뒤 "링크를 다른 이름으로 저장..."과 비슷한 옵션을 선택하면 될 것입니다. + +**Swagger UI**는 다음 파일을 사용합니다: + +* `swagger-ui-bundle.js` +* `swagger-ui.css` + +**ReDoc**은 다음 파일을 사용합니다: + +* `redoc.standalone.js` + +이후 파일 구조는 다음과 같을 수 있습니다: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +└── static + ├── redoc.standalone.js + ├── swagger-ui-bundle.js + └── swagger-ui.css +``` + +### 정적 파일 제공하기 { #serve-the-static-files } + +* `StaticFiles`를 import합니다. +* 특정 경로에 `StaticFiles()` 인스턴스를 "마운트(mount)"합니다. + +{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *} + +### 정적 파일 테스트하기 { #test-the-static-files } + +애플리케이션을 시작하고 http://127.0.0.1:8000/static/redoc.standalone.js로 이동하세요. + +**ReDoc**용 매우 긴 JavaScript 파일이 보일 것입니다. + +예를 들어 다음과 같이 시작할 수 있습니다: + +```JavaScript +/*! For license information please see redoc.standalone.js.LICENSE.txt */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")): +... +``` + +이는 앱에서 정적 파일을 제공할 수 있고, 문서용 정적 파일을 올바른 위치에 배치했다는 것을 확인해 줍니다. + +이제 문서가 이 정적 파일을 사용하도록 앱을 설정할 수 있습니다. + +### 정적 파일을 위한 자동 문서 비활성화하기 { #disable-the-automatic-docs-for-static-files } + +커스텀 CDN을 사용할 때와 마찬가지로, 첫 단계는 자동 문서를 비활성화하는 것입니다. 자동 문서는 기본적으로 CDN을 사용합니다. + +비활성화하려면 `FastAPI` 앱을 생성할 때 해당 URL을 `None`으로 설정하세요: + +{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *} + +### 정적 파일을 위한 커스텀 문서 포함하기 { #include-the-custom-docs-for-static-files } + +그리고 커스텀 CDN을 사용할 때와 동일한 방식으로, 이제 커스텀 문서를 위한 *경로 처리*를 만들 수 있습니다. + +다시 한 번, FastAPI 내부 함수를 재사용해 문서용 HTML 페이지를 생성하고, 필요한 인자를 전달할 수 있습니다: + +* `openapi_url`: 문서 HTML 페이지가 API의 OpenAPI 스키마를 가져올 수 있는 URL입니다. 여기서는 `app.openapi_url` 속성을 사용할 수 있습니다. +* `title`: API의 제목입니다. +* `oauth2_redirect_url`: 기본값을 사용하려면 여기서 `app.swagger_ui_oauth2_redirect_url`을 사용할 수 있습니다. +* `swagger_js_url`: Swagger UI 문서의 HTML이 **JavaScript** 파일을 가져올 수 있는 URL입니다. **이제는 여러분의 앱이 직접 제공하는 파일입니다**. +* `swagger_css_url`: Swagger UI 문서의 HTML이 **CSS** 파일을 가져올 수 있는 URL입니다. **이제는 여러분의 앱이 직접 제공하는 파일입니다**. + +ReDoc도 마찬가지입니다... + +{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *} + +/// tip | 팁 + +`swagger_ui_redirect`에 대한 *경로 처리*는 OAuth2를 사용할 때 도움이 되는 헬퍼입니다. + +API를 OAuth2 provider와 통합하면 인증을 수행한 뒤 획득한 자격 증명으로 API 문서로 다시 돌아올 수 있습니다. 그리고 실제 OAuth2 인증을 사용해 API와 상호작용할 수 있습니다. + +Swagger UI가 이 과정을 백그라운드에서 처리해 주지만, 이를 위해 이 "redirect" 헬퍼가 필요합니다. + +/// + +### 정적 파일 테스트용 *경로 처리* 만들기 { #create-a-path-operation-to-test-static-files } + +이제 모든 것이 제대로 동작하는지 테스트할 수 있도록 *경로 처리*를 하나 만드세요: + +{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *} + +### 정적 파일 UI 테스트하기 { #test-static-files-ui } + +이제 WiFi 연결을 끊고 http://127.0.0.1:8000/docs에서 문서에 접속한 뒤 페이지를 새로고침해 보세요. + +인터넷이 없어도 API 문서를 보고, API와 상호작용할 수 있을 것입니다. diff --git a/docs/ko/docs/how-to/custom-request-and-route.md b/docs/ko/docs/how-to/custom-request-and-route.md new file mode 100644 index 0000000000..335193bb35 --- /dev/null +++ b/docs/ko/docs/how-to/custom-request-and-route.md @@ -0,0 +1,109 @@ +# 커스텀 Request 및 APIRoute 클래스 { #custom-request-and-apiroute-class } + +일부 경우에는 `Request`와 `APIRoute` 클래스에서 사용되는 로직을 오버라이드하고 싶을 수 있습니다. + +특히, 이는 middleware에 있는 로직의 좋은 대안이 될 수 있습니다. + +예를 들어, 애플리케이션에서 처리되기 전에 요청 바디를 읽거나 조작하고 싶을 때가 그렇습니다. + +/// danger | 위험 + +이 기능은 "고급" 기능입니다. + +**FastAPI**를 이제 막 시작했다면 이 섹션은 건너뛰는 것이 좋습니다. + +/// + +## 사용 사례 { #use-cases } + +사용 사례에는 다음이 포함됩니다: + +* JSON이 아닌 요청 바디를 JSON으로 변환하기(예: `msgpack`). +* gzip으로 압축된 요청 바디 압축 해제하기. +* 모든 요청 바디를 자동으로 로깅하기. + +## 커스텀 요청 바디 인코딩 처리하기 { #handling-custom-request-body-encodings } + +커스텀 `Request` 서브클래스를 사용해 gzip 요청의 압축을 해제하는 방법을 살펴보겠습니다. + +그리고 그 커스텀 요청 클래스를 사용하기 위한 `APIRoute` 서브클래스도 함께 보겠습니다. + +### 커스텀 `GzipRequest` 클래스 만들기 { #create-a-custom-gziprequest-class } + +/// tip | 팁 + +이 예시는 동작 방식 시연을 위한 장난감 예제입니다. Gzip 지원이 필요하다면 제공되는 [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank}를 사용할 수 있습니다. + +/// + +먼저, `GzipRequest` 클래스를 만듭니다. 이 클래스는 `Request.body()` 메서드를 덮어써서, 적절한 헤더가 있는 경우 바디를 압축 해제합니다. + +헤더에 `gzip`이 없으면 바디를 압축 해제하려고 시도하지 않습니다. + +이렇게 하면 동일한 route 클래스가 gzip으로 압축된 요청과 압축되지 않은 요청을 모두 처리할 수 있습니다. + +{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *} + +### 커스텀 `GzipRoute` 클래스 만들기 { #create-a-custom-gziproute-class } + +다음으로, `GzipRequest`를 활용하는 `fastapi.routing.APIRoute`의 커스텀 서브클래스를 만듭니다. + +이번에는 `APIRoute.get_route_handler()` 메서드를 오버라이드합니다. + +이 메서드는 함수를 반환합니다. 그리고 그 함수가 요청을 받아 응답을 반환합니다. + +여기서는 원본 요청으로부터 `GzipRequest`를 만들기 위해 이를 사용합니다. + +{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *} + +/// note | 기술 세부사항 + +`Request`에는 `request.scope` 속성이 있는데, 이는 요청과 관련된 메타데이터를 담고 있는 Python `dict`입니다. + +`Request`에는 또한 `request.receive`가 있는데, 이는 요청의 바디를 "받기(receive)" 위한 함수입니다. + +`scope` `dict`와 `receive` 함수는 모두 ASGI 명세의 일부입니다. + +그리고 이 두 가지, `scope`와 `receive`가 새로운 `Request` 인스턴스를 만드는 데 필요한 것들입니다. + +`Request`에 대해 더 알아보려면 Starlette의 Requests 문서를 확인하세요. + +/// + +`GzipRequest.get_route_handler`가 반환하는 함수가 다르게 하는 유일한 것은 `Request`를 `GzipRequest`로 변환하는 것입니다. + +이렇게 하면, 우리의 `GzipRequest`가 *경로 처리*로 전달하기 전에(필요하다면) 데이터의 압축 해제를 담당하게 됩니다. + +그 이후의 모든 처리 로직은 동일합니다. + +하지만 `GzipRequest.body`에서 변경을 했기 때문에, 필요할 때 **FastAPI**가 로드하는 시점에 요청 바디는 자동으로 압축 해제됩니다. + +## 예외 핸들러에서 요청 바디 접근하기 { #accessing-the-request-body-in-an-exception-handler } + +/// tip | 팁 + +같은 문제를 해결하려면 `RequestValidationError`에 대한 커스텀 핸들러에서 `body`를 사용하는 편이 아마 훨씬 더 쉽습니다([오류 처리하기](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}). + +하지만 이 예시도 여전히 유효하며, 내부 컴포넌트와 상호작용하는 방법을 보여줍니다. + +/// + +같은 접근 방식을 사용해 예외 핸들러에서 요청 바디에 접근할 수도 있습니다. + +필요한 것은 `try`/`except` 블록 안에서 요청을 처리하는 것뿐입니다: + +{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *} + +예외가 발생하더라도 `Request` 인스턴스는 여전히 스코프 안에 남아 있으므로, 오류를 처리할 때 요청 바디를 읽고 활용할 수 있습니다: + +{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *} + +## 라우터에서의 커스텀 `APIRoute` 클래스 { #custom-apiroute-class-in-a-router } + +`APIRouter`의 `route_class` 파라미터를 설정할 수도 있습니다: + +{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *} + +이 예시에서는 `router` 아래의 *경로 처리*들이 커스텀 `TimedRoute` 클래스를 사용하며, 응답을 생성하는 데 걸린 시간을 담은 추가 `X-Response-Time` 헤더가 응답에 포함됩니다: + +{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *} diff --git a/docs/ko/docs/how-to/extending-openapi.md b/docs/ko/docs/how-to/extending-openapi.md new file mode 100644 index 0000000000..d04d6c23e3 --- /dev/null +++ b/docs/ko/docs/how-to/extending-openapi.md @@ -0,0 +1,80 @@ +# OpenAPI 확장하기 { #extending-openapi } + +생성된 OpenAPI 스키마를 수정해야 하는 경우가 있습니다. + +이 섹션에서 그 방법을 살펴보겠습니다. + +## 일반적인 과정 { #the-normal-process } + +일반적인(기본) 과정은 다음과 같습니다. + +`FastAPI` 애플리케이션(인스턴스)에는 OpenAPI 스키마를 반환해야 하는 `.openapi()` 메서드가 있습니다. + +애플리케이션 객체를 생성하는 과정에서 `/openapi.json`(또는 `openapi_url`에 설정한 경로)용 *경로 처리*가 등록됩니다. + +이 경로 처리는 애플리케이션의 `.openapi()` 메서드 결과를 JSON 응답으로 반환할 뿐입니다. + +기본적으로 `.openapi()` 메서드는 프로퍼티 `.openapi_schema`에 내용이 있는지 확인하고, 있으면 그 내용을 반환합니다. + +없으면 `fastapi.openapi.utils.get_openapi`에 있는 유틸리티 함수를 사용해 생성합니다. + +그리고 `get_openapi()` 함수는 다음을 파라미터로 받습니다: + +* `title`: 문서에 표시되는 OpenAPI 제목. +* `version`: API 버전. 예: `2.5.0`. +* `openapi_version`: 사용되는 OpenAPI 스펙 버전. 기본값은 최신인 `3.1.0`. +* `summary`: API에 대한 짧은 요약. +* `description`: API 설명. markdown을 포함할 수 있으며 문서에 표시됩니다. +* `routes`: 라우트 목록. 각각 등록된 *경로 처리*입니다. `app.routes`에서 가져옵니다. + +/// info | 정보 + +`summary` 파라미터는 OpenAPI 3.1.0 이상에서 사용할 수 있으며, FastAPI 0.99.0 이상에서 지원됩니다. + +/// + +## 기본값 덮어쓰기 { #overriding-the-defaults } + +위 정보를 바탕으로, 동일한 유틸리티 함수를 사용해 OpenAPI 스키마를 생성하고 필요한 각 부분을 덮어쓸 수 있습니다. + +예를 들어, 커스텀 로고를 포함하기 위한 ReDoc의 OpenAPI 확장을 추가해 보겠습니다. + +### 일반적인 **FastAPI** { #normal-fastapi } + +먼저, 평소처럼 **FastAPI** 애플리케이션을 모두 작성합니다: + +{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *} + +### OpenAPI 스키마 생성하기 { #generate-the-openapi-schema } + +그다음 `custom_openapi()` 함수 안에서, 동일한 유틸리티 함수를 사용해 OpenAPI 스키마를 생성합니다: + +{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *} + +### OpenAPI 스키마 수정하기 { #modify-the-openapi-schema } + +이제 OpenAPI 스키마의 `info` "object"에 커스텀 `x-logo`를 추가하여 ReDoc 확장을 더할 수 있습니다: + +{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *} + +### OpenAPI 스키마 캐시하기 { #cache-the-openapi-schema } + +생성한 스키마를 저장하기 위한 "cache"로 `.openapi_schema` 프로퍼티를 사용할 수 있습니다. + +이렇게 하면 사용자가 API 문서를 열 때마다 애플리케이션이 스키마를 매번 생성하지 않아도 됩니다. + +스키마는 한 번만 생성되고, 이후 요청에서는 같은 캐시된 스키마가 사용됩니다. + +{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *} + +### 메서드 오버라이드하기 { #override-the-method } + +이제 `.openapi()` 메서드를 새 함수로 교체할 수 있습니다. + +{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *} + +### 확인하기 { #check-it } + +http://127.0.0.1:8000/redoc로 이동하면 커스텀 로고(이 예시에서는 **FastAPI** 로고)를 사용하는 것을 확인할 수 있습니다: + + diff --git a/docs/ko/docs/how-to/general.md b/docs/ko/docs/how-to/general.md new file mode 100644 index 0000000000..a18dc68a21 --- /dev/null +++ b/docs/ko/docs/how-to/general.md @@ -0,0 +1,39 @@ +# 일반 - 사용 방법 - 레시피 { #general-how-to-recipes } + +일반적이거나 자주 나오는 질문에 대해, 문서의 다른 위치로 안내하는 몇 가지 포인터를 소개합니다. + +## 데이터 필터링 - 보안 { #filter-data-security } + +반환하면 안 되는 데이터를 과도하게 반환하지 않도록 하려면, [튜토리얼 - 응답 모델 - 반환 타입](../tutorial/response-model.md){.internal-link target=_blank} 문서를 읽어보세요. + +## 문서화 태그 - OpenAPI { #documentation-tags-openapi } + +*경로 처리*에 태그를 추가하고, 문서 UI에서 이를 그룹화하려면 [튜토리얼 - 경로 처리 구성 - 태그](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank} 문서를 읽어보세요. + +## 문서화 요약 및 설명 - OpenAPI { #documentation-summary-and-description-openapi } + +*경로 처리*에 요약과 설명을 추가하고, 문서 UI에 표시하려면 [튜토리얼 - 경로 처리 구성 - 요약 및 설명](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank} 문서를 읽어보세요. + +## 문서화 응답 설명 - OpenAPI { #documentation-response-description-openapi } + +문서 UI에 표시되는 응답의 설명을 정의하려면 [튜토리얼 - 경로 처리 구성 - 응답 설명](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank} 문서를 읽어보세요. + +## 문서화 *경로 처리* 지원 중단하기 - OpenAPI { #documentation-deprecate-a-path-operation-openapi } + +*경로 처리*를 지원 중단(deprecate)으로 표시하고, 문서 UI에 보여주려면 [튜토리얼 - 경로 처리 구성 - 지원 중단](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank} 문서를 읽어보세요. + +## 어떤 데이터든 JSON 호환으로 변환하기 { #convert-any-data-to-json-compatible } + +어떤 데이터든 JSON 호환 형식으로 변환하려면 [튜토리얼 - JSON 호환 인코더](../tutorial/encoder.md){.internal-link target=_blank} 문서를 읽어보세요. + +## OpenAPI 메타데이터 - 문서 { #openapi-metadata-docs } + +라이선스, 버전, 연락처 등의 정보를 포함해 OpenAPI 스키마에 메타데이터를 추가하려면 [튜토리얼 - 메타데이터와 문서 URL](../tutorial/metadata.md){.internal-link target=_blank} 문서를 읽어보세요. + +## OpenAPI 사용자 정의 URL { #openapi-custom-url } + +OpenAPI URL을 커스터마이즈(또는 제거)하려면 [튜토리얼 - 메타데이터와 문서 URL](../tutorial/metadata.md#openapi-url){.internal-link target=_blank} 문서를 읽어보세요. + +## OpenAPI 문서 URL { #openapi-docs-urls } + +자동으로 생성되는 문서 사용자 인터페이스에서 사용하는 URL을 업데이트하려면 [튜토리얼 - 메타데이터와 문서 URL](../tutorial/metadata.md#docs-urls){.internal-link target=_blank} 문서를 읽어보세요. diff --git a/docs/ko/docs/how-to/graphql.md b/docs/ko/docs/how-to/graphql.md new file mode 100644 index 0000000000..3cc467eb71 --- /dev/null +++ b/docs/ko/docs/how-to/graphql.md @@ -0,0 +1,60 @@ +# GraphQL { #graphql } + +**FastAPI**는 **ASGI** 표준을 기반으로 하므로, ASGI와도 호환되는 어떤 **GraphQL** 라이브러리든 매우 쉽게 통합할 수 있습니다. + +같은 애플리케이션에서 일반 FastAPI **경로 처리**와 GraphQL을 함께 조합할 수 있습니다. + +/// tip | 팁 + +**GraphQL**은 몇 가지 매우 특정한 사용 사례를 해결합니다. + +일반적인 **web API**와 비교했을 때 **장점**과 **단점**이 있습니다. + +여러분의 사용 사례에서 **이점**이 **단점**을 상쇄하는지 꼭 평가해 보세요. 🤓 + +/// + +## GraphQL 라이브러리 { #graphql-libraries } + +다음은 **ASGI** 지원이 있는 **GraphQL** 라이브러리들입니다. **FastAPI**와 함께 사용할 수 있습니다: + +* Strawberry 🍓 + * FastAPI용 문서 제공 +* Ariadne + * FastAPI용 문서 제공 +* Tartiflette + * ASGI 통합을 제공하기 위해 Tartiflette ASGI 사용 +* Graphene + * starlette-graphene3 사용 + +## Strawberry로 GraphQL 사용하기 { #graphql-with-strawberry } + +**GraphQL**로 작업해야 하거나 작업하고 싶다면, **Strawberry**를 **권장**합니다. **FastAPI**의 설계와 가장 가깝고, 모든 것이 **type annotations**에 기반해 있기 때문입니다. + +사용 사례에 따라 다른 라이브러리를 선호할 수도 있지만, 제게 묻는다면 아마 **Strawberry**를 먼저 시도해 보라고 제안할 것입니다. + +다음은 Strawberry를 FastAPI와 통합하는 방법에 대한 간단한 미리보기입니다: + +{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *} + +Strawberry 문서에서 Strawberry에 대해 더 알아볼 수 있습니다. + +또한 FastAPI에서 Strawberry 사용에 대한 문서도 확인해 보세요. + +## Starlette의 예전 `GraphQLApp` { #older-graphqlapp-from-starlette } + +이전 버전의 Starlette에는 Graphene과 통합하기 위한 `GraphQLApp` 클래스가 포함되어 있었습니다. + +이것은 Starlette에서 deprecated 되었지만, 이를 사용하던 코드가 있다면 같은 사용 사례를 다루고 **거의 동일한 인터페이스**를 가진 starlette-graphene3로 쉽게 **마이그레이션**할 수 있습니다. + +/// tip | 팁 + +GraphQL이 필요하다면, 커스텀 클래스와 타입 대신 type annotations에 기반한 Strawberry를 여전히 확인해 보시길 권장합니다. + +/// + +## 더 알아보기 { #learn-more } + +공식 GraphQL 문서에서 **GraphQL**에 대해 더 알아볼 수 있습니다. + +또한 위에서 설명한 각 라이브러리에 대해서도 해당 링크에서 더 자세히 읽어볼 수 있습니다. diff --git a/docs/ko/docs/how-to/index.md b/docs/ko/docs/how-to/index.md new file mode 100644 index 0000000000..9321c488bd --- /dev/null +++ b/docs/ko/docs/how-to/index.md @@ -0,0 +1,13 @@ +# How To - 레시피 { #how-to-recipes } + +여기에서는 **여러 주제**에 대한 다양한 레시피(“how to” 가이드)를 볼 수 있습니다. + +대부분의 아이디어는 어느 정도 **서로 독립적**이며, 대부분의 경우 **여러분의 프로젝트**에 직접 적용되는 경우에만 학습하면 됩니다. + +프로젝트에 흥미롭고 유용해 보이는 것이 있다면 확인해 보세요. 그렇지 않다면 아마 건너뛰어도 됩니다. + +/// tip | 팁 + +**FastAPI를 구조적으로 학습**하고 싶다면(권장), 대신 [튜토리얼 - 사용자 가이드](../tutorial/index.md){.internal-link target=_blank}를 장별로 읽어보세요. + +/// diff --git a/docs/ko/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md b/docs/ko/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md new file mode 100644 index 0000000000..6e528ecaf5 --- /dev/null +++ b/docs/ko/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md @@ -0,0 +1,135 @@ +# Pydantic v1에서 Pydantic v2로 마이그레이션하기 { #migrate-from-pydantic-v1-to-pydantic-v2 } + +오래된 FastAPI 앱이 있다면 Pydantic 버전 1을 사용하고 있을 수 있습니다. + +FastAPI 0.100.0 버전은 Pydantic v1 또는 v2 중 하나를 지원했습니다. 설치되어 있는 쪽을 사용했습니다. + +FastAPI 0.119.0 버전에서는 v2로의 마이그레이션을 쉽게 하기 위해, Pydantic v2 내부에서 Pydantic v1을(`pydantic.v1`로) 부분적으로 지원하기 시작했습니다. + +FastAPI 0.126.0 버전에서는 Pydantic v1 지원을 중단했지만, `pydantic.v1`은 잠시 동안 계속 지원했습니다. + +/// warning | 경고 + +Pydantic 팀은 **Python 3.14**부터 최신 Python 버전에서 Pydantic v1 지원을 중단했습니다. + +여기에는 `pydantic.v1`도 포함되며, Python 3.14 이상에서는 더 이상 지원되지 않습니다. + +Python의 최신 기능을 사용하려면 Pydantic v2를 사용하고 있는지 확인해야 합니다. + +/// + +Pydantic v1을 사용하는 오래된 FastAPI 앱이 있다면, 여기서는 이를 Pydantic v2로 마이그레이션하는 방법과 점진적 마이그레이션을 돕는 **FastAPI 0.119.0의 기능**을 소개하겠습니다. + +## 공식 가이드 { #official-guide } + +Pydantic에는 v1에서 v2로의 공식 Migration Guide가 있습니다. + +여기에는 무엇이 바뀌었는지, 검증이 이제 어떻게 더 정확하고 엄격해졌는지, 가능한 주의사항 등도 포함되어 있습니다. + +변경된 내용을 더 잘 이해하기 위해 읽어보면 좋습니다. + +## 테스트 { #tests } + +앱에 대한 [tests](../tutorial/testing.md){.internal-link target=_blank}가 있는지 확인하고, 지속적 통합(CI)에서 테스트를 실행하세요. + +이렇게 하면 업그레이드를 진행하면서도 모든 것이 기대한 대로 계속 동작하는지 확인할 수 있습니다. + +## `bump-pydantic` { #bump-pydantic } + +많은 경우, 커스터마이징 없이 일반적인 Pydantic 모델을 사용하고 있다면 Pydantic v1에서 Pydantic v2로의 마이그레이션 과정 대부분을 자동화할 수 있습니다. + +같은 Pydantic 팀이 제공하는 `bump-pydantic`를 사용할 수 있습니다. + +이 도구는 변경해야 하는 코드의 대부분을 자동으로 바꾸는 데 도움을 줍니다. + +그 다음 테스트를 실행해서 모든 것이 동작하는지 확인하면 됩니다. 잘 된다면 끝입니다. 😎 + +## v2 안의 Pydantic v1 { #pydantic-v1-in-v2 } + +Pydantic v2는 Pydantic v1의 모든 것을 서브모듈 `pydantic.v1`로 포함합니다. 하지만 이는 Python 3.13보다 높은 버전에서는 더 이상 지원되지 않습니다. + +즉, Pydantic v2의 최신 버전을 설치한 뒤, 이 서브모듈에서 예전 Pydantic v1 구성 요소를 import하여 예전 Pydantic v1을 설치한 것처럼 사용할 수 있습니다. + +{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *} + +### v2 안의 Pydantic v1에 대한 FastAPI 지원 { #fastapi-support-for-pydantic-v1-in-v2 } + +FastAPI 0.119.0부터는 v2로의 마이그레이션을 쉽게 하기 위해, Pydantic v2 내부의 Pydantic v1에 대해서도 부분적인 지원이 있습니다. + +따라서 Pydantic을 최신 v2로 업그레이드하고, import를 `pydantic.v1` 서브모듈을 사용하도록 바꾸면, 많은 경우 그대로 동작합니다. + +{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *} + +/// warning | 경고 + +Pydantic 팀이 Python 3.14부터 최신 Python 버전에서 Pydantic v1을 더 이상 지원하지 않으므로, `pydantic.v1`을 사용하는 것 역시 Python 3.14 이상에서는 지원되지 않는다는 점을 염두에 두세요. + +/// + +### 같은 앱에서 Pydantic v1과 v2 함께 사용하기 { #pydantic-v1-and-v2-on-the-same-app } + +Pydantic에서는 Pydantic v2 모델의 필드를 Pydantic v1 모델로 정의하거나 그 반대로 하는 것을 **지원하지 않습니다**. + +```mermaid +graph TB + subgraph "❌ Not Supported" + direction TB + subgraph V2["Pydantic v2 Model"] + V1Field["Pydantic v1 Model"] + end + subgraph V1["Pydantic v1 Model"] + V2Field["Pydantic v2 Model"] + end + end + + style V2 fill:#f9fff3 + style V1 fill:#fff6f0 + style V1Field fill:#fff6f0 + style V2Field fill:#f9fff3 +``` + +...하지만 같은 앱에서 Pydantic v1과 v2를 사용하되, 모델을 분리해서 둘 수는 있습니다. + +```mermaid +graph TB + subgraph "✅ Supported" + direction TB + subgraph V2["Pydantic v2 Model"] + V2Field["Pydantic v2 Model"] + end + subgraph V1["Pydantic v1 Model"] + V1Field["Pydantic v1 Model"] + end + end + + style V2 fill:#f9fff3 + style V1 fill:#fff6f0 + style V1Field fill:#fff6f0 + style V2Field fill:#f9fff3 +``` + +어떤 경우에는 FastAPI 앱의 같은 **경로 처리**에서 Pydantic v1과 v2 모델을 함께 사용하는 것도 가능합니다: + +{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *} + +위 예제에서 입력 모델은 Pydantic v1 모델이고, 출력 모델(`response_model=ItemV2`로 정의됨)은 Pydantic v2 모델입니다. + +### Pydantic v1 파라미터 { #pydantic-v1-parameters } + +Pydantic v1 모델과 함께 `Body`, `Query`, `Form` 등 파라미터용 FastAPI 전용 도구 일부를 사용해야 한다면, Pydantic v2로의 마이그레이션을 마칠 때까지 `fastapi.temp_pydantic_v1_params`에서 import할 수 있습니다: + +{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *} + +### 단계적으로 마이그레이션하기 { #migrate-in-steps } + +/// tip | 팁 + +먼저 `bump-pydantic`로 시도해 보세요. 테스트가 통과하고 잘 동작한다면, 한 번의 명령으로 끝입니다. ✨ + +/// + +`bump-pydantic`가 여러분의 사용 사례에 맞지 않는다면, 같은 앱에서 Pydantic v1과 v2 모델을 모두 지원하는 기능을 이용해 Pydantic v2로 점진적으로 마이그레이션할 수 있습니다. + +먼저 Pydantic을 최신 v2로 업그레이드하고, 모든 모델의 import를 `pydantic.v1`을 사용하도록 바꿀 수 있습니다. + +그 다음 Pydantic v1에서 v2로 모델을 그룹 단위로, 점진적인 단계로 마이그레이션을 시작하면 됩니다. 🚶 diff --git a/docs/ko/docs/how-to/separate-openapi-schemas.md b/docs/ko/docs/how-to/separate-openapi-schemas.md new file mode 100644 index 0000000000..055429c26f --- /dev/null +++ b/docs/ko/docs/how-to/separate-openapi-schemas.md @@ -0,0 +1,102 @@ +# 입력과 출력에 대해 OpenAPI 스키마를 분리할지 여부 { #separate-openapi-schemas-for-input-and-output-or-not } + +**Pydantic v2**가 릴리스된 이후, 생성되는 OpenAPI는 이전보다 조금 더 정확하고 **올바르게** 만들어집니다. 😎 + +실제로 어떤 경우에는, 같은 Pydantic 모델에 대해 OpenAPI 안에 **두 개의 JSON Schema**가 생기기도 합니다. **기본값(default value)**이 있는지 여부에 따라, 입력용과 출력용으로 나뉩니다. + +이것이 어떻게 동작하는지, 그리고 필요하다면 어떻게 변경할 수 있는지 살펴보겠습니다. + +## 입력과 출력을 위한 Pydantic 모델 { #pydantic-models-for-input-and-output } + +예를 들어, 다음처럼 기본값이 있는 Pydantic 모델이 있다고 해보겠습니다: + +{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *} + +### 입력용 모델 { #model-for-input } + +이 모델을 다음처럼 입력으로 사용하면: + +{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *} + +...`description` 필드는 **필수가 아닙니다**. `None`이라는 기본값이 있기 때문입니다. + +### 문서에서의 입력 모델 { #input-model-in-docs } + +문서에서 `description` 필드에 **빨간 별표**가 없고, 필수로 표시되지 않는 것을 확인할 수 있습니다: + +
+ +
+ +### 출력용 모델 { #model-for-output } + +하지만 같은 모델을 다음처럼 출력으로 사용하면: + +{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *} + +...`description`에 기본값이 있기 때문에, 그 필드에 대해 **아무것도 반환하지 않더라도** 여전히 그 **기본값**이 들어가게 됩니다. + +### 출력 응답 데이터용 모델 { #model-for-output-response-data } + +문서에서 직접 동작시켜 응답을 확인해 보면, 코드가 `description` 필드 중 하나에 아무것도 추가하지 않았더라도 JSON 응답에는 기본값(`null`)이 포함되어 있습니다: + +
+ +
+ +이는 해당 필드가 **항상 값을 가진다는 것**을 의미합니다. 다만 그 값이 때로는 `None`(JSON에서는 `null`)일 수 있습니다. + +즉, API를 사용하는 클라이언트는 값이 존재하는지 여부를 확인할 필요가 없고, **필드가 항상 존재한다고 가정**할 수 있습니다. 다만 어떤 경우에는 기본값 `None`이 들어갑니다. + +이를 OpenAPI에서 표현하는 방법은, 그 필드를 **required**로 표시하는 것입니다. 항상 존재하기 때문입니다. + +이 때문에, 하나의 모델이라도 **입력용인지 출력용인지**에 따라 JSON Schema가 달라질 수 있습니다: + +* **입력**에서는 `description`이 **필수가 아님** +* **출력**에서는 **필수임** (그리고 값은 `None`일 수도 있으며, JSON 용어로는 `null`) + +### 문서에서의 출력용 모델 { #model-for-output-in-docs } + +문서에서 출력 모델을 확인해 보면, `name`과 `description` **둘 다** **빨간 별표**로 **필수**로 표시되어 있습니다: + +
+ +
+ +### 문서에서의 입력과 출력 모델 { #model-for-input-and-output-in-docs } + +또 OpenAPI에서 사용 가능한 모든 Schemas(JSON Schemas)를 확인해 보면, `Item-Input` 하나와 `Item-Output` 하나, 이렇게 두 개가 있는 것을 볼 수 있습니다. + +`Item-Input`에서는 `description`이 **필수가 아니며**, 빨간 별표가 없습니다. + +하지만 `Item-Output`에서는 `description`이 **필수이며**, 빨간 별표가 있습니다. + +
+ +
+ +**Pydantic v2**의 이 기능 덕분에 API 문서는 더 **정밀**해지고, 자동 생성된 클라이언트와 SDK가 있다면 그것들도 더 정밀해져서 더 나은 **developer experience**와 일관성을 제공할 수 있습니다. 🎉 + +## 스키마를 분리하지 않기 { #do-not-separate-schemas } + +이제 어떤 경우에는 **입력과 출력에 대해 같은 스키마를 사용**하고 싶을 수도 있습니다. + +가장 대표적인 경우는, 이미 자동 생성된 클라이언트 코드/SDK가 있고, 아직은 그 자동 생성된 클라이언트 코드/SDK들을 전부 업데이트하고 싶지 않은 경우입니다. 언젠가는 업데이트해야 할 가능성이 높지만, 지금 당장은 아닐 수도 있습니다. + +그런 경우에는, **FastAPI**에서 `separate_input_output_schemas=False` 파라미터로 이 기능을 비활성화할 수 있습니다. + +/// info | 정보 + +`separate_input_output_schemas` 지원은 FastAPI `0.102.0`에 추가되었습니다. 🤓 + +/// + +{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *} + +### 문서에서 입력과 출력 모델에 같은 스키마 사용 { #same-schema-for-input-and-output-models-in-docs } + +이제 모델에 대해 입력과 출력 모두에 사용되는 단일 스키마(오직 `Item`만)가 생성되며, `description`은 **필수가 아닌 것**으로 표시됩니다: + +
+ +
diff --git a/docs/ko/docs/how-to/testing-database.md b/docs/ko/docs/how-to/testing-database.md new file mode 100644 index 0000000000..2d7798d707 --- /dev/null +++ b/docs/ko/docs/how-to/testing-database.md @@ -0,0 +1,7 @@ +# 데이터베이스 테스트하기 { #testing-a-database } + +데이터베이스, SQL, SQLModel에 대해서는 SQLModel 문서에서 학습할 수 있습니다. 🤓 + +FastAPI에서 SQLModel을 사용하는 방법에 대한 미니 튜토리얼도 있습니다. ✨ + +해당 튜토리얼에는 SQL 데이터베이스 테스트에 대한 섹션도 포함되어 있습니다. 😎 diff --git a/docs/ko/docs/tutorial/bigger-applications.md b/docs/ko/docs/tutorial/bigger-applications.md new file mode 100644 index 0000000000..cfc3900d4c --- /dev/null +++ b/docs/ko/docs/tutorial/bigger-applications.md @@ -0,0 +1,504 @@ +# 더 큰 애플리케이션 - 여러 파일 { #bigger-applications-multiple-files } + +애플리케이션이나 웹 API를 만들 때, 모든 것을 하나의 파일에 담을 수 있는 경우는 드뭅니다. + +**FastAPI**는 모든 유연성을 유지하면서도 애플리케이션을 구조화할 수 있게 해주는 편리한 도구를 제공합니다. + +/// info | 정보 + +Flask를 사용해 보셨다면, 이는 Flask의 Blueprints에 해당하는 개념입니다. + +/// + +## 예시 파일 구조 { #an-example-file-structure } + +다음과 같은 파일 구조가 있다고 해봅시다: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +│   ├── dependencies.py +│   └── routers +│   │ ├── __init__.py +│   │ ├── items.py +│   │ └── users.py +│   └── internal +│   ├── __init__.py +│   └── admin.py +``` + +/// tip | 팁 + +`__init__.py` 파일이 여러 개 있습니다: 각 디렉터리 또는 하위 디렉터리에 하나씩 있습니다. + +이 파일들이 한 파일의 코드를 다른 파일로 import할 수 있게 해줍니다. + +예를 들어 `app/main.py`에는 다음과 같은 줄이 있을 수 있습니다: + +``` +from app.routers import items +``` + +/// + +* `app` 디렉터리에는 모든 것이 들어 있습니다. 그리고 비어 있는 파일 `app/__init__.py`가 있어 "Python package"(“Python modules”의 모음)인 `app`이 됩니다. +* `app/main.py` 파일이 있습니다. Python package(`__init__.py` 파일이 있는 디렉터리) 안에 있으므로, 이 package의 "module"입니다: `app.main`. +* `app/dependencies.py` 파일도 있습니다. `app/main.py`와 마찬가지로 "module"입니다: `app.dependencies`. +* `app/routers/` 하위 디렉터리가 있고, 여기에 또 `__init__.py` 파일이 있으므로 "Python subpackage"입니다: `app.routers`. +* `app/routers/items.py` 파일은 `app/routers/` package 안에 있으므로, submodule입니다: `app.routers.items`. +* `app/routers/users.py`도 동일하게 또 다른 submodule입니다: `app.routers.users`. +* `app/internal/` 하위 디렉터리도 있고 여기에 `__init__.py`가 있으므로 또 다른 "Python subpackage"입니다: `app.internal`. +* 그리고 `app/internal/admin.py` 파일은 또 다른 submodule입니다: `app.internal.admin`. + + + +같은 파일 구조에 주석을 추가하면 다음과 같습니다: + +```bash +. +├── app # "app" is a Python package +│   ├── __init__.py # this file makes "app" a "Python package" +│   ├── main.py # "main" module, e.g. import app.main +│   ├── dependencies.py # "dependencies" module, e.g. import app.dependencies +│   └── routers # "routers" is a "Python subpackage" +│   │ ├── __init__.py # makes "routers" a "Python subpackage" +│   │ ├── items.py # "items" submodule, e.g. import app.routers.items +│   │ └── users.py # "users" submodule, e.g. import app.routers.users +│   └── internal # "internal" is a "Python subpackage" +│   ├── __init__.py # makes "internal" a "Python subpackage" +│   └── admin.py # "admin" submodule, e.g. import app.internal.admin +``` + +## `APIRouter` { #apirouter } + +사용자만 처리하는 전용 파일이 `/app/routers/users.py`의 submodule이라고 해봅시다. + +코드를 정리하기 위해 사용자와 관련된 *path operations*를 나머지 코드와 분리해 두고 싶을 것입니다. + +하지만 이것은 여전히 같은 **FastAPI** 애플리케이션/웹 API의 일부입니다(같은 "Python Package"의 일부입니다). + +`APIRouter`를 사용해 해당 모듈의 *path operations*를 만들 수 있습니다. + +### `APIRouter` import하기 { #import-apirouter } + +`FastAPI` 클래스와 동일한 방식으로 import하고 "instance"를 생성합니다: + +{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *} + +### `APIRouter`로 *path operations* 만들기 { #path-operations-with-apirouter } + +그 다음 이를 사용해 *path operations*를 선언합니다. + +`FastAPI` 클래스를 사용할 때와 동일한 방식으로 사용합니다: + +{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *} + +`APIRouter`는 "미니 `FastAPI`" 클래스라고 생각할 수 있습니다. + +동일한 옵션들이 모두 지원됩니다. + +동일한 `parameters`, `responses`, `dependencies`, `tags` 등등. + +/// tip | 팁 + +이 예시에서는 변수 이름이 `router`이지만, 원하는 이름으로 지어도 됩니다. + +/// + +이제 이 `APIRouter`를 메인 `FastAPI` 앱에 포함(include)할 것이지만, 먼저 dependencies와 다른 `APIRouter` 하나를 확인해 보겠습니다. + +## Dependencies { #dependencies } + +애플리케이션의 여러 위치에서 사용되는 dependencies가 일부 필요하다는 것을 알 수 있습니다. + +그래서 이를 별도의 `dependencies` 모듈(`app/dependencies.py`)에 둡니다. + +이제 간단한 dependency를 사용해 커스텀 `X-Token` 헤더를 읽어 보겠습니다: + +{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *} + +/// tip | 팁 + +이 예시를 단순화하기 위해 임의로 만든 헤더를 사용하고 있습니다. + +하지만 실제 상황에서는 통합된 [Security 유틸리티](security/index.md){.internal-link target=_blank}를 사용하는 것이 더 좋은 결과를 얻을 수 있습니다. + +/// + +## `APIRouter`가 있는 또 다른 모듈 { #another-module-with-apirouter } + +애플리케이션의 "items"를 처리하는 전용 endpoint들도 `app/routers/items.py` 모듈에 있다고 해봅시다. + +여기에는 다음에 대한 *path operations*가 있습니다: + +* `/items/` +* `/items/{item_id}` + +구조는 `app/routers/users.py`와 완전히 동일합니다. + +하지만 우리는 조금 더 똑똑하게, 코드를 약간 단순화하고 싶습니다. + +이 모듈의 모든 *path operations*에는 다음이 동일하게 적용됩니다: + +* 경로 `prefix`: `/items`. +* `tags`: (태그 하나: `items`). +* 추가 `responses`. +* `dependencies`: 모두 우리가 만든 `X-Token` dependency가 필요합니다. + +따라서 각 *path operation*마다 매번 모두 추가하는 대신, `APIRouter`에 한 번에 추가할 수 있습니다. + +{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *} + +각 *path operation*의 경로는 다음처럼 `/`로 시작해야 하므로: + +```Python hl_lines="1" +@router.get("/{item_id}") +async def read_item(item_id: str): + ... +``` + +...prefix에는 마지막 `/`가 포함되면 안 됩니다. + +따라서 이 경우 prefix는 `/items`입니다. + +또한 이 router에 포함된 모든 *path operations*에 적용될 `tags` 목록과 추가 `responses`도 넣을 수 있습니다. + +그리고 router의 모든 *path operations*에 추가될 `dependencies` 목록도 추가할 수 있으며, 해당 경로들로 들어오는 각 요청마다 실행/해결됩니다. + +/// tip | 팁 + +[*path operation decorator의 dependencies*](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}와 마찬가지로, *path operation function*에 어떤 값도 전달되지 않습니다. + +/// + +최종적으로 item 경로는 다음과 같습니다: + +* `/items/` +* `/items/{item_id}` + +...의도한 그대로입니다. + +* 단일 문자열 `"items"`를 포함하는 태그 목록으로 표시됩니다. + * 이 "tags"는 자동 대화형 문서 시스템(OpenAPI 사용)에 특히 유용합니다. +* 모두 미리 정의된 `responses`를 포함합니다. +* 이 모든 *path operations*는 실행되기 전에 `dependencies` 목록이 평가/실행됩니다. + * 특정 *path operation*에 dependencies를 추가로 선언하면 **그것들도 실행됩니다**. + * router dependencies가 먼저 실행되고, 그 다음에 [decorator의 `dependencies`](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, 그리고 일반 파라미터 dependencies가 실행됩니다. + * [`scopes`가 있는 `Security` dependencies](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}도 추가할 수 있습니다. + +/// tip | 팁 + +`APIRouter`에 `dependencies`를 두는 것은 예를 들어 전체 *path operations* 그룹에 인증을 요구할 때 사용할 수 있습니다. 각 경로 처리에 개별적으로 dependencies를 추가하지 않아도 됩니다. + +/// + +/// check | 확인 + +`prefix`, `tags`, `responses`, `dependencies` 파라미터는 (다른 많은 경우와 마찬가지로) 코드 중복을 피하도록 도와주는 **FastAPI**의 기능입니다. + +/// + +### dependencies import하기 { #import-the-dependencies } + +이 코드는 모듈 `app.routers.items`, 파일 `app/routers/items.py`에 있습니다. + +그리고 dependency 함수는 모듈 `app.dependencies`, 파일 `app/dependencies.py`에서 가져와야 합니다. + +그래서 dependencies에 대해 `..`를 사용하는 상대 import를 사용합니다: + +{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *} + +#### 상대 import가 동작하는 방식 { #how-relative-imports-work } + +/// tip | 팁 + +import가 동작하는 방식을 완벽히 알고 있다면, 아래 다음 섹션으로 넘어가세요. + +/// + +다음과 같이 점 하나 `.`를 쓰면: + +```Python +from .dependencies import get_token_header +``` + +의미는 다음과 같습니다: + +* 이 모듈(파일 `app/routers/items.py`)이 속한 같은 package(디렉터리 `app/routers/`)에서 시작해서... +* `dependencies` 모듈(가상의 파일 `app/routers/dependencies.py`)을 찾고... +* 그 안에서 함수 `get_token_header`를 import합니다. + +하지만 그 파일은 존재하지 않습니다. dependencies는 `app/dependencies.py` 파일에 있습니다. + +우리 앱/파일 구조를 다시 떠올려 보세요: + + + +--- + +다음처럼 점 두 개 `..`를 쓰면: + +```Python +from ..dependencies import get_token_header +``` + +의미는 다음과 같습니다: + +* 이 모듈(파일 `app/routers/items.py`)이 속한 같은 package(디렉터리 `app/routers/`)에서 시작해서... +* 상위 package(디렉터리 `app/`)로 올라가고... +* 그 안에서 `dependencies` 모듈(파일 `app/dependencies.py`)을 찾고... +* 그 안에서 함수 `get_token_header`를 import합니다. + +이렇게 하면 제대로 동작합니다! 🎉 + +--- + +같은 방식으로 점 세 개 `...`를 사용했다면: + +```Python +from ...dependencies import get_token_header +``` + +의미는 다음과 같습니다: + +* 이 모듈(파일 `app/routers/items.py`)이 속한 같은 package(디렉터리 `app/routers/`)에서 시작해서... +* 상위 package(디렉터리 `app/`)로 올라가고... +* 그 package의 상위로 또 올라가는데(상위 package가 없습니다, `app`이 최상위입니다 😱)... +* 그 안에서 `dependencies` 모듈(파일 `app/dependencies.py`)을 찾고... +* 그 안에서 함수 `get_token_header`를 import합니다. + +이는 `app/` 위쪽의 어떤 package(자신의 `__init__.py` 파일 등을 가진)에 대한 참조가 됩니다. 하지만 우리는 그런 것이 없습니다. 그래서 이 예시에서는 에러가 발생합니다. 🚨 + +이제 어떻게 동작하는지 알았으니, 앱이 얼마나 복잡하든 상대 import를 사용할 수 있습니다. 🤓 + +### 커스텀 `tags`, `responses`, `dependencies` 추가하기 { #add-some-custom-tags-responses-and-dependencies } + +`APIRouter`에 이미 prefix `/items`와 `tags=["items"]`를 추가했기 때문에 각 *path operation*에 이를 추가하지 않습니다. + +하지만 특정 *path operation*에만 적용될 _추가_ `tags`를 더할 수도 있고, 그 *path operation* 전용의 추가 `responses`도 넣을 수 있습니다: + +{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *} + +/// tip | 팁 + +이 마지막 경로 처리는 `["items", "custom"]` 태그 조합을 갖게 됩니다. + +그리고 문서에는 `404`용 응답과 `403`용 응답, 두 가지 모두가 표시됩니다. + +/// + +## 메인 `FastAPI` { #the-main-fastapi } + +이제 `app/main.py` 모듈을 봅시다. + +여기에서 `FastAPI` 클래스를 import하고 사용합니다. + +이 파일은 모든 것을 하나로 엮는 애플리케이션의 메인 파일이 될 것입니다. + +그리고 대부분의 로직이 각자의 특정 모듈로 분리되어 있으므로, 메인 파일은 꽤 단순해집니다. + +### `FastAPI` import하기 { #import-fastapi } + +평소처럼 `FastAPI` 클래스를 import하고 생성합니다. + +또한 각 `APIRouter`의 dependencies와 결합될 [global dependencies](dependencies/global-dependencies.md){.internal-link target=_blank}도 선언할 수 있습니다: + +{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *} + +### `APIRouter` import하기 { #import-the-apirouter } + +이제 `APIRouter`가 있는 다른 submodule들을 import합니다: + +{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *} + +`app/routers/users.py`와 `app/routers/items.py` 파일은 같은 Python package `app`에 속한 submodule들이므로, 점 하나 `.`를 사용해 "상대 import"로 가져올 수 있습니다. + +### import가 동작하는 방식 { #how-the-importing-works } + +다음 구문은: + +```Python +from .routers import items, users +``` + +의미는 다음과 같습니다: + +* 이 모듈(파일 `app/main.py`)이 속한 같은 package(디렉터리 `app/`)에서 시작해서... +* subpackage `routers`(디렉터리 `app/routers/`)를 찾고... +* 그 안에서 submodule `items`(파일 `app/routers/items.py`)와 `users`(파일 `app/routers/users.py`)를 import합니다... + +`items` 모듈에는 `router` 변수(`items.router`)가 있습니다. 이는 `app/routers/items.py` 파일에서 만든 것과 동일하며 `APIRouter` 객체입니다. + +그리고 `users` 모듈도 같은 방식입니다. + +다음처럼 import할 수도 있습니다: + +```Python +from app.routers import items, users +``` + +/// info | 정보 + +첫 번째 버전은 "상대 import"입니다: + +```Python +from .routers import items, users +``` + +두 번째 버전은 "절대 import"입니다: + +```Python +from app.routers import items, users +``` + +Python Packages와 Modules에 대해 더 알아보려면 Modules에 대한 Python 공식 문서를 읽어보세요. + +/// + +### 이름 충돌 피하기 { #avoid-name-collisions } + +submodule `items`를 직접 import하고, 그 안의 `router` 변수만 import하지는 않습니다. + +이는 submodule `users`에도 `router`라는 이름의 변수가 있기 때문입니다. + +만약 다음처럼 순서대로 import했다면: + +```Python +from .routers.items import router +from .routers.users import router +``` + +`users`의 `router`가 `items`의 `router`를 덮어써서 동시에 사용할 수 없게 됩니다. + +따라서 같은 파일에서 둘 다 사용할 수 있도록 submodule들을 직접 import합니다: + +{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *} + +### `users`와 `items`용 `APIRouter` 포함하기 { #include-the-apirouters-for-users-and-items } + +이제 submodule `users`와 `items`의 `router`를 포함해 봅시다: + +{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *} + +/// info | 정보 + +`users.router`는 `app/routers/users.py` 파일 안의 `APIRouter`를 담고 있습니다. + +`items.router`는 `app/routers/items.py` 파일 안의 `APIRouter`를 담고 있습니다. + +/// + +`app.include_router()`로 각 `APIRouter`를 메인 `FastAPI` 애플리케이션에 추가할 수 있습니다. + +그 router의 모든 route가 애플리케이션의 일부로 포함됩니다. + +/// note Technical Details | 기술 세부사항 + +내부적으로는 `APIRouter`에 선언된 각 *path operation*마다 *path operation*을 실제로 생성합니다. + +즉, 내부적으로는 모든 것이 동일한 하나의 앱인 것처럼 동작합니다. + +/// + +/// check | 확인 + +router를 포함(include)할 때 성능을 걱정할 필요는 없습니다. + +이 작업은 마이크로초 단위이며 시작 시에만 발생합니다. + +따라서 성능에 영향을 주지 않습니다. ⚡ + +/// + +### 커스텀 `prefix`, `tags`, `responses`, `dependencies`로 `APIRouter` 포함하기 { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies } + +이제 조직에서 `app/internal/admin.py` 파일을 받았다고 가정해 봅시다. + +여기에는 조직에서 여러 프로젝트 간에 공유하는 관리자용 *path operations*가 있는 `APIRouter`가 들어 있습니다. + +이 예시에서는 매우 단순하게 만들겠습니다. 하지만 조직 내 다른 프로젝트와 공유되기 때문에, 이를 수정할 수 없어 `prefix`, `dependencies`, `tags` 등을 `APIRouter`에 직접 추가할 수 없다고 해봅시다: + +{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *} + +하지만 `APIRouter`를 포함할 때 커스텀 `prefix`를 지정해 모든 *path operations*가 `/admin`으로 시작하게 하고, 이 프로젝트에서 이미 가진 `dependencies`로 보호하고, `tags`와 `responses`도 포함하고 싶습니다. + +원래 `APIRouter`를 수정하지 않고도 `app.include_router()`에 파라미터를 전달해서 이를 선언할 수 있습니다: + +{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *} + +이렇게 하면 원래 `APIRouter`는 수정되지 않으므로, 조직 내 다른 프로젝트에서도 동일한 `app/internal/admin.py` 파일을 계속 공유할 수 있습니다. + +결과적으로 우리 앱에서 `admin` 모듈의 각 *path operations*는 다음을 갖게 됩니다: + +* prefix `/admin`. +* tag `admin`. +* dependency `get_token_header`. +* 응답 `418`. 🍵 + +하지만 이는 우리 앱에서 그 `APIRouter`에만 영향을 주며, 이를 사용하는 다른 코드에는 영향을 주지 않습니다. + +따라서 다른 프로젝트들은 같은 `APIRouter`를 다른 인증 방식으로 사용할 수도 있습니다. + +### *path operation* 포함하기 { #include-a-path-operation } + +*path operations*를 `FastAPI` 앱에 직접 추가할 수도 있습니다. + +여기서는 가능하다는 것을 보여주기 위해... 그냥 해봅니다 🤷: + +{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *} + +그리고 `app.include_router()`로 추가한 다른 모든 *path operations*와 함께 올바르게 동작합니다. + +/// info | 정보 + +**참고**: 이는 매우 기술적인 세부사항이라 아마 **그냥 건너뛰어도 됩니다**. + +--- + +`APIRouter`는 "mount"되는 것이 아니며, 애플리케이션의 나머지 부분과 격리되어 있지 않습니다. + +이는 OpenAPI 스키마와 사용자 인터페이스에 그들의 *path operations*를 포함시키고 싶기 때문입니다. + +나머지와 독립적으로 격리해 "mount"할 수 없으므로, *path operations*는 직접 포함되는 것이 아니라 "clone"(재생성)됩니다. + +/// + +## 자동 API 문서 확인하기 { #check-the-automatic-api-docs } + +이제 앱을 실행하세요: + +
+ +```console +$ fastapi dev app/main.py + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +그리고 http://127.0.0.1:8000/docs에서 문서를 여세요. + +올바른 경로(및 prefix)와 올바른 태그를 사용해, 모든 submodule의 경로를 포함한 자동 API 문서를 볼 수 있습니다: + + + +## 같은 router를 다른 `prefix`로 여러 번 포함하기 { #include-the-same-router-multiple-times-with-different-prefix } + +`.include_router()`를 사용해 *같은* router를 서로 다른 prefix로 여러 번 포함할 수도 있습니다. + +예를 들어 `/api/v1`과 `/api/latest`처럼 서로 다른 prefix로 동일한 API를 노출할 때 유용할 수 있습니다. + +이는 고급 사용 방식이라 실제로 필요하지 않을 수도 있지만, 필요할 때를 위해 제공됩니다. + +## `APIRouter`에 다른 `APIRouter` 포함하기 { #include-an-apirouter-in-another } + +`APIRouter`를 `FastAPI` 애플리케이션에 포함할 수 있는 것과 같은 방식으로, 다음을 사용해 `APIRouter`를 다른 `APIRouter`에 포함할 수 있습니다: + +```Python +router.include_router(other_router) +``` + +`FastAPI` 앱에 `router`를 포함하기 전에 수행해야 하며, 그래야 `other_router`의 *path operations*도 함께 포함됩니다. diff --git a/docs/ko/docs/tutorial/body-updates.md b/docs/ko/docs/tutorial/body-updates.md new file mode 100644 index 0000000000..3719e1ffab --- /dev/null +++ b/docs/ko/docs/tutorial/body-updates.md @@ -0,0 +1,100 @@ +# Body - 업데이트 { #body-updates } + +## `PUT`으로 교체 업데이트하기 { #update-replacing-with-put } + +항목을 업데이트하려면 HTTP `PUT` 작업을 사용할 수 있습니다. + +`jsonable_encoder`를 사용해 입력 데이터를 JSON으로 저장할 수 있는 데이터로 변환할 수 있습니다(예: NoSQL 데이터베이스 사용 시). 예를 들어 `datetime`을 `str`로 변환하는 경우입니다. + +{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *} + +`PUT`은 기존 데이터를 **대체**해야 하는 데이터를 받는 데 사용합니다. + +### 대체 시 주의사항 { #warning-about-replacing } + +즉, `PUT`으로 항목 `bar`를 업데이트하면서 다음과 같은 body를 보낸다면: + +```Python +{ + "name": "Barz", + "price": 3, + "description": None, +} +``` + +이미 저장된 속성 `"tax": 20.2`가 포함되어 있지 않기 때문에, 입력 모델은 `"tax": 10.5`라는 기본값을 사용하게 됩니다. + +그리고 데이터는 그 “새로운” `tax` 값 `10.5`로 저장됩니다. + +## `PATCH`로 부분 업데이트하기 { #partial-updates-with-patch } + +HTTP `PATCH` 작업을 사용해 데이터를 *부분적으로* 업데이트할 수도 있습니다. + +이는 업데이트하려는 데이터만 보내고, 나머지는 그대로 두는 것을 의미합니다. + +/// note | 참고 + +`PATCH`는 `PUT`보다 덜 일반적으로 사용되고 덜 알려져 있습니다. + +그리고 많은 팀이 부분 업데이트에도 `PUT`만 사용합니다. + +여러분은 원하는 방식으로 **자유롭게** 사용할 수 있으며, **FastAPI**는 어떤 제한도 강제하지 않습니다. + +다만 이 가이드는 의도된 사용 방식이 대략 어떻게 되는지를 보여줍니다. + +/// + +### Pydantic의 `exclude_unset` 파라미터 사용하기 { #using-pydantics-exclude-unset-parameter } + +부분 업데이트를 받으려면 Pydantic 모델의 `.model_dump()`에서 `exclude_unset` 파라미터를 사용하는 것이 매우 유용합니다. + +예: `item.model_dump(exclude_unset=True)`. + +이는 `item` 모델을 만들 때 실제로 설정된 데이터만 포함하는 `dict`를 생성하고, 기본값은 제외합니다. + +그 다음 이를 사용해 (요청에서 전송되어) 설정된 데이터만 포함하고 기본값은 생략한 `dict`를 만들 수 있습니다: + +{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *} + +### Pydantic의 `update` 파라미터 사용하기 { #using-pydantics-update-parameter } + +이제 `.model_copy()`를 사용해 기존 모델의 복사본을 만들고, 업데이트할 데이터가 들어있는 `dict`를 `update` 파라미터로 전달할 수 있습니다. + +예: `stored_item_model.model_copy(update=update_data)`: + +{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *} + +### 부분 업데이트 요약 { #partial-updates-recap } + +정리하면, 부분 업데이트를 적용하려면 다음을 수행합니다: + +* (선택 사항) `PUT` 대신 `PATCH`를 사용합니다. +* 저장된 데이터를 조회합니다. +* 그 데이터를 Pydantic 모델에 넣습니다. +* 입력 모델에서 기본값이 제외된 `dict`를 생성합니다(`exclude_unset` 사용). + * 이렇게 하면 모델의 기본값으로 이미 저장된 값을 덮어쓰지 않고, 사용자가 실제로 설정한 값만 업데이트할 수 있습니다. +* 저장된 모델의 복사본을 만들고, 받은 부분 업데이트로 해당 속성들을 갱신합니다(`update` 파라미터 사용). +* 복사한 모델을 DB에 저장할 수 있는 형태로 변환합니다(예: `jsonable_encoder` 사용). + * 이는 모델의 `.model_dump()` 메서드를 다시 사용하는 것과 비슷하지만, JSON으로 변환 가능한 데이터 타입으로 값이 확실히 변환되도록 보장합니다(예: `datetime` → `str`). +* 데이터를 DB에 저장합니다. +* 업데이트된 모델을 반환합니다. + +{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *} + +/// tip | 팁 + +동일한 기법을 HTTP `PUT` 작업에서도 실제로 사용할 수 있습니다. + +하지만 여기의 예시는 이런 사용 사례를 위해 만들어진 `PATCH`를 사용합니다. + +/// + +/// note | 참고 + +입력 모델은 여전히 검증된다는 점에 유의하세요. + +따라서 모든 속성을 생략할 수 있는 부분 업데이트를 받으려면, 모든 속성이 optional로 표시된(기본값을 가지거나 `None`을 기본값으로 가지는) 모델이 필요합니다. + +**업데이트**를 위한 “모든 값이 optional인” 모델과, **생성**을 위한 “필수 값이 있는” 모델을 구분하려면 [추가 모델](extra-models.md){.internal-link target=_blank}에 설명된 아이디어를 사용할 수 있습니다. + +/// diff --git a/docs/ko/docs/tutorial/dependencies/sub-dependencies.md b/docs/ko/docs/tutorial/dependencies/sub-dependencies.md new file mode 100644 index 0000000000..d81ccf00d0 --- /dev/null +++ b/docs/ko/docs/tutorial/dependencies/sub-dependencies.md @@ -0,0 +1,105 @@ +# 하위 의존성 { #sub-dependencies } + +**하위 의존성**을 가지는 의존성을 만들 수 있습니다. + +필요한 만큼 **깊게** 중첩할 수도 있습니다. + +이것을 해결하는 일은 **FastAPI**가 알아서 처리합니다. + +## 첫 번째 의존성 "dependable" { #first-dependency-dependable } + +다음과 같이 첫 번째 의존성("dependable")을 만들 수 있습니다: + +{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *} + +이 의존성은 선택적 쿼리 파라미터 `q`를 `str`로 선언하고, 그대로 반환합니다. + +매우 단순한 예시(그다지 유용하진 않음)이지만, 하위 의존성이 어떻게 동작하는지에 집중하는 데 도움이 됩니다. + +## 두 번째 의존성 "dependable"과 "dependant" { #second-dependency-dependable-and-dependant } + +그다음, 또 다른 의존성 함수("dependable")를 만들 수 있는데, 이 함수는 동시에 자기 자신의 의존성도 선언합니다(그래서 "dependant"이기도 합니다): + +{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *} + +선언된 파라미터를 살펴보겠습니다: + +* 이 함수 자체가 의존성("dependable")이지만, 다른 의존성도 하나 선언합니다(즉, 다른 무언가에 "의존"합니다). + * `query_extractor`에 의존하며, 그 반환값을 파라미터 `q`에 할당합니다. +* 또한 선택적 `last_query` 쿠키를 `str`로 선언합니다. + * 사용자가 쿼리 `q`를 제공하지 않았다면, 이전에 쿠키에 저장해 둔 마지막 쿼리를 사용합니다. + +## 의존성 사용하기 { #use-the-dependency } + +그다음 다음과 같이 의존성을 사용할 수 있습니다: + +{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *} + +/// info | 정보 + +*경로 처리 함수*에서는 `query_or_cookie_extractor`라는 의존성 하나만 선언하고 있다는 점에 주목하세요. + +하지만 **FastAPI**는 `query_or_cookie_extractor`를 호출하는 동안 그 결과를 전달하기 위해, 먼저 `query_extractor`를 해결해야 한다는 것을 알고 있습니다. + +/// + +```mermaid +graph TB + +query_extractor(["query_extractor"]) +query_or_cookie_extractor(["query_or_cookie_extractor"]) + +read_query["/items/"] + +query_extractor --> query_or_cookie_extractor --> read_query +``` + +## 같은 의존성을 여러 번 사용하기 { #using-the-same-dependency-multiple-times } + +같은 *경로 처리*에 대해 의존성 중 하나가 여러 번 선언되는 경우(예: 여러 의존성이 공통 하위 의존성을 갖는 경우), **FastAPI**는 그 하위 의존성을 요청당 한 번만 호출해야 한다는 것을 알고 있습니다. + +그리고 같은 요청에 대해 동일한 의존성을 여러 번 호출하는 대신, 반환값을 "cache"에 저장하고, 그 요청에서 해당 값이 필요한 모든 "dependants"에 전달합니다. + +고급 시나리오로, 같은 요청에서 "cached" 값을 쓰는 대신 매 단계마다(아마도 여러 번) 의존성이 호출되어야 한다는 것을 알고 있다면, `Depends`를 사용할 때 `use_cache=False` 파라미터를 설정할 수 있습니다: + +//// tab | Python 3.9+ + +```Python hl_lines="1" +async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]): + return {"fresh_value": fresh_value} +``` + +//// + +//// tab | Python 3.9+ 비 Annotated + +/// tip | 팁 + +가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다. + +/// + +```Python hl_lines="1" +async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)): + return {"fresh_value": fresh_value} +``` + +//// + +## 정리 { #recap } + +여기서 사용한 그럴듯한 용어들을 제외하면, **Dependency Injection** 시스템은 꽤 단순합니다. + +*경로 처리 함수*와 같은 형태의 함수들일 뿐입니다. + +하지만 여전히 매우 강력하며, 임의로 깊게 중첩된 의존성 "그래프"(트리)를 선언할 수 있습니다. + +/// tip | 팁 + +이 단순한 예시만 보면 그다지 유용해 보이지 않을 수도 있습니다. + +하지만 **보안**에 관한 챕터에서 이것이 얼마나 유용한지 보게 될 것입니다. + +또한 얼마나 많은 코드를 아껴주는지도 보게 될 것입니다. + +/// diff --git a/docs/ko/docs/tutorial/handling-errors.md b/docs/ko/docs/tutorial/handling-errors.md new file mode 100644 index 0000000000..7cc37e80c0 --- /dev/null +++ b/docs/ko/docs/tutorial/handling-errors.md @@ -0,0 +1,244 @@ +# 오류 처리 { #handling-errors } + +API를 사용하는 클라이언트에 오류를 알려야 하는 상황은 많이 있습니다. + +이 클라이언트는 프론트엔드가 있는 브라우저일 수도 있고, 다른 사람이 작성한 코드일 수도 있고, IoT 장치일 수도 있습니다. + +클라이언트에 다음과 같은 내용을 알려야 할 수도 있습니다: + +* 클라이언트가 해당 작업을 수행할 충분한 권한이 없습니다. +* 클라이언트가 해당 리소스에 접근할 수 없습니다. +* 클라이언트가 접근하려고 한 항목이 존재하지 않습니다. +* 등등. + +이런 경우 보통 **400**번대(400에서 499) 범위의 **HTTP 상태 코드**를 반환합니다. + +이는 200번대 HTTP 상태 코드(200에서 299)와 비슷합니다. "200" 상태 코드는 어떤 형태로든 요청이 "성공"했음을 의미합니다. + +400번대 상태 코드는 클라이언트 측에서 오류가 발생했음을 의미합니다. + +**"404 Not Found"** 오류(그리고 농담들)도 다들 기억하시죠? + +## `HTTPException` 사용하기 { #use-httpexception } + +클라이언트에 오류가 포함된 HTTP 응답을 반환하려면 `HTTPException`을 사용합니다. + +### `HTTPException` 가져오기 { #import-httpexception } + +{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *} + +### 코드에서 `HTTPException` 발생시키기 { #raise-an-httpexception-in-your-code } + +`HTTPException`은 API와 관련된 추가 데이터를 가진 일반적인 Python 예외입니다. + +Python 예외이므로 `return` 하는 것이 아니라 `raise` 합니다. + +이는 또한, *경로 처리 함수* 내부에서 호출하는 유틸리티 함수 안에서 `HTTPException`을 `raise`하면, *경로 처리 함수*의 나머지 코드는 실행되지 않고 즉시 해당 요청이 종료되며 `HTTPException`의 HTTP 오류가 클라이언트로 전송된다는 뜻입니다. + +값을 반환하는 것보다 예외를 발생시키는 것의 이점은 의존성과 보안에 대한 섹션에서 더 분명해집니다. + +이 예시에서는, 클라이언트가 존재하지 않는 ID로 항목을 요청하면 상태 코드 `404`로 예외를 발생시킵니다: + +{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *} + +### 결과 응답 { #the-resulting-response } + +클라이언트가 `http://example.com/items/foo`( `item_id` `"foo"`)를 요청하면, HTTP 상태 코드 200과 다음 JSON 응답을 받습니다: + +```JSON +{ + "item": "The Foo Wrestlers" +} +``` + +하지만 클라이언트가 `http://example.com/items/bar`(존재하지 않는 `item_id` `"bar"`)를 요청하면, HTTP 상태 코드 404("not found" 오류)와 다음 JSON 응답을 받습니다: + +```JSON +{ + "detail": "Item not found" +} +``` + +/// tip | 팁 + +`HTTPException`을 발생시킬 때 `detail` 파라미터로 `str`만 전달할 수 있는 것이 아니라, JSON으로 변환할 수 있는 어떤 값이든 전달할 수 있습니다. + +`dict`, `list` 등을 전달할 수 있습니다. + +이들은 **FastAPI**가 자동으로 처리해 JSON으로 변환합니다. + +/// + +## 커스텀 헤더 추가하기 { #add-custom-headers } + +HTTP 오류에 커스텀 헤더를 추가할 수 있으면 유용한 상황이 있습니다. 예를 들어 특정 보안 유형에서 그렇습니다. + +아마 코드에서 직접 사용할 일은 거의 없을 것입니다. + +하지만 고급 시나리오에서 필요하다면 커스텀 헤더를 추가할 수 있습니다: + +{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *} + +## 커스텀 예외 핸들러 설치하기 { #install-custom-exception-handlers } + +Starlette의 동일한 예외 유틸리티를 사용해 커스텀 예외 핸들러를 추가할 수 있습니다. + +여러분(또는 사용하는 라이브러리)이 `raise`할 수 있는 커스텀 예외 `UnicornException`이 있다고 가정해 봅시다. + +그리고 이 예외를 FastAPI에서 전역적으로 처리하고 싶다고 해봅시다. + +`@app.exception_handler()`로 커스텀 예외 핸들러를 추가할 수 있습니다: + +{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *} + +여기서 `/unicorns/yolo`를 요청하면, *경로 처리*가 `UnicornException`을 `raise`합니다. + +하지만 `unicorn_exception_handler`가 이를 처리합니다. + +따라서 HTTP 상태 코드 `418`과 다음 JSON 내용을 가진 깔끔한 오류를 받게 됩니다: + +```JSON +{"message": "Oops! yolo did something. There goes a rainbow..."} +``` + +/// note | 기술 세부사항 + +`from starlette.requests import Request`와 `from starlette.responses import JSONResponse`를 사용할 수도 있습니다. + +**FastAPI**는 개발자의 편의를 위해 `starlette.responses`를 `fastapi.responses`로도 동일하게 제공합니다. 하지만 사용 가능한 대부분의 응답은 Starlette에서 직접 옵니다. `Request`도 마찬가지입니다. + +/// + +## 기본 예외 핸들러 오버라이드하기 { #override-the-default-exception-handlers } + +**FastAPI**에는 몇 가지 기본 예외 핸들러가 있습니다. + +이 핸들러들은 `HTTPException`을 `raise`했을 때, 그리고 요청에 유효하지 않은 데이터가 있을 때 기본 JSON 응답을 반환하는 역할을 합니다. + +이 예외 핸들러들을 여러분의 것으로 오버라이드할 수 있습니다. + +### 요청 검증 예외 오버라이드하기 { #override-request-validation-exceptions } + +요청에 유효하지 않은 데이터가 포함되면, **FastAPI**는 내부적으로 `RequestValidationError`를 `raise`합니다. + +그리고 이에 대한 기본 예외 핸들러도 포함되어 있습니다. + +이를 오버라이드하려면 `RequestValidationError`를 가져오고, `@app.exception_handler(RequestValidationError)`로 예외 핸들러를 데코레이트해 사용하세요. + +예외 핸들러는 `Request`와 예외를 받습니다. + +{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *} + +이제 `/items/foo`로 이동하면, 다음과 같은 기본 JSON 오류 대신: + +```JSON +{ + "detail": [ + { + "loc": [ + "path", + "item_id" + ], + "msg": "value is not a valid integer", + "type": "type_error.integer" + } + ] +} +``` + +다음과 같은 텍스트 버전을 받게 됩니다: + +``` +Validation errors: +Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer +``` + +### `HTTPException` 오류 핸들러 오버라이드하기 { #override-the-httpexception-error-handler } + +같은 방식으로 `HTTPException` 핸들러도 오버라이드할 수 있습니다. + +예를 들어, 이런 오류들에 대해 JSON 대신 일반 텍스트 응답을 반환하고 싶을 수 있습니다: + +{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *} + +/// note | 기술 세부사항 + +`from starlette.responses import PlainTextResponse`를 사용할 수도 있습니다. + +**FastAPI**는 개발자의 편의를 위해 `starlette.responses`를 `fastapi.responses`로도 동일하게 제공합니다. 하지만 사용 가능한 대부분의 응답은 Starlette에서 직접 옵니다. + +/// + +/// warning | 경고 + +`RequestValidationError`에는 검증 오류가 발생한 파일 이름과 줄 정보가 포함되어 있어, 원한다면 관련 정보와 함께 로그에 표시할 수 있다는 점을 유념하세요. + +하지만 이는 단순히 문자열로 변환해 그 정보를 그대로 반환하면 시스템에 대한 일부 정보를 누설할 수 있다는 뜻이기도 합니다. 그래서 여기의 코드는 각 오류를 독립적으로 추출해 보여줍니다. + +/// + +### `RequestValidationError`의 body 사용하기 { #use-the-requestvalidationerror-body } + +`RequestValidationError`에는 유효하지 않은 데이터와 함께 받은 `body`가 포함됩니다. + +앱을 개발하는 동안 body를 로그로 남기고 디버그하거나, 사용자에게 반환하는 등으로 사용할 수 있습니다. + +{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *} + +이제 다음처럼 유효하지 않은 item을 보내보세요: + +```JSON +{ + "title": "towel", + "size": "XL" +} +``` + +받은 body를 포함해 데이터가 유효하지 않다고 알려주는 응답을 받게 됩니다: + +```JSON hl_lines="12-15" +{ + "detail": [ + { + "loc": [ + "body", + "size" + ], + "msg": "value is not a valid integer", + "type": "type_error.integer" + } + ], + "body": { + "title": "towel", + "size": "XL" + } +} +``` + +#### FastAPI의 `HTTPException` vs Starlette의 `HTTPException` { #fastapis-httpexception-vs-starlettes-httpexception } + +**FastAPI**에는 자체 `HTTPException`이 있습니다. + +그리고 **FastAPI**의 `HTTPException` 오류 클래스는 Starlette의 `HTTPException` 오류 클래스를 상속합니다. + +유일한 차이는 **FastAPI**의 `HTTPException`은 `detail` 필드에 JSON으로 변환 가능한 어떤 데이터든 받을 수 있는 반면, Starlette의 `HTTPException`은 문자열만 받을 수 있다는 점입니다. + +따라서 코드에서는 평소처럼 **FastAPI**의 `HTTPException`을 계속 `raise`하면 됩니다. + +하지만 예외 핸들러를 등록할 때는 Starlette의 `HTTPException`에 대해 등록해야 합니다. + +이렇게 하면 Starlette 내부 코드의 어떤 부분, 또는 Starlette 확장/플러그인이 Starlette `HTTPException`을 `raise`하더라도, 여러분의 핸들러가 이를 잡아서 처리할 수 있습니다. + +이 예시에서는 동일한 코드에서 두 `HTTPException`을 모두 사용할 수 있도록, Starlette의 예외를 `StarletteHTTPException`으로 이름을 바꿉니다: + +```Python +from starlette.exceptions import HTTPException as StarletteHTTPException +``` + +### **FastAPI**의 예외 핸들러 재사용하기 { #reuse-fastapis-exception-handlers } + +예외를 사용하면서 **FastAPI**의 동일한 기본 예외 핸들러도 함께 사용하고 싶다면, `fastapi.exception_handlers`에서 기본 예외 핸들러를 가져와 재사용할 수 있습니다: + +{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *} + +이 예시에서는 매우 표현력 있는 메시지로 오류를 출력만 하고 있지만, 요지는 이해하셨을 겁니다. 예외를 사용한 뒤 기본 예외 핸들러를 그대로 재사용할 수 있습니다. diff --git a/docs/ko/docs/tutorial/security/first-steps.md b/docs/ko/docs/tutorial/security/first-steps.md new file mode 100644 index 0000000000..4c9181b31e --- /dev/null +++ b/docs/ko/docs/tutorial/security/first-steps.md @@ -0,0 +1,203 @@ +# 보안 - 첫 단계 { #security-first-steps } + +어떤 도메인에 **backend** API가 있다고 가정해 보겠습니다. + +그리고 다른 도메인에 **frontend**가 있거나, 같은 도메인의 다른 경로에 있거나(또는 모바일 애플리케이션에 있을 수도 있습니다). + +그리고 frontend가 **username**과 **password**를 사용해 backend에 인증할 수 있는 방법이 필요하다고 해봅시다. + +**FastAPI**와 함께 **OAuth2**를 사용해서 이를 구현할 수 있습니다. + +하지만 필요한 작은 정보 조각들을 찾기 위해 길고 긴 전체 스펙을 읽느라 시간을 쓰지 않도록 하겠습니다. + +보안을 처리하기 위해 **FastAPI**가 제공하는 도구들을 사용해 봅시다. + +## 어떻게 보이는지 { #how-it-looks } + +먼저 코드를 그냥 사용해서 어떻게 동작하는지 보고, 그다음에 무슨 일이 일어나는지 이해하러 다시 돌아오겠습니다. + +## `main.py` 만들기 { #create-main-py } + +예제를 파일 `main.py`에 복사하세요: + +{* ../../docs_src/security/tutorial001_an_py39.py *} + +## 실행하기 { #run-it } + +/// info | 정보 + +`python-multipart` 패키지는 `pip install "fastapi[standard]"` 명령을 실행하면 **FastAPI**와 함께 자동으로 설치됩니다. + +하지만 `pip install fastapi` 명령을 사용하면 `python-multipart` 패키지가 기본으로 포함되지 않습니다. + +수동으로 설치하려면, [가상 환경](../../virtual-environments.md){.internal-link target=_blank}을 만들고 활성화한 다음, 아래로 설치하세요: + +```console +$ pip install python-multipart +``` + +이는 **OAuth2**가 `username`과 `password`를 보내기 위해 "form data"를 사용하기 때문입니다. + +/// + +다음으로 예제를 실행하세요: + +
+ +```console +$ fastapi dev main.py + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +## 확인하기 { #check-it } + +대화형 문서로 이동하세요: http://127.0.0.1:8000/docs. + +다음과 비슷한 화면이 보일 것입니다: + + + +/// check | Authorize 버튼! + +반짝이는 새 "Authorize" 버튼이 이미 있습니다. + +그리고 *경로 처리*에는 오른쪽 상단에 클릭할 수 있는 작은 자물쇠가 있습니다. + +/// + +그리고 이를 클릭하면 `username`과 `password`(그리고 다른 선택적 필드들)를 입력할 수 있는 작은 인증 폼이 나타납니다: + + + +/// note | 참고 + +폼에 무엇을 입력하든 아직은 동작하지 않습니다. 하지만 곧 여기까지 구현할 것입니다. + +/// + +물론 이것은 최종 사용자를 위한 frontend는 아니지만, 모든 API를 대화형으로 문서화하는 훌륭한 자동 도구입니다. + +frontend 팀(그게 본인일 수도 있습니다)이 사용할 수 있습니다. + +서드파티 애플리케이션과 시스템에서도 사용할 수 있습니다. + +그리고 동일한 애플리케이션을 디버그하고, 확인하고, 테스트하기 위해 본인이 사용할 수도 있습니다. + +## `password` 플로우 { #the-password-flow } + +이제 조금 돌아가서 이것들이 무엇인지 이해해 봅시다. + +`password` "flow"는 보안과 인증을 처리하기 위해 OAuth2에서 정의한 여러 방식("flows") 중 하나입니다. + +OAuth2는 backend 또는 API가 사용자를 인증하는 서버와 독립적일 수 있도록 설계되었습니다. + +하지만 이 경우에는 같은 **FastAPI** 애플리케이션이 API와 인증을 모두 처리합니다. + +따라서, 단순화된 관점에서 다시 정리해보면: + +* 사용자가 frontend에서 `username`과 `password`를 입력하고 `Enter`를 누릅니다. +* frontend(사용자의 브라우저에서 실행됨)는 해당 `username`과 `password`를 우리 API의 특정 URL로 보냅니다(`tokenUrl="token"`로 선언됨). +* API는 `username`과 `password`를 확인하고 "token"으로 응답합니다(아직 아무것도 구현하지 않았습니다). + * "token"은 나중에 이 사용자를 검증하는 데 사용할 수 있는 어떤 내용이 담긴 문자열일 뿐입니다. + * 보통 token은 일정 시간이 지나면 만료되도록 설정합니다. + * 그래서 사용자는 나중에 어느 시점엔 다시 로그인해야 합니다. + * 그리고 token이 도난당하더라도 위험이 더 낮습니다. 대부분의 경우 영구적으로 항상 동작하는 키와는 다릅니다. +* frontend는 그 token을 임시로 어딘가에 저장합니다. +* 사용자가 frontend에서 클릭해서 frontend 웹 앱의 다른 섹션으로 이동합니다. +* frontend는 API에서 더 많은 데이터를 가져와야 합니다. + * 하지만 그 특정 endpoint에는 인증이 필요합니다. + * 그래서 우리 API에 인증하기 위해 `Authorization` 헤더를, 값은 `Bearer `에 token을 더한 형태로 보냅니다. + * token에 `foobar`가 들어 있다면 `Authorization` 헤더의 내용은 `Bearer foobar`가 됩니다. + +## **FastAPI**의 `OAuth2PasswordBearer` { #fastapis-oauth2passwordbearer } + +**FastAPI**는 이런 보안 기능을 구현하기 위해, 서로 다른 추상화 수준에서 여러 도구를 제공합니다. + +이 예제에서는 **OAuth2**의 **Password** 플로우와 **Bearer** token을 사용합니다. 이를 위해 `OAuth2PasswordBearer` 클래스를 사용합니다. + +/// info | 정보 + +"bearer" token만이 유일한 선택지는 아닙니다. + +하지만 이 사용 사례에는 가장 적합한 선택입니다. + +또한 OAuth2 전문가로서 왜 다른 옵션이 더 적합한지 정확히 아는 경우가 아니라면, 대부분의 사용 사례에도 가장 적합할 가능성이 큽니다. + +그런 경우를 위해서도 **FastAPI**는 이를 구성할 수 있는 도구를 제공합니다. + +/// + +`OAuth2PasswordBearer` 클래스의 인스턴스를 만들 때 `tokenUrl` 파라미터를 전달합니다. 이 파라미터에는 클라이언트(사용자의 브라우저에서 실행되는 frontend)가 token을 받기 위해 `username`과 `password`를 보낼 URL이 들어 있습니다. + +{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *} + +/// tip | 팁 + +여기서 `tokenUrl="token"`은 아직 만들지 않은 상대 URL `token`을 가리킵니다. 상대 URL이므로 `./token`과 동일합니다. + +상대 URL을 사용하므로, 예를 들어 API가 `https://example.com/`에 있다면 `https://example.com/token`을 가리킵니다. 하지만 API가 `https://example.com/api/v1/`에 있다면 `https://example.com/api/v1/token`을 가리킵니다. + +상대 URL을 사용하는 것은 [프록시 뒤에서](../../advanced/behind-a-proxy.md){.internal-link target=_blank} 같은 고급 사용 사례에서도 애플리케이션이 계속 동작하도록 보장하는 데 중요합니다. + +/// + +이 파라미터는 그 endpoint / *경로 처리*를 만들지는 않지만, URL `/token`이 클라이언트가 token을 얻기 위해 사용해야 할 URL이라고 선언합니다. 이 정보는 OpenAPI에 사용되고, 이어서 대화형 API 문서 시스템에서도 사용됩니다. + +곧 실제 경로 처리를 만들 것입니다. + +/// info | 정보 + +엄격한 "Pythonista"라면 `token_url` 대신 `tokenUrl` 같은 파라미터 이름 스타일이 마음에 들지 않을 수도 있습니다. + +이는 OpenAPI 스펙에서 사용하는 이름과 동일하게 맞춘 것이기 때문입니다. 그래서 이런 보안 스킴에 대해 더 조사해야 할 때, 그대로 복사해서 붙여 넣어 더 많은 정보를 찾을 수 있습니다. + +/// + +`oauth2_scheme` 변수는 `OAuth2PasswordBearer`의 인스턴스이지만, "callable"이기도 합니다. + +다음처럼 호출될 수 있습니다: + +```Python +oauth2_scheme(some, parameters) +``` + +따라서 `Depends`와 함께 사용할 수 있습니다. + +### 사용하기 { #use-it } + +이제 `Depends`로 `oauth2_scheme`를 의존성에 전달할 수 있습니다. + +{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *} + +이 의존성은 `str`을 제공하고, 그 값은 *경로 처리 함수*의 파라미터 `token`에 할당됩니다. + +**FastAPI**는 이 의존성을 사용해 OpenAPI 스키마(및 자동 API 문서)에 "security scheme"를 정의할 수 있다는 것을 알게 됩니다. + +/// info | 기술 세부사항 + +**FastAPI**는 (의존성에 선언된) `OAuth2PasswordBearer` 클래스를 사용해 OpenAPI에서 보안 스킴을 정의할 수 있다는 것을 알고 있습니다. 이는 `OAuth2PasswordBearer`가 `fastapi.security.oauth2.OAuth2`를 상속하고, 이것이 다시 `fastapi.security.base.SecurityBase`를 상속하기 때문입니다. + +OpenAPI(및 자동 API 문서)와 통합되는 모든 보안 유틸리티는 `SecurityBase`를 상속하며, 그래서 **FastAPI**가 이를 OpenAPI에 어떻게 통합할지 알 수 있습니다. + +/// + +## 무엇을 하는지 { #what-it-does } + +요청에서 `Authorization` 헤더를 찾아, 값이 `Bearer `에 어떤 token이 붙은 형태인지 확인한 뒤, 그 token을 `str`로 반환합니다. + +`Authorization` 헤더가 없거나, 값에 `Bearer ` token이 없다면, 곧바로 401 상태 코드 오류(`UNAUTHORIZED`)로 응답합니다. + +오류를 반환하기 위해 token이 존재하는지 직접 확인할 필요조차 없습니다. 함수가 실행되었다면 그 token에는 `str`이 들어 있다고 확신할 수 있습니다. + +대화형 문서에서 이미 시도해 볼 수 있습니다: + + + +아직 token의 유효성을 검증하진 않지만, 이것만으로도 시작은 된 셈입니다. + +## 요약 { #recap } + +즉, 추가로 3~4줄만으로도 이미 원시적인 형태의 보안을 갖추게 됩니다. diff --git a/docs/ko/docs/tutorial/security/index.md b/docs/ko/docs/tutorial/security/index.md new file mode 100644 index 0000000000..2320b06571 --- /dev/null +++ b/docs/ko/docs/tutorial/security/index.md @@ -0,0 +1,106 @@ +# 보안 { #security } + +보안, 인증(authentication), 인가(authorization)를 처리하는 방법은 매우 다양합니다. + +그리고 보통 복잡하고 "어려운" 주제이기도 합니다. + +많은 프레임워크와 시스템에서 보안과 인증만 처리하는 데도 큰 노력과 코드가 필요합니다(많은 경우 작성된 전체 코드의 50% 이상이 될 수도 있습니다). + +**FastAPI**는 모든 보안 명세를 전부 공부하고 배울 필요 없이, 표준적인 방식으로 쉽고 빠르게 **보안(Security)** 을 다룰 수 있도록 여러 도구를 제공합니다. + +하지만 먼저, 몇 가지 작은 개념을 확인해 보겠습니다. + +## 급하신가요? { #in-a-hurry } + +이 용어들에 관심이 없고 사용자명과 비밀번호 기반 인증을 사용한 보안을 *지금 당장* 추가하기만 하면 된다면, 다음 장들로 넘어가세요. + +## OAuth2 { #oauth2 } + +OAuth2는 인증과 인가를 처리하는 여러 방법을 정의하는 명세입니다. + +상당히 방대한 명세이며 여러 복잡한 사용 사례를 다룹니다. + +"제3자"를 사용해 인증하는 방법도 포함합니다. + +바로 `"Facebook, Google, X (Twitter), GitHub로 로그인"` 같은 시스템들이 내부적으로 사용하는 방식입니다. + +### OAuth 1 { #oauth-1 } + +OAuth 1도 있었는데, 이는 OAuth2와 매우 다르고 통신을 암호화하는 방법까지 직접 명세에 포함했기 때문에 더 복잡했습니다. + +요즘에는 그다지 인기 있거나 사용되지는 않습니다. + +OAuth2는 통신을 어떻게 암호화할지는 명세하지 않고, 애플리케이션이 HTTPS로 제공될 것을 기대합니다. + +/// tip | 팁 + +**배포**에 대한 섹션에서 Traefik과 Let's Encrypt를 사용해 무료로 HTTPS를 설정하는 방법을 볼 수 있습니다. + +/// + +## OpenID Connect { #openid-connect } + +OpenID Connect는 **OAuth2**를 기반으로 한 또 다른 명세입니다. + +OAuth2에서 비교적 모호한 부분을 일부 구체화하여 상호 운용성을 높이려는 확장입니다. + +예를 들어, Google 로그인은 OpenID Connect를 사용합니다(내부적으로는 OAuth2를 사용). + +하지만 Facebook 로그인은 OpenID Connect를 지원하지 않습니다. 자체적인 변형의 OAuth2를 사용합니다. + +### OpenID("OpenID Connect"가 아님) { #openid-not-openid-connect } + +"OpenID"라는 명세도 있었습니다. 이는 **OpenID Connect**와 같은 문제를 해결하려고 했지만, OAuth2를 기반으로 하지 않았습니다. + +따라서 완전히 별도의 추가 시스템이었습니다. + +요즘에는 그다지 인기 있거나 사용되지는 않습니다. + +## OpenAPI { #openapi } + +OpenAPI(이전에는 Swagger로 알려짐)는 API를 구축하기 위한 공개 명세입니다(현재 Linux Foundation의 일부). + +**FastAPI**는 **OpenAPI**를 기반으로 합니다. + +이 덕분에 여러 자동 대화형 문서 인터페이스, 코드 생성 등과 같은 기능을 사용할 수 있습니다. + +OpenAPI에는 여러 보안 "scheme"을 정의하는 방법이 있습니다. + +이를 사용하면 이러한 대화형 문서 시스템을 포함해, 표준 기반 도구들을 모두 활용할 수 있습니다. + +OpenAPI는 다음 보안 scheme들을 정의합니다: + +* `apiKey`: 다음에서 전달될 수 있는 애플리케이션 전용 키: + * 쿼리 파라미터 + * 헤더 + * 쿠키 +* `http`: 표준 HTTP 인증 시스템, 예: + * `bearer`: `Authorization` 헤더에 `Bearer ` + 토큰 값을 넣는 방식. OAuth2에서 유래했습니다. + * HTTP Basic 인증 + * HTTP Digest 등 +* `oauth2`: 보안을 처리하는 모든 OAuth2 방식(이를 "flow"라고 부릅니다). + * 이 flow들 중 여러 개는 OAuth 2.0 인증 제공자(예: Google, Facebook, X (Twitter), GitHub 등)를 구축하는 데 적합합니다: + * `implicit` + * `clientCredentials` + * `authorizationCode` + * 하지만 같은 애플리케이션에서 직접 인증을 처리하는 데 완벽하게 사용할 수 있는 특정 "flow"도 하나 있습니다: + * `password`: 다음 장들에서 이에 대한 예시를 다룹니다. +* `openIdConnect`: OAuth2 인증 데이터를 자동으로 탐색(discover)하는 방법을 정의합니다. + * 이 자동 탐색은 OpenID Connect 명세에서 정의됩니다. + + +/// tip | 팁 + +Google, Facebook, X (Twitter), GitHub 등 다른 인증/인가 제공자를 통합하는 것도 가능하며 비교적 쉽습니다. + +가장 복잡한 문제는 그런 인증/인가 제공자 자체를 구축하는 것이지만, **FastAPI**는 어려운 작업을 대신 처리해 주면서 이를 쉽게 할 수 있는 도구를 제공합니다. + +/// + +## **FastAPI** 유틸리티 { #fastapi-utilities } + +FastAPI는 `fastapi.security` 모듈에서 각 보안 scheme에 대한 여러 도구를 제공하며, 이러한 보안 메커니즘을 더 쉽게 사용할 수 있게 해줍니다. + +다음 장들에서는 **FastAPI**가 제공하는 도구를 사용해 API에 보안을 추가하는 방법을 보게 될 것입니다. + +또한 대화형 문서 시스템에 어떻게 자동으로 통합되는지도 확인하게 됩니다. diff --git a/docs/ko/llm-prompt.md b/docs/ko/llm-prompt.md index df807c9496..be2f5be5de 100644 --- a/docs/ko/llm-prompt.md +++ b/docs/ko/llm-prompt.md @@ -8,6 +8,7 @@ Language code: ko. - Use polite, instructional Korean (e.g. 합니다/하세요 style). - Keep the tone consistent with the existing Korean FastAPI docs. +- Do not translate “You” literally as “당신”. Use “여러분” where appropriate, or omit the subject if it sounds more natural in Korean. ### Headings @@ -32,6 +33,9 @@ Use the following preferred translations when they apply in documentation prose: - response (HTTP): 응답 - path operation: 경로 처리 - path operation function: 경로 처리 함수 +- app: 애플리케이션 +- command: 명령어 +- burger: 햄버거 (NOT 버거) ### `///` admonitions diff --git a/docs/pt/docs/_llm-test.md b/docs/pt/docs/_llm-test.md index 3da5e8a71d..b59292f47c 100644 --- a/docs/pt/docs/_llm-test.md +++ b/docs/pt/docs/_llm-test.md @@ -1,8 +1,8 @@ # Arquivo de teste de LLM { #llm-test-file } -Este documento testa se o LLM, que traduz a documentação, entende o `general_prompt` em `scripts/translate.py` e o prompt específico do idioma em `docs/{language code}/llm-prompt.md`. O prompt específico do idioma é anexado ao `general_prompt`. +Este documento testa se o LLM, que traduz a documentação, entende o `general_prompt` em `scripts/translate.py` e o prompt específico do idioma em `docs/{language code}/llm-prompt.md`. O prompt específico do idioma é anexado ao `general_prompt`. -Os testes adicionados aqui serão vistos por todos os autores dos prompts específicos de idioma. +Os testes adicionados aqui serão vistos por todos os designers dos prompts específicos de idioma. Use da seguinte forma: @@ -23,7 +23,7 @@ Este é um trecho de código: `foo`. E este é outro trecho de código: `bar`. E //// -//// tab | Informações +//// tab | Informação O conteúdo dos trechos de código deve ser deixado como está. @@ -45,9 +45,9 @@ O LLM provavelmente vai traduzir isso errado. O interessante é apenas se ele ma //// -//// tab | Informações +//// tab | Informação -O autor do prompt pode escolher se deseja converter aspas neutras em aspas tipográficas. Também é aceitável deixá-las como estão. +O designer do prompt pode escolher se quer converter aspas neutras em aspas tipográficas. Também é aceitável deixá-las como estão. Veja, por exemplo, a seção `### Quotes` em `docs/de/llm-prompt.md`. @@ -67,7 +67,7 @@ Pesado: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you ha //// -//// tab | Informações +//// tab | Informação ... No entanto, as aspas dentro de trechos de código devem permanecer como estão. @@ -95,24 +95,24 @@ $ fastapi run GTD -* lt -* XWT -* PSGI +* GTD +* lt +* XWT +* PSGI ### O abbr fornece uma explicação { #the-abbr-gives-an-explanation } @@ -209,12 +209,12 @@ Aqui estão algumas coisas envolvidas em elementos HTML "abbr" (algumas são inv ### O abbr fornece uma frase completa e uma explicação { #the-abbr-gives-a-full-phrase-and-an-explanation } -* MDN -* I/O. +* MDN +* I/O. //// -//// tab | Informações +//// tab | Informação Os atributos "title" dos elementos "abbr" são traduzidos seguindo algumas instruções específicas. @@ -228,7 +228,7 @@ Veja a seção `### HTML abbr elements` no prompt geral em `scripts/translate.py //// tab | Teste -### Desenvolver uma aplicação web - um tutorial { #develop-a-webapp-a-tutorial } +### Desenvolver uma webapp - um tutorial { #develop-a-webapp-a-tutorial } Olá. @@ -242,7 +242,7 @@ Olá novamente. //// -//// tab | Informações +//// tab | Informação A única regra rígida para títulos é que o LLM deixe a parte do hash dentro de chaves inalterada, o que garante que os links não quebrem. @@ -494,9 +494,9 @@ Para algumas instruções específicas do idioma, veja, por exemplo, a seção ` //// -//// tab | Informações +//// tab | Informação -Esta é uma lista não completa e não normativa de termos (principalmente) técnicos vistos na documentação. Pode ser útil para o autor do prompt descobrir para quais termos o LLM precisa de uma ajudinha. Por exemplo, quando ele continua revertendo uma boa tradução para uma tradução subótima. Ou quando tem problemas para conjugar/declinar um termo no seu idioma. +Esta é uma lista não completa e não normativa de termos (principalmente) técnicos vistos na documentação. Pode ser útil para o designer do prompt descobrir para quais termos o LLM precisa de uma ajudinha. Por exemplo, quando ele continua revertendo uma boa tradução para uma tradução subótima. Ou quando tem problemas para conjugar/declinar um termo no seu idioma. Veja, por exemplo, a seção `### List of English terms and their preferred German translations` em `docs/de/llm-prompt.md`. diff --git a/docs/pt/docs/advanced/path-operation-advanced-configuration.md b/docs/pt/docs/advanced/path-operation-advanced-configuration.md index e1c3e5ab89..b3af116a28 100644 --- a/docs/pt/docs/advanced/path-operation-advanced-configuration.md +++ b/docs/pt/docs/advanced/path-operation-advanced-configuration.md @@ -10,7 +10,7 @@ Se você não é um "especialista" no OpenAPI, você provavelmente não precisa Você pode definir o `operationId` do OpenAPI que será utilizado na sua *operação de rota* com o parâmetro `operation_id`. -Você precisa ter certeza que ele é único para cada operação. +Você deveria ter certeza que ele é único para cada operação. {* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *} @@ -18,13 +18,13 @@ Você precisa ter certeza que ele é único para cada operação. Se você quiser utilizar o nome das funções da sua API como `operationId`s, você pode iterar sobre todos esses nomes e sobrescrever o `operation_id` em cada *operação de rota* utilizando o `APIRoute.name` dela. -Você deve fazer isso depois de adicionar todas as suas *operações de rota*. +Você deveria fazer isso depois de adicionar todas as suas *operações de rota*. {* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *} /// tip | Dica -Se você chamar `app.openapi()` manualmente, os `operationId`s devem ser atualizados antes dessa chamada. +Se você chamar `app.openapi()` manualmente, você deveria atualizar os `operationId`s antes dessa chamada. /// @@ -44,11 +44,11 @@ Para excluir uma *operação de rota* do esquema OpenAPI gerado (e por consequê ## Descrição avançada a partir de docstring { #advanced-description-from-docstring } -Você pode limitar as linhas utilizadas a partir de uma docstring de uma *função de operação de rota* para o OpenAPI. +Você pode limitar as linhas utilizadas a partir da docstring de uma *função de operação de rota* para o OpenAPI. -Adicionar um `\f` (um caractere de escape para alimentação de formulário) faz com que o **FastAPI** restrinja a saída utilizada pelo OpenAPI até esse ponto. +Adicionar um `\f` (um caractere de escape para "form feed") faz com que o **FastAPI** trunque a saída usada para o OpenAPI até esse ponto. -Ele não será mostrado na documentação, mas outras ferramentas (como o Sphinx) serão capazes de utilizar o resto do texto. +Ele não será mostrado na documentação, mas outras ferramentas (como o Sphinx) serão capazes de utilizar o resto. {* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *} @@ -131,70 +131,38 @@ E se você olhar o esquema OpenAPI resultante (na rota `/openapi.json` da sua AP ### Esquema de *operação de rota* do OpenAPI personalizado { #custom-openapi-path-operation-schema } -O dicionário em `openapi_extra` vai ter todos os seus níveis mesclados dentro do esquema OpenAPI gerado automaticamente para a *operação de rota*. +O dicionário em `openapi_extra` vai ser mesclado profundamente com o esquema OpenAPI gerado automaticamente para a *operação de rota*. -Então, você pode adicionar dados extras para o esquema gerado automaticamente. +Então, você pode adicionar dados extras ao esquema gerado automaticamente. -Por exemplo, você poderia optar por ler e validar a requisição com seu próprio código, sem utilizar funcionalidades automatizadas do FastAPI com o Pydantic, mas você ainda pode quere definir a requisição no esquema OpenAPI. +Por exemplo, você poderia decidir ler e validar a requisição com seu próprio código, sem usar as funcionalidades automáticas do FastAPI com o Pydantic, mas ainda assim querer definir a requisição no esquema OpenAPI. Você pode fazer isso com `openapi_extra`: {* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *} -Nesse exemplo, nós não declaramos nenhum modelo do Pydantic. Na verdade, o corpo da requisição não está nem mesmo analisado como JSON, ele é lido diretamente como `bytes` e a função `magic_data_reader()` seria a responsável por analisar ele de alguma forma. +Nesse exemplo, nós não declaramos nenhum modelo do Pydantic. Na verdade, o corpo da requisição não está nem mesmo analisado como JSON, ele é lido diretamente como `bytes`, e a função `magic_data_reader()` seria a responsável por analisar ele de alguma forma. De toda forma, nós podemos declarar o esquema esperado para o corpo da requisição. ### Tipo de conteúdo do OpenAPI personalizado { #custom-openapi-content-type } -Utilizando esse mesmo truque, você pode utilizar um modelo Pydantic para definir o JSON Schema que é então incluído na seção do esquema personalizado do OpenAPI na *operação de rota*. +Utilizando esse mesmo truque, você pode usar um modelo Pydantic para definir o JSON Schema que é então incluído na seção do esquema personalizado do OpenAPI na *operação de rota*. -E você pode fazer isso até mesmo quando os dados da requisição não seguem o formato JSON. +E você pode fazer isso até mesmo quando o tipo de dados na requisição não é JSON. -Por exemplo, nesta aplicação nós não usamos a funcionalidade integrada ao FastAPI de extrair o JSON Schema dos modelos Pydantic nem a validação automática do JSON. Na verdade, estamos declarando o tipo do conteúdo da requisição como YAML, em vez de JSON: - -//// tab | Pydantic v2 +Por exemplo, nesta aplicação nós não usamos a funcionalidade integrada ao FastAPI de extrair o JSON Schema dos modelos Pydantic nem a validação automática para JSON. Na verdade, estamos declarando o tipo de conteúdo da requisição como YAML, em vez de JSON: {* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *} -//// +Entretanto, mesmo que não utilizemos a funcionalidade integrada por padrão, ainda estamos usando um modelo Pydantic para gerar um JSON Schema manualmente para os dados que queremos receber em YAML. -//// tab | Pydantic v1 +Então utilizamos a requisição diretamente e extraímos o corpo como `bytes`. Isso significa que o FastAPI não vai sequer tentar analisar o payload da requisição como JSON. -{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1_py39.py hl[15:20, 22] *} - -//// - -/// info | Informação - -Na versão 1 do Pydantic, o método para obter o JSON Schema de um modelo é `Item.schema()`, na versão 2 do Pydantic, o método é `Item.model_json_schema()`. - -/// - -Entretanto, mesmo que não utilizemos a funcionalidade integrada por padrão, ainda estamos usando um modelo Pydantic para gerar um JSON Schema manualmente para os dados que queremos receber no formato YAML. - -Então utilizamos a requisição diretamente, e extraímos o corpo como `bytes`. Isso significa que o FastAPI não vai sequer tentar analisar o corpo da requisição como JSON. - -E então no nosso código, nós analisamos o conteúdo YAML diretamente, e estamos utilizando o mesmo modelo Pydantic para validar o conteúdo YAML: - -//// tab | Pydantic v2 +E então no nosso código, nós analisamos o conteúdo YAML diretamente e, em seguida, estamos usando novamente o mesmo modelo Pydantic para validar o conteúdo YAML: {* ../../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 | Informação - -Na versão 1 do Pydantic, o método para analisar e validar um objeto era `Item.parse_obj()`, na versão 2 do Pydantic, o método é chamado de `Item.model_validate()`. - -/// - /// tip | Dica Aqui reutilizamos o mesmo modelo do Pydantic. diff --git a/docs/pt/docs/advanced/settings.md b/docs/pt/docs/advanced/settings.md index 6f5b7feae7..28411269bb 100644 --- a/docs/pt/docs/advanced/settings.md +++ b/docs/pt/docs/advanced/settings.md @@ -46,12 +46,6 @@ $ pip install "fastapi[all]" -/// info | Informação - -No Pydantic v1 ele vinha incluído no pacote principal. Agora é distribuído como um pacote independente para que você possa optar por instalá-lo ou não, caso não precise dessa funcionalidade. - -/// - ### Criar o objeto `Settings` { #create-the-settings-object } Importe `BaseSettings` do Pydantic e crie uma subclasse, muito parecido com um modelo do Pydantic. @@ -60,24 +54,8 @@ Da mesma forma que com modelos do Pydantic, você declara atributos de classe co Você pode usar as mesmas funcionalidades e ferramentas de validação que usa em modelos do Pydantic, como diferentes tipos de dados e validações adicionais com `Field()`. -//// tab | Pydantic v2 - {* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *} -//// - -//// tab | Pydantic v1 - -/// info | Informação - -No Pydantic v1 você importaria `BaseSettings` diretamente de `pydantic` em vez de `pydantic_settings`. - -/// - -{* ../../docs_src/settings/tutorial001_pv1_py39.py hl[2,5:8,11] *} - -//// - /// tip | Dica Se você quer algo rápido para copiar e colar, não use este exemplo, use o último abaixo. @@ -215,8 +193,6 @@ APP_NAME="ChimichangApp" E então atualizar seu `config.py` com: -//// tab | Pydantic v2 - {* ../../docs_src/settings/app03_an_py39/config.py hl[9] *} /// tip | Dica @@ -225,26 +201,6 @@ O atributo `model_config` é usado apenas para configuração do Pydantic. Você /// -//// - -//// tab | Pydantic v1 - -{* ../../docs_src/settings/app03_an_py39/config_pv1.py hl[9:10] *} - -/// tip | Dica - -A classe `Config` é usada apenas para configuração do Pydantic. Você pode ler mais em Pydantic Model Config. - -/// - -//// - -/// info | Informação - -Na versão 1 do Pydantic a configuração era feita em uma classe interna `Config`, na versão 2 do Pydantic é feita em um atributo `model_config`. Esse atributo recebe um `dict`, e para ter autocompletar e erros inline você pode importar e usar `SettingsConfigDict` para definir esse `dict`. - -/// - Aqui definimos a configuração `env_file` dentro da sua classe `Settings` do Pydantic e definimos o valor como o nome do arquivo dotenv que queremos usar. ### Criando o `Settings` apenas uma vez com `lru_cache` { #creating-the-settings-only-once-with-lru-cache } diff --git a/docs/pt/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md b/docs/pt/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md index 2a2659a03d..0995e10285 100644 --- a/docs/pt/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md +++ b/docs/pt/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md @@ -2,21 +2,23 @@ Se você tem uma aplicação FastAPI antiga, pode estar usando o Pydantic versão 1. -O FastAPI tem suporte ao Pydantic v1 ou v2 desde a versão 0.100.0. +O FastAPI versão 0.100.0 tinha suporte ao Pydantic v1 ou v2. Ele usaria aquele que você tivesse instalado. -Se você tiver o Pydantic v2 instalado, ele será utilizado. Se, em vez disso, tiver o Pydantic v1, será ele que será utilizado. +O FastAPI versão 0.119.0 introduziu suporte parcial ao Pydantic v1 a partir de dentro do Pydantic v2 (como `pydantic.v1`), para facilitar a migração para o v2. -O Pydantic v1 está agora descontinuado e o suporte a ele será removido nas próximas versões do FastAPI, você deveria migrar para o Pydantic v2. Assim, você terá as funcionalidades, melhorias e correções mais recentes. +O FastAPI 0.126.0 removeu o suporte ao Pydantic v1, enquanto ainda oferece suporte a `pydantic.v1` por mais algum tempo. /// warning | Atenção -Além disso, a equipe do Pydantic interrompeu o suporte ao Pydantic v1 para as versões mais recentes do Python, a partir do **Python 3.14**. +A equipe do Pydantic interrompeu o suporte ao Pydantic v1 para as versões mais recentes do Python, a partir do **Python 3.14**. + +Isso inclui `pydantic.v1`, que não é mais suportado no Python 3.14 e superiores. Se quiser usar as funcionalidades mais recentes do Python, você precisará garantir que usa o Pydantic v2. /// -Se você tem uma aplicação FastAPI antiga com Pydantic v1, aqui vou mostrar como migrá-la para o Pydantic v2 e as **novas funcionalidades no FastAPI 0.119.0** para ajudar em uma migração gradual. +Se você tem uma aplicação FastAPI antiga com Pydantic v1, aqui vou mostrar como migrá-la para o Pydantic v2, e as **funcionalidades no FastAPI 0.119.0** para ajudar em uma migração gradual. ## Guia oficial { #official-guide } @@ -44,7 +46,7 @@ Depois disso, você pode rodar os testes e verificar se tudo funciona. Se funcio ## Pydantic v1 no v2 { #pydantic-v1-in-v2 } -O Pydantic v2 inclui tudo do Pydantic v1 como um submódulo `pydantic.v1`. +O Pydantic v2 inclui tudo do Pydantic v1 como um submódulo `pydantic.v1`. Mas isso não é mais suportado em versões acima do Python 3.13. Isso significa que você pode instalar a versão mais recente do Pydantic v2 e importar e usar os componentes antigos do Pydantic v1 a partir desse submódulo, como se tivesse o Pydantic v1 antigo instalado. @@ -66,7 +68,7 @@ Tenha em mente que, como a equipe do Pydantic não oferece mais suporte ao Pydan ### Pydantic v1 e v2 na mesma aplicação { #pydantic-v1-and-v2-on-the-same-app } -Não é suportado pelo Pydantic ter um modelo do Pydantic v2 com campos próprios definidos como modelos do Pydantic v1, ou vice-versa. +Não é **suportado** pelo Pydantic ter um modelo do Pydantic v2 com campos próprios definidos como modelos do Pydantic v1, ou vice-versa. ```mermaid graph TB @@ -86,7 +88,7 @@ graph TB style V2Field fill:#f9fff3 ``` -...but, you can have separated models using Pydantic v1 and v2 in the same app. +...mas, você pode ter modelos separados usando Pydantic v1 e v2 na mesma aplicação. ```mermaid graph TB @@ -106,7 +108,7 @@ graph TB style V2Field fill:#f9fff3 ``` -Em alguns casos, é até possível ter modelos Pydantic v1 e v2 na mesma operação de rota na sua aplicação FastAPI: +Em alguns casos, é até possível ter modelos Pydantic v1 e v2 na mesma **operação de rota** na sua aplicação FastAPI: {* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *} @@ -122,7 +124,7 @@ Se você precisar usar algumas das ferramentas específicas do FastAPI para par /// tip | Dica -Primeiro tente com o `bump-pydantic`; se seus testes passarem e isso funcionar, então você concluiu tudo com um único comando. ✨ +Primeiro tente com o `bump-pydantic`, se seus testes passarem e isso funcionar, então você concluiu tudo com um único comando. ✨ /// diff --git a/docs/pt/docs/how-to/separate-openapi-schemas.md b/docs/pt/docs/how-to/separate-openapi-schemas.md index 8855934fd9..f757025a09 100644 --- a/docs/pt/docs/how-to/separate-openapi-schemas.md +++ b/docs/pt/docs/how-to/separate-openapi-schemas.md @@ -1,8 +1,8 @@ # Esquemas OpenAPI Separados para Entrada e Saída ou Não { #separate-openapi-schemas-for-input-and-output-or-not } -Ao usar **Pydantic v2**, o OpenAPI gerado é um pouco mais exato e **correto** do que antes. 😎 +Desde que o **Pydantic v2** foi lançado, o OpenAPI gerado é um pouco mais exato e **correto** do que antes. 😎 -Inclusive, em alguns casos, ele terá até **dois JSON Schemas** no OpenAPI para o mesmo modelo Pydantic, para entrada e saída, dependendo se eles possuem **valores padrão**. +De fato, em alguns casos, ele terá até **dois JSON Schemas** no OpenAPI para o mesmo modelo Pydantic, para entrada e saída, dependendo se eles possuem **valores padrão**. Vamos ver como isso funciona e como alterar se for necessário. @@ -95,10 +95,8 @@ O suporte para `separate_input_output_schemas` foi adicionado no FastAPI `0.102. ### Mesmo Esquema para Modelos de Entrada e Saída na Documentação { #same-schema-for-input-and-output-models-in-docs } -E agora haverá um único esquema para entrada e saída para o modelo, apenas `Item`, e `description` **não será obrigatório**: +E agora haverá um único esquema para entrada e saída para o modelo, apenas `Item`, e ele terá `description` como **não obrigatório**:
- -Esse é o mesmo comportamento do Pydantic v1. 🤓 diff --git a/docs/pt/docs/index.md b/docs/pt/docs/index.md index 0428c3a798..4e3be586da 100644 --- a/docs/pt/docs/index.md +++ b/docs/pt/docs/index.md @@ -40,8 +40,8 @@ Os recursos chave são: * **Rápido**: alta performance, equivalente a **NodeJS** e **Go** (graças ao Starlette e Pydantic). [Um dos frameworks mais rápidos disponíveis](#performance). * **Rápido para codar**: Aumenta a velocidade para desenvolver recursos entre 200% a 300%. * * **Poucos bugs**: Reduz cerca de 40% de erros induzidos por humanos (desenvolvedores). * -* **Intuitivo**: Grande suporte a _IDEs_. Preenchimento automático em todos os lugares. Menos tempo debugando. -* **Fácil**: Projetado para ser fácil de aprender e usar. Menos tempo lendo documentação. +* **Intuitivo**: Grande suporte a editores. Completação em todos os lugares. Menos tempo debugando. +* **Fácil**: Projetado para ser fácil de aprender e usar. Menos tempo lendo docs. * **Enxuto**: Minimize duplicação de código. Múltiplas funcionalidades para cada declaração de parâmetro. Menos bugs. * **Robusto**: Tenha código pronto para produção. E com documentação interativa automática. * **Baseado em padrões**: Baseado em (e totalmente compatível com) os padrões abertos para APIs: OpenAPI (anteriormente conhecido como Swagger) e JSON Schema. @@ -73,7 +73,7 @@ Os recursos chave são: ## Opiniões { #opinions } -"*[...] Estou usando **FastAPI** muito esses dias. [...] Estou na verdade planejando utilizar ele em todos os times de **serviços _Machine Learning_ na Microsoft**. Alguns deles estão sendo integrados no _core_ do produto **Windows** e alguns produtos **Office**.*" +"_[...] Estou usando **FastAPI** muito esses dias. [...] Estou na verdade planejando utilizar ele em todos os times de **serviços ML na Microsoft**. Alguns deles estão sendo integrados no _core_ do produto **Windows** e alguns produtos **Office**._"
Kabir Khan - Microsoft (ref)
@@ -91,39 +91,45 @@ Os recursos chave são: --- -"*Estou extremamente entusiasmado com o **FastAPI**. É tão divertido!*" +"_Estou muito entusiasmado com o **FastAPI**. É tão divertido!_" -
Brian Okken - Python Bytes podcaster (ref)
+
Brian Okken - Python Bytes apresentador do podcast (ref)
--- -"*Honestamente, o que você construiu parece super sólido e rebuscado. De muitas formas, eu queria que o **Hug** fosse assim - é realmente inspirador ver alguém que construiu ele.*" +"_Honestamente, o que você construiu parece super sólido e refinado. De muitas formas, é o que eu queria que o **Hug** fosse - é realmente inspirador ver alguém construir isso._"
Timothy Crosley - criador doHug (ref)
--- -"*Se você está procurando aprender um **_framework_ moderno** para construir aplicações _REST_, dê uma olhada no **FastAPI** [...] É rápido, fácil de usar e fácil de aprender [...]*" +"_Se você está procurando aprender um **framework moderno** para construir APIs REST, dê uma olhada no **FastAPI** [...] É rápido, fácil de usar e fácil de aprender [...]_" -"*Nós trocamos nossas **APIs** por **FastAPI** [...] Acredito que vocês gostarão dele [...]*" +"_Nós trocamos nossas **APIs** por **FastAPI** [...] Acredito que você gostará dele [...]_"
Ines Montani - Matthew Honnibal - fundadores da Explosion AI - criadores da spaCy (ref) - (ref)
--- -"_Se alguém estiver procurando construir uma API Python para produção, eu recomendaria fortemente o **FastAPI**. Ele é **lindamente projetado**, **simples de usar** e **altamente escalável**. Ele se tornou um **componente chave** para a nossa estratégia API first de desenvolvimento e está impulsionando diversas automações e serviços, como o nosso Virtual TAC Engineer._" +"_Se alguém estiver procurando construir uma API Python para produção, eu recomendaria fortemente o **FastAPI**. Ele é **lindamente projetado**, **simples de usar** e **altamente escalável**, e se tornou um **componente chave** para a nossa estratégia de desenvolvimento API first, impulsionando diversas automações e serviços, como o nosso Virtual TAC Engineer._"
Deon Pillsbury - Cisco (ref)
--- +## Mini documentário do FastAPI { #fastapi-mini-documentary } + +Há um mini documentário do FastAPI lançado no fim de 2025, você pode assisti-lo online: + +FastAPI Mini Documentary + ## **Typer**, o FastAPI das interfaces de linhas de comando { #typer-the-fastapi-of-clis } -Se você estiver construindo uma aplicação CLI para ser utilizada em um terminal ao invés de uma aplicação web, dê uma olhada no **Typer**. +Se você estiver construindo uma aplicação CLI para ser utilizada no terminal ao invés de uma API web, dê uma olhada no **Typer**. -**Typer** é o irmão menor do FastAPI. E seu propósito é ser o **FastAPI das _CLIs_**. ⌨️ 🚀 +**Typer** é o irmão menor do FastAPI. E seu propósito é ser o **FastAPI das CLIs**. ⌨️ 🚀 ## Requisitos { #requirements } @@ -255,10 +261,10 @@ Você verá a resposta JSON como: Você acabou de criar uma API que: -* Recebe requisições HTTP nas _rotas_ `/` e `/items/{item_id}`. -* Ambas _rotas_ fazem operações `GET` (também conhecido como _métodos_ HTTP). -* A _rota_ `/items/{item_id}` tem um _parâmetro de rota_ `item_id` que deve ser um `int`. -* A _rota_ `/items/{item_id}` tem um _parâmetro query_ `q` `str` opcional. +* Recebe requisições HTTP nos _paths_ `/` e `/items/{item_id}`. +* Ambos _paths_ fazem operações `GET` (também conhecido como _métodos_ HTTP). +* O _path_ `/items/{item_id}` tem um _parâmetro de path_ `item_id` que deve ser um `int`. +* O _path_ `/items/{item_id}` tem um _parâmetro query_ `q` `str` opcional. ### Documentação Interativa da API { #interactive-api-docs } @@ -278,7 +284,7 @@ Você verá a documentação automática alternativa (fornecida por http://127.0.0.1:8000/redoc. -* A documentação alternativa também irá refletir o novo parâmetro da _query_ e o corpo: +* A documentação alternativa também irá refletir o novo parâmetro query e o corpo: ![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) @@ -368,15 +374,15 @@ item: Item * Validação de dados: * Erros automáticos e claros quando o dado é inválido. * Validação até para objetos JSON profundamente aninhados. -* Conversão de dados de entrada: vindo da rede para dados e tipos Python. Consegue ler: +* Conversão de dados de entrada: vindo da rede para dados e tipos Python. Consegue ler: * JSON. - * Parâmetros de rota. - * Parâmetros de _query_ . - * _Cookies_. + * Parâmetros de path. + * Parâmetros query. + * Cookies. * Cabeçalhos. * Formulários. * Arquivos. -* Conversão de dados de saída de tipos e dados Python para dados de rede (como JSON): +* Conversão de dados de saída: convertendo de tipos e dados Python para dados de rede (como JSON): * Converte tipos Python (`str`, `int`, `float`, `bool`, `list` etc). * Objetos `datetime`. * Objetos `UUID`. @@ -390,17 +396,17 @@ item: Item Voltando ao código do exemplo anterior, **FastAPI** irá: -* Validar que existe um `item_id` na rota para requisições `GET` e `PUT`. +* Validar que existe um `item_id` no path para requisições `GET` e `PUT`. * Validar que `item_id` é do tipo `int` para requisições `GET` e `PUT`. - * Se não é validado, o cliente verá um útil, claro erro. -* Verificar se existe um parâmetro de _query_ opcional nomeado como `q` (como em `http://127.0.0.1:8000/items/foo?q=somequery`) para requisições `GET`. + * Se não for, o cliente verá um erro útil e claro. +* Verificar se existe um parâmetro query opcional nomeado como `q` (como em `http://127.0.0.1:8000/items/foo?q=somequery`) para requisições `GET`. * Como o parâmetro `q` é declarado com `= None`, ele é opcional. - * Sem o `None` ele poderia ser obrigatório (como o corpo no caso de `PUT`). + * Sem o `None` ele seria obrigatório (como o corpo no caso de `PUT`). * Para requisições `PUT` para `/items/{item_id}`, lerá o corpo como JSON: * Verifica que tem um atributo obrigatório `name` que deve ser `str`. - * Verifica que tem um atributo obrigatório `price` que deve ser `float`. - * Verifica que tem an atributo opcional `is_offer`, que deve ser `bool`, se presente. - * Tudo isso também funciona para objetos JSON profundamente aninhados. + * Verifica que tem um atributo obrigatório `price` que tem que ser um `float`. + * Verifica que tem um atributo opcional `is_offer`, que deve ser um `bool`, se presente. + * Tudo isso também funcionaria para objetos JSON profundamente aninhados. * Converter de e para JSON automaticamente. * Documentar tudo com OpenAPI, que poderá ser usado por: * Sistemas de documentação interativos. @@ -409,7 +415,7 @@ Voltando ao código do exemplo anterior, **FastAPI** irá: --- -Nós apenas arranhamos a superfície, mas você já tem idéia de como tudo funciona. +Nós apenas arranhamos a superfície, mas você já tem ideia de como tudo funciona. Experimente mudar a seguinte linha: @@ -437,22 +443,22 @@ Para um exemplo mais completo incluindo mais recursos, veja Injeção de Dependência**. -* Segurança e autenticação, incluindo suporte para **OAuth2** com autenticação **JWT tokens** e **HTTP Basic**. +* Declaração de **parâmetros** de diferentes lugares como: **cabeçalhos**, **cookies**, **campos de formulários** e **arquivos**. +* Como configurar **limitações de validação** como `maximum_length` ou `regex`. +* Um poderoso e fácil de usar sistema de **Injeção de Dependência**. +* Segurança e autenticação, incluindo suporte para **OAuth2** com autenticação com **JWT tokens** e **HTTP Basic**. * Técnicas mais avançadas (mas igualmente fáceis) para declaração de **modelos JSON profundamente aninhados** (graças ao Pydantic). * Integrações **GraphQL** com o Strawberry e outras bibliotecas. * Muitos recursos extras (graças ao Starlette) como: * **WebSockets** - * testes extrememamente fáceis baseados em HTTPX e `pytest` + * testes extremamente fáceis baseados em HTTPX e `pytest` * **CORS** * **Cookie Sessions** * ...e mais. ### Implemente sua aplicação (opcional) { #deploy-your-app-optional } -Você pode opcionalmente implantar sua aplicação FastAPI na FastAPI Cloud, inscreva-se na lista de espera se ainda não o fez. 🚀 +Você pode opcionalmente implantar sua aplicação FastAPI na FastAPI Cloud, vá e entre na lista de espera se ainda não o fez. 🚀 Se você já tem uma conta na **FastAPI Cloud** (nós convidamos você da lista de espera 😉), pode implantar sua aplicação com um único comando. @@ -506,7 +512,7 @@ Siga os tutoriais do seu provedor de nuvem para implantar aplicações FastAPI c Testes de performance da _Independent TechEmpower_ mostram aplicações **FastAPI** rodando sob Uvicorn como um dos _frameworks_ Python mais rápidos disponíveis, somente atrás de Starlette e Uvicorn (utilizados internamente pelo FastAPI). (*) -Para entender mais sobre performance, veja a seção Comparações. +Para entender mais sobre isso, veja a seção Comparações. ## Dependências { #dependencies } @@ -514,7 +520,7 @@ O FastAPI depende do Pydantic e do Starlette. ### Dependências `standard` { #standard-dependencies } -Quando você instala o FastAPI com `pip install "fastapi[standard]"`, ele vêm com o grupo `standard` (padrão) de dependências opcionais: +Quando você instala o FastAPI com `pip install "fastapi[standard]"`, ele vem com o grupo `standard` de dependências opcionais: Utilizado pelo Pydantic: @@ -524,7 +530,7 @@ Utilizado pelo Starlette: * httpx - Obrigatório caso você queira utilizar o `TestClient`. * jinja2 - Obrigatório se você quer utilizar a configuração padrão de templates. -* python-multipart - Obrigatório se você deseja suporte a "parsing" de formulário, com `request.form()`. +* python-multipart - Obrigatório se você deseja suporte a "parsing" de formulário, com `request.form()`. Utilizado pelo FastAPI: @@ -547,7 +553,7 @@ Existem algumas dependências adicionais que você pode querer instalar. Dependências opcionais adicionais do Pydantic: * pydantic-settings - para gerenciamento de configurações. -* pydantic-extra-types - tipos extras para serem utilizados com o Pydantic. +* pydantic-extra-types - para tipos extras a serem utilizados com o Pydantic. Dependências opcionais adicionais do FastAPI: diff --git a/docs/pt/docs/tutorial/bigger-applications.md b/docs/pt/docs/tutorial/bigger-applications.md index 9dec7b1968..87bd13375a 100644 --- a/docs/pt/docs/tutorial/bigger-applications.md +++ b/docs/pt/docs/tutorial/bigger-applications.md @@ -31,7 +31,7 @@ Digamos que você tenha uma estrutura de arquivos como esta: /// tip | Dica -Existem vários arquivos `__init__.py` presentes em cada diretório ou subdiretório. +Existem vários arquivos `__init__.py`: um em cada diretório ou subdiretório. Isso permite a importação de código de um arquivo para outro. @@ -43,32 +43,32 @@ from app.routers import items /// -* O diretório `app` contém todo o código da aplicação. Ele possui um arquivo `app/__init__.py` vazio, o que o torna um "pacote Python" (uma coleção de "módulos Python"): `app`. -* Dentro dele, o arquivo `app/main.py` está localizado em um pacote Python (diretório com `__init__.py`). Portanto, ele é um "módulo" desse pacote: `app.main`. -* Existem também um arquivo `app/dependencies.py`, assim como o `app/main.py`, ele é um "módulo": `app.dependencies`. +* O diretório `app` contém tudo. E possui um arquivo vazio `app/__init__.py`, então ele é um "pacote Python" (uma coleção de "módulos Python"): `app`. +* Ele contém um arquivo `app/main.py`. Como está dentro de um pacote Python (um diretório com um arquivo `__init__.py`), ele é um "módulo" desse pacote: `app.main`. +* Existe também um arquivo `app/dependencies.py`, assim como `app/main.py`, ele é um "módulo": `app.dependencies`. * Há um subdiretório `app/routers/` com outro arquivo `__init__.py`, então ele é um "subpacote Python": `app.routers`. -* O arquivo `app/routers/items.py` está dentro de um pacote, `app/routers/`, portanto, é um "submódulo": `app.routers.items`. -* O mesmo com `app/routers/users.py`, ele é outro submódulo: `app.routers.users`. -* Há também um subdiretório `app/internal/` com outro arquivo `__init__.py`, então ele é outro "subpacote Python":`app.internal`. +* O arquivo `app/routers/items.py` está dentro de um pacote, `app/routers/`, portanto é um submódulo: `app.routers.items`. +* O mesmo com `app/routers/users.py`, ele é outro submódulo: `app.routers.users`. +* Há também um subdiretório `app/internal/` com outro arquivo `__init__.py`, então ele é outro "subpacote Python": `app.internal`. * E o arquivo `app/internal/admin.py` é outro submódulo: `app.internal.admin`. A mesma estrutura de arquivos com comentários: -``` +```bash . -├── app # "app" é um pacote Python -│   ├── __init__.py # este arquivo torna "app" um "pacote Python" -│   ├── main.py # "main" módulo, e.g. import app.main -│   ├── dependencies.py # "dependencies" módulo, e.g. import app.dependencies -│   └── routers # "routers" é um "subpacote Python" -│   │ ├── __init__.py # torna "routers" um "subpacote Python" -│   │ ├── items.py # "items" submódulo, e.g. import app.routers.items -│   │ └── users.py # "users" submódulo, e.g. import app.routers.users -│   └── internal # "internal" é um "subpacote Python" -│   ├── __init__.py # torna "internal" um "subpacote Python" -│   └── admin.py # "admin" submódulo, e.g. import app.internal.admin +├── app # "app" is a Python package +│   ├── __init__.py # this file makes "app" a "Python package" +│   ├── main.py # "main" module, e.g. import app.main +│   ├── dependencies.py # "dependencies" module, e.g. import app.dependencies +│   └── routers # "routers" is a "Python subpackage" +│   │ ├── __init__.py # makes "routers" a "Python subpackage" +│   │ ├── items.py # "items" submodule, e.g. import app.routers.items +│   │ └── users.py # "users" submodule, e.g. import app.routers.users +│   └── internal # "internal" is a "Python subpackage" +│   ├── __init__.py # makes "internal" a "Python subpackage" +│   └── admin.py # "admin" submodule, e.g. import app.internal.admin ``` ## `APIRouter` { #apirouter } @@ -79,11 +79,11 @@ Você quer manter as *operações de rota* relacionadas aos seus usuários separ Mas ele ainda faz parte da mesma aplicação/web API **FastAPI** (faz parte do mesmo "pacote Python"). -Você pode criar as *operações de rotas* para esse módulo usando o `APIRouter`. +Você pode criar as *operações de rota* para esse módulo usando o `APIRouter`. ### Importe `APIRouter` { #import-apirouter } -você o importa e cria uma "instância" da mesma maneira que faria com a classe `FastAPI`: +Você o importa e cria uma "instância" da mesma maneira que faria com a classe `FastAPI`: {* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *} @@ -91,7 +91,7 @@ você o importa e cria uma "instância" da mesma maneira que faria com a classe E então você o utiliza para declarar suas *operações de rota*. -Utilize-o da mesma maneira que utilizaria a classe `FastAPI`: +Utilize-o da mesma maneira que utilizaria a classe `FastAPI`: {* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *} @@ -151,7 +151,7 @@ Então, em vez de adicionar tudo isso a cada *operação de rota*, podemos adici {* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *} -Como o caminho de cada *operação de rota* deve começar com `/`, como em: +Como o path de cada *operação de rota* tem que começar com `/`, como em: ```Python hl_lines="1" @router.get("/{item_id}") @@ -163,9 +163,9 @@ async def read_item(item_id: str): Então, o prefixo neste caso é `/items`. -Também podemos adicionar uma lista de `tags` e `responses` extras que serão aplicadas a todas as *operações de rota* incluídas neste roteador. +Também podemos adicionar uma list de `tags` e `responses` extras que serão aplicadas a todas as *operações de rota* incluídas neste router. -E podemos adicionar uma lista de `dependencies` que serão adicionadas a todas as *operações de rota* no roteador e serão executadas/resolvidas para cada request feita a elas. +E podemos adicionar uma list de `dependencies` que serão adicionadas a todas as *operações de rota* no router e serão executadas/resolvidas para cada request feita a elas. /// tip | Dica @@ -173,7 +173,7 @@ Observe que, assim como [dependências em *decoradores de operação de rota*](d /// -O resultado final é que os caminhos dos itens agora são: +O resultado final é que os paths dos itens agora são: * `/items/` * `/items/{item_id}` @@ -183,9 +183,9 @@ O resultado final é que os caminhos dos itens agora são: * Elas serão marcadas com uma lista de tags que contêm uma única string `"items"`. * Essas "tags" são especialmente úteis para os sistemas de documentação interativa automática (usando OpenAPI). * Todas elas incluirão as `responses` predefinidas. -* Todas essas *operações de rota* terão a lista de `dependencies` avaliada/executada antes delas. +* Todas essas *operações de rota* terão a list de `dependencies` avaliada/executada antes delas. * Se você também declarar dependências em uma *operação de rota* específica, **elas também serão executadas**. - * As dependências do roteador são executadas primeiro, depois as [`dependencies` no decorador](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank} e, em seguida, as dependências de parâmetros normais. + * As dependências do router são executadas primeiro, depois as [`dependencies` no decorador](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank} e, em seguida, as dependências de parâmetros normais. * Você também pode adicionar [dependências de `Segurança` com `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}. /// tip | Dica @@ -246,7 +246,7 @@ from ..dependencies import get_token_header significa: -* Começando no mesmo pacote em que este módulo (o arquivo `app/routers/items.py`) reside (o diretório `app/routers/`)... +* Começando no mesmo pacote em que este módulo (o arquivo `app/routers/items.py`) vive (o diretório `app/routers/`)... * vá para o pacote pai (o diretório `app/`)... * e lá, encontre o módulo `dependencies` (o arquivo em `app/dependencies.py`)... * e dele, importe a função `get_token_header`. @@ -283,9 +283,9 @@ Mas ainda podemos adicionar _mais_ `tags` que serão aplicadas a uma *operação /// tip | Dica -Esta última operação de caminho terá a combinação de tags: `["items", "custom"]`. +Esta última operação de rota terá a combinação de tags: `["items", "custom"]`. -E também terá ambas as respostas na documentação, uma para `404` e uma para `403`. +E também terá ambas as responses na documentação, uma para `404` e uma para `403`. /// @@ -325,7 +325,7 @@ from .routers import items, users significa: -* Começando no mesmo pacote em que este módulo (o arquivo `app/main.py`) reside (o diretório `app/`)... +* Começando no mesmo pacote em que este módulo (o arquivo `app/main.py`) vive (o diretório `app/`)... * procure o subpacote `routers` (o diretório em `app/routers/`)... * e dele, importe o submódulo `items` (o arquivo em `app/routers/items.py`) e `users` (o arquivo em `app/routers/users.py`)... @@ -376,7 +376,7 @@ Então, para poder usar ambos no mesmo arquivo, importamos os submódulos direta {* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *} -### Inclua os `APIRouter`s para `usuários` e `itens` { #include-the-apirouters-for-users-and-items } +### Inclua os `APIRouter`s para `users` e `items` { #include-the-apirouters-for-users-and-items } Agora, vamos incluir os `router`s dos submódulos `users` e `items`: @@ -392,7 +392,7 @@ E `items.router` contém o `APIRouter` dentro do arquivo `app/routers/items.py`. Com `app.include_router()` podemos adicionar cada `APIRouter` ao aplicativo principal `FastAPI`. -Ele incluirá todas as rotas daquele roteador como parte dele. +Ele incluirá todas as rotas daquele router como parte dele. /// note | Detalhes Técnicos @@ -404,7 +404,7 @@ Então, nos bastidores, ele realmente funcionará como se tudo fosse o mesmo apl /// check | Verifique -Você não precisa se preocupar com desempenho ao incluir roteadores. +Você não precisa se preocupar com desempenho ao incluir routers. Isso levará microssegundos e só acontecerá na inicialização. @@ -453,7 +453,7 @@ e funcionará corretamente, junto com todas as outras *operações de rota* adic /// note | Detalhes Técnicos Avançados -**Observação**: este é um detalhe muito técnico que você provavelmente pode **simplesmente pular**. +**Nota**: este é um detalhe muito técnico que você provavelmente pode **simplesmente pular**. --- @@ -479,15 +479,15 @@ $ fastapi dev app/main.py -E abra os documentos em http://127.0.0.1:8000/docs. +E abra a documentação em http://127.0.0.1:8000/docs. -Você verá a documentação automática da API, incluindo os caminhos de todos os submódulos, usando os caminhos (e prefixos) corretos e as tags corretas: +Você verá a documentação automática da API, incluindo os paths de todos os submódulos, usando os paths (e prefixos) corretos e as tags corretas: -## Inclua o mesmo roteador várias vezes com `prefix` diferentes { #include-the-same-router-multiple-times-with-different-prefix } +## Inclua o mesmo router várias vezes com `prefix` diferentes { #include-the-same-router-multiple-times-with-different-prefix } -Você também pode usar `.include_router()` várias vezes com o *mesmo* roteador usando prefixos diferentes. +Você também pode usar `.include_router()` várias vezes com o *mesmo* router usando prefixos diferentes. Isso pode ser útil, por exemplo, para expor a mesma API sob prefixos diferentes, por exemplo, `/api/v1` e `/api/latest`. @@ -495,10 +495,10 @@ Esse é um uso avançado que você pode não precisar, mas está lá caso precis ## Inclua um `APIRouter` em outro { #include-an-apirouter-in-another } -Da mesma forma que você pode incluir um `APIRouter` em um aplicativo `FastAPI`, você pode incluir um `APIRouter` em outro `APIRouter` usando: +Da mesma forma que você pode incluir um `APIRouter` em uma aplicação `FastAPI`, você pode incluir um `APIRouter` em outro `APIRouter` usando: ```Python router.include_router(other_router) ``` -Certifique-se de fazer isso antes de incluir `router` no aplicativo `FastAPI`, para que as *operações de rota* de `other_router` também sejam incluídas. +Certifique-se de fazer isso antes de incluir `router` na aplicação `FastAPI`, para que as *operações de rota* de `other_router` também sejam incluídas. diff --git a/docs/pt/docs/tutorial/body-updates.md b/docs/pt/docs/tutorial/body-updates.md index 67bf684925..95f89c8d23 100644 --- a/docs/pt/docs/tutorial/body-updates.md +++ b/docs/pt/docs/tutorial/body-updates.md @@ -1,6 +1,6 @@ # Corpo - Atualizações { #body-updates } -## Atualização de dados existentes com `PUT` { #update-replacing-with-put } +## Atualização substituindo com `PUT` { #update-replacing-with-put } Para atualizar um item, você pode usar a operação HTTP `PUT`. @@ -22,13 +22,13 @@ Isso significa que, se você quiser atualizar o item `bar` usando `PUT` com um c } ``` -Como ele não inclui o atributo já armazenado `"tax": 20.2`, o modelo de entrada assumiria o valor padrão de `"tax": 10.5`. +como ele não inclui o atributo já armazenado `"tax": 20.2`, o modelo de entrada assumiria o valor padrão de `"tax": 10.5`. E os dados seriam salvos com esse "novo" `tax` de `10.5`. ## Atualizações parciais com `PATCH` { #partial-updates-with-patch } -Você também pode usar a operação HTTP `PATCH` para atualizar parcialmente os dados. +Você também pode usar a operação HTTP `PATCH` para atualizar dados *parcialmente*. Isso significa que você pode enviar apenas os dados que deseja atualizar, deixando o restante intacto. @@ -40,25 +40,17 @@ E muitas equipes usam apenas `PUT`, mesmo para atualizações parciais. Você é **livre** para usá-los como preferir, **FastAPI** não impõe restrições. -Mas este guia te dá uma ideia de como eles são destinados a serem usados. +Mas este guia mostra, mais ou menos, como eles são destinados a serem usados. /// ### Usando o parâmetro `exclude_unset` do Pydantic { #using-pydantics-exclude-unset-parameter } -Se você quiser receber atualizações parciais, é muito útil usar o parâmetro `exclude_unset` no método `.model_dump()` do modelo do Pydantic. +Se você quiser receber atualizações parciais, é muito útil usar o parâmetro `exclude_unset` no `.model_dump()` do modelo do Pydantic. Como `item.model_dump(exclude_unset=True)`. -/// info | Informação - -No Pydantic v1, o método que era chamado `.dict()` e foi descontinuado (mas ainda suportado) no Pydantic v2. Agora, deve-se usar o método `.model_dump()`. - -Os exemplos aqui usam `.dict()` para compatibilidade com o Pydantic v1, mas você deve usar `.model_dump()` a partir do Pydantic v2. - -/// - -Isso gera um `dict` com apenas os dados definidos ao criar o modelo `item`, excluindo os valores padrão. +Isso geraria um `dict` com apenas os dados que foram definidos ao criar o modelo `item`, excluindo os valores padrão. Então, você pode usar isso para gerar um `dict` com apenas os dados definidos (enviados na solicitação), omitindo valores padrão: @@ -68,31 +60,23 @@ Então, você pode usar isso para gerar um `dict` com apenas os dados definidos Agora, você pode criar uma cópia do modelo existente usando `.model_copy()`, e passar o parâmetro `update` com um `dict` contendo os dados para atualizar. -/// info | Informação - -No Pydantic v1, o método era chamado `.copy()`, ele foi descontinuado (mas ainda suportado) no Pydantic v2, e renomeado para `.model_copy()`. - -Os exemplos aqui usam `.copy()` para compatibilidade com o Pydantic v1, mas você deve usar `.model_copy()` com o Pydantic v2. - -/// - Como `stored_item_model.model_copy(update=update_data)`: {* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *} ### Recapitulando as atualizações parciais { #partial-updates-recap } -Resumindo, para aplicar atualizações parciais você pode: +Resumindo, para aplicar atualizações parciais você deveria: * (Opcionalmente) usar `PATCH` em vez de `PUT`. * Recuperar os dados armazenados. * Colocar esses dados em um modelo do Pydantic. * Gerar um `dict` sem valores padrão a partir do modelo de entrada (usando `exclude_unset`). - * Dessa forma, você pode atualizar apenas os valores definidos pelo usuário, em vez de substituir os valores já armazenados com valores padrão em seu modelo. + * Dessa forma, você pode atualizar apenas os valores realmente definidos pelo usuário, em vez de substituir valores já armazenados por valores padrão do modelo. * Criar uma cópia do modelo armazenado, atualizando seus atributos com as atualizações parciais recebidas (usando o parâmetro `update`). -* Converter o modelo copiado em algo que possa ser armazenado no seu banco de dados (por exemplo, usando o `jsonable_encoder`). - * Isso é comparável ao uso do método `.model_dump()`, mas garante (e converte) os valores para tipos de dados que possam ser convertidos em JSON, por exemplo, `datetime` para `str`. -* Salvar os dados no seu banco de dados. +* Converter o modelo copiado em algo que possa ser armazenado no seu BD (por exemplo, usando o `jsonable_encoder`). + * Isso é comparável a usar o método `.model_dump()` do modelo novamente, mas garante (e converte) os valores para tipos de dados que possam ser convertidos em JSON, por exemplo, `datetime` para `str`. +* Salvar os dados no seu BD. * Retornar o modelo atualizado. {* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *} @@ -109,8 +93,8 @@ Mas o exemplo aqui usa `PATCH` porque foi criado para esses casos de uso. Observe que o modelo de entrada ainda é validado. -Portanto, se você quiser receber atualizações parciais que possam omitir todos os atributos, precisará ter um modelo com todos os atributos marcados como opcionais (com valores padrão ou `None`). +Portanto, se você quiser receber atualizações parciais que possam omitir todos os atributos, você precisa ter um modelo com todos os atributos marcados como opcionais (com valores padrão ou `None`). -Para distinguir os modelos com todos os valores opcionais para **atualizações** e modelos com valores obrigatórios para **criação**, você pode usar as ideias descritas em [Modelos Adicionais](extra-models.md){.internal-link target=_blank}. +Para distinguir entre os modelos com todos os valores opcionais para **atualizações** e modelos com valores obrigatórios para **criação**, você pode usar as ideias descritas em [Modelos Adicionais](extra-models.md){.internal-link target=_blank}. /// diff --git a/docs/pt/docs/tutorial/body.md b/docs/pt/docs/tutorial/body.md index 1330f4458f..669334439a 100644 --- a/docs/pt/docs/tutorial/body.md +++ b/docs/pt/docs/tutorial/body.md @@ -10,11 +10,11 @@ Para declarar um corpo da **requisição**, você utiliza os modelos do 0.95.0) exigiam que você usasse `Query` como valor padrão do seu parâmetro, em vez de colocá-lo em `Annotated`. É muito provável que você veja código assim por aí, então vou te explicar. +Versões anteriores do FastAPI (antes de 0.95.0) exigiam que você usasse `Query` como valor padrão do seu parâmetro, em vez de colocá-lo em `Annotated`, há uma grande chance de você ver código usando isso por aí, então vou explicar. /// tip | Dica @@ -192,7 +192,7 @@ Você também pode adicionar um parâmetro `min_length`: ## Adicione expressões regulares { #add-regular-expressions } -Você pode definir um `pattern` de expressão regular que o parâmetro deve corresponder: +Você pode definir um `pattern` de expressão regular que o parâmetro deve corresponder: {* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *} @@ -206,20 +206,6 @@ Se você se sentir perdido com essas ideias de **"expressão regular"**, não se Agora você sabe que, sempre que precisar delas, pode usá-las no **FastAPI**. -### Pydantic v1 `regex` em vez de `pattern` { #pydantic-v1-regex-instead-of-pattern } - -Antes da versão 2 do Pydantic e antes do FastAPI 0.100.0, o parâmetro se chamava `regex` em vez de `pattern`, mas agora está descontinuado. - -Você ainda pode ver algum código usando isso: - -//// tab | Pydantic v1 - -{* ../../docs_src/query_params_str_validations/tutorial004_regex_an_py310.py hl[11] *} - -//// - -Mas saiba que isso está descontinuado e deve ser atualizado para usar o novo parâmetro `pattern`. 🤓 - ## Valores padrão { #default-values } Você pode, claro, usar valores padrão diferentes de `None`. @@ -280,7 +266,7 @@ Então, com uma URL como: http://localhost:8000/items/?q=foo&q=bar ``` -você receberá os múltiplos valores do *parâmetro de consulta* `q` (`foo` e `bar`) em uma `list` Python dentro da sua *função de operação de rota*, no *parâmetro da função* `q`. +você receberia os múltiplos valores dos *parâmetros de consulta* `q` (`foo` e `bar`) em uma `list` Python dentro da sua *função de operação de rota*, no *parâmetro da função* `q`. Assim, a resposta para essa URL seria: @@ -350,7 +336,7 @@ Essas informações serão incluídas no OpenAPI gerado e usadas pelas interface Tenha em mente que ferramentas diferentes podem ter níveis diferentes de suporte ao OpenAPI. -Algumas delas podem ainda não mostrar todas as informações extras declaradas, embora na maioria dos casos o recurso ausente já esteja planejado para desenvolvimento. +Algumas delas podem ainda não mostrar todas as informações extras declaradas, embora na maioria dos casos a funcionalidade ausente já esteja planejada para desenvolvimento. /// @@ -386,7 +372,7 @@ Então você pode declarar um `alias`, e esse alias será usado para encontrar o Agora digamos que você não gosta mais desse parâmetro. -Você tem que deixá-lo por um tempo, pois há clientes usando-o, mas quer que a documentação mostre claramente que ele está descontinuado. +Você tem que deixá-lo por um tempo, pois há clientes usando-o, mas quer que a documentação mostre claramente que ele está deprecated. Então passe o parâmetro `deprecated=True` para `Query`: @@ -416,7 +402,7 @@ O Pydantic também tem ISBN ou com `imdb-` para um ID de URL de filme IMDB: +Por exemplo, este validador personalizado verifica se o ID do item começa com `isbn-` para um número de livro ISBN ou com `imdb-` para um ID de URL de filme IMDB: {* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *} @@ -428,7 +414,7 @@ Isso está disponível com a versão 2 do Pydantic ou superior. 😎 /// tip | Dica -Se você precisar fazer qualquer tipo de validação que exija comunicação com algum **componente externo**, como um banco de dados ou outra API, você deve usar **Dependências do FastAPI** em vez disso; você aprenderá sobre elas mais adiante. +Se você precisar fazer qualquer tipo de validação que exija comunicação com algum **componente externo**, como um banco de dados ou outra API, você deveria usar **Dependências do FastAPI** em vez disso; você aprenderá sobre elas mais adiante. Esses validadores personalizados são para coisas que podem ser verificadas **apenas** com os **mesmos dados** fornecidos na requisição. @@ -440,7 +426,7 @@ O ponto importante é apenas usar **`AfterValidator` com uma função dentro de --- -Mas se você está curioso sobre este exemplo específico e ainda entretido, aqui vão alguns detalhes extras. +Mas se você estiver curioso sobre este exemplo de código específico e ainda entretido, aqui vão alguns detalhes extras. #### String com `value.startswith()` { #string-with-value-startswith } @@ -450,7 +436,7 @@ Percebeu? Uma string usando `value.startswith()` pode receber uma tupla, e verif #### Um item aleatório { #a-random-item } -Com `data.items()` obtemos um objeto iterável com tuplas contendo a chave e o valor de cada item do dicionário. +Com `data.items()` obtemos um objeto iterável com tuplas contendo a chave e o valor de cada item do dicionário. Convertimos esse objeto iterável em uma `list` adequada com `list(data.items())`. diff --git a/docs/pt/docs/tutorial/response-model.md b/docs/pt/docs/tutorial/response-model.md index dc66bb46c4..8a7a712488 100644 --- a/docs/pt/docs/tutorial/response-model.md +++ b/docs/pt/docs/tutorial/response-model.md @@ -252,20 +252,6 @@ Então, se você enviar uma solicitação para essa *operação de rota* para o /// info | Informação -No Pydantic v1, o método era chamado `.dict()`, ele foi descontinuado (mas ainda suportado) no Pydantic v2 e renomeado para `.model_dump()`. - -Os exemplos aqui usam `.dict()` para compatibilidade com Pydantic v1, mas você deve usar `.model_dump()` em vez disso se puder usar Pydantic v2. - -/// - -/// info | Informação - -O FastAPI usa `.dict()` do modelo Pydantic com seu parâmetro `exclude_unset` para chegar a isso. - -/// - -/// info | Informação - Você também pode usar: * `response_model_exclude_defaults=True` diff --git a/docs/pt/docs/tutorial/schema-extra-example.md b/docs/pt/docs/tutorial/schema-extra-example.md index bddd320cd3..2d62ffd851 100644 --- a/docs/pt/docs/tutorial/schema-extra-example.md +++ b/docs/pt/docs/tutorial/schema-extra-example.md @@ -8,39 +8,17 @@ Aqui estão várias maneiras de fazer isso. Você pode declarar `examples` para um modelo Pydantic que serão adicionados ao JSON Schema gerado. -//// tab | Pydantic v2 - {* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *} -//// - -//// tab | Pydantic v1 - -{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *} - -//// - Essas informações extras serão adicionadas como estão ao **JSON Schema** de saída para esse modelo e serão usadas na documentação da API. -//// tab | Pydantic v2 - -Na versão 2 do Pydantic, você usaria o atributo `model_config`, que recebe um `dict`, conforme descrito na documentação do Pydantic: Configuration. +Você pode usar o atributo `model_config`, que recebe um `dict`, conforme descrito na documentação do Pydantic: Configuration. Você pode definir `"json_schema_extra"` com um `dict` contendo quaisquer dados adicionais que você queira que apareçam no JSON Schema gerado, incluindo `examples`. -//// - -//// tab | Pydantic v1 - -Na versão 1 do Pydantic, você usaria uma classe interna `Config` e `schema_extra`, conforme descrito na documentação do Pydantic: Schema customization. - -Você pode definir `schema_extra` com um `dict` contendo quaisquer dados adicionais que você queira que apareçam no JSON Schema gerado, incluindo `examples`. - -//// - /// tip | Dica -Você pode usar a mesma técnica para estender o JSON Schema e adicionar suas próprias informações extras personalizadas. +Você poderia usar a mesma técnica para estender o JSON Schema e adicionar suas próprias informações extras personalizadas. Por exemplo, você poderia usá-la para adicionar metadados para uma interface de usuário de front-end, etc. @@ -50,7 +28,7 @@ Por exemplo, você poderia usá-la para adicionar metadados para uma interface d O OpenAPI 3.1.0 (usado desde o FastAPI 0.99.0) adicionou suporte a `examples`, que faz parte do padrão **JSON Schema**. -Antes disso, ele suportava apenas a palavra‑chave `example` com um único exemplo. Isso ainda é suportado pelo OpenAPI 3.1.0, mas é descontinuado e não faz parte do padrão JSON Schema. Portanto, é recomendado migrar de `example` para `examples`. 🤓 +Antes disso, ele suportava apenas a palavra‑chave `example` com um único exemplo. Isso ainda é suportado pelo OpenAPI 3.1.0, mas é descontinuado e não faz parte do padrão JSON Schema. Portanto, você é incentivado a migrar de `example` para `examples`. 🤓 Você pode ler mais no final desta página. @@ -102,7 +80,7 @@ No entanto, no momento em que isto foi escrito, Antes do **JSON Schema** suportar `examples`, o OpenAPI já tinha suporte para um campo diferente também chamado `examples`. -Esse `examples` específico do OpenAPI vai em outra seção da especificação. Ele fica nos **detalhes de cada função de operação de rota**, não dentro de cada JSON Schema. +Esse `examples` **específico do OpenAPI** vai em outra seção da especificação OpenAPI. Ele fica nos **detalhes de cada *operação de rota***, não dentro de cada JSON Schema. E o Swagger UI tem suportado esse campo `examples` particular há algum tempo. Então, você pode usá-lo para **mostrar** diferentes **exemplos na UI da documentação**. @@ -189,9 +167,9 @@ Depois, o JSON Schema adicionou um campo LLM, переводящая документацию, `general_prompt` в `scripts/translate.py` и языковой специфичный промпт в `docs/{language code}/llm-prompt.md`. Языковой специфичный промпт добавляется к `general_prompt`. +Этот документ проверяет, понимает ли LLM, переводящая документацию, `general_prompt` в `scripts/translate.py` и языковой специфичный промпт в `docs/{language code}/llm-prompt.md`. Языковой специфичный промпт добавляется к `general_prompt`. -Тесты, добавленные здесь, увидят все создатели языковых промптов. +Тесты, добавленные здесь, увидят все создатели языковых специфичных промптов. Использование: @@ -11,7 +11,7 @@ * Проверьте, всё ли в порядке в переводе. * При необходимости улучшите ваш языковой специфичный промпт, общий промпт или английский документ. * Затем вручную исправьте оставшиеся проблемы в переводе, чтобы он был хорошим. -* Переведите заново, имея хороший перевод на месте. Идеальным результатом будет ситуация, когда LLM больше не вносит изменений в перевод. Это означает, что общий промпт и ваш языковой специфичный промпт максимально хороши (иногда он будет делать несколько, казалось бы, случайных изменений, причина в том, что LLM — недетерминированные алгоритмы). +* Переведите заново, имея хороший перевод на месте. Идеальным результатом будет ситуация, когда LLM больше не вносит изменений в перевод. Это означает, что общий промпт и ваш языковой специфичный промпт настолько хороши, насколько это возможно (иногда он будет делать несколько, казалось бы, случайных изменений, причина в том, что LLM — недетерминированные алгоритмы). Тесты: @@ -197,10 +197,10 @@ works(foo="bar") # Это работает 🎉 ### abbr даёт полную расшифровку { #the-abbr-gives-a-full-phrase } -* GTD -* lt -* XWT -* PSGI +* GTD +* lt +* XWT +* PSGI ### abbr даёт объяснение { #the-abbr-gives-an-explanation } @@ -209,8 +209,8 @@ works(foo="bar") # Это работает 🎉 ### abbr даёт полную расшифровку и объяснение { #the-abbr-gives-a-full-phrase-and-an-explanation } -* MDN -* I/O. +* MDN +* I/O. //// diff --git a/docs/ru/docs/advanced/path-operation-advanced-configuration.md b/docs/ru/docs/advanced/path-operation-advanced-configuration.md index eaf9ad0528..86d3a5b630 100644 --- a/docs/ru/docs/advanced/path-operation-advanced-configuration.md +++ b/docs/ru/docs/advanced/path-operation-advanced-configuration.md @@ -14,7 +14,7 @@ {* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *} -### Использование имени функции-обработчика пути как operationId { #using-the-path-operation-function-name-as-the-operationid } +### Использование имени *функции-обработчика пути* как operationId { #using-the-path-operation-function-name-as-the-operationid } Если вы хотите использовать имена функций ваших API в качестве `operationId`, вы можете пройти по всем из них и переопределить `operation_id` каждой *операции пути* с помощью их `APIRoute.name`. @@ -38,7 +38,7 @@ ## Исключить из OpenAPI { #exclude-from-openapi } -Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматической документации), используйте параметр `include_in_schema` и установите его в `False`: +Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматических систем документации), используйте параметр `include_in_schema` и установите его в `False`: {* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *} @@ -48,7 +48,7 @@ Добавление `\f` (экранированного символа «form feed») заставит **FastAPI** обрезать текст, используемый для OpenAPI, в этой точке. -Эта часть не попадёт в документацию, но другие инструменты (например, Sphinx) смогут использовать остальное. +Это не отобразится в документации, но другие инструменты (например, Sphinx) смогут использовать остальное. {* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *} @@ -56,7 +56,7 @@ Вы, вероятно, уже видели, как объявлять `response_model` и `status_code` для *операции пути*. -Это определяет метаданные об основном ответе *операции пути*. +Это определяет метаданные об основном HTTP-ответе *операции пути*. Также можно объявлять дополнительные ответы с их моделями, статус-кодами и т.д. @@ -76,7 +76,7 @@ Там есть `tags`, `parameters`, `requestBody`, `responses` и т.д. -Эта спецификация OpenAPI, специфичная для *операции пути*, обычно генерируется автоматически **FastAPI**, но вы также можете её расширить. +Эта специфичная для *операции пути* схема OpenAPI обычно генерируется автоматически **FastAPI**, но вы также можете её расширить. /// tip | Совет @@ -129,13 +129,13 @@ } ``` -### Пользовательская схема OpenAPI для операции пути { #custom-openapi-path-operation-schema } +### Пользовательская схема OpenAPI для *операции пути* { #custom-openapi-path-operation-schema } -Словарь в `openapi_extra` будет объединён с автоматически сгенерированной схемой OpenAPI для *операции пути*. +Словарь в `openapi_extra` будет глубоко объединён с автоматически сгенерированной схемой OpenAPI для *операции пути*. Таким образом, вы можете добавить дополнительные данные к автоматически сгенерированной схеме. -Например, вы можете решить читать и валидировать запрос своим кодом, не используя автоматические возможности FastAPI и Pydantic, но при этом захотите описать запрос в схеме OpenAPI. +Например, вы можете решить читать и валидировать HTTP-запрос своим кодом, не используя автоматические возможности FastAPI и Pydantic, но при этом захотите описать HTTP-запрос в схеме OpenAPI. Это можно сделать с помощью `openapi_extra`: @@ -149,52 +149,20 @@ Используя тот же приём, вы можете воспользоваться Pydantic-моделью, чтобы определить JSON Schema, которая затем будет включена в пользовательский раздел схемы OpenAPI для *операции пути*. -И вы можете сделать это, даже если тип данных в запросе — не JSON. +И вы можете сделать это, даже если тип данных в HTTP-запросе — не JSON. -Например, в этом приложении мы не используем встроенную функциональность FastAPI для извлечения JSON Schema из моделей Pydantic, равно как и автоматическую валидацию JSON. Мы объявляем тип содержимого запроса как YAML, а не JSON: - -//// tab | Pydantic v2 +Например, в этом приложении мы не используем встроенную функциональность FastAPI для извлечения JSON Schema из моделей Pydantic, равно как и автоматическую валидацию JSON. Мы объявляем тип содержимого HTTP-запроса как YAML, а не JSON: {* ../../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 | Информация - -В Pydantic версии 1 метод для получения JSON Schema модели назывался `Item.schema()`, в Pydantic версии 2 метод называется `Item.model_json_schema()`. - -/// - Тем не менее, хотя мы не используем встроенную функциональность по умолчанию, мы всё равно используем Pydantic-модель, чтобы вручную сгенерировать JSON Schema для данных, которые мы хотим получить в YAML. -Затем мы работаем с запросом напрямую и извлекаем тело как `bytes`. Это означает, что FastAPI даже не попытается распарсить полезную нагрузку запроса как JSON. +Затем мы работаем с HTTP-запросом напрямую и извлекаем тело как `bytes`. Это означает, что FastAPI даже не попытается распарсить полезную нагрузку HTTP-запроса как JSON. -А затем в нашем коде мы напрямую парсим этот YAML и снова используем ту же Pydantic-модель для валидации YAML-содержимого: - -//// tab | Pydantic v2 +А затем в нашем коде мы напрямую парсим это содержимое YAML и снова используем ту же Pydantic-модель, чтобы валидировать YAML-содержимое: {* ../../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 | Информация - -В Pydantic версии 1 метод для парсинга и валидации объекта назывался `Item.parse_obj()`, в Pydantic версии 2 метод называется `Item.model_validate()`. - -/// - /// tip | Совет Здесь мы переиспользуем ту же Pydantic-модель. diff --git a/docs/ru/docs/advanced/settings.md b/docs/ru/docs/advanced/settings.md index b96ee44a3a..8408faebff 100644 --- a/docs/ru/docs/advanced/settings.md +++ b/docs/ru/docs/advanced/settings.md @@ -46,12 +46,6 @@ $ pip install "fastapi[all]" -/// info | Информация - -В Pydantic v1 он входил в основной пакет. Теперь он распространяется как отдельный пакет, чтобы вы могли установить его только при необходимости. - -/// - ### Создание объекта `Settings` { #create-the-settings-object } Импортируйте `BaseSettings` из Pydantic и создайте подкласс, очень похожий на Pydantic‑модель. @@ -60,24 +54,8 @@ $ pip install "fastapi[all]" Вы можете использовать все те же возможности валидации и инструменты, что и для Pydantic‑моделей, например разные типы данных и дополнительную валидацию через `Field()`. -//// tab | Pydantic v2 - {* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *} -//// - -//// tab | Pydantic v1 - -/// info | Информация - -В Pydantic v1 вы бы импортировали `BaseSettings` напрямую из `pydantic`, а не из `pydantic_settings`. - -/// - -{* ../../docs_src/settings/tutorial001_pv1_py39.py hl[2,5:8,11] *} - -//// - /// tip | Совет Если вам нужно что-то быстро скопировать и вставить, не используйте этот пример — воспользуйтесь последним ниже. @@ -215,8 +193,6 @@ APP_NAME="ChimichangApp" Затем обновите ваш `config.py` так: -//// tab | Pydantic v2 - {* ../../docs_src/settings/app03_an_py39/config.py hl[9] *} /// tip | Совет @@ -225,26 +201,6 @@ APP_NAME="ChimichangApp" /// -//// - -//// tab | Pydantic v1 - -{* ../../docs_src/settings/app03_an_py39/config_pv1.py hl[9:10] *} - -/// tip | Совет - -Класс `Config` используется только для конфигурации Pydantic. Подробнее см. Pydantic Model Config. - -/// - -//// - -/// info | Информация - -В Pydantic версии 1 конфигурация задавалась во внутреннем классе `Config`, в Pydantic версии 2 — в атрибуте `model_config`. Этот атрибут принимает `dict`, и чтобы получить автозавершение и ошибки «на лету», вы можете импортировать и использовать `SettingsConfigDict` для описания этого `dict`. - -/// - Здесь мы задаем параметр конфигурации `env_file` внутри вашего класса Pydantic `Settings` и устанавливаем значение равным имени файла dotenv, который хотим использовать. ### Создание `Settings` только один раз с помощью `lru_cache` { #creating-the-settings-only-once-with-lru-cache } diff --git a/docs/ru/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md b/docs/ru/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md index 95481bc668..2b47c08f67 100644 --- a/docs/ru/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md +++ b/docs/ru/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md @@ -2,21 +2,23 @@ Если у вас старое приложение FastAPI, возможно, вы используете Pydantic версии 1. -FastAPI поддерживает и Pydantic v1, и v2 начиная с версии 0.100.0. +FastAPI версии 0.100.0 поддерживал либо Pydantic v1, либо v2. Он использовал ту версию, которая была установлена. -Если у вас был установлен Pydantic v2, использовался он. Если вместо этого был установлен Pydantic v1 — использовался он. +FastAPI версии 0.119.0 добавил частичную поддержку Pydantic v1 изнутри Pydantic v2 (как `pydantic.v1`), чтобы упростить миграцию на v2. -Сейчас Pydantic v1 объявлен устаревшим, и поддержка его будет удалена в следующих версиях FastAPI, поэтому вам следует **перейти на Pydantic v2**. Так вы получите последние возможности, улучшения и исправления. +FastAPI 0.126.0 убрал поддержку Pydantic v1, при этом ещё некоторое время продолжал поддерживать `pydantic.v1`. /// warning | Предупреждение -Кроме того, команда Pydantic прекратила поддержку Pydantic v1 для последних версий Python, начиная с **Python 3.14**. +Команда Pydantic прекратила поддержку Pydantic v1 для последних версий Python, начиная с **Python 3.14**. + +Это включает `pydantic.v1`, который больше не поддерживается в Python 3.14 и выше. Если вы хотите использовать последние возможности Python, вам нужно убедиться, что вы используете Pydantic v2. /// -Если у вас старое приложение FastAPI с Pydantic v1, здесь я покажу, как мигрировать на Pydantic v2, и **новые возможности в FastAPI 0.119.0**, которые помогут выполнить постепенную миграцию. +Если у вас старое приложение FastAPI с Pydantic v1, здесь я покажу, как мигрировать на Pydantic v2, и **возможности FastAPI 0.119.0**, которые помогут выполнить постепенную миграцию. ## Официальное руководство { #official-guide } @@ -38,13 +40,13 @@ FastAPI поддерживает и Pydantic v1, и v2 начиная с вер Вы можете использовать `bump-pydantic` от той же команды Pydantic. -Этот инструмент поможет автоматически внести большую часть необходимых изменений в код. +Этот инструмент поможет автоматически изменить большую часть кода, который нужно изменить. -После этого запустите тесты и проверьте, что всё работает. Если да — на этом всё. 😎 +После этого вы можете запустить тесты и проверить, что всё работает. Если да — на этом всё. 😎 ## Pydantic v1 в v2 { #pydantic-v1-in-v2 } -Pydantic v2 включает всё из Pydantic v1 как подмодуль `pydantic.v1`. +Pydantic v2 включает всё из Pydantic v1 как подмодуль `pydantic.v1`. Но это больше не поддерживается в версиях Python выше 3.13. Это означает, что вы можете установить последнюю версию Pydantic v2 и импортировать и использовать старые компоненты Pydantic v1 из этого подмодуля так, как если бы у вас был установлен старый Pydantic v1. @@ -52,7 +54,7 @@ Pydantic v2 включает всё из Pydantic v1 как подмодуль ` ### Поддержка FastAPI для Pydantic v1 внутри v2 { #fastapi-support-for-pydantic-v1-in-v2 } -Начиная с FastAPI 0.119.0, есть также частичная поддержка Pydantic v1 в составе Pydantic v2, чтобы упростить миграцию на v2. +Начиная с FastAPI 0.119.0, есть также частичная поддержка Pydantic v1 изнутри Pydantic v2, чтобы упростить миграцию на v2. Таким образом, вы можете обновить Pydantic до последней версии 2 и сменить импорты на подмодуль `pydantic.v1` — во многих случаях всё просто заработает. @@ -106,7 +108,7 @@ graph TB style V2Field fill:#f9fff3 ``` -В некоторых случаях можно использовать и модели Pydantic v1, и v2 в одной и той же операции пути (обработчике пути) вашего приложения FastAPI: +В некоторых случаях можно использовать и модели Pydantic v1, и v2 в одной и той же **операции пути** (обработчике пути) вашего приложения FastAPI: {* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *} @@ -122,12 +124,12 @@ graph TB /// tip | Совет -Сначала попробуйте `bump-pydantic`. Если тесты проходят и всё работает, вы справились одной командой. ✨ +Сначала попробуйте `bump-pydantic`: если тесты проходят и всё работает, вы справились одной командой. ✨ /// Если `bump-pydantic` не подходит для вашего случая, вы можете использовать поддержку одновременной работы моделей Pydantic v1 и v2 в одном приложении, чтобы мигрировать на Pydantic v2 постепенно. -Сначала обновите Pydantic до последней 2-й версии и измените импорты так, чтобы все ваши модели использовали `pydantic.v1`. +Сначала вы можете обновить Pydantic до последней 2-й версии и изменить импорты так, чтобы все ваши модели использовали `pydantic.v1`. -Затем начните мигрировать ваши модели с Pydantic v1 на v2 группами, поэтапно. 🚶 +Затем вы можете начать мигрировать ваши модели с Pydantic v1 на v2 группами, поэтапно. 🚶 diff --git a/docs/ru/docs/how-to/separate-openapi-schemas.md b/docs/ru/docs/how-to/separate-openapi-schemas.md index 5b12140167..8f6c83e7ec 100644 --- a/docs/ru/docs/how-to/separate-openapi-schemas.md +++ b/docs/ru/docs/how-to/separate-openapi-schemas.md @@ -2,7 +2,7 @@ При использовании **Pydantic v2** сгенерированный OpenAPI становится чуть более точным и **корректным**, чем раньше. 😎 -На самом деле, в некоторых случаях в OpenAPI будет даже **две JSON схемы** для одной и той же Pydantic‑модели: для входа и для выхода — в зависимости от наличия **значений по умолчанию**. +На самом деле, в некоторых случаях в OpenAPI будет даже **две JSON-схемы** для одной и той же Pydantic‑модели: для входа и для выхода — в зависимости от наличия **значений по умолчанию**. Посмотрим, как это работает, и как это изменить при необходимости. @@ -34,7 +34,7 @@ {* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *} -…то, поскольку у `description` есть значение по умолчанию, даже если вы **ничего не вернёте** для этого поля, оно всё равно будет иметь это **значение по умолчанию**. +…то, поскольку у `description` есть значение по умолчанию, если вы **ничего не вернёте** для этого поля, оно всё равно будет иметь это **значение по умолчанию**. ### Модель для данных ответа { #model-for-output-response-data } @@ -46,13 +46,13 @@ Это означает, что у него **всегда будет какое‑то значение**, просто иногда это значение может быть `None` (или `null` в JSON). -Следовательно, клиентам, использующим ваш API, не нужно проверять наличие этого значения: они могут **исходить из того, что поле всегда присутствует**, а в некоторых случаях имеет значение по умолчанию `None`. +Это означает, что клиентам, использующим ваш API, не нужно проверять, существует ли это значение или нет: они могут **исходить из того, что поле всегда присутствует**, но в некоторых случаях оно будет иметь значение по умолчанию `None`. В OpenAPI это описывается тем, что поле помечается как **обязательное**, поскольку оно всегда присутствует. Из‑за этого JSON Schema для модели может отличаться в зависимости от использования для **входа** или **выхода**: -* для **входа** `description` не будет обязательным +* для **входа** `description` **не будет обязательным** * для **выхода** оно будет **обязательным** (и при этом может быть `None`, или, в терминах JSON, `null`) ### Выходная модель в документации { #model-for-output-in-docs } @@ -81,9 +81,9 @@ Однако бывают случаи, когда вы хотите иметь **одну и ту же схему для входа и выхода**. -Главный сценарий — когда у вас уже есть сгенерированный клиентский код/SDK, и вы пока не хотите обновлять весь этот автогенерируемый код/SDK (рано или поздно вы это сделаете, но не сейчас). +Главный сценарий — когда у вас уже есть сгенерированный клиентский код/SDK, и вы пока не хотите обновлять весь этот автогенерируемый клиентский код/SDK, вероятно, вы захотите сделать это в какой-то момент, но, возможно, не прямо сейчас. -В таком случае вы можете отключить эту функциональность в FastAPI с помощью параметра `separate_input_output_schemas=False`. +В таком случае вы можете отключить эту функциональность в **FastAPI** с помощью параметра `separate_input_output_schemas=False`. /// info | Информация @@ -95,10 +95,8 @@ ### Одна и та же схема для входной и выходной моделей в документации { #same-schema-for-input-and-output-models-in-docs } -Теперь для этой модели будет одна общая схема и для входа, и для выхода — только `Item`, и в ней `description` будет **не обязательным**: +И теперь для модели будет одна общая схема и для входа, и для выхода — только `Item`, и в ней `description` будет **не обязательным**:
- -Это то же поведение, что и в Pydantic v1. 🤓 diff --git a/docs/ru/docs/index.md b/docs/ru/docs/index.md index b562cbe5bc..02b1c9a286 100644 --- a/docs/ru/docs/index.md +++ b/docs/ru/docs/index.md @@ -5,10 +5,10 @@

- FastAPI + FastAPI

- Фреймворк FastAPI: высокая производительность, прост в изучении, быстрый в разработке, готов к продакшн + Фреймворк FastAPI: высокая производительность, прост в изучении, позволяет быстро писать код, готов к продакшн

@@ -40,7 +40,7 @@ FastAPI — это современный, быстрый (высокопрои * **Скорость**: Очень высокая производительность, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic). [Один из самых быстрых доступных фреймворков Python](#performance). * **Быстрота разработки**: Увеличьте скорость разработки фич примерно на 200–300%. * * **Меньше ошибок**: Сократите примерно на 40% количество ошибок, вызванных человеком (разработчиком). * -* **Интуитивность**: Отличная поддержка редактора кода. Автозавершение везде. Меньше времени на отладку. +* **Интуитивность**: Отличная поддержка редактора кода. Автозавершение везде. Меньше времени на отладку. * **Простота**: Разработан так, чтобы его было легко использовать и осваивать. Меньше времени на чтение документации. * **Краткость**: Минимизируйте дублирование кода. Несколько возможностей из каждого объявления параметров. Меньше ошибок. * **Надежность**: Получите код, готовый к продакшн. С автоматической интерактивной документацией. @@ -117,6 +117,12 @@ FastAPI — это современный, быстрый (высокопрои --- +## Мини-документальный фильм о FastAPI { #fastapi-mini-documentary } + +В конце 2025 года вышел мини-документальный фильм о FastAPI, вы можете посмотреть его онлайн: + +FastAPI Mini Documentary + ## **Typer**, FastAPI для CLI { #typer-the-fastapi-of-clis } @@ -257,7 +263,7 @@ INFO: Application startup complete. * Получает HTTP-запросы по _путям_ `/` и `/items/{item_id}`. * Оба _пути_ используют `GET` операции (также известные как HTTP _методы_). -* _Путь_ `/items/{item_id}` имеет _параметр пути_ `item_id`, который должен быть `int`. +* _Путь_ `/items/{item_id}` имеет _path-параметр_ `item_id`, который должен быть `int`. * _Путь_ `/items/{item_id}` имеет необязательный `str` _параметр запроса_ `q`. ### Интерактивная документация API { #interactive-api-docs } @@ -278,9 +284,9 @@ INFO: Application startup complete. ## Пример обновления { #example-upgrade } -Теперь измените файл `main.py`, чтобы принимать тело запроса из `PUT` запроса. +Теперь измените файл `main.py`, чтобы принимать тело запроса из `PUT` HTTP-запроса. -Объявите тело, используя стандартные типы Python, спасибо Pydantic. +Объявите тело запроса, используя стандартные типы Python, спасибо Pydantic. ```Python hl_lines="4 9-12 25-27" from typing import Union @@ -318,7 +324,7 @@ def update_item(item_id: int, item: Item): Перейдите на http://127.0.0.1:8000/docs. -* Интерактивная документация API будет автоматически обновлена, включая новое тело: +* Интерактивная документация API будет автоматически обновлена, включая новое тело запроса: ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) @@ -334,13 +340,13 @@ def update_item(item_id: int, item: Item): Теперь откройте http://127.0.0.1:8000/redoc. -* Альтернативная документация также отразит новый параметр запроса и тело: +* Альтернативная документация также отразит новый параметр запроса и тело запроса: ![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) ### Подведём итоги { #recap } -Итак, вы объявляете **один раз** типы параметров, тела запроса и т.д. как параметры функции. +Итак, вы объявляете **один раз** типы параметров, тело запроса и т.д. как параметры функции. Вы делаете это с помощью стандартных современных типов Python. @@ -390,13 +396,13 @@ item: Item Возвращаясь к предыдущему примеру кода, **FastAPI** будет: -* Валидировать наличие `item_id` в пути для `GET` и `PUT` запросов. -* Валидировать, что `item_id` имеет тип `int` для `GET` и `PUT` запросов. +* Валидировать наличие `item_id` в пути для `GET` и `PUT` HTTP-запросов. +* Валидировать, что `item_id` имеет тип `int` для `GET` и `PUT` HTTP-запросов. * Если это не так, клиент увидит полезную понятную ошибку. -* Проверять, есть ли необязательный параметр запроса с именем `q` (например, `http://127.0.0.1:8000/items/foo?q=somequery`) для `GET` запросов. +* Проверять, есть ли необязательный параметр запроса с именем `q` (например, `http://127.0.0.1:8000/items/foo?q=somequery`) для `GET` HTTP-запросов. * Поскольку параметр `q` объявлен с `= None`, он необязателен. * Без `None` он был бы обязательным (как тело запроса в случае с `PUT`). -* Для `PUT` запросов к `/items/{item_id}` читать тело запроса как JSON: +* Для `PUT` HTTP-запросов к `/items/{item_id}` читать тело запроса как JSON: * Проверять, что есть обязательный атрибут `name`, который должен быть `str`. * Проверять, что есть обязательный атрибут `price`, который должен быть `float`. * Проверять, что есть необязательный атрибут `is_offer`, который должен быть `bool`, если он присутствует. @@ -435,11 +441,11 @@ item: Item Более полный пример с дополнительными возможностями см. в Учебник - Руководство пользователя. -**Осторожно, спойлер**: учебник - руководство включает: +**Осторожно, спойлер**: учебник - руководство пользователя включает: * Объявление **параметров** из других источников: **HTTP-заголовки**, **cookies**, **поля формы** и **файлы**. * Как задать **ограничения валидации** вроде `maximum_length` или `regex`. -* Очень мощную и простую в использовании систему **внедрения зависимостей**. +* Очень мощную и простую в использовании систему **внедрения зависимостей**. * Безопасность и аутентификацию, включая поддержку **OAuth2** с **JWT токенами** и **HTTP Basic** аутентификацию. * Более продвинутые (но столь же простые) приёмы объявления **глубоко вложенных JSON-моделей** (спасибо Pydantic). * Интеграцию **GraphQL** с Strawberry и другими библиотеками. @@ -524,11 +530,11 @@ FastAPI зависит от Pydantic и Starlette. * httpx — обязателен, если вы хотите использовать `TestClient`. * jinja2 — обязателен, если вы хотите использовать конфигурацию шаблонов по умолчанию. -* python-multipart — обязателен, если вы хотите поддерживать «парсинг» форм через `request.form()`. +* python-multipart - обязателен, если вы хотите поддерживать «парсинг» форм через `request.form()`. Используется FastAPI: -* uvicorn — сервер, который загружает и обслуживает ваше приложение. Включает `uvicorn[standard]`, содержащий некоторые зависимости (например, `uvloop`), нужные для высокой производительности. +* uvicorn — сервер, который загружает и «отдаёт» ваше приложение. Включает `uvicorn[standard]`, содержащий некоторые зависимости (например, `uvloop`), нужные для высокой производительности. * `fastapi-cli[standard]` — чтобы предоставить команду `fastapi`. * Включает `fastapi-cloud-cli`, который позволяет развернуть ваше приложение FastAPI в FastAPI Cloud. diff --git a/docs/ru/docs/translation-banner.md b/docs/ru/docs/translation-banner.md new file mode 100644 index 0000000000..78ebd676c5 --- /dev/null +++ b/docs/ru/docs/translation-banner.md @@ -0,0 +1,11 @@ +/// details | 🌐 Перевод выполнен с помощью ИИ и людей + +Этот перевод был сделан ИИ под руководством людей. 🤝 + +В нем могут быть ошибки из-за неправильного понимания оригинального смысла или неестественности и т. д. 🤖 + +Вы можете улучшить этот перевод, [помогая нам лучше направлять ИИ LLM](https://fastapi.tiangolo.com/ru/contributing/#translations). + +[Английская версия](ENGLISH_VERSION_URL) + +/// diff --git a/docs/ru/docs/tutorial/bigger-applications.md b/docs/ru/docs/tutorial/bigger-applications.md index 5e5d6ada94..76304523c9 100644 --- a/docs/ru/docs/tutorial/bigger-applications.md +++ b/docs/ru/docs/tutorial/bigger-applications.md @@ -1,4 +1,4 @@ -# Большие приложения, в которых много файлов { #bigger-applications-multiple-files } +# Большие приложения — несколько файлов { #bigger-applications-multiple-files } При построении приложения или веб-API нам редко удается поместить всё в один файл. @@ -31,7 +31,7 @@ /// tip | Подсказка -Обратите внимание, что в каждом каталоге и подкаталоге имеется файл `__init__.py` +Есть несколько файлов `__init__.py`: по одному в каждом каталоге или подкаталоге. Это как раз то, что позволяет импортировать код из одного файла в другой. @@ -43,61 +43,63 @@ from app.routers import items /// -* Всё помещается в каталоге `app`. В нём также находится пустой файл `app/__init__.py`. Таким образом, `app` является "Python-пакетом" (коллекцией модулей Python). -* Он содержит файл `app/main.py`. Данный файл является частью пакета (т.е. находится внутри каталога, содержащего файл `__init__.py`), и, соответственно, он является модулем пакета: `app.main`. +* Всё помещается в каталоге `app`. В нём также находится пустой файл `app/__init__.py`. Таким образом, `app` является "Python-пакетом" (коллекцией "Python-модулей"): `app`. +* Он содержит файл `app/main.py`. Данный файл является частью Python-пакета (т.е. находится внутри каталога, содержащего файл `__init__.py`), и, соответственно, он является модулем этого пакета: `app.main`. * Он также содержит файл `app/dependencies.py`, который также, как и `app/main.py`, является модулем: `app.dependencies`. -* Здесь также находится подкаталог `app/routers/`, содержащий `__init__.py`. Он является суб-пакетом: `app.routers`. -* Файл `app/routers/items.py` находится внутри пакета `app/routers/`. Таким образом, он является суб-модулем: `app.routers.items`. -* Точно также `app/routers/users.py` является ещё одним суб-модулем: `app.routers.users`. -* Подкаталог `app/internal/`, содержащий файл `__init__.py`, является ещё одним суб-пакетом: `app.internal`. -* А файл `app/internal/admin.py` является ещё одним суб-модулем: `app.internal.admin`. +* Здесь также находится подкаталог `app/routers/`, содержащий `__init__.py`. Он является Python-подпакетом: `app.routers`. +* Файл `app/routers/items.py` находится внутри пакета `app/routers/`. Таким образом, он является подмодулем: `app.routers.items`. +* Точно так же `app/routers/users.py` является ещё одним подмодулем: `app.routers.users`. +* Подкаталог `app/internal/`, содержащий файл `__init__.py`, является ещё одним Python-подпакетом: `app.internal`. +* А файл `app/internal/admin.py` является ещё одним подмодулем: `app.internal.admin`. Та же самая файловая структура приложения, но с комментариями: -``` +```bash . ├── app # "app" пакет │   ├── __init__.py # этот файл превращает "app" в "Python-пакет" │   ├── main.py # модуль "main", напр.: import app.main │   ├── dependencies.py # модуль "dependencies", напр.: import app.dependencies -│   └── routers # суб-пакет "routers" -│   │ ├── __init__.py # превращает "routers" в суб-пакет -│   │ ├── items.py # суб-модуль "items", напр.: import app.routers.items -│   │ └── users.py # суб-модуль "users", напр.: import app.routers.users -│   └── internal # суб-пакет "internal" -│   ├── __init__.py # превращает "internal" в суб-пакет -│   └── admin.py # суб-модуль "admin", напр.: import app.internal.admin +│   └── routers # подпакет "routers" +│   │ ├── __init__.py # превращает "routers" в подпакет +│   │ ├── items.py # подмодуль "items", напр.: import app.routers.items +│   │ └── users.py # подмодуль "users", напр.: import app.routers.users +│   └── internal # подпакет "internal" +│   ├── __init__.py # превращает "internal" в подпакет +│   └── admin.py # подмодуль "admin", напр.: import app.internal.admin ``` ## `APIRouter` { #apirouter } -Давайте предположим, что для работы с пользователями используется отдельный файл (суб-модуль) `/app/routers/users.py`. +Давайте предположим, что для работы с пользователями используется отдельный файл (подмодуль) `/app/routers/users.py`. -Для лучшей организации приложения, вы хотите отделить операции пути, связанные с пользователями, от остального кода. +Вы хотите отделить *операции пути*, связанные с пользователями, от остального кода, чтобы сохранить порядок. -Но так, чтобы эти операции по-прежнему оставались частью **FastAPI** приложения/веб-API (частью одного пакета) +Но это всё равно часть того же приложения/веб-API на **FastAPI** (часть того же «Python-пакета»). -С помощью `APIRouter` вы можете создать *операции пути* (*эндпоинты*) для данного модуля. +С помощью `APIRouter` вы можете создать *операции пути* для этого модуля. ### Импорт `APIRouter` { #import-apirouter } -Точно также, как и в случае с классом `FastAPI`, вам нужно импортировать и создать объект класса `APIRouter`. +Точно так же, как и в случае с классом `FastAPI`, вам нужно импортировать и создать его «экземпляр»: {* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *} -### Создание *эндпоинтов* с помощью `APIRouter` { #path-operations-with-apirouter } +### *Операции пути* с `APIRouter` { #path-operations-with-apirouter } -В дальнейшем используйте `APIRouter` для объявления *эндпоинтов*, точно также, как вы используете класс `FastAPI`: +И затем вы используете его, чтобы объявить ваши *операции пути*. + +Используйте его так же, как вы использовали бы класс `FastAPI`: {* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *} -Вы можете думать об `APIRouter` как об "уменьшенной версии" класса FastAPI`. +Вы можете думать об `APIRouter` как об «мини-классе `FastAPI`». -`APIRouter` поддерживает все те же самые опции. +Поддерживаются все те же опции. -`APIRouter` поддерживает все те же самые параметры, такие как `parameters`, `responses`, `dependencies`, `tags`, и т. д. +Все те же `parameters`, `responses`, `dependencies`, `tags` и т.д. /// tip | Подсказка @@ -105,21 +107,21 @@ from app.routers import items /// -Мы собираемся подключить данный `APIRouter` к нашему основному приложению на `FastAPI`, но сначала давайте проверим зависимости и создадим ещё один модуль с `APIRouter`. +Мы собираемся подключить данный `APIRouter` к нашему основному приложению на `FastAPI`, но сначала давайте проверим зависимости и ещё один `APIRouter`. ## Зависимости { #dependencies } -Нам понадобятся некоторые зависимости, которые мы будем использовать в разных местах нашего приложения. +Мы видим, что нам понадобятся некоторые зависимости, которые будут использоваться в нескольких местах приложения. -Мы поместим их в отдельный модуль `dependencies` (`app/dependencies.py`). +Поэтому мы поместим их в отдельный модуль `dependencies` (`app/dependencies.py`). -Теперь мы воспользуемся простой зависимостью, чтобы прочитать кастомизированный `X-Token` из заголовка: +Теперь мы воспользуемся простой зависимостью, чтобы прочитать кастомный HTTP-заголовок `X-Token`: {* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *} /// tip | Подсказка -Для простоты мы воспользовались неким воображаемым заголовоком. +Для простоты мы воспользовались выдуманным заголовком. В реальных случаях для получения наилучших результатов используйте интегрированные [утилиты безопасности](security/index.md){.internal-link target=_blank}. @@ -127,30 +129,29 @@ from app.routers import items ## Ещё один модуль с `APIRouter` { #another-module-with-apirouter } -Давайте также предположим, что у вас есть *эндпоинты*, отвечающие за обработку "items", и они находятся в модуле `app/routers/items.py`. +Давайте также предположим, что у вас есть эндпоинты, отвечающие за обработку «items» в вашем приложении, и они находятся в модуле `app/routers/items.py`. -У вас определены следующие *операции пути* (*эндпоинты*): +У вас определены *операции пути* для: * `/items/` * `/items/{item_id}` -Тут всё точно также, как и в ситуации с `app/routers/users.py`. +Тут всё та же структура, как и в случае с `app/routers/users.py`. -Но теперь мы хотим поступить немного умнее и слегка упростить код. +Но мы хотим поступить умнее и слегка упростить код. -Мы знаем, что все *эндпоинты* данного модуля имеют некоторые общие свойства: +Мы знаем, что все *операции пути* этого модуля имеют одинаковые: -* Префикс пути: `/items`. -* Теги: (один единственный тег: `items`). -* Дополнительные ответы (responses) -* Зависимости: использование созданной нами зависимости `X-token` +* `prefix` пути: `/items`. +* `tags`: (один единственный тег: `items`). +* Дополнительные `responses`. +* `dependencies`: всем им нужна та зависимость `X-Token`, которую мы создали. -Таким образом, вместо того чтобы добавлять все эти свойства в функцию каждого отдельного *эндпоинта*, -мы добавим их в `APIRouter`. +Таким образом, вместо того чтобы добавлять всё это в каждую *операцию пути*, мы можем добавить это в `APIRouter`. {* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *} -Так как каждый *эндпоинт* начинается с символа `/`: +Так как путь каждой *операции пути* должен начинаться с `/`, как здесь: ```Python hl_lines="1" @router.get("/{item_id}") @@ -162,73 +163,74 @@ async def read_item(item_id: str): В нашем случае префиксом является `/items`. -Мы также можем добавить в наш маршрутизатор (router) список `тегов` (`tags`) и дополнительных `ответов` (`responses`), которые являются общими для каждого *эндпоинта*. +Мы также можем добавить список `tags` и дополнительные `responses`, которые будут применяться ко всем *операциям пути*, включённым в этот маршрутизатор. -И ещё мы можем добавить в наш маршрутизатор список `зависимостей`, которые должны вызываться при каждом обращении к *эндпоинтам*. +И ещё мы можем добавить список `dependencies`, которые будут добавлены ко всем *операциям пути* в маршрутизаторе и будут выполняться/разрешаться для каждого HTTP-запроса к ним. /// tip | Подсказка -Обратите внимание, что также, как и в случае с зависимостями в декораторах *эндпоинтов* ([зависимости в декораторах операций пути](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), никакого значения в *функцию эндпоинта* передано не будет. +Обратите внимание, что так же, как и в случае с [зависимостями в декораторах *операций пути*](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, никакое значение не будет передано в вашу *функцию-обработчик пути*. /// -В результате мы получим следующие эндпоинты: +В результате пути для items теперь такие: * `/items/` * `/items/{item_id}` ...как мы и планировали. -* Они будут помечены тегами из заданного списка, в нашем случае это `"items"`. - * Эти теги особенно полезны для системы автоматической интерактивной документации (с использованием OpenAPI). -* Каждый из них будет включать предопределенные ответы `responses`. -* Каждый *эндпоинт* будет иметь список зависимостей (`dependencies`), исполняемых перед вызовом *эндпоинта*. - * Если вы определили зависимости в самой операции пути, **то она также будет выполнена**. - * Сначала выполняются зависимости маршрутизатора, затем вызываются [зависимости в декораторе](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, и, наконец, обычные параметрические зависимости. - * Вы также можете добавить [зависимости `Security` с `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}. +* Они будут помечены списком тегов, содержащим одну строку `"items"`. + * Эти «теги» особенно полезны для систем автоматической интерактивной документации (с использованием OpenAPI). +* Все они будут включать предопределённые `responses`. +* Все эти *операции пути* будут иметь список `dependencies`, вычисляемых/выполняемых перед ними. + * Если вы также объявите зависимости в конкретной *операции пути*, **они тоже будут выполнены**. + * Сначала выполняются зависимости маршрутизатора, затем [`dependencies` в декораторе](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, и затем обычные параметрические зависимости. + * Вы также можете добавить [`Security`-зависимости с `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}. /// tip | Подсказка -Например, с помощью зависимостей в `APIRouter` мы можем потребовать аутентификации для доступа ко всей группе *эндпоинтов*, не указывая зависимости для каждой отдельной функции *эндпоинта*. +Например, с помощью зависимостей в `APIRouter` мы можем потребовать аутентификации для доступа ко всей группе *операций пути*. Даже если зависимости не добавляются по отдельности к каждой из них. /// /// check | Заметка -Параметры `prefix`, `tags`, `responses` и `dependencies` относятся к функционалу **FastAPI**, помогающему избежать дублирования кода. +Параметры `prefix`, `tags`, `responses` и `dependencies` — это (как и во многих других случаях) просто возможность **FastAPI**, помогающая избежать дублирования кода. /// ### Импорт зависимостей { #import-the-dependencies } -Наш код находится в модуле `app.routers.items` (файл `app/routers/items.py`). +Этот код находится в модуле `app.routers.items`, в файле `app/routers/items.py`. -И нам нужно вызвать функцию зависимости из модуля `app.dependencies` (файл `app/dependencies.py`). +И нам нужно получить функцию зависимости из модуля `app.dependencies`, файла `app/dependencies.py`. -Мы используем операцию относительного импорта `..` для импорта зависимости: +Поэтому мы используем относительный импорт с `..` для зависимостей: {* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *} -#### Как работает относительный импорт? { #how-relative-imports-work } +#### Как работает относительный импорт { #how-relative-imports-work } /// tip | Подсказка -Если вы прекрасно знаете, как работает импорт в Python, то переходите к следующему разделу. +Если вы прекрасно знаете, как работает импорт, переходите к следующему разделу ниже. /// -Одна точка `.`, как в данном примере: +Одна точка `.`, как здесь: ```Python from .dependencies import get_token_header ``` + означает: -* Начните с пакета, в котором находится данный модуль (файл `app/routers/items.py` расположен в каталоге `app/routers/`)... -* ... найдите модуль `dependencies` (файл `app/routers/dependencies.py`)... -* ... и импортируйте из него функцию `get_token_header`. +* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) (каталог `app/routers/`)... +* найти модуль `dependencies` (воображаемый файл `app/routers/dependencies.py`)... +* и импортировать из него функцию `get_token_header`. -К сожалению, такого файла не существует, и наши зависимости находятся в файле `app/dependencies.py`. +Но такого файла не существует, наши зависимости находятся в файле `app/dependencies.py`. Вспомните, как выглядит файловая структура нашего приложения: @@ -236,7 +238,7 @@ from .dependencies import get_token_header --- -Две точки `..`, как в данном примере: +Две точки `..`, как здесь: ```Python from ..dependencies import get_token_header @@ -244,12 +246,12 @@ from ..dependencies import get_token_header означают: -* Начните с пакета, в котором находится данный модуль (файл `app/routers/items.py` находится в каталоге `app/routers/`)... -* ... перейдите в родительский пакет (каталог `app/`)... -* ... найдите в нём модуль `dependencies` (файл `app/dependencies.py`)... -* ... и импортируйте из него функцию `get_token_header`. +* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) (каталог `app/routers/`)... +* перейти в родительский пакет (каталог `app/`)... +* и там найти модуль `dependencies` (файл `app/dependencies.py`)... +* и импортировать из него функцию `get_token_header`. -Это работает верно! 🎉 +Это работает корректно! 🎉 --- @@ -261,29 +263,29 @@ from ...dependencies import get_token_header то это бы означало: -* Начните с пакета, в котором находится данный модуль (файл `app/routers/items.py` находится в каталоге `app/routers/`)... -* ... перейдите в родительский пакет (каталог `app/`)... -* ... затем перейдите в родительский пакет текущего пакета (такого пакета не существует, `app` находится на самом верхнем уровне 😱)... -* ... найдите в нём модуль `dependencies` (файл `app/dependencies.py`)... -* ... и импортируйте из него функцию `get_token_header`. +* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) расположен в (каталоге `app/routers/`)... +* перейти в родительский пакет (каталог `app/`)... +* затем перейти в родительский пакет этого пакета (родительского пакета нет, `app` — верхний уровень 😱)... +* и там найти модуль `dependencies` (файл `app/dependencies.py`)... +* и импортировать из него функцию `get_token_header`. -Это будет относиться к некоторому пакету, находящемуся на один уровень выше чем `app/` и содержащему свой собственный файл `__init__.py`. Но ничего такого у нас нет. Поэтому это приведет к ошибке в нашем примере. 🚨 +Это ссылалось бы на какой-то пакет выше `app/`, со своим файлом `__init__.py` и т.п. Но у нас такого нет. Поэтому это вызвало бы ошибку в нашем примере. 🚨 -Теперь вы знаете, как работает импорт в Python, и сможете использовать относительное импортирование в своих собственных приложениях любого уровня сложности. 🤓 +Но теперь вы знаете, как это работает, так что можете использовать относительные импорты в своих приложениях, независимо от того, насколько они сложные. 🤓 -### Добавление пользовательских тегов (`tags`), ответов (`responses`) и зависимостей (`dependencies`) { #add-some-custom-tags-responses-and-dependencies } +### Добавление пользовательских `tags`, `responses` и `dependencies` { #add-some-custom-tags-responses-and-dependencies } -Мы не будем добавлять префикс `/items` и список тегов `tags=["items"]` для каждого *эндпоинта*, т.к. мы уже их добавили с помощью `APIRouter`. +Мы не добавляем префикс `/items` и `tags=["items"]` к каждой *операции пути*, потому что мы добавили их в `APIRouter`. -Но помимо этого мы можем добавить новые теги для каждого отдельного *эндпоинта*, а также некоторые дополнительные ответы (`responses`), характерные для данного *эндпоинта*: +Но мы всё равно можем добавить _ещё_ `tags`, которые будут применяться к конкретной *операции пути*, а также дополнительные `responses`, специфичные для этой *операции пути*: {* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *} /// tip | Подсказка -Последний *эндпоинт* будет иметь следующую комбинацию тегов: `["items", "custom"]`. +Эта последняя операция пути будет иметь комбинацию тегов: `["items", "custom"]`. -А также в его документации будут содержаться оба ответа: один для `404` и другой для `403`. +И в документации у неё будут оба ответа: один для `404` и один для `403`. /// @@ -293,29 +295,29 @@ from ...dependencies import get_token_header Именно сюда вы импортируете и именно здесь вы используете класс `FastAPI`. -Это основной файл вашего приложения, который объединяет всё в одно целое. +Это основной файл вашего приложения, который связывает всё воедино. -И теперь, когда большая часть логики приложения разделена на отдельные модули, основной файл `app/main.py` будет достаточно простым. +И так как большая часть вашей логики теперь будет находиться в отдельных специфичных модулях, основной файл будет довольно простым. ### Импорт `FastAPI` { #import-fastapi } -Вы импортируете и создаете класс `FastAPI` как обычно. +Вы импортируете и создаёте класс `FastAPI` как обычно. -Мы даже можем объявить [глобальные зависимости](dependencies/global-dependencies.md){.internal-link target=_blank}, которые будут объединены с зависимостями для каждого отдельного маршрутизатора: +И мы даже можем объявить [глобальные зависимости](dependencies/global-dependencies.md){.internal-link target=_blank}, которые будут объединены с зависимостями для каждого `APIRouter`: {* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *} ### Импорт `APIRouter` { #import-the-apirouter } -Теперь мы импортируем другие суб-модули, содержащие `APIRouter`: +Теперь мы импортируем другие подмодули, содержащие `APIRouter`: {* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *} -Так как файлы `app/routers/users.py` и `app/routers/items.py` являются суб-модулями одного и того же Python-пакета `app`, то мы сможем их импортировать, воспользовавшись операцией относительного импорта `.`. +Так как файлы `app/routers/users.py` и `app/routers/items.py` являются подмодулями, входящими в один и тот же Python-пакет `app`, мы можем использовать одну точку `.` для импорта через «относительные импорты». -### Как работает импорт? { #how-the-importing-works } +### Как работает импорт { #how-the-importing-works } -Данная строка кода: +Этот фрагмент: ```Python from .routers import items, users @@ -323,15 +325,15 @@ from .routers import items, users означает: -* Начните с пакета, в котором содержится данный модуль (файл `app/main.py` содержится в каталоге `app/`)... -* ... найдите суб-пакет `routers` (каталог `app/routers/`)... -* ... и из него импортируйте суб-модули `items` (файл `app/routers/items.py`) и `users` (файл `app/routers/users.py`)... +* Начать в том же пакете, в котором находится этот модуль (файл `app/main.py`) расположен в (каталоге `app/`)... +* найти подпакет `routers` (каталог `app/routers/`)... +* и импортировать из него подмодули `items` (файл `app/routers/items.py`) и `users` (файл `app/routers/users.py`)... -В модуле `items` содержится переменная `router` (`items.router`), та самая, которую мы создали в файле `app/routers/items.py`, она является объектом класса `APIRouter`. +В модуле `items` будет переменная `router` (`items.router`). Это та же самая, которую мы создали в файле `app/routers/items.py`, это объект `APIRouter`. -И затем мы сделаем то же самое для модуля `users`. +И затем мы делаем то же самое для модуля `users`. -Мы также могли бы импортировать и другим методом: +Мы также могли бы импортировать их так: ```Python from app.routers import items, users @@ -339,44 +341,44 @@ from app.routers import items, users /// info | Примечание -Первая версия является примером относительного импорта: +Первая версия — это «относительный импорт»: ```Python from .routers import items, users ``` -Вторая версия является примером абсолютного импорта: +Вторая версия — это «абсолютный импорт»: ```Python from app.routers import items, users ``` -Узнать больше о пакетах и модулях в Python вы можете из официальной документации Python о модулях +Чтобы узнать больше о Python-пакетах и модулях, прочитайте официальную документацию Python о модулях. /// -### Избегайте конфликтов имен { #avoid-name-collisions } +### Избегайте конфликтов имён { #avoid-name-collisions } -Вместо того чтобы импортировать только переменную `router`, мы импортируем непосредственно суб-модуль `items`. +Мы импортируем подмодуль `items` напрямую, вместо того чтобы импортировать только его переменную `router`. -Мы делаем это потому, что у нас есть ещё одна переменная `router` в суб-модуле `users`. +Это потому, что у нас также есть другая переменная с именем `router` в подмодуле `users`. -Если бы мы импортировали их одну за другой, как показано в примере: +Если бы мы импортировали их одну за другой, как здесь: ```Python from .routers.items import router from .routers.users import router ``` -то переменная `router` из `users` переписал бы переменную `router` из `items`, и у нас не было бы возможности использовать их одновременно. +то `router` из `users` перезаписал бы `router` из `items`, и мы не смогли бы использовать их одновременно. -Поэтому, для того чтобы использовать обе эти переменные в одном файле, мы импортировали соответствующие суб-модули: +Поэтому, чтобы иметь возможность использовать обе в одном файле, мы импортируем подмодули напрямую: {* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *} -### Подключение маршрутизаторов (`APIRouter`) для `users` и для `items` { #include-the-apirouters-for-users-and-items } +### Подключение `APIRouter` для `users` и `items` { #include-the-apirouters-for-users-and-items } -Давайте подключим маршрутизаторы (`router`) из суб-модулей `users` и `items`: +Теперь давайте подключим `router` из подмодулей `users` и `items`: {* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *} @@ -388,79 +390,78 @@ from .routers.users import router /// -С помощью `app.include_router()` мы можем добавить каждый из маршрутизаторов (`APIRouter`) в основное приложение `FastAPI`. +С помощью `app.include_router()` мы можем добавить каждый `APIRouter` в основное приложение `FastAPI`. -Он подключит все маршруты заданного маршрутизатора к нашему приложению. +Он включит все маршруты этого маршрутизатора как часть приложения. /// note | Технические детали -Фактически, внутри он создаст все *операции пути* для каждой операции пути объявленной в `APIRouter`. +Фактически, внутри он создаст *операцию пути* для каждой *операции пути*, объявленной в `APIRouter`. -И под капотом всё будет работать так, как будто бы мы имеем дело с одним файлом приложения. +Так что под капотом всё будет работать так, как будто всё было одним приложением. /// /// check | Заметка -При подключении маршрутизаторов не стоит беспокоиться о производительности. +При подключении маршрутизаторов не нужно беспокоиться о производительности. -Операция подключения займёт микросекунды и понадобится только при запуске приложения. +Это займёт микросекунды и произойдёт только при старте. -Таким образом, это не повлияет на производительность. ⚡ +Так что это не повлияет на производительность. ⚡ /// -### Подключение `APIRouter` с пользовательскими префиксом (`prefix`), тегами (`tags`), ответами (`responses`), и зависимостями (`dependencies`) { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies } +### Подключение `APIRouter` с пользовательскими `prefix`, `tags`, `responses` и `dependencies` { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies } Теперь давайте представим, что ваша организация передала вам файл `app/internal/admin.py`. -Он содержит `APIRouter` с некоторыми *эндпоитами* администрирования, которые ваша организация использует для нескольких проектов. +Он содержит `APIRouter` с некоторыми административными *операциями пути*, которые ваша организация использует в нескольких проектах. -В данном примере это сделать очень просто. Но давайте предположим, что поскольку файл используется для нескольких проектов, -то мы не можем модифицировать его, добавляя префиксы (`prefix`), зависимости (`dependencies`), теги (`tags`), и т.д. непосредственно в `APIRouter`: +Для этого примера всё будет очень просто. Но допустим, что поскольку он используется совместно с другими проектами в организации, мы не можем модифицировать его и добавить `prefix`, `dependencies`, `tags` и т.д. непосредственно в `APIRouter`: {* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *} -Но, несмотря на это, мы хотим использовать кастомный префикс (`prefix`) для подключенного маршрутизатора (`APIRouter`), в результате чего, каждая *операция пути* будет начинаться с `/admin`. Также мы хотим защитить наш маршрутизатор с помощью зависимостей, созданных для нашего проекта. И ещё мы хотим включить теги (`tags`) и ответы (`responses`). +Но мы всё равно хотим задать пользовательский `prefix` при подключении `APIRouter`, чтобы все его *операции пути* начинались с `/admin`, хотим защитить его с помощью `dependencies`, которые у нас уже есть для этого проекта, и хотим включить `tags` и `responses`. -Мы можем применить все вышеперечисленные настройки, не изменяя начальный `APIRouter`. Нам всего лишь нужно передать нужные параметры в `app.include_router()`. +Мы можем объявить всё это, не изменяя исходный `APIRouter`, передав эти параметры в `app.include_router()`: {* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *} -Таким образом, оригинальный `APIRouter` не будет модифицирован, и мы сможем использовать файл `app/internal/admin.py` сразу в нескольких проектах организации. +Таким образом исходный `APIRouter` не будет модифицирован, и мы сможем использовать файл `app/internal/admin.py` сразу в нескольких проектах организации. -В результате, в нашем приложении каждый *эндпоинт* модуля `admin` будет иметь: +В результате в нашем приложении каждая из *операций пути* из модуля `admin` будет иметь: * Префикс `/admin`. * Тег `admin`. * Зависимость `get_token_header`. * Ответ `418`. 🍵 -Это будет иметь место исключительно для `APIRouter` в нашем приложении, и не затронет любой другой код, использующий его. +Но это повлияет только на этот `APIRouter` в нашем приложении, а не на любой другой код, который его использует. -Например, другие проекты, могут использовать тот же самый `APIRouter` с другими методами аутентификации. +Так что, например, другие проекты могут использовать тот же `APIRouter` с другим методом аутентификации. -### Подключение отдельного *эндпоинта* { #include-a-path-operation } +### Подключение *операции пути* { #include-a-path-operation } -Мы также можем добавить *эндпоинт* непосредственно в основное приложение `FastAPI`. +Мы также можем добавлять *операции пути* напрямую в приложение `FastAPI`. -Здесь мы это делаем ... просто, чтобы показать, что это возможно 🤷: +Здесь мы делаем это... просто чтобы показать, что можем 🤷: {* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *} -и это будет работать корректно вместе с другими *эндпоинтами*, добавленными с помощью `app.include_router()`. +и это будет работать корректно вместе со всеми другими *операциями пути*, добавленными через `app.include_router()`. -/// info | Сложные технические детали +/// info | Очень технические детали -**Примечание**: это сложная техническая деталь, которую, скорее всего, **вы можете пропустить**. +**Примечание**: это очень техническая деталь, которую, вероятно, можно **просто пропустить**. --- -Маршрутизаторы (`APIRouter`) не "монтируются" по-отдельности и не изолируются от остального приложения. +`APIRouter` не «монтируются», они не изолированы от остального приложения. -Это происходит потому, что нужно включить их *эндпоинты* в OpenAPI схему и в интерфейс пользователя. +Это потому, что мы хотим включить их *операции пути* в OpenAPI-схему и пользовательские интерфейсы. -В силу того, что мы не можем их изолировать и "примонтировать" независимо от остальных, *эндпоинты* клонируются (пересоздаются) и не подключаются напрямую. +Так как мы не можем просто изолировать их и «смонтировать» независимо от остального, *операции пути* «клонируются» (пересоздаются), а не включаются напрямую. /// @@ -480,24 +481,24 @@ $ fastapi dev app/main.py Откройте документацию по адресу http://127.0.0.1:8000/docs. -Вы увидите автоматическую API документацию. Она включает в себя маршруты из суб-модулей, используя верные маршруты, префиксы и теги: +Вы увидите автоматическую документацию API, включая пути из всех подмодулей, с использованием корректных путей (и префиксов) и корректных тегов: -## Подключение существующего маршрута через новый префикс (`prefix`) { #include-the-same-router-multiple-times-with-different-prefix } +## Подключение одного и того же маршрутизатора несколько раз с разными `prefix` { #include-the-same-router-multiple-times-with-different-prefix } -Вы можете использовать `.include_router()` несколько раз с одним и тем же маршрутом, применив различные префиксы. +Вы можете использовать `.include_router()` несколько раз с *одним и тем же* маршрутизатором, используя разные префиксы. -Это может быть полезным, если нужно предоставить доступ к одному и тому же API через различные префиксы, например, `/api/v1` и `/api/latest`. +Это может быть полезно, например, чтобы предоставить доступ к одному и тому же API с разными префиксами, например `/api/v1` и `/api/latest`. -Это продвинутый способ, который вам может и не пригодится. Мы приводим его на случай, если вдруг вам это понадобится. +Это продвинутое использование, которое вам может и не понадобиться, но оно есть на случай, если понадобится. -## Включение одного маршрутизатора (`APIRouter`) в другой { #include-an-apirouter-in-another } +## Подключение `APIRouter` в другой `APIRouter` { #include-an-apirouter-in-another } -Точно так же, как вы включаете `APIRouter` в приложение `FastAPI`, вы можете включить `APIRouter` в другой `APIRouter`: +Точно так же, как вы можете подключить `APIRouter` к приложению `FastAPI`, вы можете подключить `APIRouter` к другому `APIRouter`, используя: ```Python router.include_router(other_router) ``` -Удостоверьтесь, что вы сделали это до того, как подключить маршрутизатор (`router`) к вашему `FastAPI` приложению, и *эндпоинты* маршрутизатора `other_router` были также подключены. +Убедитесь, что вы сделали это до подключения `router` к приложению `FastAPI`, чтобы *операции пути* из `other_router` также были подключены. diff --git a/docs/ru/docs/tutorial/body-updates.md b/docs/ru/docs/tutorial/body-updates.md index 73f4e66c76..4a7adb2559 100644 --- a/docs/ru/docs/tutorial/body-updates.md +++ b/docs/ru/docs/tutorial/body-updates.md @@ -2,13 +2,13 @@ ## Обновление с заменой при помощи `PUT` { #update-replacing-with-put } -Для полного обновления элемента можно воспользоваться операцией HTTP `PUT`. +Чтобы обновить элемент, вы можете использовать операцию HTTP `PUT`. Вы можете использовать `jsonable_encoder`, чтобы преобразовать входные данные в данные, которые можно сохранить как JSON (например, в NoSQL-базе данных). Например, преобразование `datetime` в `str`. {* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *} -`PUT` используется для получения данных, которые должны полностью заменить существующие данные. +`PUT` используется для получения данных, которые должны заменить существующие данные. ### Предупреждение о замене { #warning-about-replacing } @@ -24,11 +24,11 @@ поскольку оно не включает уже сохраненный атрибут `"tax": 20.2`, входная модель примет значение по умолчанию `"tax": 10.5`. -И данные будут сохранены с этим "новым" `tax`, равным `10,5`. +И данные будут сохранены с этим «новым» `tax`, равным `10.5`. ## Частичное обновление с помощью `PATCH` { #partial-updates-with-patch } -Также можно использовать HTTP `PATCH` операцию для *частичного* обновления данных. +Также можно использовать операцию HTTP `PATCH` для *частичного* обновления данных. Это означает, что можно передавать только те данные, которые необходимо обновить, оставляя остальные нетронутыми. @@ -46,19 +46,13 @@ ### Использование параметра `exclude_unset` в Pydantic { #using-pydantics-exclude-unset-parameter } -Если необходимо выполнить частичное обновление, то очень полезно использовать параметр `exclude_unset` в методе `.model_dump()` модели Pydantic. +Если вы хотите получать частичные обновления, очень полезно использовать параметр `exclude_unset` в `.model_dump()` модели Pydantic. Например, `item.model_dump(exclude_unset=True)`. -/// info | Информация +В результате будет сгенерирован `dict`, содержащий только те данные, которые были заданы при создании модели `item`, без учета значений по умолчанию. -В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он помечен как устаревший (но все еще поддерживается) и переименован в `.model_dump()`. - -Примеры здесь используют `.dict()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, лучше используйте `.model_dump()`. - -/// - -В результате будет сгенерирован словарь, содержащий только те данные, которые были заданы при создании модели `item`, без учета значений по умолчанию. Затем вы можете использовать это для создания словаря только с теми данными, которые были установлены (отправлены в запросе), опуская значения по умолчанию: +Затем вы можете использовать это для создания `dict` только с теми данными, которые были установлены (отправлены в запросе), опуская значения по умолчанию: {* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *} @@ -66,14 +60,6 @@ Теперь можно создать копию существующей модели, используя `.model_copy()`, и передать параметр `update` с `dict`, содержащим данные для обновления. -/// info | Информация - -В Pydantic v1 метод назывался `.copy()`, в Pydantic v2 он помечен как устаревший (но все еще поддерживается) и переименован в `.model_copy()`. - -Примеры здесь используют `.copy()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, лучше используйте `.model_copy()`. - -/// - Например, `stored_item_model.model_copy(update=update_data)`: {* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *} @@ -84,9 +70,9 @@ * (Опционально) использовать `PATCH` вместо `PUT`. * Извлечь сохранённые данные. -* Поместить эти данные в Pydantic модель. +* Поместить эти данные в Pydantic-модель. * Сгенерировать `dict` без значений по умолчанию из входной модели (с использованием `exclude_unset`). - * Таким образом, можно обновлять только те значения, которые действительно установлены пользователем, вместо того чтобы переопределять значения, уже сохраненные в модели по умолчанию. + * Таким образом, можно обновлять только те значения, которые действительно установлены пользователем, вместо того чтобы переопределять уже сохраненные значения значениями по умолчанию из вашей модели. * Создать копию хранимой модели, обновив ее атрибуты полученными частичными обновлениями (с помощью параметра `update`). * Преобразовать скопированную модель в то, что может быть сохранено в вашей БД (например, с помощью `jsonable_encoder`). * Это сравнимо с повторным использованием метода модели `.model_dump()`, но при этом происходит проверка (и преобразование) значений в типы данных, которые могут быть преобразованы в JSON, например, `datetime` в `str`. @@ -97,7 +83,7 @@ /// tip | Подсказка -Эту же технику можно использовать и для операции HTTP `PUT`. +На самом деле эту же технику можно использовать и для операции HTTP `PUT`. Но в приведенном примере используется `PATCH`, поскольку он был создан именно для таких случаев использования. diff --git a/docs/ru/docs/tutorial/body.md b/docs/ru/docs/tutorial/body.md index b61f3e7a09..537d7ebc96 100644 --- a/docs/ru/docs/tutorial/body.md +++ b/docs/ru/docs/tutorial/body.md @@ -32,9 +32,10 @@ {* ../../docs_src/body/tutorial001_py310.py hl[5:9] *} + Так же, как при объявлении параметров запроса: когда атрибут модели имеет значение по умолчанию, он не обязателен. Иначе он обязателен. Используйте `None`, чтобы сделать его просто необязательным. -Например, модель выше описывает такой JSON "объект" (или Python `dict`): +Например, модель выше описывает такой JSON "`object`" (или Python `dict`): ```JSON { @@ -45,7 +46,7 @@ } ``` -...так как `description` и `tax` являются необязательными (со значением по умолчанию `None`), такой JSON "объект" тоже будет корректным: +...так как `description` и `tax` являются необязательными (со значением по умолчанию `None`), такой JSON "`object`" тоже будет корректным: ```JSON { @@ -73,7 +74,7 @@ * Передаст полученные данные в параметр `item`. * Поскольку внутри функции вы объявили его с типом `Item`, у вас будет поддержка со стороны редактора кода (автозавершение и т. п.) для всех атрибутов и их типов. * Сгенерирует определения JSON Schema для вашей модели; вы можете использовать их и в других местах, если это имеет смысл для вашего проекта. -* Эти схемы будут частью сгенерированной схемы OpenAPI и будут использоваться автоматической документацией UIs. +* Эти схемы будут частью сгенерированной схемы OpenAPI и будут использоваться автоматической документацией UIs. ## Автоматическая документация { #automatic-docs } @@ -127,14 +128,6 @@ JSON Schema ваших моделей будет частью сгенериро {* ../../docs_src/body/tutorial002_py310.py *} -/// info | Информация - -В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он был помечен как устаревший (но всё ещё поддерживается) и переименован в `.model_dump()`. - -Примеры здесь используют `.dict()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, используйте `.model_dump()`. - -/// - ## Тело запроса + параметры пути { #request-body-path-parameters } Вы можете одновременно объявить параметры пути и тело запроса. @@ -143,6 +136,7 @@ JSON Schema ваших моделей будет частью сгенериро {* ../../docs_src/body/tutorial003_py310.py hl[15:16] *} + ## Тело запроса + параметры пути + параметры запроса { #request-body-path-query-parameters } Вы также можете одновременно объявить параметры **тела**, **пути** и **запроса**. @@ -153,7 +147,7 @@ JSON Schema ваших моделей будет частью сгенериро Параметры функции будут распознаны следующим образом: -* Если параметр также объявлен в **пути**, он будет использоваться как параметр пути. +* Если параметр также объявлен в **пути**, он будет использоваться как path-параметр. * Если параметр имеет **скалярный тип** (например, `int`, `float`, `str`, `bool` и т. п.), он будет интерпретирован как параметр **запроса**. * Если параметр объявлен как тип **модели Pydantic**, он будет интерпретирован как **тело** запроса. @@ -161,7 +155,7 @@ JSON Schema ваших моделей будет частью сгенериро FastAPI понимает, что значение `q` не является обязательным из-за значения по умолчанию `= None`. -Аннотации типов `str | None` (Python 3.10+) или `Union[str, None]` (Python 3.9+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`. +Аннотации типов `str | None` (Python 3.10+) или `Union` в `Union[str, None]` (Python 3.9+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`. Но добавление аннотаций типов позволит вашему редактору кода лучше вас поддерживать и обнаруживать ошибки. @@ -169,4 +163,4 @@ FastAPI понимает, что значение `q` не является об ## Без Pydantic { #without-pydantic } -Если вы не хотите использовать модели Pydantic, вы также можете использовать параметры **Body**. См. раздел документации [Тело — Несколько параметров: Единичные значения в теле](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}. +Если вы не хотите использовать модели Pydantic, вы также можете использовать параметры **Body**. См. раздел документации [Тело запроса - Несколько параметров: Единичные значения в теле](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}. diff --git a/docs/ru/docs/tutorial/extra-models.md b/docs/ru/docs/tutorial/extra-models.md index 2f0ce4e33e..03156f2b4e 100644 --- a/docs/ru/docs/tutorial/extra-models.md +++ b/docs/ru/docs/tutorial/extra-models.md @@ -22,21 +22,13 @@ {* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *} -/// info | Информация +### Про `**user_in.model_dump()` { #about-user-in-model-dump } -В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он помечен как устаревший (но всё ещё поддерживается) и переименован в `.model_dump()`. +#### `.model_dump()` из Pydantic { #pydantics-model-dump } -В примерах здесь используется `.dict()` для совместимости с Pydantic v1, но если вы используете Pydantic v2, следует использовать `.model_dump()`. +`user_in` — это Pydantic-модель класса `UserIn`. -/// - -### Про `**user_in.dict()` { #about-user-in-dict } - -#### `.dict()` из Pydantic { #pydantics-dict } - -`user_in` - это Pydantic-модель класса `UserIn`. - -У Pydantic-моделей есть метод `.dict()`, который возвращает `dict` с данными модели. +У Pydantic-моделей есть метод `.model_dump()`, который возвращает `dict` с данными модели. Поэтому, если мы создадим Pydantic-объект `user_in` таким способом: @@ -47,10 +39,10 @@ user_in = UserIn(username="john", password="secret", email="john.doe@example.com и затем вызовем: ```Python -user_dict = user_in.dict() +user_dict = user_in.model_dump() ``` -то теперь у нас есть `dict` с данными модели в переменной `user_dict` (это `dict` вместо объекта Pydantic-модели). +то теперь у нас есть `dict` с данными в переменной `user_dict` (это `dict` вместо объекта Pydantic-модели). И если мы вызовем: @@ -58,7 +50,7 @@ user_dict = user_in.dict() print(user_dict) ``` -мы можем получить `dict` с такими данными: +мы получим Python `dict` с: ```Python { @@ -71,7 +63,7 @@ print(user_dict) #### Распаковка `dict` { #unpacking-a-dict } -Если мы возьмём `dict` наподобие `user_dict` и передадим его в функцию (или класс), используя `**user_dict`, Python распакует его. Он передаст ключи и значения `user_dict` напрямую как аргументы типа ключ-значение. +Если мы возьмём `dict` наподобие `user_dict` и передадим его в функцию (или класс), используя `**user_dict`, Python его "распакует". Он передаст ключи и значения `user_dict` напрямую как аргументы типа ключ-значение. Поэтому, продолжая описанный выше пример с `user_dict`, написание такого кода: @@ -79,7 +71,7 @@ print(user_dict) UserInDB(**user_dict) ``` -Будет работать так же, как примерно такой код: +будет эквивалентно: ```Python UserInDB( @@ -90,7 +82,7 @@ UserInDB( ) ``` -Или, если для большей точности мы напрямую используем `user_dict` с любым потенциальным содержимым, то этот пример будет выглядеть так: +Или, более точно, если использовать `user_dict` напрямую, с любым содержимым, которое он может иметь в будущем: ```Python UserInDB( @@ -101,22 +93,22 @@ UserInDB( ) ``` -#### Pydantic-модель из содержимого другой модели { #a-pydantic-model-from-the-contents-of-another } +#### Pydantic-модель из содержимого другой { #a-pydantic-model-from-the-contents-of-another } -Как в примере выше мы получили `user_dict` из `user_in.dict()`, этот код: +Как в примере выше мы получили `user_dict` из `user_in.model_dump()`, этот код: ```Python -user_dict = user_in.dict() +user_dict = user_in.model_dump() UserInDB(**user_dict) ``` будет равнозначен такому: ```Python -UserInDB(**user_in.dict()) +UserInDB(**user_in.model_dump()) ``` -...потому что `user_in.dict()` - это `dict`, и затем мы указываем, чтобы Python его "распаковал", когда передаём его в `UserInDB` и ставим перед ним `**`. +...потому что `user_in.model_dump()` — это `dict`, и затем мы указываем, чтобы Python его "распаковал", когда передаём его в `UserInDB` с префиксом `**`. Таким образом мы получаем Pydantic-модель на основе данных из другой Pydantic-модели. @@ -125,10 +117,10 @@ UserInDB(**user_in.dict()) И затем, если мы добавим дополнительный именованный аргумент `hashed_password=hashed_password` как здесь: ```Python -UserInDB(**user_in.dict(), hashed_password=hashed_password) +UserInDB(**user_in.model_dump(), hashed_password=hashed_password) ``` -... то мы получим что-то подобное: +...то в итоге получится что-то подобное: ```Python UserInDB( @@ -142,13 +134,13 @@ UserInDB( /// warning | Предупреждение -Вспомогательные функции `fake_password_hasher` и `fake_save_user` используются только для демонстрации возможного потока данных и, конечно, не обеспечивают настоящую безопасность. +Вспомогательные дополнительные функции `fake_password_hasher` и `fake_save_user` используются только для демонстрации возможного потока данных и, конечно, не обеспечивают настоящую безопасность. /// ## Сократите дублирование { #reduce-duplication } -Сокращение дублирования кода - это одна из главных идей **FastAPI**. +Сокращение дублирования кода — это одна из главных идей **FastAPI**. Поскольку дублирование кода повышает риск появления багов, проблем с безопасностью, проблем десинхронизации кода (когда вы обновляете код в одном месте, но не обновляете в другом), и т.д. @@ -166,7 +158,7 @@ UserInDB( ## `Union` или `anyOf` { #union-or-anyof } -Вы можете определить ответ как `Union` из двух или более типов. Это означает, что ответ должен соответствовать одному из них. +Вы можете объявить HTTP-ответ как `Union` из двух или более типов. Это означает, что HTTP-ответ может быть любым из них. Он будет определён в OpenAPI как `anyOf`. @@ -174,7 +166,7 @@ UserInDB( /// note | Примечание -При объявлении `Union`, сначала указывайте наиболее детальные типы, затем менее детальные. В примере ниже более детальный `PlaneItem` стоит перед `CarItem` в `Union[PlaneItem, CarItem]`. +При объявлении `Union` сначала указывайте наиболее специфичный тип, затем менее специфичный. В примере ниже более специфичный `PlaneItem` стоит перед `CarItem` в `Union[PlaneItem, CarItem]`. /// @@ -192,19 +184,19 @@ UserInDB( some_variable: PlaneItem | CarItem ``` -Но если мы помещаем его в `response_model=PlaneItem | CarItem` мы получим ошибку, потому что Python попытается произвести **некорректную операцию** между `PlaneItem` и `CarItem` вместо того, чтобы интерпретировать это как аннотацию типа. +Но если мы поместим это в присваивание `response_model=PlaneItem | CarItem`, мы получим ошибку, потому что Python попытается произвести **некорректную операцию** между `PlaneItem` и `CarItem` вместо того, чтобы интерпретировать это как аннотацию типа. ## Список моделей { #list-of-models } -Таким же образом вы можете определять ответы как списки объектов. +Таким же образом вы можете объявлять HTTP-ответы, возвращающие списки объектов. -Для этого используйте `typing.List` из стандартной библиотеки Python (или просто `list` в Python 3.9 и выше): +Для этого используйте стандартный `typing.List` в Python (или просто `list` в Python 3.9 и выше): {* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *} ## Ответ с произвольным `dict` { #response-with-arbitrary-dict } -Вы также можете определить ответ, используя произвольный одноуровневый `dict` и определяя только типы ключей и значений без использования Pydantic-моделей. +Вы также можете объявить HTTP-ответ, используя обычный произвольный `dict`, объявив только тип ключей и значений, без использования Pydantic-модели. Это полезно, если вы заранее не знаете корректных названий полей/атрибутов (которые будут нужны при использовании Pydantic-модели). @@ -214,6 +206,6 @@ some_variable: PlaneItem | CarItem ## Резюме { #recap } -Используйте несколько Pydantic-моделей и свободно применяйте наследование для каждой из них. +Используйте несколько Pydantic-моделей и свободно применяйте наследование для каждого случая. -Вам не обязательно иметь единственную модель данных для каждой сущности, если эта сущность должна иметь возможность быть в разных "состояниях". Как в случае с "сущностью" пользователя, у которого есть состояния с полями `password`, `password_hash` и без пароля. +Вам не обязательно иметь единственную модель данных для каждой сущности, если эта сущность должна иметь возможность быть в разных "состояниях". Как в случае с "сущностью" пользователя, у которого есть состояние, включающее `password`, `password_hash` и отсутствие пароля. diff --git a/docs/ru/docs/tutorial/query-params-str-validations.md b/docs/ru/docs/tutorial/query-params-str-validations.md index 3a4ecc37dc..2bc2fb22c5 100644 --- a/docs/ru/docs/tutorial/query-params-str-validations.md +++ b/docs/ru/docs/tutorial/query-params-str-validations.md @@ -8,7 +8,7 @@ Query-параметр `q` имеет тип `str | None`, это означает, что он имеет тип `str`, но также может быть `None`. Значение по умолчанию действительно `None`, поэтому FastAPI будет знать, что он не обязателен. -/// note | Технические детали +/// note | Примечание FastAPI поймёт, что значение `q` не обязательно, из‑за значения по умолчанию `= None`. @@ -177,7 +177,7 @@ q: str = Query(default="rick") **Значение по умолчанию** у **параметра функции** — это **настоящее значение по умолчанию**, что более интуитивно для Python. 😌 -Вы можете **вызвать** эту же функцию в **других местах** без FastAPI, и она будет **работать как ожидается**. Если есть **обязательный** параметр (без значения по умолчанию), ваш **редактор кода** сообщит об ошибке, **Python** тоже пожалуется, если вы запустите её без передачи обязательного параметра. +Вы можете **вызвать** эту же функцию в **других местах** без FastAPI, и она будет **работать как ожидается**. Если есть **обязательный** параметр (без значения по умолчанию), ваш **редактор** сообщит об ошибке, **Python** тоже пожалуется, если вы запустите её без передачи обязательного параметра. Если вы не используете `Annotated`, а применяете **(устаревший) стиль со значением по умолчанию**, то при вызове этой функции без FastAPI в **других местах** вам нужно **помнить** о том, что надо передать аргументы, чтобы всё работало корректно, иначе значения будут не такими, как вы ожидаете (например, вместо `str` будет `QueryInfo` или что-то подобное). И ни редактор, ни Python не будут ругаться при самом вызове функции — ошибка проявится лишь при операциях внутри. @@ -191,7 +191,7 @@ q: str = Query(default="rick") ## Регулярные выражения { #add-regular-expressions } -Вы можете определить регулярное выражение `pattern`, которому должен соответствовать параметр: +Вы можете определить регулярное выражение `pattern`, которому должен соответствовать параметр: {* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *} @@ -205,20 +205,6 @@ q: str = Query(default="rick") Теперь вы знаете, что когда они понадобятся, вы сможете использовать их в **FastAPI**. -### `regex` из Pydantic v1 вместо `pattern` { #pydantic-v1-regex-instead-of-pattern } - -До Pydantic версии 2 и до FastAPI 0.100.0 этот параметр назывался `regex`, а не `pattern`, но сейчас он устарел. - -Вы всё ещё можете встретить такой код: - -//// tab | Pydantic v1 - -{* ../../docs_src/query_params_str_validations/tutorial004_regex_an_py310.py hl[11] *} - -//// - -Имейте в виду, что это устарело, и код следует обновить на использование нового параметра `pattern`. 🤓 - ## Значения по умолчанию { #default-values } Конечно, можно использовать и другие значения по умолчанию, не только `None`. @@ -279,7 +265,7 @@ q: Annotated[str | None, Query(min_length=3)] = None http://localhost:8000/items/?q=foo&q=bar ``` -вы получите множественные значения query-параметра `q` (`foo` и `bar`) в виде Python-`list` внутри вашей *функции обработки пути*, в *параметре функции* `q`. +вы получите множественные значения *query-параметров* `q` (`foo` и `bar`) в виде Python-`list` внутри вашей *функции-обработчика пути*, в *параметре функции* `q`. Таким образом, ответ на этот URL будет: @@ -331,7 +317,7 @@ http://localhost:8000/items/ {* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *} -/// note | Технические детали +/// note | Примечание Имейте в виду, что в этом случае FastAPI не будет проверять содержимое списка. @@ -345,7 +331,7 @@ http://localhost:8000/items/ Эта информация будет включена в сгенерированную OpenAPI-схему и использована интерфейсами документации и внешними инструментами. -/// note | Технические детали +/// note | Примечание Помните, что разные инструменты могут иметь разный уровень поддержки OpenAPI. @@ -415,7 +401,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems /// -Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги ISBN или с `imdb-` для ID URL фильма на IMDB: +Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги ISBN или с `imdb-` для ID URL фильма на IMDB: {* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *} @@ -455,7 +441,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems Затем с `random.choice()` можно получить **случайное значение** из списка — то есть кортеж вида `(id, name)`. Это будет что‑то вроде `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`. -После этого мы **распаковываем** эти два значения кортежа в переменные `id` и `name`. +После этого мы **присваиваем эти два значения** кортежа переменным `id` и `name`. Так что, если пользователь не передал ID элемента, он всё равно получит случайную рекомендацию. diff --git a/docs/ru/docs/tutorial/response-model.md b/docs/ru/docs/tutorial/response-model.md index 07308c1db2..22a811cd57 100644 --- a/docs/ru/docs/tutorial/response-model.md +++ b/docs/ru/docs/tutorial/response-model.md @@ -6,11 +6,11 @@ {* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *} -FastAPI будет использовать этот тип ответа для: +FastAPI будет использовать этот возвращаемый тип, чтобы: -* **Валидации** возвращаемых данных. - * Если данные невалидны (например, отсутствует поле), это означает, что код *вашего* приложения работает некорректно и возвращает не то, что должен. В таком случае будет возвращена ошибка сервера вместо неправильных данных. Так вы и ваши клиенты можете быть уверены, что получите ожидаемые данные и ожидаемую структуру. -* Добавления **JSON Schema** для ответа в OpenAPI *операции пути*. +* **Валидировать** возвращаемые данные. + * Если данные невалидны (например, отсутствует поле), это означает, что код *вашего* приложения работает некорректно и возвращает не то, что должен. В таком случае будет возвращена ошибка сервера вместо неправильных данных. Так вы и ваши клиенты можете быть уверены, что получите ожидаемые данные и ожидаемую структуру данных. +* Добавить **JSON Schema** для ответа в OpenAPI *операции пути*. * Это будет использовано **автоматической документацией**. * Это также будет использовано инструментами автоматической генерации клиентского кода. @@ -23,7 +23,7 @@ FastAPI будет использовать этот тип ответа для: Бывают случаи, когда вам нужно или хочется возвращать данные, которые не в точности соответствуют объявленному типу. -Например, вы можете хотеть **возвращать словарь (dict)** или объект из базы данных, но **объявить его как Pydantic-модель**. Тогда Pydantic-модель выполнит документирование данных, валидацию и т.п. для объекта, который вы вернули (например, словаря или объекта из базы данных). +Например, вы можете хотеть **возвращать словарь** или объект из базы данных, но **объявить его как Pydantic-модель**. Тогда Pydantic-модель выполнит документирование данных, валидацию и т.п. для объекта, который вы вернули (например, словаря или объекта из базы данных). Если вы добавите аннотацию возвращаемого типа, инструменты и редакторы кода начнут жаловаться (и будут правы), что функция возвращает тип (например, dict), отличный от объявленного (например, Pydantic-модель). @@ -47,13 +47,13 @@ FastAPI будет использовать этот тип ответа для: `response_model` принимает тот же тип, что вы бы объявили для поля Pydantic-модели, то есть это может быть одна Pydantic-модель, а может быть, например, `list` Pydantic-моделей, как `List[Item]`. -FastAPI будет использовать `response_model` для документации, валидации и т. п., а также для **конвертации и фильтрации выходных данных** к объявленному типу. +FastAPI будет использовать этот `response_model` для документирования, валидации данных и т.п., а также для **конвертации и фильтрации выходных данных** к объявленному типу. /// tip | Совет -Если у вас в редакторе кода, mypy и т. п. включены строгие проверки типов, вы можете объявить возвращаемый тип функции как `Any`. +Если у вас в редакторе кода, mypy и т.п. включены строгие проверки типов, вы можете объявить возвращаемый тип функции как `Any`. -Так вы сообщите редактору, что намеренно возвращаете что угодно. Но FastAPI всё равно выполнит документацию данных, валидацию, фильтрацию и т.д. с помощью `response_model`. +Так вы сообщите редактору, что намеренно возвращаете что угодно. Но FastAPI всё равно выполнит документирование, валидацию, фильтрацию данных и т.д. с помощью `response_model`. /// @@ -61,7 +61,7 @@ FastAPI будет использовать `response_model` для докуме Если вы объявите и возвращаемый тип, и `response_model`, приоритет будет у `response_model`, именно его использует FastAPI. -Так вы можете добавить корректные аннотации типов к своим функциям, даже если фактически возвращаете тип, отличный от модели ответа, чтобы ими пользовались редактор и инструменты вроде mypy. И при этом FastAPI продолжит выполнять валидацию данных, документацию и т.д. с использованием `response_model`. +Так вы можете добавить корректные аннотации типов к своим функциям, даже если фактически возвращаете тип, отличный от модели ответа, чтобы ими пользовались редактор кода и инструменты вроде mypy. И при этом FastAPI продолжит выполнять валидацию данных, документацию и т.д. с использованием `response_model`. Вы также можете указать `response_model=None`, чтобы отключить создание модели ответа для данной *операции пути*. Это может понадобиться, если вы добавляете аннотации типов для вещей, не являющихся валидными полями Pydantic. Пример вы увидите ниже. @@ -75,7 +75,7 @@ FastAPI будет использовать `response_model` для докуме Чтобы использовать `EmailStr`, сначала установите `email-validator`. -Создайте [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активируйте его и затем установите пакет, например: +Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установите пакет, например: ```console $ pip install email-validator @@ -105,7 +105,7 @@ $ pip install "pydantic[email]" /// -## Добавить модель для ответа { #add-an-output-model } +## Добавить выходную модель { #add-an-output-model } Вместо этого мы можем создать входную модель с паролем в открытом виде и выходную модель без него: @@ -123,7 +123,7 @@ $ pip install "pydantic[email]" ### `response_model` или возвращаемый тип { #response-model-or-return-type } -В этом случае, поскольку две модели различаются, если бы мы аннотировали возвращаемый тип функции как `UserOut`, редактор и инструменты пожаловались бы, что мы возвращаем неверный тип, так как это разные классы. +В этом случае, поскольку две модели различаются, если бы мы аннотировали возвращаемый тип функции как `UserOut`, редактор кода и инструменты пожаловались бы, что мы возвращаем неверный тип, так как это разные классы. Поэтому в этом примере мы должны объявить тип ответа в параметре `response_model`. @@ -135,33 +135,33 @@ $ pip install "pydantic[email]" Мы хотим, чтобы FastAPI продолжал **фильтровать** данные с помощью модели ответа. Так что, даже если функция возвращает больше данных, в ответ будут включены только поля, объявленные в модели ответа. -В предыдущем примере, поскольку классы были разными, нам пришлось использовать параметр `response_model`. Но это также означает, что мы теряем поддержку от редактора и инструментов, проверяющих возвращаемый тип функции. +В предыдущем примере, поскольку классы были разными, нам пришлось использовать параметр `response_model`. Но это также означает, что мы теряем поддержку от редактора кода и инструментов, проверяющих возвращаемый тип функции. Однако в большинстве таких случаев нам нужно лишь **отфильтровать/убрать** некоторые данные, как в этом примере. -И в этих случаях мы можем использовать классы и наследование, чтобы воспользоваться **аннотациями типов** функций для лучшей поддержки в редакторе и инструментах и при этом получить **фильтрацию данных** от FastAPI. +И в этих случаях мы можем использовать классы и наследование, чтобы воспользоваться **аннотациями типов** функций для лучшей поддержки в редакторе кода и инструментах и при этом получить **фильтрацию данных** от FastAPI. {* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *} -Так мы получаем поддержку инструментов (редакторы, mypy) — код корректен с точки зрения типов — и одновременно получаем фильтрацию данных от FastAPI. +Так мы получаем поддержку инструментов — редакторов кода и mypy, так как этот код корректен с точки зрения типов — и одновременно получаем фильтрацию данных от FastAPI. Как это работает? Давайте разберёмся. 🤓 ### Аннотации типов и инструменты { #type-annotations-and-tooling } -Сначала посмотрим, как это увидят редакторы, mypy и другие инструменты. +Сначала посмотрим, как это увидят редактор кода, mypy и другие инструменты. -`BaseUser` содержит базовые поля. Затем `UserIn` наследуется от `BaseUser` и добавляет поле `password`, то есть он включает все поля обеих моделей. +`BaseUser` содержит базовые поля. Затем `UserIn` наследуется от `BaseUser` и добавляет поле `password`, то есть он будет включать все поля обеих моделей. Мы аннотируем возвращаемый тип функции как `BaseUser`, но фактически возвращаем экземпляр `UserIn`. -Редактор, mypy и другие инструменты не будут возражать, потому что с точки зрения типов `UserIn` — подкласс `BaseUser`, что означает, что это *валидный* тип везде, где ожидается что-то, являющееся `BaseUser`. +Редактор кода, mypy и другие инструменты не будут возражать, потому что с точки зрения типов `UserIn` — подкласс `BaseUser`, что означает, что это *валидный* тип везде, где ожидается что-то, являющееся `BaseUser`. ### Фильтрация данных FastAPI { #fastapi-data-filtering } -Теперь, для FastAPI: он увидит возвращаемый тип и убедится, что то, что вы возвращаете, включает **только** поля, объявленные в этом типе. +Теперь для FastAPI: он увидит возвращаемый тип и убедится, что то, что вы возвращаете, включает **только** поля, объявленные в этом типе. -FastAPI делает несколько вещей внутри вместе с Pydantic, чтобы гарантировать, что те же правила наследования классов не используются для фильтрации возвращаемых данных, иначе вы могли бы вернуть гораздо больше данных, чем ожидали. +FastAPI делает несколько вещей внутри вместе с Pydantic, чтобы гарантировать, что те же правила наследования классов не используются для фильтрации возвращаемых данных, иначе вы могли бы в итоге вернуть намного больше данных, чем ожидали. Таким образом вы получаете лучшее из обоих миров: аннотации типов с **поддержкой инструментов** и **фильтрацию данных**. @@ -171,17 +171,17 @@ FastAPI делает несколько вещей внутри вместе с -И обе модели используются в интерактивной документации API: +И обе модели будут использоваться в интерактивной документации API: ## Другие аннотации возвращаемых типов { #other-return-type-annotations } -Бывают случаи, когда вы возвращаете что-то, что не является валидным полем Pydantic, и аннотируете это в функции только ради поддержки инструментов (редактор, mypy и т. д.). +Бывают случаи, когда вы возвращаете что-то, что не является валидным полем Pydantic, и аннотируете это в функции только ради поддержки инструментов (редактор кода, mypy и т.д.). ### Возврат Response напрямую { #return-a-response-directly } -Самый распространённый случай — [возвращать Response напрямую, как описано далее в разделах для продвинутых](../advanced/response-directly.md){.internal-link target=_blank}. +Самый распространённый случай — [возвращать Response напрямую, как описано далее в разделах документации для продвинутых](../advanced/response-directly.md){.internal-link target=_blank}. {* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *} @@ -195,7 +195,7 @@ FastAPI делает несколько вещей внутри вместе с {* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *} -Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот случай. +Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот простой случай. ### Некорректные аннотации возвращаемых типов { #invalid-return-type-annotations } @@ -209,15 +209,15 @@ FastAPI делает несколько вещей внутри вместе с ### Отключить модель ответа { #disable-response-model } -Продолжая пример выше, вы можете не хотеть использовать стандартную валидацию данных, документацию, фильтрацию и т.д., выполняемые FastAPI. +Продолжая пример выше, вы можете не хотеть использовать стандартные валидацию данных, документирование, фильтрацию и т.п., выполняемые FastAPI. -Но при этом вы можете хотеть сохранить аннотацию возвращаемого типа в функции, чтобы пользоваться поддержкой инструментов (редакторы, проверки типов вроде mypy). +Но при этом вы можете хотеть сохранить аннотацию возвращаемого типа в функции, чтобы пользоваться поддержкой инструментов вроде редакторов кода и инструментов проверки типов (например, mypy). В этом случае вы можете отключить генерацию модели ответа, установив `response_model=None`: {* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *} -Так FastAPI пропустит генерацию модели ответа, и вы сможете использовать любые аннотации возвращаемых типов, не влияя на ваше приложение FastAPI. 🤓 +Так FastAPI пропустит генерацию модели ответа, и вы сможете использовать любые аннотации возвращаемых типов, которые вам нужны, без влияния на ваше приложение FastAPI. 🤓 ## Параметры кодирования модели ответа { #response-model-encoding-parameters } @@ -252,20 +252,6 @@ FastAPI делает несколько вещей внутри вместе с /// info | Информация -В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он был помечен как устаревший (но всё ещё поддерживается) и переименован в `.model_dump()`. - -Примеры здесь используют `.dict()` для совместимости с Pydantic v1, но если вы используете Pydantic v2, применяйте `.model_dump()`. - -/// - -/// info | Информация - -FastAPI использует метод `.dict()` у Pydantic-моделей с параметром `exclude_unset`, чтобы добиться такого поведения. - -/// - -/// info | Информация - Вы также можете использовать: * `response_model_exclude_defaults=True` @@ -312,7 +298,7 @@ FastAPI достаточно умен (на самом деле, это Pydantic Обратите внимание, что значения по умолчанию могут быть любыми, не только `None`. -Это может быть список (`[]`), число с плавающей точкой `10.5` и т. д. +Это может быть список (`[]`), число с плавающей точкой `10.5` и т.д. /// @@ -346,7 +332,7 @@ FastAPI достаточно умен (на самом деле, это Pydantic #### Использование `list` вместо `set` { #using-lists-instead-of-sets } -Если вы забыли использовать `set` и применили `list` или `tuple`, FastAPI всё равно преобразует это в `set`, и всё будет работать корректно: +Если вы забыли использовать `set` и применили `list` или `tuple` вместо него, FastAPI всё равно преобразует это в `set`, и всё будет работать корректно: {* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *} diff --git a/docs/ru/docs/tutorial/schema-extra-example.md b/docs/ru/docs/tutorial/schema-extra-example.md index 5891f0d12d..e4a97c8801 100644 --- a/docs/ru/docs/tutorial/schema-extra-example.md +++ b/docs/ru/docs/tutorial/schema-extra-example.md @@ -8,36 +8,14 @@ Вы можете объявить `examples` для модели Pydantic, которые будут добавлены в сгенерированную JSON Schema. -//// tab | Pydantic v2 - {* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *} -//// - -//// tab | Pydantic v1 - -{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *} - -//// - Эта дополнительная информация будет добавлена как есть в выходную **JSON Schema** этой модели и будет использоваться в документации API. -//// tab | Pydantic v2 - -В Pydantic версии 2 вы будете использовать атрибут `model_config`, который принимает `dict`, как описано в Документации Pydantic: Конфигурация. +Вы можете использовать атрибут `model_config`, который принимает `dict`, как описано в Документации Pydantic: Конфигурация. Вы можете задать `"json_schema_extra"` с `dict`, содержащим любые дополнительные данные, которые вы хотите видеть в сгенерированной JSON Schema, включая `examples`. -//// - -//// tab | Pydantic v1 - -В Pydantic версии 1 вы будете использовать внутренний класс `Config` и `schema_extra`, как описано в Документации Pydantic: Настройка схемы. - -Вы можете задать `schema_extra` со `dict`, содержащим любые дополнительные данные, которые вы хотите видеть в сгенерированной JSON Schema, включая `examples`. - -//// - /// tip | Подсказка Вы можете использовать тот же приём, чтобы расширить JSON Schema и добавить свою собственную дополнительную информацию. @@ -124,7 +102,7 @@ OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) доб Ключи `dict` идентифицируют каждый пример, а каждое значение — это ещё один `dict`. -Каждый конкретный пример‑`dict` в `examples` может содержать: +Каждый конкретный пример `dict` в `examples` может содержать: * `summary`: Краткое описание примера. * `description`: Подробное описание, которое может содержать текст в Markdown. @@ -135,7 +113,7 @@ OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) доб {* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *} -### OpenAPI-примеры в UI документации { #openapi-examples-in-the-docs-ui } +### OpenAPI-примеры в UI документации { #openapi-examples-in-the-docs-ui } С `openapi_examples`, добавленным в `Body()`, страница `/docs` будет выглядеть так: @@ -213,7 +191,7 @@ OpenAPI также добавила поля `example` и `examples` в друг ### Swagger UI и специфичные для OpenAPI `examples` { #swagger-ui-and-openapi-specific-examples } -Раньше, поскольку Swagger UI не поддерживал несколько примеров JSON Schema (по состоянию на 2023-08-26), у пользователей не было способа показать несколько примеров в документации. +Теперь, поскольку Swagger UI не поддерживал несколько примеров JSON Schema (по состоянию на 2023-08-26), у пользователей не было способа показать несколько примеров в документации. Чтобы решить это, FastAPI `0.103.0` **добавил поддержку** объявления того же старого, **специфичного для OpenAPI**, поля `examples` с новым параметром `openapi_examples`. 🤓 diff --git a/docs/ru/llm-prompt.md b/docs/ru/llm-prompt.md index 6a437bdd14..9131a5d3b4 100644 --- a/docs/ru/llm-prompt.md +++ b/docs/ru/llm-prompt.md @@ -90,5 +90,12 @@ For the following technical terms, use these specific translations to ensure con * serve (meaning providing access to something): «отдавать» (or `предоставлять доступ к`) * recap (noun): резюме * utility function: вспомогательная функция +* fast to code: позволяет быстро писать код +* Tutorial - User Guide: Учебник - Руководство пользователя +* submodule: подмодуль +* subpackage: подпакет +* router: роутер +* building, deploying, accessing (when describing features of FastAPI Cloud): созданиe образа, развертывание и доступ +* type checker tool: инструмент проверки типов Do not add whitespace in `т.д.`, `т.п.`. diff --git a/docs/tr/docs/about/index.md b/docs/tr/docs/about/index.md index e9dee5217c..a638fb0cf8 100644 --- a/docs/tr/docs/about/index.md +++ b/docs/tr/docs/about/index.md @@ -1,3 +1,3 @@ -# Hakkında +# Hakkında { #about } FastAPI, tasarımı, ilham kaynağı ve daha fazlası hakkında. 🤓 diff --git a/docs/tr/docs/advanced/index.md b/docs/tr/docs/advanced/index.md index 836e63c8ab..3995109e25 100644 --- a/docs/tr/docs/advanced/index.md +++ b/docs/tr/docs/advanced/index.md @@ -1,36 +1,21 @@ -# Gelişmiş Kullanıcı Rehberi +# Gelişmiş Kullanıcı Rehberi { #advanced-user-guide } -## Ek Özellikler +## Ek Özellikler { #additional-features } -[Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} sayfası **FastAPI**'ın tüm ana özelliklerini tanıtmaya yetecektir. +Ana [Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} sayfası, **FastAPI**'ın tüm temel özelliklerini tanımanız için yeterli olmalıdır. -İlerleyen bölümlerde diğer seçenekler, konfigürasyonlar ve ek özellikleri göreceğiz. +Sonraki bölümlerde diğer seçenekleri, konfigürasyonları ve ek özellikleri göreceksiniz. /// tip | İpucu Sonraki bölümler **mutlaka "gelişmiş" olmak zorunda değildir**. -Kullanım şeklinize bağlı olarak, çözümünüz bu bölümlerden birinde olabilir. +Ve kullanım amacınıza bağlı olarak, çözüm bunlardan birinde olabilir. /// -## Önce Öğreticiyi Okuyun +## Önce Tutorial'ı Okuyun { #read-the-tutorial-first } -[Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} sayfasındaki bilgilerle **FastAPI**'nın çoğu özelliğini kullanabilirsiniz. +Ana [Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} sayfasındaki bilgilerle **FastAPI**'nın çoğu özelliğini yine de kullanabilirsiniz. -Sonraki bölümler bu sayfayı okuduğunuzu ve bu ana fikirleri bildiğinizi varsayarak hazırlanmıştır. - -## Diğer Kurslar - -[Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} sayfası ve bu **Gelişmiş Kullanıcı Rehberi**, öğretici bir kılavuz (bir kitap gibi) şeklinde yazılmıştır ve **FastAPI'ı öğrenmek** için yeterli olsa da, ek kurslarla desteklemek isteyebilirsiniz. - -Belki de öğrenme tarzınıza daha iyi uyduğu için başka kursları tercih edebilirsiniz. - -Bazı kurs sağlayıcıları ✨ [**FastAPI destekçileridir**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, bu FastAPI ve **ekosisteminin** sürekli ve sağlıklı bir şekilde **gelişmesini** sağlar. - -Ayrıca, size **iyi bir öğrenme deneyimi** sağlamakla kalmayıp, **iyi ve sağlıklı bir framework** olan FastAPI'a ve ve **topluluğuna** (yani size) olan gerçek bağlılıklarını gösterir. - -Onların kurslarını denemek isteyebilirsiniz: - -* Talk Python Training -* Test-Driven Development +Ve sonraki bölümler, onu zaten okuduğunuzu ve bu temel fikirleri bildiğinizi varsayar. diff --git a/docs/tr/docs/advanced/security/index.md b/docs/tr/docs/advanced/security/index.md index 709f74c721..9b30781f29 100644 --- a/docs/tr/docs/advanced/security/index.md +++ b/docs/tr/docs/advanced/security/index.md @@ -1,6 +1,6 @@ -# Gelişmiş Güvenlik +# Gelişmiş Güvenlik { #advanced-security } -## Ek Özellikler +## Ek Özellikler { #additional-features } [Tutorial - User Guide: Security](../../tutorial/security/index.md){.internal-link target=_blank} sayfasında ele alınanların dışında güvenlikle ilgili bazı ek özellikler vardır. @@ -8,12 +8,12 @@ Sonraki bölümler **mutlaka "gelişmiş" olmak zorunda değildir**. -Kullanım şeklinize bağlı olarak, çözümünüz bu bölümlerden birinde olabilir. +Ve kullanım durumunuza göre, çözüm bu bölümlerden birinde olabilir. /// -## Önce Öğreticiyi Okuyun +## Önce Öğreticiyi Okuyun { #read-the-tutorial-first } -Sonraki bölümler [Tutorial - User Guide: Security](../../tutorial/security/index.md){.internal-link target=_blank} sayfasını okuduğunuzu varsayarak hazırlanmıştır. +Sonraki bölümler, ana [Tutorial - User Guide: Security](../../tutorial/security/index.md){.internal-link target=_blank} sayfasını zaten okuduğunuzu varsayar. -Bu bölümler aynı kavramlara dayanır, ancak bazı ek işlevsellikler sağlar. +Hepsi aynı kavramlara dayanır, ancak bazı ek işlevselliklere izin verir. diff --git a/docs/tr/docs/advanced/testing-websockets.md b/docs/tr/docs/advanced/testing-websockets.md index effe557d19..da12abadb8 100644 --- a/docs/tr/docs/advanced/testing-websockets.md +++ b/docs/tr/docs/advanced/testing-websockets.md @@ -1,13 +1,13 @@ -# WebSockets'i Test Etmek +# WebSockets'i Test Etmek { #testing-websockets } -WebSockets testi yapmak için `TestClient`'ı kullanabilirsiniz. +WebSockets'i test etmek için aynı `TestClient`'ı kullanabilirsiniz. -Bu işlem için, `TestClient`'ı bir `with` ifadesinde kullanarak WebSocket'e bağlanabilirsiniz: +Bunun için `TestClient`'ı bir `with` ifadesinde kullanarak WebSocket'e bağlanırsınız: -{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *} +{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *} /// note | Not -Daha fazla detay için Starlette'in Websockets'i Test Etmek dokümantasyonunu inceleyin. +Daha fazla detay için Starlette'in WebSockets'i test etme dokümantasyonuna bakın. /// diff --git a/docs/tr/docs/advanced/wsgi.md b/docs/tr/docs/advanced/wsgi.md index 00815a4b2f..442f83a59a 100644 --- a/docs/tr/docs/advanced/wsgi.md +++ b/docs/tr/docs/advanced/wsgi.md @@ -1,32 +1,32 @@ -# WSGI - Flask, Django ve Daha Fazlasını FastAPI ile Kullanma +# WSGI'yi Dahil Etme - Flask, Django ve Diğerleri { #including-wsgi-flask-django-others } -WSGI uygulamalarını [Sub Applications - Mounts](sub-applications.md){.internal-link target=_blank}, [Behind a Proxy](behind-a-proxy.md){.internal-link target=_blank} bölümlerinde gördüğünüz gibi bağlayabilirsiniz. +WSGI uygulamalarını [Sub Applications - Mounts](sub-applications.md){.internal-link target=_blank}, [Behind a Proxy](behind-a-proxy.md){.internal-link target=_blank} bölümlerinde gördüğünüz gibi mount edebilirsiniz. -Bunun için `WSGIMiddleware` ile Flask, Django vb. WSGI uygulamanızı sarmalayabilir ve FastAPI'ya bağlayabilirsiniz. +Bunun için `WSGIMiddleware`'ı kullanabilir ve bunu WSGI uygulamanızı (örneğin Flask, Django vb.) sarmalamak için kullanabilirsiniz. -## `WSGIMiddleware` Kullanımı +## `WSGIMiddleware` Kullanımı { #using-wsgimiddleware } -`WSGIMiddleware`'ı projenize dahil edin. +`WSGIMiddleware`'ı import etmeniz gerekir. -Ardından WSGI (örneğin Flask) uygulamanızı middleware ile sarmalayın. +Ardından WSGI (örn. Flask) uygulamasını middleware ile sarmalayın. -Son olarak da bir yol altında bağlama işlemini gerçekleştirin. +Ve sonra bunu bir path'in altına mount edin. -{* ../../docs_src/wsgi/tutorial001.py hl[2:3,23] *} +{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *} -## Kontrol Edelim +## Kontrol Edelim { #check-it } -Artık `/v1/` yolunun altındaki her istek Flask uygulaması tarafından işlenecektir. +Artık `/v1/` path'i altındaki her request Flask uygulaması tarafından işlenecektir. Geri kalanı ise **FastAPI** tarafından işlenecektir. -Eğer uygulamanızı çalıştırıp http://localhost:8000/v1/ adresine giderseniz, Flask'tan gelen yanıtı göreceksiniz: +Eğer uygulamanızı çalıştırıp http://localhost:8000/v1/ adresine giderseniz, Flask'tan gelen response'u göreceksiniz: ```txt Hello, World from Flask! ``` -Eğer http://localhost:8000/v2/ adresine giderseniz, FastAPI'dan gelen yanıtı göreceksiniz: +Ve eğer http://localhost:8000/v2 adresine giderseniz, FastAPI'dan gelen response'u göreceksiniz: ```JSON { diff --git a/docs/tr/docs/benchmarks.md b/docs/tr/docs/benchmarks.md index eb5472869a..f2b8585856 100644 --- a/docs/tr/docs/benchmarks.md +++ b/docs/tr/docs/benchmarks.md @@ -1,34 +1,34 @@ -# Kıyaslamalar +# Kıyaslamalar { #benchmarks } -Bağımsız TechEmpower kıyaslamaları gösteriyor ki en hızlı Python frameworklerinden birisi olan Uvicorn ile çalıştırılan **FastAPI** uygulamaları, sadece Starlette ve Uvicorn'dan daha düşük sıralamada (FastAPI bu frameworklerin üzerine kurulu) yer alıyor. (*) +Bağımsız TechEmpower kıyaslamaları, Uvicorn altında çalışan **FastAPI** uygulamalarının mevcut en hızlı Python frameworklerinden biri olduğunu, yalnızca Starlette ve Uvicorn'un kendilerinin altında yer aldığını gösteriyor (FastAPI bunları dahili olarak kullanır). Fakat kıyaslamaları ve karşılaştırmaları incelerken şunları aklınızda bulundurmalısınız. -## Kıyaslamalar ve Hız +## Kıyaslamalar ve Hız { #benchmarks-and-speed } -Kıyaslamaları incelediğinizde, farklı özelliklere sahip araçların eşdeğer olarak karşılaştırıldığını yaygın bir şekilde görebilirsiniz. +Kıyaslamalara baktığınızda, farklı türlerdeki birkaç aracın eşdeğermiş gibi karşılaştırıldığını görmek yaygındır. Özellikle, (diğer birçok araç arasında) Uvicorn, Starlette ve FastAPI'ın birlikte karşılaştırıldığını görebilirsiniz. -Aracın çözdüğü problem ne kadar basitse, performansı o kadar iyi olacaktır. Ancak kıyaslamaların çoğu, aracın sağladığı ek özellikleri test etmez. +Aracın çözdüğü problem ne kadar basitse, elde edeceği performans o kadar iyi olur. Ayrıca kıyaslamaların çoğu, aracın sağladığı ek özellikleri test etmez. Hiyerarşi şöyledir: * **Uvicorn**: bir ASGI sunucusu - * **Starlette**: (Uvicorn'u kullanır) bir web mikroframeworkü - * **FastAPI**: (Starlette'i kullanır) veri doğrulama vb. çeşitli ek özelliklere sahip, API oluşturmak için kullanılan bir API mikroframeworkü + * **Starlette**: (Uvicorn'u kullanır) bir web mikroframework'ü + * **FastAPI**: (Starlette'i kullanır) veri doğrulama vb. ile API'lar oluşturmak için çeşitli ek özelliklere sahip bir API mikroframework'ü * **Uvicorn**: - * Sunucunun kendisi dışında ekstra bir kod içermediği için en iyi performansa sahip olacaktır. - * Doğrudan Uvicorn ile bir uygulama yazmazsınız. Bu, yazdığınız kodun en azından Starlette tarafından sağlanan tüm kodu (veya **FastAPI**) az çok içermesi gerektiği anlamına gelir. Eğer bunu yaptıysanız, son uygulamanız bir framework kullanmak ve uygulama kodlarını ve hataları en aza indirmekle aynı ek yüke sahip olacaktır. - * Eğer Uvicorn'u karşılaştırıyorsanız, Daphne, Hypercorn, uWSGI, vb. uygulama sunucuları ile karşılaştırın. + * Sunucunun kendisi dışında çok fazla ekstra kod içermediği için en iyi performansa sahip olacaktır. + * Uvicorn ile doğrudan bir uygulama yazmazsınız. Bu, kodunuzun en azından Starlette'in (veya **FastAPI**'ın) sağladığı kodun aşağı yukarı tamamını içermesi gerektiği anlamına gelir. Bunu yaparsanız, nihai uygulamanız; bir framework kullanmış olmanın ve uygulama kodunu ve bug'ları en aza indirmenin getirdiği ek yükle aynı ek yüke sahip olur. + * Uvicorn'u karşılaştırıyorsanız, Daphne, Hypercorn, uWSGI vb. application server'larla karşılaştırın. * **Starlette**: - * Uvicorn'dan sonraki en iyi performansa sahip olacaktır. İşin aslı, Starlette çalışmak için Uvicorn'u kullanıyor. Dolayısıyla, daha fazla kod çalıştırmaası gerektiğinden muhtemelen Uvicorn'dan sadece "daha yavaş" olabilir. - * Ancak yol bazlı yönlendirme vb. basit web uygulamaları oluşturmak için araçlar sağlar. - * Eğer Starlette'i karşılaştırıyorsanız, Sanic, Flask, Django, vb. frameworkler (veya mikroframeworkler) ile karşılaştırın. + * Uvicorn'dan sonra en iyi performansa sahip olacaktır. Aslında Starlette çalışmak için Uvicorn'u kullanır. Bu yüzden muhtemelen yalnızca daha fazla kod çalıştırmak zorunda kaldığı için Uvicorn'dan "daha yavaş" olabilir. + * Ancak path tabanlı routing vb. ile basit web uygulamaları oluşturmanız için araçlar sağlar. + * Starlette'i karşılaştırıyorsanız, Sanic, Flask, Django vb. web framework'lerle (veya mikroframework'lerle) karşılaştırın. * **FastAPI**: - * Starlette'in Uvicorn'u kullandığı ve ondan daha hızlı olamayacağı gibi, **FastAPI**'da Starlette'i kullanır, dolayısıyla ondan daha hızlı olamaz. - * FastAPI, Starlette'e ek olarak daha fazla özellik sunar. Bunlar veri doğrulama ve dönüşümü gibi API'lar oluştururken neredeyse ve her zaman ihtiyaç duyduğunuz özelliklerdir. Ve bunu kullanarak, ücretsiz olarak otomatik dokümantasyon elde edersiniz (otomatik dokümantasyon çalışan uygulamalara ek yük getirmez, başlangıçta oluşturulur). - * FastAPI'ı kullanmadıysanız ve Starlette'i doğrudan kullandıysanız (veya başka bir araç, Sanic, Flask, Responder, vb.) tüm veri doğrulama ve dönüştürme araçlarını kendiniz geliştirmeniz gerekir. Dolayısıyla, son uygulamanız FastAPI kullanılarak oluşturulmuş gibi hâlâ aynı ek yüke sahip olacaktır. Çoğu durumda, uygulamalarda yazılan kodun büyük bir kısmını veri doğrulama ve dönüştürme kodları oluşturur. - * Dolayısıyla, FastAPI'ı kullanarak geliştirme süresinden, hatalardan, kod satırlarından tasarruf edersiniz ve kullanmadığınız durumda (birçok özelliği geliştirmek zorunda kalmakla birlikte) muhtemelen aynı performansı (veya daha iyisini) elde ederdiniz. - * Eğer FastAPI'ı karşılaştırıyorsanız, Flask-apispec, NestJS, Molten, vb. gibi veri doğrulama, dönüştürme ve dokümantasyon sağlayan bir web uygulaması frameworkü ile (veya araç setiyle) karşılaştırın. + * Starlette'in Uvicorn'u kullanıp ondan daha hızlı olamaması gibi, **FastAPI** da Starlette'i kullanır; dolayısıyla ondan daha hızlı olamaz. + * FastAPI, Starlette'in üzerine daha fazla özellik sağlar. API'lar oluştururken neredeyse her zaman ihtiyaç duyduğunuz veri doğrulama ve serialization gibi özellikler. Ayrıca bunu kullanarak ücretsiz olarak otomatik dokümantasyon elde edersiniz (otomatik dokümantasyon, çalışan uygulamalara ek yük bile getirmez; startup'ta üretilir). + * FastAPI'ı kullanmayıp Starlette'i doğrudan kullansaydınız (veya Sanic, Flask, Responder vb. başka bir aracı), tüm veri doğrulama ve serialization işlemlerini kendiniz uygulamak zorunda kalırdınız. Dolayısıyla nihai uygulamanız, FastAPI kullanılarak inşa edilmiş olsaydı sahip olacağı ek yükle hâlâ aynı ek yüke sahip olurdu. Ve çoğu durumda, uygulamalarda yazılan en büyük kod miktarı veri doğrulama ve serialization kısmıdır. + * Bu nedenle FastAPI kullanarak geliştirme süresinden, bug'lardan, kod satırlarından tasarruf edersiniz; ayrıca muhtemelen, onu kullanmasaydınız (tüm bunları kodunuzda kendiniz uygulamak zorunda kalacağınız için) elde edeceğiniz performansın aynısını (veya daha iyisini) elde edersiniz. + * FastAPI'ı karşılaştırıyorsanız, Flask-apispec, NestJS, Molten vb. veri doğrulama, serialization ve dokümantasyon sağlayan bir web uygulaması framework'ü (veya araç seti) ile karşılaştırın. Entegre otomatik veri doğrulama, serialization ve dokümantasyona sahip framework'ler. diff --git a/docs/tr/docs/deployment/cloud.md b/docs/tr/docs/deployment/cloud.md index 4f82e9d0bb..25ce6ca8dd 100644 --- a/docs/tr/docs/deployment/cloud.md +++ b/docs/tr/docs/deployment/cloud.md @@ -1,13 +1,24 @@ -# FastAPI Uygulamasını Bulut Sağlayıcılar Üzerinde Yayınlama +# Bulut Sağlayıcılar Üzerinde FastAPI Yayınlama { #deploy-fastapi-on-cloud-providers } -FastAPI uygulamasını yayınlamak için hemen hemen **herhangi bir bulut sağlayıcıyı** kullanabilirsiniz. +FastAPI uygulamanızı yayınlamak için neredeyse **herhangi bir bulut sağlayıcıyı** kullanabilirsiniz. -Büyük bulut sağlayıcıların çoğu FastAPI uygulamasını yayınlamak için kılavuzlara sahiptir. +Çoğu durumda, ana bulut sağlayıcıların FastAPI'yi onlarla birlikte yayınlamak için kılavuzları vardır. -## Bulut Sağlayıcılar - Sponsorlar +## FastAPI Cloud { #fastapi-cloud } -Bazı bulut sağlayıcılar ✨ [**FastAPI destekçileridir**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, bu FastAPI ve **ekosisteminin** sürekli ve sağlıklı bir şekilde **gelişmesini** sağlar. +**FastAPI Cloud**, **FastAPI**'nin arkasındaki aynı yazar ve ekip tarafından geliştirilmiştir. -Ayrıca, size **iyi servisler** sağlamakla kalmayıp, **iyi ve sağlıklı bir framework** olan FastAPI'a bağlılıklarını gösterir. +Bir API'yi minimum çabayla **oluşturma**, **yayınlama** ve **erişme** sürecini kolaylaştırır. -Bu hizmetleri denemek ve kılavuzlarını incelemek isteyebilirsiniz. +FastAPI ile uygulama geliştirirken elde edilen aynı **geliştirici deneyimini**, onları buluta **yayınlamaya** da taşır. 🎉 + +FastAPI Cloud, *FastAPI and friends* açık kaynak projelerinin birincil sponsoru ve finansman sağlayıcısıdır. ✨ + +## Bulut Sağlayıcılar - Sponsorlar { #cloud-providers-sponsors } + +Diğer bazı bulut sağlayıcılar da ✨ [**FastAPI'ye sponsor olur**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨. 🙇 + +Kılavuzlarını takip etmek ve servislerini denemek için onları da değerlendirmek isteyebilirsiniz: + +* Render +* Railway diff --git a/docs/tr/docs/deployment/index.md b/docs/tr/docs/deployment/index.md index e03bb4ee0e..055d999294 100644 --- a/docs/tr/docs/deployment/index.md +++ b/docs/tr/docs/deployment/index.md @@ -1,21 +1,23 @@ -# Deployment (Yayınlama) +# Deployment { #deployment } -**FastAPI** uygulamasını deploy etmek oldukça kolaydır. +**FastAPI** uygulamasını deploy etmek nispeten kolaydır. -## Deployment Ne Anlama Gelir? +## Deployment Ne Anlama Gelir? { #what-does-deployment-mean } -Bir uygulamayı **deploy** etmek (yayınlamak), uygulamayı **kullanıcılara erişilebilir hale getirmek** için gerekli adımları gerçekleştirmek anlamına gelir. +Bir uygulamayı **deploy** etmek, onu **kullanıcılara erişilebilir hale getirmek** için gerekli adımları gerçekleştirmek anlamına gelir. -Bir **Web API** için bu süreç normalde uygulamayı **uzak bir makineye** yerleştirmeyi, iyi performans, kararlılık vb. özellikler sağlayan bir **sunucu programı** ile **kullanıcılarınızın** uygulamaya etkili ve kesintisiz bir şekilde **erişebilmesini** kapsar. +Bir **web API** için bu süreç normalde uygulamayı **uzak bir makineye** yerleştirmeyi, iyi performans, kararlılık vb. özellikler sağlayan bir **sunucu programı** ile **kullanıcılarınızın** uygulamaya etkili ve kesintisiz bir şekilde, sorun yaşamadan **erişebilmesini** kapsar. -Bu, kodu sürekli olarak değiştirdiğiniz, hata alıp hata giderdiğiniz, geliştirme sunucusunu durdurup yeniden başlattığınız vb. **geliştirme** aşamalarının tam tersidir. +Bu, kodu sürekli olarak değiştirdiğiniz, bozup düzelttiğiniz, geliştirme sunucusunu durdurup yeniden başlattığınız vb. **geliştirme** aşamalarının tam tersidir. -## Deployment Stratejileri +## Deployment Stratejileri { #deployment-strategies } -Kullanım durumunuza ve kullandığınız araçlara bağlı olarak bir kaç farklı yol izleyebilirsiniz. +Kullanım durumunuza ve kullandığınız araçlara bağlı olarak bunu yapmanın birkaç yolu vardır. -Bir dizi araç kombinasyonunu kullanarak kendiniz **bir sunucu yayınlayabilirsiniz**, yayınlama sürecinin bir kısmını sizin için gerçekleştiren bir **bulut hizmeti** veya diğer olası seçenekleri kullanabilirsiniz. +Bir dizi araç kombinasyonunu kullanarak kendiniz **bir sunucu deploy edebilirsiniz**, yayınlama sürecinin bir kısmını sizin için gerçekleştiren bir **bulut hizmeti** veya diğer olası seçenekleri kullanabilirsiniz. + +Örneğin, FastAPI'nin arkasındaki ekip olarak, FastAPI uygulamalarını buluta mümkün olduğunca akıcı şekilde deploy etmeyi sağlamak için, FastAPI ile çalışmanın aynı geliştirici deneyimini sunarak **FastAPI Cloud**'u oluşturduk. **FastAPI** uygulamasını yayınlarken aklınızda bulundurmanız gereken ana kavramlardan bazılarını size göstereceğim (ancak bunların çoğu diğer web uygulamaları için de geçerlidir). -Sonraki bölümlerde akılda tutulması gereken diğer ayrıntıları ve yayınlama tekniklerinden bazılarını göreceksiniz. ✨ +Sonraki bölümlerde akılda tutulması gereken diğer ayrıntıları ve bunu yapmaya yönelik bazı teknikleri göreceksiniz. ✨ diff --git a/docs/tr/docs/how-to/general.md b/docs/tr/docs/how-to/general.md index cbfa7beb27..e3154921a4 100644 --- a/docs/tr/docs/how-to/general.md +++ b/docs/tr/docs/how-to/general.md @@ -1,39 +1,39 @@ -# Genel - Nasıl Yapılır - Tarifler +# Genel - Nasıl Yapılır - Tarifler { #general-how-to-recipes } -Bu sayfada genel ve sıkça sorulan sorular için dokümantasyonun diğer sayfalarına yönlendirmeler bulunmaktadır. +Bu sayfada genel veya sık sorulan sorular için dokümantasyonun diğer bölümlerine çeşitli yönlendirmeler bulunmaktadır. -## Veri Filtreleme - Güvenlik +## Veri Filtreleme - Güvenlik { #filter-data-security } -Döndürmeniz gereken veriden fazlasını döndürmediğinizden emin olmak için, [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank} sayfasını okuyun. +Döndürmeniz gerekenden daha fazla veri döndürmediğinizden emin olmak için, [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank} dokümantasyonunu okuyun. -## Dokümantasyon Etiketleri - OpenAPI +## Dokümantasyon Etiketleri - OpenAPI { #documentation-tags-openapi } -*Yol operasyonlarınıza* etiketler ekleyerek dokümantasyon arayüzünde gruplar halinde görünmesini sağlamak için, [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank} sayfasını okuyun. +*path operation*'larınıza etiketler eklemek ve dokümantasyon arayüzünde gruplamak için, [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank} dokümantasyonunu okuyun. -## Dokümantasyon Özeti ve Açıklaması - OpenAPI +## Dokümantasyon Özeti ve Açıklaması - OpenAPI { #documentation-summary-and-description-openapi } -*Yol operasyonlarınıza* özet ve açıklama ekleyip dokümantasyon arayüzünde görünmesini sağlamak için, [Tutorial - Path Operation Configurations - Summary and Description](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank} sayfasını okuyun. +*path operation*'larınıza özet ve açıklama eklemek ve bunları dokümantasyon arayüzünde göstermek için, [Tutorial - Path Operation Configurations - Summary and Description](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank} dokümantasyonunu okuyun. -## Yanıt Açıklaması Dokümantasyonu - OpenAPI +## Dokümantasyon Yanıt Açıklaması - OpenAPI { #documentation-response-description-openapi } -Dokümantasyon arayüzünde yer alan yanıt açıklamasını tanımlamak için, [Tutorial - Path Operation Configurations - Response description](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank} sayfasını okuyun. +Dokümantasyon arayüzünde gösterilen response açıklamasını tanımlamak için, [Tutorial - Path Operation Configurations - Response description](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank} dokümantasyonunu okuyun. -## *Yol Operasyonunu* Kullanımdan Kaldırma - OpenAPI +## Dokümantasyonda Bir *Path Operation*'ı Kullanımdan Kaldırma - OpenAPI { #documentation-deprecate-a-path-operation-openapi } -Bir *yol işlemi*ni kullanımdan kaldırmak ve bunu dokümantasyon arayüzünde göstermek için, [Tutorial - Path Operation Configurations - Deprecation](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank} sayfasını okuyun. +Bir *path operation*'ı kullanımdan kaldırmak ve bunu dokümantasyon arayüzünde göstermek için, [Tutorial - Path Operation Configurations - Deprecation](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank} dokümantasyonunu okuyun. -## Herhangi Bir Veriyi JSON Uyumlu Hale Getirme +## Herhangi Bir Veriyi JSON Uyumlu Hale Getirme { #convert-any-data-to-json-compatible } -Herhangi bir veriyi JSON uyumlu hale getirmek için, [Tutorial - JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} sayfasını okuyun. +Herhangi bir veriyi JSON uyumlu hale getirmek için, [Tutorial - JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} dokümantasyonunu okuyun. -## OpenAPI Meta Verileri - Dokümantasyon +## OpenAPI Meta Verileri - Dokümantasyon { #openapi-metadata-docs } -OpenAPI şemanıza lisans, sürüm, iletişim vb. meta veriler eklemek için, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md){.internal-link target=_blank} sayfasını okuyun. +Lisans, sürüm, iletişim vb. dahil olmak üzere OpenAPI şemanıza meta veriler eklemek için, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md){.internal-link target=_blank} dokümantasyonunu okuyun. -## OpenAPI Bağlantı Özelleştirme +## OpenAPI Özel URL { #openapi-custom-url } -OpenAPI bağlantısını özelleştirmek (veya kaldırmak) için, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#openapi-url){.internal-link target=_blank} sayfasını okuyun. +OpenAPI URL'ini özelleştirmek (veya kaldırmak) için, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#openapi-url){.internal-link target=_blank} dokümantasyonunu okuyun. -## OpenAPI Dokümantasyon Bağlantıları +## OpenAPI Dokümantasyon URL'leri { #openapi-docs-urls } -Dokümantasyonu arayüzünde kullanılan bağlantıları güncellemek için, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#docs-urls){.internal-link target=_blank} sayfasını okuyun. +Otomatik olarak oluşturulan dokümantasyon kullanıcı arayüzlerinde kullanılan URL'leri güncellemek için, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#docs-urls){.internal-link target=_blank} dokümantasyonunu okuyun. diff --git a/docs/tr/docs/how-to/index.md b/docs/tr/docs/how-to/index.md index 26dd9026ce..5ec2e0268f 100644 --- a/docs/tr/docs/how-to/index.md +++ b/docs/tr/docs/how-to/index.md @@ -1,13 +1,13 @@ -# Nasıl Yapılır - Tarifler +# Nasıl Yapılır - Tarifler { #how-to-recipes } -Burada çeşitli konular hakkında farklı tarifler veya "nasıl yapılır" kılavuzları yer alıyor. +Burada **çeşitli konular** hakkında farklı tarifler veya "nasıl yapılır" kılavuzları göreceksiniz. -Bu fikirlerin büyük bir kısmı aşağı yukarı **bağımsız** olacaktır, çoğu durumda bunları sadece **projenize** hitap ediyorsa incelemelisiniz. +Bu fikirlerin büyük bir kısmı aşağı yukarı **bağımsız** olacaktır ve çoğu durumda bunları yalnızca doğrudan **projenize** uygulanıyorsa incelemeniz yeterli olacaktır. -Projeniz için ilginç ve yararlı görünen bir şey varsa devam edin ve inceleyin, aksi halde bunları atlayabilirsiniz. +Projeniz için ilginç ve yararlı görünen bir şey varsa devam edin ve inceleyin; aksi halde muhtemelen bunları atlayabilirsiniz. /// tip | İpucu -**FastAPI**'ı düzgün (ve önerilen) şekilde öğrenmek istiyorsanız [Öğretici - Kullanıcı Rehberi](../tutorial/index.md){.internal-link target=_blank}'ni bölüm bölüm okuyun. +**FastAPI**'ı yapılandırılmış bir şekilde (önerilir) **öğrenmek** istiyorsanız bunun yerine [Öğretici - Kullanıcı Rehberi](../tutorial/index.md){.internal-link target=_blank}'ni bölüm bölüm okuyun. /// diff --git a/docs/tr/docs/index.md b/docs/tr/docs/index.md index 516d5959ef..9cffd4274f 100644 --- a/docs/tr/docs/index.md +++ b/docs/tr/docs/index.md @@ -1,14 +1,14 @@ -# FastAPI +# FastAPI { #fastapi }

- FastAPI + FastAPI

- FastAPI framework, yüksek performanslı, öğrenmesi oldukça kolay, kodlaması hızlı, kullanıma hazır + FastAPI framework, yüksek performanslı, öğrenmesi kolay, kodlaması hızlı, production'a hazır

@@ -27,59 +27,65 @@ --- -**Dokümantasyon**: https://fastapi.tiangolo.com +**Dokümantasyon**: https://fastapi.tiangolo.com **Kaynak Kod**: https://github.com/fastapi/fastapi --- -FastAPI, Python 'nin standart tip belirteçlerine dayalı, modern ve hızlı (yüksek performanslı) API'lar oluşturmak için kullanılabilecek web framework'tür. +FastAPI, Python'un standart type hints'lerine dayalı olarak Python ile API'lar oluşturmak için kullanılan modern ve hızlı (yüksek performanslı) bir web framework'üdür. Temel özellikleri şunlardır: -* **Hızlı**: Çok yüksek performanslı, **NodeJS** ve **Go** ile eşit düzeyde (Starlette ve Pydantic sayesinde). [En hızlı Python framework'lerinden bir tanesidir](#performans). -* **Kodlaması Hızlı**: Geliştirme hızını yaklaşık %200 ile %300 aralığında arttırır. * +* **Hızlı**: Çok yüksek performanslı, **NodeJS** ve **Go** ile eşit düzeyde (Starlette ve Pydantic sayesinde). [Mevcut en hızlı Python framework'lerinden biri](#performance). +* **Kodlaması Hızlı**: Özellik geliştirme hızını yaklaşık %200 ile %300 aralığında artırır. * * **Daha az hata**: İnsan (geliştirici) kaynaklı hataları yaklaşık %40 azaltır. * -* **Sezgisel**: Muhteşem bir editör desteği. Her yerde otomatik tamamlama. Hata ayıklama ile daha az zaman harcayacaksınız. -* **Kolay**: Öğrenmesi ve kullanması kolay olacak şekilde tasarlandı. Doküman okuma ile daha az zaman harcayacaksınız. -* **Kısa**: Kod tekrarı minimize edildi. Her parametre tanımlamasında birden fazla özellik ve daha az hatayla karşılaşacaksınız. -* **Güçlü**: Otomatik ve etkileşimli dokümantasyon ile birlikte, kullanıma hazır kod elde edebilirsiniz. -* **Standard öncelikli**: API'lar için açık standartlara dayalı (ve tamamen uyumlu); OpenAPI (eski adıyla Swagger) ve JSON Schema. +* **Sezgisel**: Harika bir editör desteği. Her yerde Completion. Hata ayıklamaya daha az zaman. +* **Kolay**: Kullanımı ve öğrenmesi kolay olacak şekilde tasarlandı. Doküman okumaya daha az zaman. +* **Kısa**: Kod tekrarını minimize eder. Her parametre tanımından birden fazla özellik. Daha az hata. +* **Sağlam**: Production'a hazır kod elde edersiniz. Otomatik etkileşimli dokümantasyon ile birlikte. +* **Standardlara dayalı**: API'lar için açık standartlara dayalıdır (ve tamamen uyumludur); OpenAPI (önceden Swagger olarak biliniyordu) ve JSON Schema. -* ilgili kanılar, dahili geliştirme ekibinin geliştirdikleri ürünlere yaptıkları testlere dayanmaktadır. +* tahmin, production uygulamalar geliştiren dahili bir geliştirme ekibinin yaptığı testlere dayanmaktadır. -## Sponsorlar +## Sponsorlar { #sponsors } -{% if sponsors %} +### Keystone Sponsor { #keystone-sponsor } + +{% for sponsor in sponsors.keystone -%} + +{% endfor -%} + +### Gold and Silver Sponsors { #gold-and-silver-sponsors } + {% for sponsor in sponsors.gold -%} {% endfor -%} {%- for sponsor in sponsors.silver -%} {% endfor %} -{% endif %} -Diğer Sponsorlar +Diğer sponsorlar -## Görüşler +## Görüşler { #opinions } -"_[...] Bugünlerde **FastAPI**'ı çok fazla kullanıyorum. [...] Aslında bunu ekibimin **Microsoft'taki Machine Learning servislerinin** tamamında kullanmayı planlıyorum. Bunlardan bazıları **Windows**'un ana ürünlerine ve **Office** ürünlerine entegre ediliyor._" +"_[...] Bugünlerde **FastAPI**'ı çok fazla kullanıyorum. [...] Aslında bunu ekibimin **Microsoft'taki ML servislerinin** tamamında kullanmayı planlıyorum. Bunlardan bazıları ana **Windows** ürününe ve bazı **Office** ürünlerine entegre ediliyor._"

Kabir Khan - Microsoft (ref)
--- -"_**FastAPI**'ı **tahminlerimiz**'i sorgulanabilir hale getirecek bir **REST** sunucu oluşturmak için benimsedik/kullanmaya başladık._" +"_**predictions** almak için sorgulanabilecek bir **REST** server oluşturmak amacıyla **FastAPI** kütüphanesini benimsedik. [Ludwig için]_"
Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber (ref)
--- -"_**Netflix**, **kriz yönetiminde** orkestrasyon yapabilmek için geliştirdiği yeni framework'ü **Dispatch**'in, açık kaynak sürümünü paylaşmaktan gurur duyuyor. [**FastAPI** ile yapıldı.]_" +"_**Netflix**, **kriz yönetimi** orkestrasyon framework'ümüz: **Dispatch**'in open-source sürümünü duyurmaktan memnuniyet duyar! [**FastAPI** ile geliştirildi]_"
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix (ref)
@@ -91,70 +97,68 @@ Temel özellikleri şunlardır: --- -"_Dürüst olmak gerekirse, inşa ettiğiniz şey gerçekten sağlam ve profesyonel görünüyor. Birçok açıdan **Hug**'ın olmasını istediğim şey tam da bu - böyle bir şeyi inşa eden birini görmek gerçekten ilham verici._" +"_Dürüst olmak gerekirse, inşa ettiğiniz şey gerçekten sağlam ve profesyonel görünüyor. Birçok açıdan, **Hug**'ın olmasını istediğim şey tam da bu - böyle bir şeyi inşa eden birini görmek gerçekten ilham verici._" -
Timothy Crosley - Hug'ın Yaratıcısı (ref)
+
Timothy Crosley - Hug yaratıcısı (ref)
--- -"_Eğer REST API geliştirmek için **modern bir framework** öğrenme arayışında isen, **FastAPI**'a bir göz at [...] Hızlı, kullanımı ve öğrenmesi kolay. [...]_" +"_REST API'lar geliştirmek için **modern bir framework** öğrenmek istiyorsanız, **FastAPI**'a bir göz atın [...] Hızlı, kullanımı ve öğrenmesi kolay [...]_" -"_**API** servislerimizi **FastAPI**'a taşıdık [...] Sizin de beğeneceğinizi düşünüyoruz. [...]_" +"_**API**'larımız için **FastAPI**'a geçtik [...] Bence hoşunuza gidecek [...]_"
Ines Montani - Matthew Honnibal - Explosion AI kurucuları - spaCy yaratıcıları (ref) - (ref)
--- -"_Python ile kullanıma hazır bir API oluşturmak isteyen herhangi biri için, **FastAPI**'ı şiddetle tavsiye ederim. **Harika tasarlanmış**, **kullanımı kolay** ve **yüksek ölçeklenebilir**, API odaklı geliştirme stratejimizin **ana bileşeni** haline geldi ve Virtual TAC Engineer gibi birçok otomasyon ve servisi yönetiyor._" +"_Production'da Python API geliştirmek isteyen herkese **FastAPI**'ı şiddetle tavsiye ederim. **Harika tasarlanmış**, **kullanımı kolay** ve **yüksek ölçeklenebilir**; API-first geliştirme stratejimizin **kilit bir bileşeni** haline geldi ve Virtual TAC Engineer gibi birçok otomasyon ve servise güç veriyor._"
Deon Pillsbury - Cisco (ref)
--- -## Komut Satırı Uygulamalarının FastAPI'ı: **Typer** +## FastAPI mini belgeseli { #fastapi-mini-documentary } + +2025'in sonunda yayınlanan bir FastAPI mini belgeseli var, online olarak izleyebilirsiniz: + +FastAPI Mini Documentary + +## CLI'ların FastAPI'ı: **Typer** { #typer-the-fastapi-of-clis } -Eğer API yerine, terminalde kullanılmak üzere bir komut satırı uygulaması geliştiriyorsanız **Typer**'a göz atabilirsiniz. +Web API yerine terminalde kullanılacak bir CLI uygulaması geliştiriyorsanız **Typer**'a göz atın. -**Typer** kısaca FastAPI'ın küçük kardeşi. Ve hedefi komut satırı uygulamalarının **FastAPI'ı** olmak. ⌨️ 🚀 +**Typer**, FastAPI'ın küçük kardeşi. Ve hedefi CLI'ların **FastAPI'ı** olmak. ⌨️ 🚀 -## Gereksinimler +## Gereksinimler { #requirements } FastAPI iki devin omuzları üstünde duruyor: -* Web tarafı için Starlette. -* Data tarafı için Pydantic. +* Web kısımları için Starlette. +* Data kısımları için Pydantic. -## Kurulum +## Kurulum { #installation } + +Bir virtual environment oluşturup etkinleştirelim ve ardından FastAPI'ı yükleyelim:
```console -$ pip install fastapi +$ pip install "fastapi[standard]" ---> 100% ```
-Uygulamamızı kullanılabilir hale getirmek için Uvicorn ya da Hypercorn gibi bir ASGI sunucusuna ihtiyacımız olacak. +**Not**: Tüm terminallerde çalıştığından emin olmak için `"fastapi[standard]"` ifadesini tırnak içinde yazdığınızdan emin olun. -
+## Örnek { #example } -```console -$ pip install "uvicorn[standard]" +### Oluşturalım { #create-it } ----> 100% -``` - -
- -## Örnek - -### Kodu Oluşturalım - -* `main.py` adında bir dosya oluşturup içine şu kodu yapıştıralım: +Şu içerikle `main.py` adında bir dosya oluşturalım: ```Python from typing import Union @@ -175,9 +179,9 @@ def read_item(item_id: int, q: Union[str, None] = None): ```
-Ya da async def... +Ya da async def kullanalım... -Eğer kodunuzda `async` / `await` varsa, `async def` kullanalım: +Eğer kodunuz `async` / `await` kullanıyorsa, `async def` kullanın: ```Python hl_lines="9 14" from typing import Union @@ -199,22 +203,35 @@ async def read_item(item_id: int, q: Union[str, None] = None): **Not**: -Eğer bu konu hakkında bilginiz yoksa `async` ve `await` dokümantasyonundaki _"Aceleniz mi var?"_ kısmını kontrol edebilirsiniz. +Eğer bilmiyorsanız, dokümanlardaki `async` ve `await` hakkında _"Aceleniz mi var?"_ bölümüne bakın.
-### Kodu Çalıştıralım +### Çalıştıralım { #run-it } -Sunucuyu aşağıdaki komutla çalıştıralım: +Sunucuyu şu komutla çalıştıralım:
```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. ``` @@ -222,54 +239,54 @@ INFO: Application startup complete.
-uvicorn main:app --reload komutuyla ilgili... +fastapi dev main.py komutu hakkında... -`uvicorn main:app` komutunu şu şekilde açıklayabiliriz: +`fastapi dev` komutu, `main.py` dosyanızı okur, içindeki **FastAPI** uygulamasını algılar ve Uvicorn kullanarak bir server başlatır. -* `main`: dosya olan `main.py` (yani Python "modülü"). -* `app`: ise `main.py` dosyasının içerisinde `app = FastAPI()` satırında oluşturduğumuz `FastAPI` nesnesi. -* `--reload`: kod değişikliklerinin ardından sunucuyu otomatik olarak yeniden başlatır. Bu parameteyi sadece geliştirme aşamasında kullanmalıyız. +Varsayılan olarak `fastapi dev`, local geliştirme için auto-reload etkin şekilde başlar. + +Daha fazla bilgi için FastAPI CLI dokümantasyonu'nu okuyabilirsiniz.
-### Şimdi de Kontrol Edelim +### Kontrol Edelim { #check-it } -Tarayıcımızda şu bağlantıyı açalım http://127.0.0.1:8000/items/5?q=somequery. +Tarayıcınızda şu bağlantıyı açın: http://127.0.0.1:8000/items/5?q=somequery. -Aşağıdaki gibi bir JSON yanıtıyla karşılaşacağız: +Şu JSON response'unu göreceksiniz: ```JSON {"item_id": 5, "q": "somequery"} ``` -Az önce oluşturduğumuz API: +Artık şunları yapan bir API oluşturdunuz: -* `/` ve `/items/{item_id}` _yollarına_ HTTP isteği alabilir. -* İki _yolda_ `GET` operasyonlarını (HTTP _metodları_ olarak da bilinen) kabul ediyor. -* `/items/{item_id}` _yolu_ `item_id` adında bir _yol parametresine_ sahip ve bu parametre `int` değer almak zorundadır. -* `/items/{item_id}` _yolu_ `q` adında bir _yol parametresine_ sahip ve bu parametre opsiyonel olmakla birlikte, `str` değer almak zorundadır. +* `/` ve `/items/{item_id}` _path_'lerinde HTTP request'leri alır. +* Her iki _path_ de `GET` operasyonlarını (HTTP _method_'ları olarak da bilinir) kabul eder. +* `/items/{item_id}` _path_'i, `int` olması gereken `item_id` adlı bir _path parameter_'a sahiptir. +* `/items/{item_id}` _path_'i, opsiyonel `str` bir _query parameter_ olan `q`'ya sahiptir. -### Etkileşimli API Dokümantasyonu +### Etkileşimli API dokümantasyonu { #interactive-api-docs } -Şimdi http://127.0.0.1:8000/docs bağlantısını açalım. +Şimdi http://127.0.0.1:8000/docs adresine gidin. -Swagger UI tarafından sağlanan otomatik etkileşimli bir API dokümantasyonu göreceğiz: +Otomatik etkileşimli API dokümantasyonunu göreceksiniz (Swagger UI tarafından sağlanır): ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) -### Alternatif API Dokümantasyonu +### Alternatif API dokümantasyonu { #alternative-api-docs } -Şimdi http://127.0.0.1:8000/redoc bağlantısını açalım. +Ve şimdi http://127.0.0.1:8000/redoc adresine gidin. -ReDoc tarafından sağlanan otomatik dokümantasyonu göreceğiz: +Alternatif otomatik dokümantasyonu göreceksiniz (ReDoc tarafından sağlanır): ![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) -## Örneği Güncelleyelim +## Örneği Güncelleyelim { #example-upgrade } -Şimdi `main.py` dosyasını, `PUT` isteğiyle birlikte bir gövde alacak şekilde değiştirelim. +Şimdi `main.py` dosyasını, `PUT` request'iyle gelen bir body alacak şekilde değiştirelim. -Gövdeyi Pydantic sayesinde standart python tiplerini kullanarak tanımlayalım. +Body'yi Pydantic sayesinde standart Python tiplerini kullanarak tanımlayalım. ```Python hl_lines="4 9-12 25-27" from typing import Union @@ -301,174 +318,248 @@ def update_item(item_id: int, item: Item): return {"item_name": item.name, "item_id": item_id} ``` -Sunucu otomatik olarak yeniden başlamış olmalı (çünkü yukarıda `uvicorn` komutuyla birlikte `--reload` parametresini kullandık). +`fastapi dev` server'ı otomatik olarak yeniden yüklemelidir. -### Etkileşimli API Dokümantasyonundaki Değişimi Görelim +### Etkileşimli API dokümantasyonu güncellemesi { #interactive-api-docs-upgrade } -Şimdi http://127.0.0.1:8000/docs bağlantısına tekrar gidelim. +Şimdi http://127.0.0.1:8000/docs adresine gidin. -* Etkileşimli API dokümantasyonu, yeni gövdede dahil olmak üzere otomatik olarak güncellenmiş olacak: +* Etkileşimli API dokümantasyonu, yeni body dahil olacak şekilde otomatik olarak güncellenecek: ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) -* "Try it out" butonuna tıklayalım, bu işlem API parametleri üzerinde değişiklik yapmamıza ve doğrudan API ile etkileşime geçmemize imkan sağlayacak: +* "Try it out" butonuna tıklayın; parametreleri doldurmanıza ve API ile doğrudan etkileşime girmenize olanak sağlar: ![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png) -* Şimdi "Execute" butonuna tıklayalım, kullanıcı arayüzü API'ımız ile bağlantı kurup parametreleri gönderecek ve sonucu ekranımıza getirecek: +* Sonra "Execute" butonuna tıklayın; kullanıcı arayüzü API'nız ile iletişim kuracak, parametreleri gönderecek, sonuçları alacak ve ekranda gösterecek: ![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png) -### Alternatif API Dokümantasyonundaki Değişimi Görelim +### Alternatif API dokümantasyonu güncellemesi { #alternative-api-docs-upgrade } -Şimdi ise http://127.0.0.1:8000/redoc bağlantısına tekrar gidelim. +Ve şimdi http://127.0.0.1:8000/redoc adresine gidin. -* Alternatif dokümantasyonda yaptığımız değişiklikler ile birlikte yeni sorgu parametresi ve gövde bilgisi ile güncelemiş olacak: +* Alternatif dokümantasyon da yeni query parameter ve body'yi yansıtacak: ![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) -### Özet +### Özet { #recap } -Özetlemek gerekirse, parametrelerin, gövdenin, vb. veri tiplerini fonksiyon parametreleri olarak **bir kere** tanımlıyoruz. +Özetle, parametrelerin, body'nin vb. type'larını fonksiyon parametreleri olarak **bir kere** tanımlarsınız. -Bu işlemi standart modern Python tipleriyle yapıyoruz. +Bunu standart modern Python tipleriyle yaparsınız. -Yeni bir sözdizimi yapısını, bir kütüphane özel metod veya sınıfları öğrenmeye gerek yoktur. +Yeni bir syntax, belirli bir kütüphanenin method'larını ya da class'larını vb. öğrenmeniz gerekmez. -Hepsi sadece **Python** standartlarına dayalıdır. +Sadece standart **Python**. -Örnek olarak, `int` tanımlamak için: +Örneğin bir `int` için: ```Python item_id: int ``` -ya da daha kompleks herhangi bir python modelini tanımlayabiliriz, örneğin `Item` modeli için: +ya da daha karmaşık bir `Item` modeli için: ```Python item: Item ``` -...ve sadece kısa bir parametre tipi belirterek elde ettiklerimiz: +...ve bu tek tanımla şunları elde edersiniz: -* Editör desteğiyle birlikte: - * Otomatik tamamlama. - * Tip kontrolü. -* Veri Doğrulama: - * Veri geçerli değilse, otomatik olarak açıklayıcı hatalar gösterir. - * Çok derin JSON nesnelerinde bile doğrulama yapar. -* Gelen verinin dönüşümünü aşağıdaki veri tiplerini kullanarak gerçekleştirir: +* Şunlar dahil editör desteği: + * Completion. + * Type kontrolleri. +* Verinin doğrulanması: + * Veri geçersiz olduğunda otomatik ve anlaşılır hatalar. + * Çok derin iç içe JSON nesneleri için bile doğrulama. +* Girdi verisinin Dönüşümü: network'ten gelen veriyi Python verisine ve type'larına çevirir. Şunlardan okur: * JSON. - * Yol parametreleri. - * Sorgu parametreleri. - * Çerezler. - * Headers. - * Formlar. - * Dosyalar. -* Giden verinin dönüşümünü aşağıdaki veri tiplerini kullanarak gerçekleştirir (JSON olarak): - * Python tiplerinin (`str`, `int`, `float`, `bool`, `list`, vb) dönüşümü. - * `datetime` nesnesi. - * `UUID` nesnesi. + * Path parameter'lar. + * Query parameter'lar. + * Cookie'ler. + * Header'lar. + * Form'lar. + * File'lar. +* Çıktı verisinin Dönüşümü: Python verisini ve type'larını network verisine çevirir (JSON olarak): + * Python type'larını dönüştürür (`str`, `int`, `float`, `bool`, `list`, vb.). + * `datetime` nesneleri. + * `UUID` nesneleri. * Veritabanı modelleri. - * ve çok daha fazlası... -* 2 alternatif kullanıcı arayüzü dahil olmak üzere, otomatik etkileşimli API dokümantasyonu sağlar: + * ...ve daha fazlası. +* 2 alternatif kullanıcı arayüzü dahil otomatik etkileşimli API dokümantasyonu: * Swagger UI. * ReDoc. --- -Az önceki örneğe geri dönelim, **FastAPI**'ın yapacaklarına bir bakış atalım: +Önceki kod örneğine dönersek, **FastAPI** şunları yapacaktır: -* `item_id`'nin `GET` ve `PUT` istekleri için, yolda olup olmadığının kontol edecek. -* `item_id`'nin `GET` ve `PUT` istekleri için, tipinin `int` olduğunu doğrulayacak. - * Eğer değilse, sebebini belirten bir hata mesajı gösterecek. -* Opsiyonel bir `q` parametresinin `GET` isteği içinde (`http://127.0.0.1:8000/items/foo?q=somequery` gibi) olup olmadığını kontrol edecek - * `q` parametresini `= None` ile oluşturduğumuz için, opsiyonel bir parametre olacak. - * Eğer `None` olmasa zorunlu bir parametre olacaktı (`PUT` metodunun gövdesinde olduğu gibi). -* `PUT` isteği için `/items/{item_id}`'nin gövdesini, JSON olarak doğrulayıp okuyacak: - * `name` adında zorunlu bir parametre olup olmadığını ve varsa tipinin `str` olup olmadığını kontol edecek. - * `price` adında zorunlu bir parametre olup olmadığını ve varsa tipinin `float` olup olmadığını kontol edecek. - * `is_offer` adında opsiyonel bir parametre olup olmadığını ve varsa tipinin `float` olup olmadığını kontol edecek. - * Bunların hepsi en derin JSON nesnelerinde bile çalışacak. -* Verilerin JSON'a ve JSON'ın python nesnesine dönüşümü otomatik olarak yapılacak. -* Her şeyi OpenAPI ile uyumlu bir şekilde otomatik olarak dokümanlayacak ve bunlarda aşağıdaki gibi kullanılabilecek: +* `GET` ve `PUT` request'leri için path'te `item_id` olduğunu doğrular. +* `GET` ve `PUT` request'leri için `item_id`'nin type'ının `int` olduğunu doğrular. + * Değilse, client faydalı ve anlaşılır bir hata görür. +* `GET` request'leri için `q` adlı opsiyonel bir query parameter olup olmadığını kontrol eder (`http://127.0.0.1:8000/items/foo?q=somequery` örneğindeki gibi). + * `q` parametresi `= None` ile tanımlandığı için opsiyoneldir. + * `None` olmasaydı zorunlu olurdu (tıpkı `PUT` örneğindeki body gibi). +* `/items/{item_id}`'ye yapılan `PUT` request'leri için body'yi JSON olarak okur: + * `str` olması gereken, zorunlu `name` alanı olduğunu kontrol eder. + * `float` olması gereken, zorunlu `price` alanı olduğunu kontrol eder. + * Varsa, `bool` olması gereken opsiyonel `is_offer` alanını kontrol eder. + * Bunların hepsi çok derin iç içe JSON nesneleri için de çalışır. +* JSON'a ve JSON'dan dönüşümü otomatik yapar. +* Her şeyi OpenAPI ile dokümante eder; bu dokümantasyon şunlar tarafından kullanılabilir: * Etkileşimli dokümantasyon sistemleri. - * Bir çok programlama dili için otomatik istemci kodu üretim sistemleri. -* İki ayrı etkileşimli dokümantasyon arayüzünü doğrudan sağlayacak. + * Birçok dil için otomatik client kodu üretim sistemleri. +* 2 etkileşimli dokümantasyon web arayüzünü doğrudan sunar. --- -Daha yeni başladık ama çalışma mantığını çoktan anlamış oldunuz. +Daha yolun başındayız, ama bunun nasıl çalıştığı hakkında fikri kaptınız. -Şimdi aşağıdaki satırı değiştirmeyi deneyin: +Şu satırı değiştirmeyi deneyin: ```Python return {"item_name": item.name, "item_id": item_id} ``` -...bundan: +...şundan: ```Python ... "item_name": item.name ... ``` -...buna: +...şuna: ```Python ... "item_price": item.price ... ``` -...ve editörünün veri tiplerini bildiğini ve otomatik tamamladığını göreceksiniz: +...ve editörünüzün alanları otomatik tamamladığını ve type'larını bildiğini görün: ![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png) -Daha fazal özellik içeren, daha eksiksiz bir örnek için Öğretici - Kullanıcı Rehberi sayfasını ziyaret edebilirsin. +Daha fazla özellik içeren daha kapsamlı bir örnek için Öğretici - Kullanıcı Rehberi'ne bakın. -**Spoiler**: Öğretici - Kullanıcı rehberi şunları içerir: +**Spoiler alert**: öğretici - kullanıcı rehberi şunları içerir: -* **Parameterlerin**, **headers**, **çerezler**, **form alanları** ve **dosyalar** olarak tanımlanması. -* `maximum_length` ya da `regex` gibi **doğrulama kısıtlamalarının** nasıl yapılabileceği. -* Çok güçlü ve kullanımı kolay **Bağımlılık Enjeksiyonu** sistemi oluşturmayı. -* Güvenlik ve kimlik doğrulama, **JWT tokenleri** ile **OAuth2** desteği, ve **HTTP Basic** doğrulaması. -* İleri seviye fakat bir o kadarda basit olan **çok derin JSON modelleri** (Pydantic sayesinde). -* **GraphQL** entegrasyonu: Strawberry ve diğer kütüphaneleri kullanarak. -* Diğer ekstra özellikler (Starlette sayesinde): - * **WebSocketler** - * HTTPX ve `pytest` sayesinde aşırı kolay testler. +* **parameter**'ların farklı yerlerden: **header**'lar, **cookie**'ler, **form alanları** ve **file**'lar olarak tanımlanması. +* `maximum_length` ya da `regex` gibi **doğrulama kısıtlamalarının** nasıl ayarlanacağı. +* Çok güçlü ve kullanımı kolay bir **Dependency Injection** sistemi. +* **JWT tokens** ve **HTTP Basic** auth ile **OAuth2** desteği dahil güvenlik ve kimlik doğrulama. +* **Çok derin iç içe JSON modelleri** tanımlamak için daha ileri (ama aynı derecede kolay) teknikler (Pydantic sayesinde). +* Strawberry ve diğer kütüphaneler ile **GraphQL** entegrasyonu. +* Starlette sayesinde gelen birçok ek özellik: + * **WebSockets** + * HTTPX ve `pytest` tabanlı aşırı kolay testler * **CORS** * **Cookie Sessions** * ...ve daha fazlası. -## Performans +### Uygulamanızı deploy edin (opsiyonel) { #deploy-your-app-optional } -Bağımsız TechEmpower kıyaslamaları gösteriyor ki, Uvicorn ile çalıştırılan **FastAPI** uygulamaları en hızlı Python framework'lerinden birisi, sadece Starlette ve Uvicorn'dan yavaş, ki FastAPI bunların üzerine kurulu bir kütüphanedir. +İsterseniz FastAPI uygulamanızı FastAPI Cloud'a deploy edebilirsiniz; eğer henüz yapmadıysanız gidip bekleme listesine katılın. 🚀 -Daha fazla bilgi için, bu bölüme bir göz at Kıyaslamalar. +Zaten bir **FastAPI Cloud** hesabınız varsa (bekleme listesinden sizi davet ettiysek 😉), uygulamanızı tek bir komutla deploy edebilirsiniz. -## Opsiyonel Gereksinimler +Deploy etmeden önce, giriş yaptığınızdan emin olun: -Pydantic tarafında kullanılan: +
+ +```console +$ fastapi login + +You are logged in to FastAPI Cloud 🚀 +``` + +
+ +Sonra uygulamanızı deploy edin: + +
+ +```console +$ fastapi deploy + +Deploying to FastAPI Cloud... + +✅ Deployment successful! + +🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev +``` + +
+ +Hepsi bu! Artık uygulamanıza bu URL'den erişebilirsiniz. ✨ + +#### FastAPI Cloud hakkında { #about-fastapi-cloud } + +**FastAPI Cloud**, **FastAPI**'ın arkasındaki aynı yazar ve ekip tarafından geliştirilmiştir. + +**Bir API'ı build etmek**, **deploy etmek** ve **erişmek** süreçlerini minimum eforla kolaylaştırır. + +FastAPI ile uygulama geliştirmenin sağladığı aynı **developer experience**'ı, onları cloud'a **deploy etmeye** de taşır. 🎉 + +FastAPI Cloud, *FastAPI and friends* open source projelerinin ana sponsoru ve finansman sağlayıcısıdır. ✨ + +#### Diğer cloud sağlayıcılarına deploy { #deploy-to-other-cloud-providers } + +FastAPI open source'tur ve standartlara dayanır. FastAPI uygulamalarını seçtiğiniz herhangi bir cloud sağlayıcısına deploy edebilirsiniz. + +FastAPI uygulamalarını onlarla deploy etmek için cloud sağlayıcınızın rehberlerini takip edin. 🤓 + +## Performans { #performance } + +Bağımsız TechEmpower kıyaslamaları, Uvicorn altında çalışan **FastAPI** uygulamalarının mevcut en hızlı Python framework'lerinden biri olduğunu gösteriyor; sadece Starlette ve Uvicorn'un kendisinin gerisinde (FastAPI tarafından dahili olarak kullanılır). (*) + +Daha iyi anlamak için Kıyaslamalar bölümüne bakın. + +## Bağımlılıklar { #dependencies } + +FastAPI, Pydantic ve Starlette'a bağımlıdır. + +### `standard` Bağımlılıkları { #standard-dependencies } + +FastAPI'ı `pip install "fastapi[standard]"` ile yüklediğinizde, opsiyonel bağımlılıkların `standard` grubuyla birlikte gelir: + +Pydantic tarafından kullanılanlar: * email-validator - email doğrulaması için. + +Starlette tarafından kullanılanlar: + +* httpx - `TestClient` kullanmak istiyorsanız gereklidir. +* jinja2 - varsayılan template yapılandırmasını kullanmak istiyorsanız gereklidir. +* python-multipart - `request.form()` ile, form "parsing" desteği istiyorsanız gereklidir. + +FastAPI tarafından kullanılanlar: + +* uvicorn - uygulamanızı yükleyen ve servis eden server için. Buna, yüksek performanslı servis için gereken bazı bağımlılıkları (örn. `uvloop`) içeren `uvicorn[standard]` dahildir. +* `fastapi-cli[standard]` - `fastapi` komutunu sağlamak için. + * Buna, FastAPI uygulamanızı FastAPI Cloud'a deploy etmenizi sağlayan `fastapi-cloud-cli` dahildir. + +### `standard` Bağımlılıkları Olmadan { #without-standard-dependencies } + +`standard` opsiyonel bağımlılıklarını dahil etmek istemiyorsanız, `pip install "fastapi[standard]"` yerine `pip install fastapi` ile kurabilirsiniz. + +### `fastapi-cloud-cli` Olmadan { #without-fastapi-cloud-cli } + +FastAPI'ı standard bağımlılıklarla ama `fastapi-cloud-cli` olmadan kurmak istiyorsanız, `pip install "fastapi[standard-no-fastapi-cloud-cli]"` ile yükleyebilirsiniz. + +### Ek Opsiyonel Bağımlılıklar { #additional-optional-dependencies } + +Yüklemek isteyebileceğiniz bazı ek bağımlılıklar da vardır. + +Ek opsiyonel Pydantic bağımlılıkları: + * pydantic-settings - ayar yönetimi için. -* pydantic-extra-types - Pydantic ile birlikte kullanılabilecek ek tipler için. +* pydantic-extra-types - Pydantic ile kullanılacak ek type'lar için. -Starlette tarafında kullanılan: +Ek opsiyonel FastAPI bağımlılıkları: -* httpx - Eğer `TestClient` yapısını kullanacaksanız gereklidir. -* jinja2 - Eğer varsayılan template konfigürasyonunu kullanacaksanız gereklidir. -* python-multipart - Eğer `request.form()` ile form dönüşümü desteğini kullanacaksanız gereklidir. -* itsdangerous - `SessionMiddleware` desteği için gerekli. -* pyyaml - `SchemaGenerator` desteği için gerekli (Muhtemelen FastAPI kullanırken ihtiyacınız olmaz). +* orjson - `ORJSONResponse` kullanmak istiyorsanız gereklidir. +* ujson - `UJSONResponse` kullanmak istiyorsanız gereklidir. -Hem FastAPI hem de Starlette tarafından kullanılan: +## Lisans { #license } -* uvicorn - oluşturduğumuz uygulamayı servis edecek web sunucusu görevini üstlenir. -* orjson - `ORJSONResponse` kullanacaksanız gereklidir. -* ujson - `UJSONResponse` kullanacaksanız gerekli. - -Bunların hepsini `pip install fastapi[all]` ile yükleyebilirsin. - -## Lisans - -Bu proje, MIT lisansı şartları altında lisanslanmıştır. +Bu proje MIT lisansı şartları altında lisanslanmıştır. diff --git a/docs/tr/docs/learn/index.md b/docs/tr/docs/learn/index.md index 52e3aa54df..accf971aa7 100644 --- a/docs/tr/docs/learn/index.md +++ b/docs/tr/docs/learn/index.md @@ -1,5 +1,5 @@ -# Öğren +# Öğren { #learn } **FastAPI** öğrenmek için giriş bölümleri ve öğreticiler burada yer alıyor. -Burayı, bir **kitap**, bir **kurs**, ve FastAPI öğrenmenin **resmi** ve önerilen yolu olarak düşünülebilirsiniz. 😎 +Burayı, bir **kitap**, bir **kurs**, FastAPI öğrenmenin **resmi** ve önerilen yolu olarak düşünebilirsiniz. 😎 diff --git a/docs/tr/docs/project-generation.md b/docs/tr/docs/project-generation.md index c9dc24acc4..bdc28f0c01 100644 --- a/docs/tr/docs/project-generation.md +++ b/docs/tr/docs/project-generation.md @@ -1,84 +1,28 @@ -# Proje oluşturma - Şablonlar +# Full Stack FastAPI Şablonu { #full-stack-fastapi-template } -Başlamak için bir proje oluşturucu kullanabilirsiniz, çünkü sizin için önceden yapılmış birçok başlangıç ​​kurulumu, güvenlik, veritabanı ve temel API endpoinlerini içerir. +Şablonlar genellikle belirli bir kurulumla gelir, ancak esnek ve özelleştirilebilir olacak şekilde tasarlanırlar. Bu sayede şablonu projenizin gereksinimlerine göre değiştirip uyarlayabilir, çok iyi bir başlangıç noktası olarak kullanabilirsiniz. 🏁 -Bir proje oluşturucu, her zaman kendi ihtiyaçlarınıza göre güncellemeniz ve uyarlamanız gereken esnek bir kuruluma sahip olacaktır, ancak bu, projeniz için iyi bir başlangıç ​​noktası olabilir. +Bu şablonu başlangıç için kullanabilirsiniz; çünkü ilk kurulumun, güvenliğin, veritabanının ve bazı API endpoint'lerinin önemli bir kısmı sizin için zaten hazırlanmıştır. -## Full Stack FastAPI PostgreSQL +GitHub Repository: Full Stack FastAPI Template -GitHub: https://github.com/tiangolo/full-stack-fastapi-postgresql +## Full Stack FastAPI Şablonu - Teknoloji Yığını ve Özellikler { #full-stack-fastapi-template-technology-stack-and-features } -### Full Stack FastAPI PostgreSQL - Özellikler - -* Full **Docker** entegrasyonu (Docker based). -* Docker Swarm Mode ile deployment. -* **Docker Compose** entegrasyonu ve lokal geliştirme için optimizasyon. -* Uvicorn ve Gunicorn ile **Production ready** Python web server'ı. -* Python **FastAPI** backend: - * **Hızlı**: **NodeJS** ve **Go** ile eşit, çok yüksek performans (Starlette ve Pydantic'e teşekkürler). - * **Sezgisel**: Editor desteğı. Otomatik tamamlama. Daha az debugging. - * **Kolay**: Kolay öğrenip kolay kullanmak için tasarlandı. Daha az döküman okuma daha çok iş. - * **Kısa**: Minimum kod tekrarı. Her parametre bildiriminde birden çok özellik. - * **Güçlü**: Production-ready. Otomatik interaktif dökümantasyon. - * **Standartlara dayalı**: API'ler için açık standartlara dayanır (ve tamamen uyumludur): OpenAPI ve JSON Şeması. - * **Birçok diger özelliği** dahili otomatik doğrulama, serialization, interaktif dokümantasyon, OAuth2 JWT token ile authentication, vb. -* **Güvenli şifreleme** . -* **JWT token** kimlik doğrulama. -* **SQLAlchemy** models (Flask dan bağımsızdır. Celery worker'ları ile kullanılabilir). -* Kullanıcılar için temel başlangıç ​​modeli (gerektiği gibi değiştirin ve kaldırın). -* **Alembic** migration. -* **CORS** (Cross Origin Resource Sharing). -* **Celery** worker'ları ile backend içerisinden seçilen işleri çalıştırabilirsiniz. -* **Pytest**'e dayalı, Docker ile entegre REST backend testleri ile veritabanından bağımsız olarak tam API etkileşimini test edebilirsiniz. Docker'da çalıştığı için her seferinde sıfırdan yeni bir veri deposu oluşturabilir (böylece ElasticSearch, MongoDB, CouchDB veya ne istersen kullanabilirsin ve sadece API'nin çalışıp çalışmadığını test edebilirsin). -* Atom Hydrogen veya Visual Studio Code Jupyter gibi uzantılarla uzaktan veya Docker içi geliştirme için **Jupyter Çekirdekleri** ile kolay Python entegrasyonu. -* **Vue** ile frontend: - * Vue CLI ile oluşturulmuş. - * Dahili **JWT kimlik doğrulama**. - * Dahili Login. - * Login sonrası, Kontrol paneli. - * Kullanıcı oluşturma ve düzenleme kontrol paneli - * Kendi kendine kullanıcı sürümü. - * **Vuex**. - * **Vue-router**. - * **Vuetify** güzel material design kompanentleri için. - * **TypeScript**. - * **Nginx** tabanlı Docker sunucusu (Vue-router için yapılandırılmış). - * Docker ile multi-stage yapı, böylece kodu derlemeniz, kaydetmeniz veya işlemeniz gerekmez. - * Derleme zamanında Frontend testi (devre dışı bırakılabilir). - * Mümkün olduğu kadar modüler yapılmıştır, bu nedenle kutudan çıktığı gibi çalışır, ancak Vue CLI ile yeniden oluşturabilir veya ihtiyaç duyduğunuz şekilde oluşturabilir ve istediğinizi yeniden kullanabilirsiniz. -* **PGAdmin** PostgreSQL database admin tool'u, PHPMyAdmin ve MySQL ile kolayca değiştirilebilir. -* **Flower** ile Celery job'larını monitörleme. -* **Traefik** ile backend ve frontend arasında yük dengeleme, böylece her ikisini de aynı domain altında, path ile ayrılmış, ancak farklı kapsayıcılar tarafından sunulabilirsiniz. -* Let's Encrypt **HTTPS** sertifikalarının otomatik oluşturulması dahil olmak üzere Traefik entegrasyonu. -* GitLab **CI** (sürekli entegrasyon), backend ve frontend testi dahil. - -## Full Stack FastAPI Couchbase - -GitHub: https://github.com/tiangolo/full-stack-fastapi-couchbase - -⚠️ **UYARI** ⚠️ - -Sıfırdan bir projeye başlıyorsanız alternatiflerine bakın. - -Örneğin, Full Stack FastAPI PostgreSQL daha iyi bir alternatif olabilir, aktif olarak geliştiriliyor ve kullanılıyor. Ve yeni özellik ve ilerlemelere sahip. - -İsterseniz Couchbase tabanlı generator'ı kullanmakta özgürsünüz, hala iyi çalışıyor olmalı ve onunla oluşturulmuş bir projeniz varsa bu da sorun değil (ve muhtemelen zaten ihtiyaçlarınıza göre güncellediniz). - -Bununla ilgili daha fazla bilgiyi repo belgelerinde okuyabilirsiniz. - -## Full Stack FastAPI MongoDB - -... müsaitliğime ve diğer faktörlere bağlı olarak daha sonra gelebilir. 😅 🎉 - -## Machine Learning modelleri, spaCy ve FastAPI - -GitHub: https://github.com/microsoft/cookiecutter-spacy-fastapi - -### Machine Learning modelleri, spaCy ve FastAPI - Features - -* **spaCy** NER model entegrasyonu. -* **Azure Cognitive Search** yerleşik istek biçimi. -* Uvicorn ve Gunicorn ile **Production ready** Python web server'ı. -* Dahili **Azure DevOps** Kubernetes (AKS) CI/CD deployment. -* **Multilingual**, Proje kurulumu sırasında spaCy'nin yerleşik dillerinden birini kolayca seçin. -* **Esnetilebilir** diğer frameworkler (Pytorch, Tensorflow) ile de çalışır sadece spaCy değil. +- ⚡ Python backend API için [**FastAPI**](https://fastapi.tiangolo.com/tr). + - 🧰 Python SQL veritabanı etkileşimleri (ORM) için [SQLModel](https://sqlmodel.tiangolo.com). + - 🔍 FastAPI'nin kullandığı; veri doğrulama ve ayarlar yönetimi için [Pydantic](https://docs.pydantic.dev). + - 💾 SQL veritabanı olarak [PostgreSQL](https://www.postgresql.org). +- 🚀 frontend için [React](https://react.dev). + - 💃 TypeScript, hooks, Vite ve modern bir frontend stack'inin diğer parçalarını kullanır. + - 🎨 frontend component'leri için [Tailwind CSS](https://tailwindcss.com) ve [shadcn/ui](https://ui.shadcn.com). + - 🤖 Otomatik üretilen bir frontend client. + - 🧪 End-to-End testleri için [Playwright](https://playwright.dev). + - 🦇 Dark mode desteği. +- 🐋 Geliştirme ve production için [Docker Compose](https://www.docker.com). +- 🔒 Varsayılan olarak güvenli password hashing. +- 🔑 JWT (JSON Web Token) authentication. +- 📫 E-posta tabanlı şifre kurtarma. +- ✅ [Pytest](https://pytest.org) ile testler. +- 📞 Reverse proxy / load balancer olarak [Traefik](https://traefik.io). +- 🚢 Docker Compose kullanarak deployment talimatları; otomatik HTTPS sertifikalarını yönetmek için bir frontend Traefik proxy'sini nasıl kuracağınız dahil. +- 🏭 GitHub Actions tabanlı CI (continuous integration) ve CD (continuous deployment). diff --git a/docs/tr/docs/python-types.md b/docs/tr/docs/python-types.md index b44aa3b9d5..01a3efe98c 100644 --- a/docs/tr/docs/python-types.md +++ b/docs/tr/docs/python-types.md @@ -1,76 +1,74 @@ -# Python Veri Tiplerine Giriş +# Python Tiplerine Giriş { #python-types-intro } -Python isteğe bağlı olarak "tip belirteçlerini" destekler. +Python, isteğe bağlı "type hints" (diğer adıyla "type annotations") desteğine sahiptir. - **"Tip belirteçleri"** bir değişkenin tipinin belirtilmesine olanak sağlayan özel bir sözdizimidir. +Bu **"type hints"** veya annotations, bir değişkenin type'ını bildirmeye yarayan özel bir sözdizimidir. -Değişkenlerin tiplerini belirterek editör ve araçlardan daha fazla destek alabilirsiniz. +Değişkenleriniz için type bildirerek, editörler ve araçlar size daha iyi destek sağlayabilir. -Bu pythonda tip belirteçleri için **hızlı bir başlangıç / bilgi tazeleme** rehberidir . Bu rehber **FastAPI** kullanmak için gereken minimum konuyu kapsar ki bu da çok az bir miktardır. +Bu, Python type hints hakkında sadece **hızlı bir eğitim / bilgi tazeleme** dokümanıdır. **FastAPI** ile kullanmak için gereken minimum bilgiyi kapsar... ki aslında bu çok azdır. -**FastAPI' nin** tamamı bu tür tip belirteçleri ile donatılmıştır ve birçok avantaj sağlamaktadır. +**FastAPI** tamamen bu type hints üzerine kuruludur; bunlar ona birçok avantaj ve fayda sağlar. -**FastAPI** kullanmayacak olsanız bile tür belirteçleri hakkında bilgi edinmenizde fayda var. +Ancak hiç **FastAPI** kullanmasanız bile, bunlar hakkında biraz öğrenmeniz size fayda sağlayacaktır. /// note | Not -Python uzmanıysanız ve tip belirteçleri ilgili her şeyi zaten biliyorsanız, sonraki bölüme geçin. +Eğer bir Python uzmanıysanız ve type hints hakkında her şeyi zaten biliyorsanız, sonraki bölüme geçin. /// -## Motivasyon +## Motivasyon { #motivation } -Basit bir örnek ile başlayalım: +Basit bir örnekle başlayalım: -{* ../../docs_src/python_types/tutorial001.py *} +{* ../../docs_src/python_types/tutorial001_py39.py *} - -Programın çıktısı: +Bu programı çalıştırınca şu çıktıyı alırsınız: ``` John Doe ``` -Fonksiyon sırayla şunları yapar: +Fonksiyon şunları yapar: * `first_name` ve `last_name` değerlerini alır. -* `title()` ile değişkenlerin ilk karakterlerini büyütür. -* Değişkenleri aralarında bir boşlukla beraber Birleştirir. +* `title()` ile her birinin ilk harfini büyük harfe çevirir. +* Ortada bir boşluk olacak şekilde Concatenates eder. -{* ../../docs_src/python_types/tutorial001.py hl[2] *} +{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *} - -### Düzenle +### Düzenleyelim { #edit-it } Bu çok basit bir program. -Ama şimdi sıfırdan yazdığınızı hayal edin. +Ama şimdi bunu sıfırdan yazdığınızı hayal edin. -Bir noktada fonksiyonun tanımına başlayacaktınız, parametreleri hazır hale getirdiniz... +Bir noktada fonksiyon tanımını yazmaya başlamış olacaktınız, parametreler hazır... -Ama sonra "ilk harfi büyük harfe dönüştüren yöntemi" çağırmanız gerekir. +Ama sonra "ilk harfi büyük harfe çeviren method"u çağırmanız gerekiyor. - `upper` mıydı ? Yoksa `uppercase`' mi? `first_uppercase`? `capitalize`? +`upper` mıydı? `uppercase` miydi? `first_uppercase`? `capitalize`? -Ardından, programcıların en iyi arkadaşı olan otomatik tamamlama ile denediniz. +Sonra eski programcı dostuyla denersiniz: editör autocomplete. -'first_name', ardından bir nokta ('.') yazıp otomatik tamamlamayı tetiklemek için 'Ctrl+Space' tuşlarına bastınız. +Fonksiyonun ilk parametresi olan `first_name`'i yazarsınız, sonra bir nokta (`.`) ve ardından autocomplete'i tetiklemek için `Ctrl+Space`'e basarsınız. -Ancak, ne yazık ki, yararlı hiçbir şey elde edemediniz: +Ama ne yazık ki, işe yarar bir şey göremezsiniz: -### Tipleri ekle +### Tipleri ekleyelim { #add-types } -Önceki sürümden sadece bir satırı değiştirelim. +Önceki sürümden tek bir satırı değiştirelim. -Tam olarak bu parçayı, işlevin parametrelerini değiştireceğiz: +Fonksiyonun parametreleri olan şu parçayı: ```Python first_name, last_name ``` -ve bu hale getireceğiz: +şuna çevireceğiz: ```Python first_name: str, last_name: str @@ -78,58 +76,55 @@ ve bu hale getireceğiz: Bu kadar. -İşte bunlar "tip belirteçleri": +Bunlar "type hints": -{* ../../docs_src/python_types/tutorial002.py hl[1] *} +{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *} - -Bu, aşağıdaki gibi varsayılan değerleri bildirmekle aynı şey değildir: +Bu, aşağıdaki gibi default değerler bildirmekle aynı şey değildir: ```Python first_name="john", last_name="doe" ``` -Bu tamamen farklı birşey +Bu farklı bir şey. -İki nokta üst üste (`:`) kullanıyoruz , eşittir (`=`) değil. +Eşittir (`=`) değil, iki nokta (`:`) kullanıyoruz. -Normalde tip belirteçleri eklemek, kod üzerinde olacakları değiştirmez. +Ve type hints eklemek, normalde onlarsız ne oluyorsa onu değiştirmez. -Şimdi programı sıfırdan birdaha yazdığınızı hayal edin. +Ama şimdi, type hints ile o fonksiyonu oluşturmanın ortasında olduğunuzu tekrar hayal edin. -Aynı noktada, `Ctrl+Space` ile otomatik tamamlamayı tetiklediniz ve şunu görüyorsunuz: +Aynı noktada, `Ctrl+Space` ile autocomplete'i tetiklemeye çalışırsınız ve şunu görürsünüz: -Aradığınızı bulana kadar seçenekleri kaydırabilirsiniz: +Bununla birlikte, seçenekleri görerek kaydırabilirsiniz; ta ki "tanıdık gelen" seçeneği bulana kadar: -## Daha fazla motivasyon +## Daha fazla motivasyon { #more-motivation } -Bu fonksiyon, zaten tür belirteçlerine sahip: +Şu fonksiyona bakın, zaten type hints içeriyor: -{* ../../docs_src/python_types/tutorial003.py hl[1] *} +{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *} - -Editör değişkenlerin tiplerini bildiğinden, yalnızca otomatik tamamlama değil, hata kontrolleri de sağlar: +Editör değişkenlerin tiplerini bildiği için, sadece completion değil, aynı zamanda hata kontrolleri de alırsınız: -Artık `age` değişkenini `str(age)` olarak kullanmanız gerektiğini biliyorsunuz: +Artık bunu düzeltmeniz gerektiğini, `age`'i `str(age)` ile string'e çevirmeniz gerektiğini biliyorsunuz: -{* ../../docs_src/python_types/tutorial004.py hl[2] *} +{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *} +## Tipleri bildirmek { #declaring-types } -## Tip bildirme +Type hints bildirmek için ana yeri az önce gördünüz: fonksiyon parametreleri. -Az önce tip belirteçlerinin en çok kullanıldığı yeri gördünüz. +Bu, **FastAPI** ile kullanırken de onları en çok kullanacağınız yerdir. - **FastAPI**ile çalışırken tip belirteçlerini en çok kullanacağımız yer yine fonksiyonlardır. +### Basit tipler { #simple-types } -### Basit tipler - -Yalnızca `str` değil, tüm standart Python tiplerinin bildirebilirsiniz. +Sadece `str` değil, tüm standart Python tiplerini bildirebilirsiniz. Örneğin şunları kullanabilirsiniz: @@ -138,176 +133,332 @@ Yalnızca `str` değil, tüm standart Python tiplerinin bildirebilirsiniz. * `bool` * `bytes` -{* ../../docs_src/python_types/tutorial005.py hl[1] *} +{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *} +### Tip parametreleri ile Generic tipler { #generic-types-with-type-parameters } -### Tip parametreleri ile Generic tipler +`dict`, `list`, `set` ve `tuple` gibi, başka değerler içerebilen bazı veri yapıları vardır. Ve iç değerlerin kendi tipi de olabilir. -"dict", "list", "set" ve "tuple" gibi diğer değerleri içerebilen bazı veri yapıları vardır. Ve dahili değerlerinin de tip belirtecleri olabilir. +İç tipleri olan bu tiplere "**generic**" tipler denir. Ve bunları, iç tipleriyle birlikte bildirmek mümkündür. -Bu tipleri ve dahili tpileri bildirmek için standart Python modülünü "typing" kullanabilirsiniz. +Bu tipleri ve iç tipleri bildirmek için standart Python modülü `typing`'i kullanabilirsiniz. Bu modül, özellikle bu type hints desteği için vardır. -Bu tür tip belirteçlerini desteklemek için özel olarak mevcuttur. +#### Python'un daha yeni sürümleri { #newer-versions-of-python } -#### `List` +`typing` kullanan sözdizimi, Python 3.6'dan en yeni sürümlere kadar (Python 3.9, Python 3.10, vb. dahil) tüm sürümlerle **uyumludur**. -Örneğin `str` değerlerden oluşan bir `list` tanımlayalım. +Python geliştikçe, **daha yeni sürümler** bu type annotations için daha iyi destekle gelir ve çoğu durumda type annotations bildirmek için `typing` modülünü import edip kullanmanız bile gerekmez. -From `typing`, import `List` (büyük harf olan `L` ile): +Projeniz için daha yeni bir Python sürümü seçebiliyorsanız, bu ek sadelikten yararlanabilirsiniz. -{* ../../docs_src/python_types/tutorial006.py hl[1] *} +Tüm dokümanlarda her Python sürümüyle uyumlu örnekler vardır (fark olduğunda). +Örneğin "**Python 3.6+**", Python 3.6 veya üstüyle (3.7, 3.8, 3.9, 3.10, vb. dahil) uyumludur. "**Python 3.9+**" ise Python 3.9 veya üstüyle (3.10 vb. dahil) uyumludur. -Değişkenin tipini yine iki nokta üstüste (`:`) ile belirleyin. +Eğer **Python'un en güncel sürümlerini** kullanabiliyorsanız, en güncel sürüme ait örnekleri kullanın; bunlar **en iyi ve en basit sözdizimine** sahip olur, örneğin "**Python 3.10+**". -tip olarak `List` kullanın. +#### List { #list } -Liste, bazı dahili tipleri içeren bir tür olduğundan, bunları köşeli parantez içine alırsınız: +Örneğin, `str`'lerden oluşan bir `list` olan bir değişken tanımlayalım. -{* ../../docs_src/python_types/tutorial006.py hl[4] *} +Değişkeni, aynı iki nokta (`:`) sözdizimiyle bildirin. +Type olarak `list` yazın. -/// tip | Ipucu +`list`, bazı iç tipleri barındıran bir tip olduğundan, bunları köşeli parantez içine yazarsınız: -Köşeli parantez içindeki bu dahili tiplere "tip parametreleri" denir. +{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *} -Bu durumda `str`, `List`e iletilen tür parametresidir. +/// info | Bilgi + +Köşeli parantez içindeki bu iç tiplere "type parameters" denir. + +Bu durumda `str`, `list`'e verilen type parameter'dır. /// -Bunun anlamı şudur: "`items` değişkeni bir `list`tir ve bu listedeki öğelerin her biri bir `str`dir". +Bu şu demektir: "`items` değişkeni bir `list` ve bu listedeki her bir öğe `str`". -Bunu yaparak, düzenleyicinizin listedeki öğeleri işlerken bile destek sağlamasını sağlayabilirsiniz: +Bunu yaparak, editörünüz listeden öğeleri işlerken bile destek sağlayabilir: -Tip belirteçleri olmadan, bunu başarmak neredeyse imkansızdır. +Tipler olmadan, bunu başarmak neredeyse imkansızdır. -`item` değişkeninin `items` listesindeki öğelerden biri olduğuna dikkat edin. +`item` değişkeninin, `items` listesindeki elemanlardan biri olduğuna dikkat edin. -Ve yine, editör bunun bir `str` ​​olduğunu biliyor ve bunun için destek sağlıyor. +Ve yine de editör bunun bir `str` olduğunu bilir ve buna göre destek sağlar. -#### `Tuple` ve `Set` +#### Tuple ve Set { #tuple-and-set } -`Tuple` ve `set`lerin tiplerini bildirmek için de aynısını yapıyoruz: - -{* ../../docs_src/python_types/tutorial007.py hl[1,4] *} - - -Bu şu anlama geliyor: - -* `items_t` değişkeni sırasıyla `int`, `int`, ve `str` tiplerinden oluşan bir `tuple` türündedir . -* `items_s` ise her öğesi `bytes` türünde olan bir `set` örneğidir. - -#### `Dict` - -Bir `dict` tanımlamak için virgülle ayrılmış iki parametre verebilirsiniz. - -İlk tip parametresi `dict` değerinin `key` değeri içindir. - -İkinci parametre ise `dict` değerinin `value` değeri içindir: - -{* ../../docs_src/python_types/tutorial008.py hl[1,4] *} +`tuple`'ları ve `set`'leri bildirmek için de aynısını yaparsınız: +{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *} Bu şu anlama gelir: -* `prices` değişkeni `dict` tipindedir: - * `dict` değişkeninin `key` değeri `str` tipindedir (herbir item'ın "name" değeri). - * `dict` değişkeninin `value` değeri `float` tipindedir (lherbir item'ın "price" değeri). +* `items_t` değişkeni 3 öğeli bir `tuple`'dır: bir `int`, bir başka `int` ve bir `str`. +* `items_s` değişkeni bir `set`'tir ve her bir öğesi `bytes` tipindedir. -#### `Optional` +#### Dict { #dict } -`Optional` bir değişkenin `str`gibi bir tipi olabileceğini ama isteğe bağlı olarak tipinin `None` olabileceğini belirtir: +Bir `dict` tanımlamak için, virgülle ayrılmış 2 type parameter verirsiniz. -```Python hl_lines="1 4" -{!../../docs_src/python_types/tutorial009.py!} +İlk type parameter, `dict`'in key'leri içindir. + +İkinci type parameter, `dict`'in value'ları içindir: + +{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *} + +Bu şu anlama gelir: + +* `prices` değişkeni bir `dict`'tir: + * Bu `dict`'in key'leri `str` tipindedir (örneğin her bir öğenin adı). + * Bu `dict`'in value'ları `float` tipindedir (örneğin her bir öğenin fiyatı). + +#### Union { #union } + +Bir değişkenin **birkaç tipten herhangi biri** olabileceğini bildirebilirsiniz; örneğin bir `int` veya bir `str`. + +Python 3.6 ve üzeri sürümlerde (Python 3.10 dahil), `typing` içinden `Union` tipini kullanabilir ve köşeli parantez içine kabul edilecek olası tipleri yazabilirsiniz. + +Python 3.10'da ayrıca, olası tipleri vertical bar (`|`) ile ayırabildiğiniz **yeni bir sözdizimi** de vardır. + +//// tab | Python 3.10+ + +```Python hl_lines="1" +{!> ../../docs_src/python_types/tutorial008b_py310.py!} ``` -`str` yerine `Optional[str]` kullanmak editorün bu değerin her zaman `str` tipinde değil bazen `None` tipinde de olabileceğini belirtir ve hataları tespit etmemizde yardımcı olur. +//// -#### Generic tipler +//// tab | Python 3.9+ -Köşeli parantez içinde tip parametreleri alan bu türler, örneğin: +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial008b_py39.py!} +``` -* `List` -* `Tuple` -* `Set` -* `Dict` +//// + +Her iki durumda da bu, `item`'ın `int` veya `str` olabileceği anlamına gelir. + +#### Muhtemelen `None` { #possibly-none } + +Bir değerin `str` gibi bir tipi olabileceğini ama aynı zamanda `None` da olabileceğini bildirebilirsiniz. + +Python 3.6 ve üzeri sürümlerde (Python 3.10 dahil), `typing` modülünden `Optional` import edip kullanarak bunu bildirebilirsiniz. + +```Python hl_lines="1 4" +{!../../docs_src/python_types/tutorial009_py39.py!} +``` + +Sadece `str` yerine `Optional[str]` kullanmak, aslında değer `None` olabilecekken her zaman `str` olduğunu varsaydığınız hataları editörün yakalamanıza yardımcı olmasını sağlar. + +`Optional[Something]`, aslında `Union[Something, None]` için bir kısayoldur; eşdeğerdirler. + +Bu aynı zamanda Python 3.10'da `Something | None` kullanabileceğiniz anlamına gelir: + +//// 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+ alternatif + +```Python hl_lines="1 4" +{!> ../../docs_src/python_types/tutorial009b_py39.py!} +``` + +//// + +#### `Union` veya `Optional` kullanmak { #using-union-or-optional } + +Python sürümünüz 3.10'un altındaysa, benim oldukça **öznel** bakış açıma göre küçük bir ipucu: + +* 🚨 `Optional[SomeType]` kullanmaktan kaçının +* Bunun yerine ✨ **`Union[SomeType, None]` kullanın** ✨. + +İkisi eşdeğerdir ve altta aynı şeydir; ama ben `Optional` yerine `Union` önermeyi tercih ederim. Çünkü "**optional**" kelimesi değerin optional olduğunu ima ediyor gibi durur; ama gerçekte anlamı "değer `None` olabilir"dir. Değer optional olmasa ve hâlâ required olsa bile. + +Bence `Union[SomeType, None]` ne anlama geldiğini daha açık şekilde ifade ediyor. + +Bu, tamamen kelimeler ve isimlendirmelerle ilgili. Ancak bu kelimeler, sizin ve ekip arkadaşlarınızın kod hakkında nasıl düşündüğünü etkileyebilir. + +Örnek olarak şu fonksiyonu ele alalım: + +{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *} + +`name` parametresi `Optional[str]` olarak tanımlanmış, ama **optional değil**; parametre olmadan fonksiyonu çağıramazsınız: + +```Python +say_hi() # Oh, no, this throws an error! 😱 +``` + +`name` parametresi **hâlâ required**'dır (*optional* değildir) çünkü bir default değeri yoktur. Yine de `name`, değer olarak `None` kabul eder: + +```Python +say_hi(name=None) # This works, None is valid 🎉 +``` + +İyi haber şu ki, Python 3.10'a geçtiğinizde bununla uğraşmanız gerekmeyecek; çünkü tiplerin union'larını tanımlamak için doğrudan `|` kullanabileceksiniz: + +{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *} + +Ve böylece `Optional` ve `Union` gibi isimlerle de uğraşmanız gerekmeyecek. 😎 + +#### Generic tipler { #generic-types } + +Köşeli parantez içinde type parameter alan bu tiplere **Generic types** veya **Generics** denir, örneğin: + +//// tab | Python 3.10+ + +Aynı builtin tipleri generics olarak kullanabilirsiniz (köşeli parantez ve içindeki tiplerle): + +* `list` +* `tuple` +* `set` +* `dict` + +Ve önceki Python sürümlerinde olduğu gibi `typing` modülünden: + +* `Union` * `Optional` * ...and others. -**Generic types** yada **Generics** olarak adlandırılır. +Python 3.10'da, `Union` ve `Optional` generics'lerini kullanmaya alternatif olarak, tip union'larını bildirmek için vertical bar (`|`) kullanabilirsiniz; bu çok daha iyi ve daha basittir. -### Tip olarak Sınıflar +//// -Bir değişkenin tipini bir sınıf ile bildirebilirsiniz. +//// tab | Python 3.9+ -Diyelim ki `name` değerine sahip `Person` sınıfınız var: +Aynı builtin tipleri generics olarak kullanabilirsiniz (köşeli parantez ve içindeki tiplerle): -{* ../../docs_src/python_types/tutorial010.py hl[1:3] *} +* `list` +* `tuple` +* `set` +* `dict` +Ve `typing` modülünden gelen generics: -Sonra bir değişkeni 'Person' tipinde tanımlayabilirsiniz: +* `Union` +* `Optional` +* ...and others. -{* ../../docs_src/python_types/tutorial010.py hl[6] *} +//// +### Tip olarak sınıflar { #classes-as-types } -Ve yine bütün editör desteğini alırsınız: +Bir sınıfı da bir değişkenin tipi olarak bildirebilirsiniz. + +Örneğin, adı olan bir `Person` sınıfınız olsun: + +{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *} + +Sonra bir değişkeni `Person` tipinde olacak şekilde bildirebilirsiniz: + +{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *} + +Ve sonra, yine tüm editör desteğini alırsınız: -## Pydantic modelleri +Bunun "`one_person`, `Person` sınıfının bir **instance**'ıdır" anlamına geldiğine dikkat edin. -Pydantic veri doğrulaması yapmak için bir Python kütüphanesidir. +"`one_person`, `Person` adlı **class**'tır" anlamına gelmez. -Verilerin "biçimini" niteliklere sahip sınıflar olarak düzenlersiniz. +## Pydantic modelleri { #pydantic-models } -Ve her niteliğin bir türü vardır. +Pydantic, data validation yapmak için bir Python kütüphanesidir. -Sınıfın bazı değerlerle bir örneğini oluşturursunuz ve değerleri doğrular, bunları uygun türe dönüştürür ve size tüm verileri içeren bir nesne verir. +Verinin "shape"'ini attribute'lara sahip sınıflar olarak tanımlarsınız. -Ve ortaya çıkan nesne üzerindeki bütün editör desteğini alırsınız. +Ve her attribute'un bir tipi vardır. -Resmi Pydantic dokümanlarından alınmıştır: +Ardından o sınıfın bir instance'ını bazı değerlerle oluşturursunuz; bu değerleri doğrular, uygun tipe dönüştürür (gerekliyse) ve size tüm veriyi içeren bir nesne verir. -{* ../../docs_src/python_types/tutorial011.py *} +Ve bu ortaya çıkan nesne ile tüm editör desteğini alırsınız. +Resmî Pydantic dokümanlarından bir örnek: -/// info +{* ../../docs_src/python_types/tutorial011_py310.py *} -Daha fazla şey öğrenmek için Pydantic'i takip edin. +/// info | Bilgi + +Daha fazlasını öğrenmek için Pydantic'in dokümanlarına bakın. /// -**FastAPI** tamamen Pydantic'e dayanmaktadır. +**FastAPI** tamamen Pydantic üzerine kuruludur. -Daha fazlasini görmek için [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}. +Bunların pratikte nasıl çalıştığını [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank} içinde çok daha fazla göreceksiniz. -## **FastAPI** tip belirteçleri +/// tip | İpucu -**FastAPI** birkaç şey yapmak için bu tür tip belirteçlerinden faydalanır. - -**FastAPI** ile parametre tiplerini bildirirsiniz ve şunları elde edersiniz: - -* **Editor desteği**. -* **Tip kontrolü**. - -...ve **FastAPI** aynı belirteçleri şunlar için de kullanıyor: - -* **Gereksinimleri tanımlama**: request path parameters, query parameters, headers, bodies, dependencies, ve benzeri gereksinimlerden -* **Verileri çevirme**: Gönderilen veri tipinden istenilen veri tipine çevirme. -* **Verileri doğrulama**: Her gönderilen verinin: - * doğrulanması ve geçersiz olduğunda **otomatik hata** oluşturma. -* OpenAPI kullanarak apinizi **Belgeleyin** : - * bu daha sonra otomatik etkileşimli dokümantasyon kullanıcı arayüzü tarafından kullanılır. - -Bütün bunlar kulağa soyut gelebilir. Merak etme. Tüm bunları çalışırken göreceksiniz. [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}. - -Önemli olan, standart Python türlerini tek bir yerde kullanarak (daha fazla sınıf, dekoratör vb. eklemek yerine), **FastAPI**'nin bizim için işi yapmasını sağlamak. - -/// info - -Tüm öğreticiyi zaten okuduysanız ve türler hakkında daha fazla bilgi için geri döndüyseniz, iyi bir kaynak: the "cheat sheet" from `mypy`. +Pydantic, default value olmadan `Optional` veya `Union[Something, None]` kullandığınızda özel bir davranışa sahiptir; bununla ilgili daha fazla bilgiyi Pydantic dokümanlarında Required Optional fields bölümünde okuyabilirsiniz. + +/// + +## Metadata Annotations ile Type Hints { #type-hints-with-metadata-annotations } + +Python'da ayrıca, `Annotated` kullanarak bu type hints içine **ek metadata** koymayı sağlayan bir özellik de vardır. + +Python 3.9'dan itibaren `Annotated`, standart kütüphanenin bir parçasıdır; bu yüzden `typing` içinden import edebilirsiniz. + +{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *} + +Python'un kendisi bu `Annotated` ile bir şey yapmaz. Editörler ve diğer araçlar için tip hâlâ `str`'dir. + +Ama **FastAPI**'ye uygulamanızın nasıl davranmasını istediğinize dair ek metadata sağlamak için `Annotated` içindeki bu alanı kullanabilirsiniz. + +Hatırlanması gereken önemli nokta: `Annotated`'a verdiğiniz **ilk *type parameter***, **gerçek tip**tir. Geri kalanı ise diğer araçlar için metadatadır. + +Şimdilik, sadece `Annotated`'ın var olduğunu ve bunun standart Python olduğunu bilmeniz yeterli. 😎 + +İleride bunun ne kadar **güçlü** olabildiğini göreceksiniz. + +/// tip | İpucu + +Bunun **standart Python** olması, editörünüzde mümkün olan **en iyi developer experience**'ı almaya devam edeceğiniz anlamına gelir; kodu analiz etmek ve refactor etmek için kullandığınız araçlarla da, vb. ✨ + +Ayrıca kodunuzun pek çok başka Python aracı ve kütüphanesiyle çok uyumlu olacağı anlamına gelir. 🚀 + +/// + +## **FastAPI**'de type hints { #type-hints-in-fastapi } + +**FastAPI**, birkaç şey yapmak için bu type hints'ten faydalanır. + +**FastAPI** ile type hints kullanarak parametreleri bildirirsiniz ve şunları elde edersiniz: + +* **Editör desteği**. +* **Tip kontrolleri**. + +...ve **FastAPI** aynı bildirimleri şunlar için de kullanır: + +* **Gereksinimleri tanımlamak**: request path parameters, query parameters, headers, bodies, dependencies, vb. +* **Veriyi dönüştürmek**: request'ten gerekli tipe. +* **Veriyi doğrulamak**: her request'ten gelen veriyi: + * Veri geçersiz olduğunda client'a dönen **otomatik hatalar** üretmek. +* OpenAPI kullanarak API'yi **dokümante etmek**: + * bu, daha sonra otomatik etkileşimli dokümantasyon kullanıcı arayüzleri tarafından kullanılır. + +Bunların hepsi kulağa soyut gelebilir. Merak etmeyin. Tüm bunları [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank} içinde çalışırken göreceksiniz. + +Önemli olan, standart Python tiplerini tek bir yerde kullanarak (daha fazla sınıf, decorator vb. eklemek yerine), **FastAPI**'nin sizin için işin büyük kısmını yapmasıdır. + +/// info | Bilgi + +Tüm tutorial'ı zaten bitirdiyseniz ve tipler hakkında daha fazlasını görmek için geri döndüyseniz, iyi bir kaynak: `mypy`'nin "cheat sheet"i. /// diff --git a/docs/tr/docs/resources/index.md b/docs/tr/docs/resources/index.md index fc71a9ca1b..884052f79c 100644 --- a/docs/tr/docs/resources/index.md +++ b/docs/tr/docs/resources/index.md @@ -1,3 +1,3 @@ -# Kaynaklar +# Kaynaklar { #resources } -Ek kaynaklar, dış bağlantılar, makaleler ve daha fazlası. ✈️ +Ek kaynaklar, dış bağlantılar ve daha fazlası. ✈️ diff --git a/docs/tr/docs/tutorial/cookie-params.md b/docs/tr/docs/tutorial/cookie-params.md index f07508c2fd..18eedab7f0 100644 --- a/docs/tr/docs/tutorial/cookie-params.md +++ b/docs/tr/docs/tutorial/cookie-params.md @@ -1,35 +1,45 @@ -# Çerez (Cookie) Parametreleri +# Çerez (Cookie) Parametreleri { #cookie-parameters } -`Query` (Sorgu) ve `Path` (Yol) parametrelerini tanımladığınız şekilde çerez parametreleri tanımlayabilirsiniz. +`Query` ve `Path` parametrelerini tanımladığınız şekilde Cookie parametreleri tanımlayabilirsiniz. -## Import `Cookie` +## `Cookie`'yi Import Edin { #import-cookie } -Öncelikle, `Cookie`'yi projenize dahil edin: +Öncelikle, `Cookie`'yi import edin: {* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *} -## `Cookie` Parametrelerini Tanımlayın +## `Cookie` Parametrelerini Tanımlayın { #declare-cookie-parameters } -Çerez parametrelerini `Path` veya `Query` tanımlaması yapar gibi tanımlayın. +Ardından, `Path` ve `Query` ile aynı yapıyı kullanarak Cookie parametrelerini tanımlayın. -İlk değer varsayılan değerdir; tüm ekstra doğrulama veya belirteç parametrelerini kullanabilirsiniz: +Varsayılan değeri ve tüm ekstra doğrulama veya annotation parametrelerini tanımlayabilirsiniz: {* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *} /// note | Teknik Detaylar -`Cookie` sınıfı `Path` ve `Query` sınıflarının kardeşidir. Diğerleri gibi `Param` sınıfını miras alan bir sınıftır. +`Cookie`, `Path` ve `Query`'nin "kardeş" sınıfıdır. O da aynı ortak `Param` sınıfından miras alır. -Ancak `fastapi`'dan projenize dahil ettiğiniz `Query`, `Path`, `Cookie` ve diğerleri aslında özel sınıflar döndüren birer fonksiyondur. +Ancak `fastapi`'dan `Query`, `Path`, `Cookie` ve diğerlerini import ettiğinizde, bunlar aslında özel sınıflar döndüren fonksiyonlardır, bunu unutmayın. /// /// info | Bilgi -Çerez tanımlamak için `Cookie` sınıfını kullanmanız gerekmektedir, aksi taktirde parametreler sorgu parametreleri olarak yorumlanır. +Çerezleri tanımlamak için `Cookie` kullanmanız gerekir, aksi halde parametreler query parametreleri olarak yorumlanır. /// -## Özet +/// info | Bilgi -Çerez tanımlamalarını `Cookie` sınıfını kullanarak `Query` ve `Path` tanımlar gibi tanımlayın. +**Tarayıcılar çerezleri** özel şekillerde ve arka planda işlediği için, **JavaScript**'in onlara dokunmasına kolayca izin **vermezler**. + +`/docs` adresindeki **API docs UI**'a giderseniz, *path operation*'larınız için çerezlerin **dokümantasyonunu** görebilirsiniz. + +Ancak **veriyi doldurup** "Execute" düğmesine tıklasanız bile, docs UI **JavaScript** ile çalıştığı için çerezler gönderilmez ve herhangi bir değer yazmamışsınız gibi bir **hata** mesajı görürsünüz. + +/// + +## Özet { #recap } + +`Query` ve `Path` ile aynı ortak deseni kullanarak, çerezleri `Cookie` ile tanımlayın. diff --git a/docs/tr/docs/tutorial/first-steps.md b/docs/tr/docs/tutorial/first-steps.md index 9a8ef762dc..332f5c5590 100644 --- a/docs/tr/docs/tutorial/first-steps.md +++ b/docs/tr/docs/tutorial/first-steps.md @@ -1,102 +1,118 @@ -# İlk Adımlar +# İlk Adımlar { #first-steps } En sade FastAPI dosyası şu şekilde görünür: -{* ../../docs_src/first_steps/tutorial001.py *} +{* ../../docs_src/first_steps/tutorial001_py39.py *} -Yukarıdaki içeriği bir `main.py` dosyasına kopyalayalım. +Yukarıdakini `main.py` adlı bir dosyaya kopyalayın. -Uygulamayı çalıştıralım: +Canlı sunucuyu çalıştırın:
```console -$ uvicorn main:app --reload +$ fastapi dev main.py -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: Waiting for application startup. -INFO: Application startup complete. + FastAPI Starting development server 🚀 + + Searching for package file structure from directories + with __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with + the following code: + + from main import app + + app Using import string: main:app + + server Server started at http://127.0.0.1:8000 + server Documentation at http://127.0.0.1:8000/docs + + tip Running in development mode, for production use: + fastapi run + + Logs: + + 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 [383138] using WatchFiles + INFO Started server process [383153] + INFO Waiting for application startup. + INFO Application startup complete. ```
-/// note | Not - -`uvicorn main:app` komutunu şu şekilde açıklayabiliriz: - -* `main`: dosya olan `main.py` (yani Python "modülü"). -* `app`: ise `main.py` dosyasının içerisinde `app = FastAPI()` satırında oluşturduğumuz `FastAPI` nesnesi. -* `--reload`: kod değişikliklerinin ardından sunucuyu otomatik olarak yeniden başlatır. Bu parameteyi sadece geliştirme aşamasında kullanmalıyız. - -/// - -Çıktı olarak şöyle bir satır ile karşılaşacaksınız: +Çıktıda, şuna benzer bir satır göreceksiniz: ```hl_lines="4" INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` -Bu satır, yerel makinenizde uygulamanızın çalıştığı bağlantıyı gösterir. +Bu satır, uygulamanızın yerel makinenizde hangi URL'de sunulduğunu gösterir. -### Kontrol Edelim +### Kontrol Edelim { #check-it } -Tarayıcınızı açıp http://127.0.0.1:8000 bağlantısına gidin. +Tarayıcınızı açıp http://127.0.0.1:8000 adresine gidin. -Şu şekilde bir JSON yanıtı ile karşılaşacağız: +Şu şekilde bir JSON response göreceksiniz: ```JSON {"message": "Hello World"} ``` -### Etkileşimli API Dokümantasyonu +### Etkileşimli API Dokümantasyonu { #interactive-api-docs } -Şimdi http://127.0.0.1:8000/docs bağlantısını açalım. +Şimdi http://127.0.0.1:8000/docs adresine gidin. -Swagger UI tarafından sağlanan otomatik etkileşimli bir API dokümantasyonu göreceğiz: +Otomatik etkileşimli API dokümantasyonunu ( Swagger UI tarafından sağlanan) göreceksiniz: ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) -### Alternatif API Dokümantasyonu +### Alternatif API Dokümantasyonu { #alternative-api-docs } -Şimdi http://127.0.0.1:8000/redoc bağlantısını açalım. +Ve şimdi http://127.0.0.1:8000/redoc adresine gidin. -ReDoc tarafından sağlanan otomatik dokümantasyonu göreceğiz: +Alternatif otomatik dokümantasyonu ( ReDoc tarafından sağlanan) göreceksiniz: ![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) -### OpenAPI +### OpenAPI { #openapi } -**FastAPI**, **OpenAPI** standardını kullanarak tüm API'ınızın tamamını tanımlayan bir "şema" oluşturur. +**FastAPI**, API'ları tanımlamak için **OpenAPI** standardını kullanarak tüm API'nızın tamamını içeren bir "şema" üretir. -#### "Şema" +#### "Şema" { #schema } -"Şema", bir şeyin tanımı veya açıklamasıdır. Geliştirilen koddan ziyade soyut bir açıklamadır. +"Şema", bir şeyin tanımı veya açıklamasıdır. Onu uygulayan kod değil, sadece soyut bir açıklamadır. -#### API "Şeması" +#### API "şeması" { #api-schema } -Bu durumda, OpenAPI, API şemasını nasıl tanımlayacağınızı belirten bir şartnamedir. +Bu durumda, OpenAPI, API'nızın şemasını nasıl tanımlayacağınızı belirleyen bir şartnamedir. -Bu şema tanımı, API yollarınızla birlikte yollarınızın aldığı olası parametreler gibi tanımlamaları içerir. +Bu şema tanımı, API path'leriniz, alabilecekleri olası parametreler vb. şeyleri içerir. -#### Veri "Şeması" +#### Veri "şeması" { #data-schema } "Şema" terimi, JSON içeriği gibi bazı verilerin şeklini de ifade edebilir. -Bu durumda, JSON özellikleri ve sahip oldukları veri türleri gibi anlamlarına gelir. +Bu durumda, JSON attribute'ları ve sahip oldukları veri türleri vb. anlamına gelir. -#### OpenAPI ve JSON Şema +#### OpenAPI ve JSON Schema { #openapi-and-json-schema } -OpenAPI, API'niz için bir API şeması tanımlar. Ve bu şema, JSON veri şemaları standardı olan **JSON Şema** kullanılarak API'niz tarafından gönderilen ve alınan verilerin tanımlarını (veya "şemalarını") içerir. +OpenAPI, API'nız için bir API şeması tanımlar. Ve bu şema, JSON veri şemaları standardı olan **JSON Schema** kullanılarak API'nız tarafından gönderilen ve alınan verilerin tanımlarını (veya "şemalarını") içerir. -#### `openapi.json` Dosyasına Göz At +#### `openapi.json` Dosyasına Göz At { #check-the-openapi-json } -Ham OpenAPI şemasının nasıl göründüğünü merak ediyorsanız, FastAPI otomatik olarak tüm API'ınızın tanımlamalarını içeren bir JSON (şeması) oluşturur. +Ham OpenAPI şemasının nasıl göründüğünü merak ediyorsanız, FastAPI otomatik olarak tüm API'nızın açıklamalarını içeren bir JSON (şema) üretir. -Bu şemayı direkt olarak http://127.0.0.1:8000/openapi.json bağlantısından görüntüleyebilirsiniz. +Bunu doğrudan şuradan görebilirsiniz: http://127.0.0.1:8000/openapi.json. -Aşağıdaki gibi başlayan bir JSON ile karşılaşacaksınız: +Şuna benzer bir şekilde başlayan bir JSON gösterecektir: ```JSON { @@ -119,79 +135,87 @@ Aşağıdaki gibi başlayan bir JSON ile karşılaşacaksınız: ... ``` -#### OpenAPI Ne İşe Yarar? +#### OpenAPI Ne İşe Yarar? { #what-is-openapi-for } -OpenAPI şeması, FastAPI projesinde bulunan iki etkileşimli dokümantasyon sistemine güç veren şeydir. +OpenAPI şeması, dahil edilen iki etkileşimli dokümantasyon sistemine güç veren şeydir. -OpenAPI'ya dayalı düzinelerce alternatif etkileşimli dokümantasyon aracı mevcuttur. **FastAPI** ile oluşturulmuş uygulamanıza bu alternatiflerden herhangi birini kolayca ekleyebilirsiniz. +Ve OpenAPI tabanlı düzinelerce alternatif vardır. **FastAPI** ile oluşturulmuş uygulamanıza bu alternatiflerden herhangi birini kolayca ekleyebilirsiniz. -Ayrıca, API'ınızla iletişim kuracak önyüz, mobil veya IoT uygulamaları gibi istemciler için otomatik olarak kod oluşturabilirsiniz. +Ayrıca, API'nızla iletişim kuran istemciler için otomatik olarak kod üretmekte de kullanabilirsiniz. Örneğin frontend, mobil veya IoT uygulamaları. -## Adım Adım Özetleyelim +### Uygulamanızı Yayınlayın (opsiyonel) { #deploy-your-app-optional } -### Adım 1: `FastAPI`yı Projemize Dahil Edelim +İsterseniz FastAPI uygulamanızı FastAPI Cloud'a deploy edebilirsiniz; henüz katılmadıysanız gidip bekleme listesine yazılın. 🚀 -{* ../../docs_src/first_steps/tutorial001.py hl[1] *} +Zaten bir **FastAPI Cloud** hesabınız varsa (bekleme listesinden sizi davet ettiysek 😉), uygulamanızı tek komutla deploy edebilirsiniz. -`FastAPI`, API'niz için tüm işlevselliği sağlayan bir Python sınıfıdır. +Deploy etmeden önce giriş yaptığınızdan emin olun: + +
+ +```console +$ fastapi login + +You are logged in to FastAPI Cloud 🚀 +``` + +
+ +Ardından uygulamanızı deploy edin: + +
+ +```console +$ fastapi deploy + +Deploying to FastAPI Cloud... + +✅ Deployment successful! + +🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev +``` + +
+ +Bu kadar! Artık uygulamanıza o URL üzerinden erişebilirsiniz. ✨ + +## Adım Adım Özetleyelim { #recap-step-by-step } + +### Adım 1: `FastAPI` import edin { #step-1-import-fastapi } + +{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *} + +`FastAPI`, API'nız için tüm işlevselliği sağlayan bir Python class'ıdır. /// note | Teknik Detaylar -`FastAPI` doğrudan `Starlette`'i miras alan bir sınıftır. +`FastAPI`, doğrudan `Starlette`'ten miras alan bir class'tır. Starlette'in tüm işlevselliğini `FastAPI` ile de kullanabilirsiniz. /// -### Adım 2: Bir `FastAPI` "Örneği" Oluşturalım +### Adım 2: bir `FastAPI` "instance"ı oluşturun { #step-2-create-a-fastapi-instance } -{* ../../docs_src/first_steps/tutorial001.py hl[3] *} +{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *} -Burada `app` değişkeni `FastAPI` sınıfının bir örneği olacaktır. +Burada `app` değişkeni `FastAPI` class'ının bir "instance"ı olacaktır. -Bu, tüm API'yı oluşturmak için ana etkileşim noktası olacaktır. +Bu, tüm API'nızı oluşturmak için ana etkileşim noktası olacaktır. -Bu `app` değişkeni, `uvicorn` komutunda atıfta bulunulan değişkenin ta kendisidir. +### Adım 3: bir *path operation* oluşturun { #step-3-create-a-path-operation } -
+#### Path { #path } -```console -$ uvicorn main:app --reload +Buradaki "Path", URL'in ilk `/` işaretinden başlayarak son kısmını ifade eder. -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -``` - -
- -Uygulamanızı aşağıdaki gibi oluşturursanız: - -{* ../../docs_src/first_steps/tutorial002.py hl[3] *} - -Ve bunu `main.py` dosyasına yerleştirirseniz eğer `uvicorn` komutunu şu şekilde çalıştırabilirsiniz: - -
- -```console -$ uvicorn main:my_awesome_api --reload - -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -``` - -
- -### Adım 3: Bir *Yol Operasyonu* Oluşturalım - -#### Yol - -Burada "yol" bağlantıda bulunan ilk `/` ile başlayan ve sonrasında gelen kısmı ifade eder. - -Yani, şu şekilde bir bağlantıda: +Yani, şu şekilde bir URL'de: ``` https://example.com/items/foo ``` -... yol şöyle olur: +...path şöyle olur: ``` /items/foo @@ -199,77 +223,77 @@ https://example.com/items/foo /// info | Bilgi -"Yol" genellikle "endpoint" veya "route" olarak adlandırılır. +Bir "path" genellikle "endpoint" veya "route" olarak da adlandırılır. /// -Bir API oluştururken, "yol", "kaynaklar" ile "endişeleri" ayırmanın ana yöntemidir. +Bir API oluştururken, "path", "concerns" ve "resources" ayrımını yapmanın ana yoludur. -#### Operasyonlar +#### Operation { #operation } -Burada "operasyon" HTTP "metodlarından" birini ifade eder. +Burada "Operation", HTTP "method"larından birini ifade eder. -Bunlardan biri: +Şunlardan biri: * `POST` * `GET` * `PUT` * `DELETE` -...veya daha az kullanılan diğerleri: +...ve daha egzotik olanlar: * `OPTIONS` * `HEAD` * `PATCH` * `TRACE` -HTTP protokolünde, bu "metodlardan" birini (veya daha fazlasını) kullanarak her bir yol ile iletişim kurabilirsiniz. +HTTP protokolünde, her bir path ile bu "method"lardan biri (veya birden fazlası) ile iletişim kurabilirsiniz. --- -API oluştururkan, belirli bir amaca hizmet eden belirli HTTP metodlarını kullanırsınız. +API oluştururken, normalde belirli bir aksiyon için bu spesifik HTTP method'larını kullanırsınız. -Normalde kullanılan: +Normalde şunları kullanırsınız: -* `POST`: veri oluşturmak. -* `GET`: veri okumak. -* `PUT`: veriyi güncellemek. -* `DELETE`: veriyi silmek. +* `POST`: veri oluşturmak için. +* `GET`: veri okumak için. +* `PUT`: veriyi güncellemek için. +* `DELETE`: veriyi silmek için. -Bu nedenle, OpenAPI'da HTTP metodlarından her birine "operasyon" denir. +Bu nedenle, OpenAPI'da HTTP method'larının her birine "operation" denir. -Biz de onları "**operasyonlar**" olarak adlandıracağız. +Biz de bunlara "**operation**" diyeceğiz. -#### Bir *Yol Operasyonu Dekoratörü* Tanımlayalım +#### Bir *path operation decorator* tanımlayın { #define-a-path-operation-decorator } -{* ../../docs_src/first_steps/tutorial001.py hl[6] *} +{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *} -`@app.get("/")` dekoratörü, **FastAPI**'a hemen altındaki fonksiyonun aşağıdaki durumlardan sorumlu olduğunu söyler: +`@app.get("/")`, **FastAPI**'a hemen altındaki fonksiyonun şuraya giden request'leri ele almakla sorumlu olduğunu söyler: -* get operasyonu ile -* `/` yoluna gelen istekler +* path `/` +* get operation kullanarak /// info | `@decorator` Bilgisi -Python'da `@something` sözdizimi "dekoratör" olarak adlandırılır. +Python'daki `@something` söz dizimi "decorator" olarak adlandırılır. -Dekoratörler, dekoratif bir şapka gibi (sanırım terim buradan geliyor) fonksiyonların üzerlerine yerleştirilirler. +Onu bir fonksiyonun üstüne koyarsınız. Güzel, dekoratif bir şapka gibi (sanırım terim de buradan geliyor). -Bir "dekoratör" hemen altında bulunan fonksiyonu alır ve o fonksiyon ile bazı işlemler gerçekleştirir. +Bir "decorator", altındaki fonksiyonu alır ve onunla bir şey yapar. -Bizim durumumuzda, kullandığımız dekoratör, **FastAPI**'a altındaki fonksiyonun `/` yoluna gelen `get` metodlu isteklerden sorumlu olduğunu söyler. +Bizim durumumuzda bu decorator, **FastAPI**'a altındaki fonksiyonun **path** `/` ile **operation** `get`'e karşılık geldiğini söyler. -Bu bir **yol operasyonu dekoratörüdür**. +Bu, "**path operation decorator**"dır. /// -Ayrıca diğer operasyonları da kullanabilirsiniz: +Diğer operation'ları da kullanabilirsiniz: * `@app.post()` * `@app.put()` * `@app.delete()` -Daha az kullanılanları da kullanabilirsiniz: +Ve daha egzotik olanları: * `@app.options()` * `@app.head()` @@ -278,58 +302,79 @@ Daha az kullanılanları da kullanabilirsiniz: /// tip | İpucu -Her işlemi (HTTP metod) istediğiniz gibi kullanmakta özgürsünüz. +Her bir operation'ı (HTTP method'unu) istediğiniz gibi kullanmakta özgürsünüz. -**FastAPI** herhangi bir özel amacı veya anlamı olması konusunda ısrarcı olmaz. +**FastAPI** herhangi bir özel anlamı zorunlu kılmaz. Buradaki bilgiler bir gereklilik değil, bir kılavuz olarak sunulmaktadır. -Mesela GraphQL kullanırkan genelde tüm işlemleri yalnızca `POST` operasyonunu kullanarak gerçekleştirirsiniz. +Örneğin GraphQL kullanırken, normalde tüm aksiyonları yalnızca `POST` operation'ları kullanarak gerçekleştirirsiniz. /// -### Adım 4: **Yol Operasyonu Fonksiyonunu** Tanımlayın +### Adım 4: **path operation function**'ı tanımlayın { #step-4-define-the-path-operation-function } -Aşağıdaki, bizim **yol operasyonu fonksiyonumuzdur**: +Bu bizim "**path operation function**"ımız: -* **yol**: `/` -* **operasyon**: `get` -* **fonksiyon**: "dekoratör"ün (`@app.get("/")`'in) altındaki fonksiyondur. +* **path**: `/`. +* **operation**: `get`. +* **function**: "decorator"ün altındaki fonksiyondur (`@app.get("/")`'in altındaki). -{* ../../docs_src/first_steps/tutorial001.py hl[7] *} +{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *} Bu bir Python fonksiyonudur. -Bu fonksiyon bir `GET` işlemi kullanılarak "`/`" bağlantısına bir istek geldiğinde **FastAPI** tarafından çağrılır. +**FastAPI**, "`/`" URL'ine `GET` operation kullanarak bir request aldığında bu fonksiyonu çağıracaktır. -Bu durumda bu fonksiyon bir `async` fonksiyondur. +Bu durumda, bu bir `async` fonksiyondur. --- -Bu fonksiyonu `async def` yerine normal bir fonksiyon olarak da tanımlayabilirsiniz. +Bunu `async def` yerine normal bir fonksiyon olarak da tanımlayabilirsiniz: -{* ../../docs_src/first_steps/tutorial003.py hl[7] *} +{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *} /// note | Not -Eğer farkı bilmiyorsanız, [Async: *"Aceleniz mi var?"*](../async.md#in-a-hurry){.internal-link target=_blank} sayfasını kontrol edebilirsiniz. +Eğer farkı bilmiyorsanız, [Async: *"Aceleniz mi var?"*](../async.md#in-a-hurry){.internal-link target=_blank} sayfasına bakın. /// -### Adım 5: İçeriği Geri Döndürün +### Adım 5: içeriği döndürün { #step-5-return-the-content } -{* ../../docs_src/first_steps/tutorial001.py hl[8] *} +{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *} -Bir `dict`, `list` veya `str`, `int` gibi tekil değerler döndürebilirsiniz. +Bir `dict`, `list`, `str`, `int` vb. tekil değerler döndürebilirsiniz. -Ayrıca, Pydantic modelleri de döndürebilirsiniz (bu konu ileriki aşamalarda irdelenecektir). +Ayrıca Pydantic modelleri de döndürebilirsiniz (bununla ilgili daha fazlasını ileride göreceksiniz). -Otomatik olarak JSON'a dönüştürülecek (ORM'ler vb. dahil) başka birçok nesne ve model vardır. En beğendiklerinizi kullanmayı deneyin, yüksek ihtimalle destekleniyordur. +Otomatik olarak JSON'a dönüştürülecek (ORM'ler vb. dahil) başka birçok nesne ve model vardır. En sevdiğiniz nesne/model'leri kullanmayı deneyin; büyük ihtimalle zaten destekleniyordur. -## Özet +### Adım 6: Deploy edin { #step-6-deploy-it } -* `FastAPI`'yı projemize dahil ettik. -* Bir `app` örneği oluşturduk. -* Bir **yol operasyonu dekoratörü** (`@app.get("/")` gibi) yazdık. -* Bir **yol operasyonu fonksiyonu** (`def root(): ...` gibi) yazdık. -* Geliştirme sunucumuzu (`uvicorn main:app --reload` gibi) çalıştırdık. +Uygulamanızı tek komutla **FastAPI Cloud**'a deploy edin: `fastapi deploy`. 🎉 + +#### FastAPI Cloud Hakkında { #about-fastapi-cloud } + +**FastAPI Cloud**, **FastAPI**'ın arkasındaki aynı yazar ve ekip tarafından geliştirilmiştir. + +Minimum eforla bir API'ı **oluşturma**, **deploy etme** ve **erişme** sürecini sadeleştirir. + +FastAPI ile uygulama geliştirirken yaşadığınız aynı **developer experience**'ı, onları buluta **deploy etme** aşamasına da taşır. 🎉 + +FastAPI Cloud, *FastAPI and friends* açık kaynak projelerinin birincil sponsoru ve finansman sağlayıcısıdır. ✨ + +#### Diğer cloud sağlayıcılarına deploy edin { #deploy-to-other-cloud-providers } + +FastAPI açık kaynaklıdır ve standartlara dayanır. FastAPI uygulamalarını seçtiğiniz herhangi bir cloud sağlayıcısına deploy edebilirsiniz. + +FastAPI uygulamalarını onlarla deploy etmek için cloud sağlayıcınızın kılavuzlarını takip edin. 🤓 + +## Özet { #recap } + +* `FastAPI` import edin. +* Bir `app` instance'ı oluşturun. +* `@app.get("/")` gibi decorator'ları kullanarak bir **path operation decorator** yazın. +* Bir **path operation function** tanımlayın; örneğin `def root(): ...`. +* `fastapi dev` komutunu kullanarak geliştirme sunucusunu çalıştırın. +* İsterseniz `fastapi deploy` ile uygulamanızı deploy edin. diff --git a/docs/tr/docs/tutorial/path-params.md b/docs/tr/docs/tutorial/path-params.md index 408dd25ca2..db676f1eed 100644 --- a/docs/tr/docs/tutorial/path-params.md +++ b/docs/tr/docs/tutorial/path-params.md @@ -1,34 +1,34 @@ -# Yol Parametreleri +# Yol Parametreleri { #path-parameters } -Yol "parametrelerini" veya "değişkenlerini" Python string biçimlemede kullanılan sözdizimi ile tanımlayabilirsiniz. +Python string biçimlemede kullanılan sözdizimiyle path "parametreleri"ni veya "değişkenleri"ni tanımlayabilirsiniz: -{* ../../docs_src/path_params/tutorial001.py hl[6:7] *} +{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *} -Yol parametresi olan `item_id`'nin değeri, fonksiyonunuza `item_id` argümanı olarak aktarılacaktır. +Path parametresi `item_id`'nin değeri, fonksiyonunuza `item_id` argümanı olarak aktarılacaktır. -Eğer bu örneği çalıştırıp http://127.0.0.1:8000/items/foo sayfasına giderseniz, şöyle bir çıktı ile karşılaşırsınız: +Yani, bu örneği çalıştırıp http://127.0.0.1:8000/items/foo adresine giderseniz, şöyle bir response görürsünüz: ```JSON {"item_id":"foo"} ``` -## Tip İçeren Yol Parametreleri +## Tip İçeren Yol Parametreleri { #path-parameters-with-types } -Standart Python tip belirteçlerini kullanarak yol parametresinin tipini fonksiyonun içerisinde tanımlayabilirsiniz. +Standart Python tip belirteçlerini kullanarak path parametresinin tipini fonksiyonun içinde tanımlayabilirsiniz: -{* ../../docs_src/path_params/tutorial002.py hl[7] *} +{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *} -Bu durumda, `item_id` bir `int` olarak tanımlanacaktır. +Bu durumda, `item_id` bir `int` olarak tanımlanır. /// check | Ek bilgi -Bu sayede, fonksiyon içerisinde hata denetimi, kod tamamlama gibi konularda editör desteğine kavuşacaksınız. +Bu sayede, fonksiyon içinde hata denetimi, kod tamamlama vb. konularda editör desteğine kavuşursunuz. /// -## Veri Dönüşümü +## Veri conversion { #data-conversion } -Eğer bu örneği çalıştırıp tarayıcınızda http://127.0.0.1:8000/items/3 sayfasını açarsanız, şöyle bir yanıt ile karşılaşırsınız: +Bu örneği çalıştırıp tarayıcınızda http://127.0.0.1:8000/items/3 adresini açarsanız, şöyle bir response görürsünüz: ```JSON {"item_id":3} @@ -36,15 +36,15 @@ Eğer bu örneği çalıştırıp tarayıcınızda "ayrıştırma" özelliği sağlar. +Yani, bu tip tanımıyla birlikte **FastAPI** size otomatik request "parsing" sağlar. /// -## Veri Doğrulama +## Veri Doğrulama { #data-validation } -Eğer tarayıcınızda http://127.0.0.1:8000/items/foo sayfasını açarsanız, şuna benzer güzel bir HTTP hatası ile karşılaşırsınız: +Ancak tarayıcınızda http://127.0.0.1:8000/items/foo adresine giderseniz, şuna benzer güzel bir HTTP hatası görürsünüz: ```JSON { @@ -62,141 +62,135 @@ Eğer tarayıcınızda http://127.0.0.1:8000/items/4.2 sayfasında olduğu gibi `int` yerine `float` bir değer verseydik de ortaya çıkardı. +Aynı hata, şu örnekte olduğu gibi `int` yerine `float` verirseniz de ortaya çıkar: http://127.0.0.1:8000/items/4.2 /// check | Ek bilgi -Böylece, aynı Python tip tanımlaması ile birlikte, **FastAPI** veri doğrulama özelliği sağlar. +Yani, aynı Python tip tanımıyla birlikte **FastAPI** size veri doğrulama sağlar. -Dikkatinizi çekerim ki, karşılaştığınız hata, doğrulamanın geçersiz olduğu mutlak noktayı da açık bir şekilde belirtiyor. +Dikkat edin: hata ayrıca doğrulamanın geçmediği noktayı da açıkça belirtir. -Bu özellik, API'ınızla iletişime geçen kodu geliştirirken ve ayıklarken inanılmaz derecede yararlı olacaktır. +Bu, API'ınızla etkileşime giren kodu geliştirirken ve debug ederken inanılmaz derecede faydalıdır. /// -## Dokümantasyon +## Dokümantasyon { #documentation } -Ayrıca, tarayıcınızı http://127.0.0.1:8000/docs adresinde açarsanız, aşağıdaki gibi otomatik ve interaktif bir API dökümantasyonu ile karşılaşırsınız: +Tarayıcınızı http://127.0.0.1:8000/docs adresinde açtığınızda, aşağıdaki gibi otomatik ve interaktif bir API dokümantasyonu görürsünüz: /// check | Ek bilgi -Üstelik, sadece aynı Python tip tanımlaması ile, **FastAPI** size otomatik ve interaktif (Swagger UI ile entegre) bir dokümantasyon sağlar. +Yine, sadece aynı Python tip tanımıyla **FastAPI** size otomatik ve interaktif dokümantasyon (Swagger UI entegrasyonuyla) sağlar. -Dikkatinizi çekerim ki, yol parametresi integer olarak tanımlanmıştır. +Dikkat edin: path parametresi integer olarak tanımlanmıştır. /// -## Standartlara Dayalı Avantajlar, Alternatif Dokümantasyon +## Standartlara Dayalı Avantajlar, Alternatif Dokümantasyon { #standards-based-benefits-alternative-documentation } -Oluşturulan şema OpenAPI standardına uygun olduğu için birçok uyumlu araç mevcuttur. +Üretilen şema OpenAPI standardından geldiği için birçok uyumlu araç vardır. -Bu sayede, **FastAPI**'ın bizzat kendisi http://127.0.0.1:8000/redoc sayfasından erişebileceğiniz alternatif (ReDoc kullanan) bir API dokümantasyonu sağlar: +Bu nedenle **FastAPI**'ın kendisi, http://127.0.0.1:8000/redoc adresinden erişebileceğiniz alternatif bir API dokümantasyonu (ReDoc kullanarak) sağlar: -Aynı şekilde, farklı diller için kod türetme araçları da dahil olmak üzere çok sayıda uyumlu araç bulunur. +Aynı şekilde, birçok uyumlu araç vardır. Birçok dil için kod üretme araçları da buna dahildir. -## Pydantic +## Pydantic { #pydantic } -Tüm veri doğrulamaları Pydantic tarafından arka planda gerçekleştirilir, bu sayede tüm avantajlardan faydalanabilirsiniz. Böylece, emin ellerde olduğunuzu hissedebilirsiniz. +Tüm veri doğrulamaları, arka planda Pydantic tarafından gerçekleştirilir; böylece onun tüm avantajlarından faydalanırsınız. Ve emin ellerde olduğunuzu bilirsiniz. -Aynı tip tanımlamalarını `str`, `float`, `bool` ve diğer karmaşık veri tipleri ile kullanma imkanınız vardır. +Aynı tip tanımlarını `str`, `float`, `bool` ve daha birçok karmaşık veri tipiyle kullanabilirsiniz. -Bunlardan birkaçı, bu eğitimin ileriki bölümlerinde irdelenmiştir. +Bunların birkaçı, eğitimin sonraki bölümlerinde ele alınacaktır. -## Sıralama Önem Arz Eder +## Sıralama Önemlidir { #order-matters } -*Yol operasyonları* tasarlarken sabit yol barındıran durumlar ile karşılaşabilirsiniz. +*Path operation*'lar oluştururken sabit bir path'e sahip olduğunuz durumlarla karşılaşabilirsiniz. -Farz edelim ki `/users/me` yolu geçerli kullanıcı hakkında bilgi almak için kullanılıyor olsun. +Örneğin `/users/me`'nin, geçerli kullanıcı hakkında veri almak için kullanıldığını varsayalım. -Benzer şekilde `/users/{user_id}` gibi tanımlanmış ve belirli bir kullanıcı hakkında veri almak için kullanıcının ID bilgisini kullanan bir yolunuz da mevcut olabilir. +Sonra belirli bir kullanıcı hakkında, kullanıcı ID'si ile veri almak için `/users/{user_id}` şeklinde bir path'iniz de olabilir. -*Yol operasyonları* sıralı bir şekilde gözden geçirildiğinden dolayı `/users/me` yolunun `/users/{user_id}` yolundan önce tanımlanmış olmasından emin olmanız gerekmektedir: +*Path operation*'lar sırayla değerlendirildiği için, `/users/me` için olan path'in `/users/{user_id}` olandan önce tanımlandığından emin olmanız gerekir: -{* ../../docs_src/path_params/tutorial003.py hl[6,11] *} +{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *} -Aksi halde, `/users/{user_id}` yolu `"me"` değerinin `user_id` parametresi için gönderildiğini "düşünerek" `/users/me` ile de eşleşir. +Aksi halde, `/users/{user_id}` için olan path, `"me"` değerini `user_id` parametresi olarak aldığını "düşünerek" `/users/me` için de eşleşir. -Benzer şekilde, bir yol operasyonunu yeniden tanımlamanız mümkün değildir: +Benzer şekilde, bir path operation'ı yeniden tanımlayamazsınız: -{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *} +{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *} -Yol, ilk kısım ile eşleştiğinden dolayı her koşulda ilk yol operasyonu kullanılacaktır. +Path önce eşleştiği için her zaman ilk olan kullanılır. -## Ön Tanımlı Değerler +## Ön Tanımlı Değerler { #predefined-values } -Eğer *yol parametresi* alan bir *yol operasyonunuz* varsa ve alabileceği *yol parametresi* değerlerinin ön tanımlı olmasını istiyorsanız, standart Python `Enum` tipini kullanabilirsiniz. +Bir *path operation*'ınız *path parameter* alıyorsa ama olası geçerli *path parameter* değerlerinin önceden tanımlı olmasını istiyorsanız, standart bir Python `Enum` kullanabilirsiniz. -### Bir `Enum` Sınıfı Oluşturalım +### Bir `Enum` Sınıfı Oluşturalım { #create-an-enum-class } -`Enum` sınıfını projemize dahil edip `str` ile `Enum` sınıflarını miras alan bir alt sınıf yaratalım. +`Enum`'u import edin ve `str` ile `Enum`'dan miras alan bir alt sınıf oluşturun. -`str` sınıfı miras alındığından dolayı, API dokümanı, değerlerin `string` tipinde olması gerektiğini anlayabilecek ve doğru bir şekilde işlenecektir. +`str`'den miras aldığınızda API dokümanları değerlerin `string` tipinde olması gerektiğini anlayabilir ve doğru şekilde render edebilir. -Sonrasında, sınıf içerisinde, mevcut ve geçerli değerler olacak olan sabit değerli özelliklerini oluşturalım: +Sonra, kullanılabilir geçerli değerler olacak sabit değerli class attribute'ları oluşturun: -{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *} - -/// info | Bilgi - -3.4 sürümünden beri enumerationlar (ya da enumlar) Python'da mevcuttur. - -/// +{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *} /// tip | İpucu -Merak ediyorsanız söyleyeyim, "AlexNet", "ResNet" ve "LeNet" isimleri Makine Öğrenmesi modellerini temsil eder. +Merak ediyorsanız: "AlexNet", "ResNet" ve "LeNet", Makine Öğrenmesi modellerinin sadece isimleridir. /// -### Bir *Yol Parametresi* Tanımlayalım +### Bir *Path Parameter* Tanımlayalım { #declare-a-path-parameter } -Sonrasında, yarattığımız enum sınıfını (`ModelName`) kullanarak tip belirteci aracılığıyla bir *yol parametresi* oluşturalım: +Ardından oluşturduğunuz enum sınıfını (`ModelName`) kullanarak tip belirteciyle bir *path parameter* oluşturun: -{* ../../docs_src/path_params/tutorial005.py hl[16] *} +{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *} -### Dokümana Göz Atalım +### Dokümana Göz Atalım { #check-the-docs } -*Yol parametresi* için mevcut değerler ön tanımlı olduğundan dolayı, interaktif döküman onları güzel bir şekilde gösterebilir: +*Path parameter* için kullanılabilir değerler ön tanımlı olduğu için, interaktif dokümanlar bunları güzelce gösterebilir: -### Python *Enumerationları* ile Çalışmak +### Python *Enumeration*'ları ile Çalışmak { #working-with-python-enumerations } -*Yol parametresinin* değeri bir *enumeration üyesi* olacaktır. +*Path parameter*'ın değeri bir *enumeration member* olacaktır. -#### *Enumeration Üyelerini* Karşılaştıralım +#### *Enumeration Member*'ları Karşılaştıralım { #compare-enumeration-members } -Parametreyi, yarattığınız enum olan `ModelName` içerisindeki *enumeration üyesi* ile karşılaştırabilirsiniz: +Bunu, oluşturduğunuz enum `ModelName` içindeki *enumeration member* ile karşılaştırabilirsiniz: -{* ../../docs_src/path_params/tutorial005.py hl[17] *} +{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *} -#### *Enumeration Değerini* Edinelim +#### *Enumeration Value*'yu Alalım { #get-the-enumeration-value } -`model_name.value` veya genel olarak `your_enum_member.value` tanımlarını kullanarak (bu durumda bir `str` olan) gerçek değere ulaşabilirsiniz: +Gerçek değeri (bu durumda bir `str`) `model_name.value` ile veya genel olarak `your_enum_member.value` ile alabilirsiniz: -{* ../../docs_src/path_params/tutorial005.py hl[20] *} +{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *} /// tip | İpucu -`"lenet"` değerine `ModelName.lenet.value` tanımı ile de ulaşabilirsiniz. +`"lenet"` değerine `ModelName.lenet.value` ile de erişebilirsiniz. /// -#### *Enumeration Üyelerini* Döndürelim +#### *Enumeration Member*'ları Döndürelim { #return-enumeration-members } -JSON gövdesine (örneğin bir `dict`) gömülü olsalar bile *yol operasyonundaki* *enum üyelerini* döndürebilirsiniz. +*Path operation*'ınızdan, bir JSON body'nin içine gömülü olsalar bile (ör. bir `dict`) *enum member*'ları döndürebilirsiniz. -Bu üyeler istemciye iletilmeden önce kendilerine karşılık gelen değerlerine (bu durumda string) dönüştürüleceklerdir: +İstemciye dönmeden önce, karşılık gelen değerlerine (bu durumda string) dönüştürülürler: -{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *} +{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *} -İstemci tarafında şuna benzer bir JSON yanıtı ile karşılaşırsınız: +İstemcinizde şöyle bir JSON response alırsınız: ```JSON { @@ -205,53 +199,53 @@ Bu üyeler istemciye iletilmeden önce kendilerine karşılık gelen değerlerin } ``` -## Yol İçeren Yol Parametreleri +## Path İçeren Path Parametreleri { #path-parameters-containing-paths } -Farz edelim ki elinizde `/files/{file_path}` isminde bir *yol operasyonu* var. +Diyelim ki `/files/{file_path}` path'ine sahip bir *path operation*'ınız var. -Fakat `file_path` değerinin `home/johndoe/myfile.txt` gibi bir *yol* barındırmasını istiyorsunuz. +Ama `file_path`'in kendisinin `home/johndoe/myfile.txt` gibi bir *path* içermesi gerekiyor. -Sonuç olarak, oluşturmak istediğin URL `/files/home/johndoe/myfile.txt` gibi bir şey olacaktır. +Böylece, o dosyanın URL'si şu şekilde olur: `/files/home/johndoe/myfile.txt`. -### OpenAPI Desteği +### OpenAPI Desteği { #openapi-support } -Test etmesi ve tanımlaması zor senaryolara sebebiyet vereceğinden dolayı OpenAPI, *yol* barındıran *yol parametrelerini* tanımlayacak bir çözüm sunmuyor. +OpenAPI, içinde bir *path* barındıracak bir *path parameter* tanımlamak için bir yöntem desteklemez; çünkü bu, test etmesi ve tanımlaması zor senaryolara yol açabilir. -Ancak bunu, Starlette kütüphanesinin dahili araçlarından birini kullanarak **FastAPI**'da gerçekleştirebilirsiniz. +Yine de, Starlette'in dahili araçlarından birini kullanarak bunu **FastAPI**'da yapabilirsiniz. -Parametrenin bir yol içermesi gerektiğini belirten herhangi bir doküman eklemememize rağmen dokümanlar yine de çalışacaktır. +Ve dokümanlar, parametrenin bir path içermesi gerektiğini söyleyen herhangi bir dokümantasyon eklemese bile çalışmaya devam eder. -### Yol Dönüştürücü +### Path Dönüştürücü { #path-convertor } -Direkt olarak Starlette kütüphanesinden gelen bir opsiyon sayesinde aşağıdaki gibi *yol* içeren bir *yol parametresi* bağlantısı tanımlayabilirsiniz: +Starlette'ten doğrudan gelen bir seçenekle, *path* içeren bir *path parameter*'ı şu URL ile tanımlayabilirsiniz: ``` /files/{file_path:path} ``` -Bu durumda, parametrenin adı `file_path` olacaktır ve son kısım olan `:path` kısmı, parametrenin herhangi bir *yol* ile eşleşmesi gerektiğini belirtecektir. +Bu durumda parametrenin adı `file_path`'tir ve son kısım olan `:path`, parametrenin herhangi bir *path* ile eşleşmesi gerektiğini söyler. -Böylece şunun gibi bir kullanım yapabilirsiniz: +Yani şununla kullanabilirsiniz: -{* ../../docs_src/path_params/tutorial004.py hl[6] *} +{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *} /// tip | İpucu -Parametrenin başında `/home/johndoe/myfile.txt` yolunda olduğu gibi (`/`) işareti ile birlikte kullanmanız gerektiği durumlar olabilir. +Parametrenin başında `/home/johndoe/myfile.txt` örneğinde olduğu gibi bir eğik çizgi (`/`) ile başlaması gerekebilir. -Bu durumda, URL, `files` ile `home` arasında iki eğik çizgiye (`//`) sahip olup `/files//home/johndoe/myfile.txt` gibi gözükecektir. +Bu durumda URL, `files` ile `home` arasında çift eğik çizgi (`//`) olacak şekilde `/files//home/johndoe/myfile.txt` olur. /// -## Özet +## Özet { #recap } -**FastAPI** ile kısa, sezgisel ve standart Python tip tanımlamaları kullanarak şunları elde edersiniz: +**FastAPI** ile kısa, sezgisel ve standart Python tip tanımlarını kullanarak şunları elde edersiniz: -* Editör desteği: hata denetimi, otomatik tamamlama, vb. -* Veri "dönüştürme" +* Editör desteği: hata denetimleri, otomatik tamamlama vb. +* Veri "parsing" * Veri doğrulama -* API tanımlamaları ve otomatik dokümantasyon +* API annotation ve otomatik dokümantasyon -Ve sadece, bunları bir kez tanımlamanız yeterli. +Ve bunları sadece bir kez tanımlamanız yeterlidir. -Diğer frameworkler ile karşılaştırıldığında (ham performans dışında), üstte anlatılan durum muhtemelen **FastAPI**'ın göze çarpan başlıca avantajıdır. +Bu, (ham performans dışında) **FastAPI**'ın alternatif framework'lere kıyasla muhtemelen en görünür ana avantajıdır. diff --git a/docs/tr/docs/tutorial/query-params.md b/docs/tr/docs/tutorial/query-params.md index a8ba883edc..89cfa3fb35 100644 --- a/docs/tr/docs/tutorial/query-params.md +++ b/docs/tr/docs/tutorial/query-params.md @@ -1,83 +1,83 @@ -# Sorgu Parametreleri +# Sorgu Parametreleri { #query-parameters } -Fonksiyonda yol parametrelerinin parçası olmayan diğer tanımlamalar otomatik olarak "sorgu" parametresi olarak yorumlanır. +Fonksiyonda path parametrelerinin parçası olmayan diğer parametreleri tanımladığınızda, bunlar otomatik olarak "query" parametreleri olarak yorumlanır. -{* ../../docs_src/query_params/tutorial001.py hl[9] *} +{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *} -Sorgu, bağlantıdaki `?` kısmından sonra gelen ve `&` işareti ile ayrılan anahtar-değer çiftlerinin oluşturduğu bir kümedir. +Query, bir URL'de `?` işaretinden sonra gelen ve `&` karakterleriyle ayrılan anahtar-değer çiftlerinin kümesidir. -Örneğin, aşağıdaki bağlantıda: +Örneğin, şu URL'de: ``` http://127.0.0.1:8000/items/?skip=0&limit=10 ``` -...sorgu parametreleri şunlardır: +...query parametreleri şunlardır: -* `skip`: değeri `0`'dır -* `limit`: değeri `10`'dır +* `skip`: değeri `0` +* `limit`: değeri `10` -Parametreler bağlantının bir parçası oldukları için doğal olarak string olarak değerlendirilirler. +URL'nin bir parçası oldukları için "doğal olarak" string'tirler. -Fakat, Python tipleri ile tanımlandıkları zaman (yukarıdaki örnekte `int` oldukları gibi), parametreler o tiplere dönüştürülür ve o tipler çerçevesinde doğrulanırlar. +Ancak, bunları Python tipleriyle (yukarıdaki örnekte `int` olarak) tanımladığınızda, o tipe dönüştürülürler ve o tipe göre doğrulanırlar. -Yol parametreleri için geçerli olan her türlü işlem aynı şekilde sorgu parametreleri için de geçerlidir: +Path parametreleri için geçerli olan aynı süreç query parametreleri için de geçerlidir: -* Editör desteği (şüphesiz) -* Veri "ayrıştırma" +* Editör desteği (tabii ki) +* Veri "parsing" * Veri doğrulama * Otomatik dokümantasyon -## Varsayılanlar +## Varsayılanlar { #defaults } -Sorgu parametreleri, adres yolunun sabit bir parçası olmadıklarından dolayı isteğe bağlı ve varsayılan değere sahip olabilirler. +Query parametreleri path'in sabit bir parçası olmadığından, opsiyonel olabilir ve varsayılan değerlere sahip olabilir. -Yukarıdaki örnekte `skip=0` ve `limit=10` varsayılan değere sahiplerdir. +Yukarıdaki örnekte varsayılan değerleri `skip=0` ve `limit=10`'dur. -Yani, aşağıdaki bağlantıya gitmek: +Yani şu URL'ye gitmek: ``` http://127.0.0.1:8000/items/ ``` -şu adrese gitmek ile aynı etkiye sahiptir: +şuraya gitmekle aynı olur: ``` http://127.0.0.1:8000/items/?skip=0&limit=10 ``` -Ancak, mesela şöyle bir adresi ziyaret ederseniz: +Ancak örneğin şuraya giderseniz: ``` http://127.0.0.1:8000/items/?skip=20 ``` -Fonksiyonunuzdaki parametre değerleri aşağıdaki gibi olacaktır: +Fonksiyonunuzdaki parametre değerleri şöyle olacaktır: -* `skip=20`: çünkü bağlantıda böyle tanımlandı. -* `limit=10`: çünkü varsayılan değer buydu. +* `skip=20`: çünkü URL'de siz ayarladınız +* `limit=10`: çünkü varsayılan değer oydu -## İsteğe Bağlı Parametreler +## İsteğe bağlı parametreler { #optional-parameters } -Aynı şekilde, varsayılan değerlerini `None` olarak atayarak isteğe bağlı parametreler tanımlayabilirsiniz: +Aynı şekilde, varsayılan değerlerini `None` yaparak isteğe bağlı query parametreleri tanımlayabilirsiniz: {* ../../docs_src/query_params/tutorial002_py310.py hl[7] *} -Bu durumda, `q` fonksiyon parametresi isteğe bağlı olacak ve varsayılan değer olarak `None` alacaktır. +Bu durumda, fonksiyon parametresi `q` isteğe bağlı olur ve varsayılan olarak `None` olur. /// check | Ek bilgi -Ayrıca, dikkatinizi çekerim ki; **FastAPI**, `item_id` parametresinin bir yol parametresi olduğunu ve `q` parametresinin yol değil bir sorgu parametresi olduğunu fark edecek kadar beceriklidir. +Ayrıca, **FastAPI** path parametresi olan `item_id`'nin bir path parametresi olduğunu ve `q`'nun path olmadığını fark edecek kadar akıllıdır; dolayısıyla bu bir query parametresidir. /// -## Sorgu Parametresi Tip Dönüşümü +## Sorgu parametresi tip dönüşümü { #query-parameter-type-conversion } -Aşağıda görüldüğü gibi dönüştürülmek üzere `bool` tipleri de tanımlayabilirsiniz: +`bool` tipleri de tanımlayabilirsiniz, ve bunlar dönüştürülür: {* ../../docs_src/query_params/tutorial003_py310.py hl[7] *} -Bu durumda, eğer şu adrese giderseniz: +Bu durumda, şuraya giderseniz: ``` http://127.0.0.1:8000/items/foo?short=1 @@ -107,38 +107,38 @@ veya http://127.0.0.1:8000/items/foo?short=yes ``` -veya adres, herhangi farklı bir harf varyasyonu içermesi durumuna rağmen (büyük harf, sadece baş harfi büyük kelime, vb.) fonksiyonunuz, `bool` tipli `short` parametresini `True` olarak algılayacaktır. Aksi halde `False` olarak algılanacaktır. +veya başka herhangi bir büyük/küçük harf varyasyonunda (tamamı büyük, ilk harf büyük, vb.), fonksiyonunuz `short` parametresini `bool` değeri `True` olarak görecektir. Aksi halde `False` olarak görür. -## Çoklu Yol ve Sorgu Parametreleri +## Çoklu path ve query parametreleri { #multiple-path-and-query-parameters } -**FastAPI** neyin ne olduğunu ayırt edebileceğinden dolayı aynı anda birden fazla yol ve sorgu parametresi tanımlayabilirsiniz. +Aynı anda birden fazla path parametresi ve query parametresi tanımlayabilirsiniz; **FastAPI** hangisinin hangisi olduğunu bilir. -Ve parametreleri, herhangi bir sıraya koymanıza da gerek yoktur. +Ayrıca bunları belirli bir sırayla tanımlamanız gerekmez. -İsimlerine göre belirleneceklerdir: +İsme göre tespit edilirler: {* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *} -## Zorunlu Sorgu Parametreleri +## Zorunlu query parametreleri { #required-query-parameters } -Türü yol olmayan bir parametre (şu ana kadar sadece sorgu parametrelerini gördük) için varsayılan değer tanımlarsanız o parametre zorunlu olmayacaktır. +Path olmayan parametreler (şimdilik sadece query parametrelerini gördük) için varsayılan değer tanımladığınızda, bu parametre zorunlu olmaz. -Parametre için belirli bir değer atamak istemeyip parametrenin sadece isteğe bağlı olmasını istiyorsanız değerini `None` olarak atayabilirsiniz. +Belirli bir değer eklemek istemiyor ama sadece opsiyonel olmasını istiyorsanız, varsayılanı `None` olarak ayarlayın. -Fakat, bir sorgu parametresini zorunlu yapmak istiyorsanız varsayılan bir değer atamamanız yeterli olacaktır: +Ancak bir query parametresini zorunlu yapmak istediğinizde, herhangi bir varsayılan değer tanımlamamanız yeterlidir: -{* ../../docs_src/query_params/tutorial005.py hl[6:7] *} +{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *} -Burada `needy` parametresi `str` tipinden oluşan zorunlu bir sorgu parametresidir. +Burada query parametresi `needy`, `str` tipinde zorunlu bir query parametresidir. -Eğer tarayıcınızda şu bağlantıyı: +Tarayıcınızda şöyle bir URL açarsanız: ``` http://127.0.0.1:8000/items/foo-item ``` -...`needy` parametresini eklemeden açarsanız şuna benzer bir hata ile karşılaşırsınız: +...zorunlu `needy` parametresini eklemeden, şuna benzer bir hata görürsünüz: ```JSON { @@ -156,13 +156,13 @@ http://127.0.0.1:8000/items/foo-item } ``` -`needy` zorunlu bir parametre olduğundan dolayı bağlantıda tanımlanması gerekir: +`needy` zorunlu bir parametre olduğundan, URL'de ayarlamanız gerekir: ``` http://127.0.0.1:8000/items/foo-item?needy=sooooneedy ``` -...bu iş görür: +...bu çalışır: ```JSON { @@ -171,11 +171,11 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy } ``` -Ve elbette, bazı parametreleri zorunlu, bazılarını varsayılan değerli ve bazılarını tamamen opsiyonel olarak tanımlayabilirsiniz: +Ve elbette, bazı parametreleri zorunlu, bazılarını varsayılan değerli, bazılarını da tamamen isteğe bağlı olarak tanımlayabilirsiniz: {* ../../docs_src/query_params/tutorial006_py310.py hl[8] *} -Bu durumda, 3 tane sorgu parametresi var olacaktır: +Bu durumda, 3 tane query parametresi vardır: * `needy`, zorunlu bir `str`. * `skip`, varsayılan değeri `0` olan bir `int`. @@ -183,6 +183,6 @@ Bu durumda, 3 tane sorgu parametresi var olacaktır: /// tip | İpucu -Ayrıca, [Yol Parametrelerinde](path-params.md#on-tanml-degerler){.internal-link target=_blank} de kullanıldığı şekilde `Enum` sınıfından faydalanabilirsiniz. +[Path Parametreleri](path-params.md#predefined-values){.internal-link target=_blank} ile aynı şekilde `Enum`'ları da kullanabilirsiniz. /// diff --git a/docs/tr/docs/tutorial/request-forms.md b/docs/tr/docs/tutorial/request-forms.md index e4e04f5f99..4608a6b79b 100644 --- a/docs/tr/docs/tutorial/request-forms.md +++ b/docs/tr/docs/tutorial/request-forms.md @@ -1,69 +1,73 @@ -# Form Verisi +# Form Verisi { #form-data } -İstek gövdesinde JSON verisi yerine form alanlarını karşılamanız gerketiğinde `Form` sınıfını kullanabilirsiniz. +JSON yerine form alanlarını almanız gerektiğinde `Form` kullanabilirsiniz. /// info | Bilgi -Formları kullanmak için öncelikle `python-multipart` paketini indirmeniz gerekmektedir. +Formları kullanmak için önce `python-multipart` paketini kurun. -Örneğin `pip install python-multipart`. +Bir [virtual environment](../virtual-environments.md){.internal-link target=_blank} oluşturduğunuzdan, onu etkinleştirdiğinizden emin olun ve ardından örneğin şöyle kurun: + +```console +$ pip install python-multipart +``` /// -## `Form` Sınıfını Projenize Dahil Edin +## `Form`'u Import Edin { #import-form } -`Form` sınıfını `fastapi`'den projenize dahil edin: +`Form`'u `fastapi`'den import edin: {* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *} -## `Form` Parametrelerini Tanımlayın +## `Form` Parametrelerini Tanımlayın { #define-form-parameters } Form parametrelerini `Body` veya `Query` için yaptığınız gibi oluşturun: {* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *} -Örneğin, OAuth2 spesifikasyonunun kullanılabileceği ("şifre akışı" olarak adlandırılan) yollardan birinde, form alanları olarak "username" ve "password" gönderilmesi gerekir. +Örneğin OAuth2 spesifikasyonunun kullanılabileceği ("password flow" olarak adlandırılan) yollardan birinde, form alanları olarak bir `username` ve `password` göndermek zorunludur. -Bu spesifikasyon form alanlarını adlandırırken isimlerinin birebir `username` ve `password` olmasını ve JSON verisi yerine form verisi olarak gönderilmesini gerektirir. +spec, alanların adının tam olarak `username` ve `password` olmasını ve JSON değil form alanları olarak gönderilmesini gerektirir. -`Form` sınıfıyla tanımlama yaparken `Body`, `Query`, `Path` ve `Cookie` sınıflarında kullandığınız aynı validasyon, örnekler, isimlendirme (örneğin `username` yerine `user-name` kullanımı) ve daha fazla konfigurasyonu kullanabilirsiniz. +`Form` ile `Body` (ve `Query`, `Path`, `Cookie`) ile yaptığınız aynı konfigürasyonları tanımlayabilirsiniz; validasyon, örnekler, alias (örn. `username` yerine `user-name`) vb. dahil. /// info | Bilgi -`Form` doğrudan `Body` sınıfını miras alan bir sınıftır. +`Form`, doğrudan `Body`'den miras alan bir sınıftır. /// /// tip | İpucu -Form gövdelerini tanımlamak için `Form` sınıfını kullanmanız gerekir; çünkü bu olmadan parametreler sorgu parametreleri veya gövde (JSON) parametreleri olarak yorumlanır. +Form gövdelerini tanımlamak için `Form`'u açıkça kullanmanız gerekir; çünkü bunu yapmazsanız parametreler query parametreleri veya body (JSON) parametreleri olarak yorumlanır. /// -## "Form Alanları" Hakkında +## "Form Alanları" Hakkında { #about-form-fields } -HTML formlarının (`
`) verileri sunucuya gönderirken JSON'dan farklı özel bir kodlama kullanır. +HTML formlarının (`
`) verileri sunucuya gönderme şekli normalde bu veri için JSON'dan farklı "özel" bir encoding kullanır. -**FastAPI** bu verilerin JSON yerine doğru şekilde okunmasını sağlayacaktır. +**FastAPI** bu veriyi JSON yerine doğru yerden okuyacaktır. /// note | Teknik Detaylar -Form verileri normalde `application/x-www-form-urlencoded` medya tipiyle kodlanır. +Formlardan gelen veri normalde "media type" `application/x-www-form-urlencoded` kullanılarak encode edilir. -Ancak form içerisinde dosyalar yer aldığında `multipart/form-data` olarak kodlanır. Bir sonraki bölümde dosyaların işlenmesi hakkında bilgi edineceksiniz. +Ancak form dosyalar içerdiğinde `multipart/form-data` olarak encode edilir. Dosyaları ele almayı bir sonraki bölümde okuyacaksınız. -Form kodlama türleri ve form alanları hakkında daha fazla bilgi edinmek istiyorsanız MDN web docs for POST sayfasını ziyaret edebilirsiniz. +Bu encoding'ler ve form alanları hakkında daha fazla okumak isterseniz, MDN web docs for POST sayfasına gidin. /// /// warning | Uyarı -*Yol operasyonları* içerisinde birden fazla `Form` parametresi tanımlayabilirsiniz ancak bunlarla birlikte JSON verisi kabul eden `Body` alanları tanımlayamazsınız çünkü bu durumda istek gövdesi `application/json` yerine `application/x-www-form-urlencoded` ile kodlanmış olur. +Bir *path operation* içinde birden fazla `Form` parametresi tanımlayabilirsiniz, ancak JSON olarak almayı beklediğiniz `Body` alanlarını da ayrıca tanımlayamazsınız; çünkü bu durumda request'in body'si `application/json` yerine `application/x-www-form-urlencoded` ile encode edilmiş olur. -Bu **FastAPI**'ın getirdiği bir kısıtlama değildir, HTTP protokolünün bir parçasıdır. +Bu **FastAPI**'ın bir kısıtlaması değildir, HTTP protokolünün bir parçasıdır. /// -## Özet +## Özet { #recap } -Form verisi girdi parametreleri tanımlamak için `Form` sınıfını kullanın. +Form verisi girdi parametrelerini tanımlamak için `Form` kullanın. diff --git a/docs/tr/docs/tutorial/static-files.md b/docs/tr/docs/tutorial/static-files.md index 4542aca773..d30b4389d0 100644 --- a/docs/tr/docs/tutorial/static-files.md +++ b/docs/tr/docs/tutorial/static-files.md @@ -1,40 +1,40 @@ -# Statik Dosyalar +# Statik Dosyalar { #static-files } -`StaticFiles`'ı kullanarak statik dosyaları bir yol altında sunabilirsiniz. +`StaticFiles` kullanarak bir dizindeki statik dosyaları otomatik olarak sunabilirsiniz. -## `StaticFiles` Kullanımı +## `StaticFiles` Kullanımı { #use-staticfiles } -* `StaticFiles` sınıfını projenize dahil edin. -* Bir `StaticFiles()` örneğini belirli bir yola bağlayın. +* `StaticFiles`'ı import edin. +* Belirli bir path'te bir `StaticFiles()` örneğini "mount" edin. -{* ../../docs_src/static_files/tutorial001.py hl[2,6] *} +{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *} /// note | Teknik Detaylar -Projenize dahil etmek için `from starlette.staticfiles import StaticFiles` kullanabilirsiniz. +`from starlette.staticfiles import StaticFiles` da kullanabilirsiniz. -**FastAPI**, geliştiricilere kolaylık sağlamak amacıyla `starlette.staticfiles`'ı `fastapi.staticfiles` olarak sağlar. Ancak `StaticFiles` sınıfı aslında doğrudan Starlette'den gelir. +**FastAPI**, geliştirici olarak size kolaylık olsun diye `starlette.staticfiles`'ı `fastapi.staticfiles` olarak da sağlar. Ancak aslında doğrudan Starlette'den gelir. /// -### Bağlama (Mounting) Nedir? +### "Mounting" Nedir { #what-is-mounting } -"Bağlamak", belirli bir yola tamamen "bağımsız" bir uygulama eklemek anlamına gelir ve ardından tüm alt yollara gelen istekler bu uygulama tarafından işlenir. +"Mounting", belirli bir path'te tamamen "bağımsız" bir uygulama eklemek ve sonrasında tüm alt path'leri handle etmesini sağlamak demektir. -Bu, bir `APIRouter` kullanmaktan farklıdır çünkü bağlanmış bir uygulama tamamen bağımsızdır. Ana uygulamanızın OpenAPI ve dokümanlar, bağlanmış uygulamadan hiçbir şey içermez, vb. +Bu, bir `APIRouter` kullanmaktan farklıdır; çünkü mount edilen uygulama tamamen bağımsızdır. Ana uygulamanızın OpenAPI ve docs'ları, mount edilen uygulamadan hiçbir şey içermez, vb. -[Advanced User Guide](../advanced/index.md){.internal-link target=_blank} bölümünde daha fazla bilgi edinebilirsiniz. +Bununla ilgili daha fazla bilgiyi [Advanced User Guide](../advanced/index.md){.internal-link target=_blank} içinde okuyabilirsiniz. -## Detaylar +## Detaylar { #details } -`"/static"` ifadesi, bu "alt uygulamanın" "bağlanacağı" alt yolu belirtir. Bu nedenle, `"/static"` ile başlayan her yol, bu uygulama tarafından işlenir. +İlk `"/static"`, bu "alt uygulamanın" "mount" edileceği alt path'i ifade eder. Dolayısıyla `"/static"` ile başlayan herhangi bir path bunun tarafından handle edilir. -`directory="static"` ifadesi, statik dosyalarınızı içeren dizinin adını belirtir. +`directory="static"`, statik dosyalarınızı içeren dizinin adını ifade eder. -`name="static"` ifadesi, alt uygulamanın **FastAPI** tarafından kullanılacak ismini belirtir. +`name="static"`, **FastAPI**'nin dahili olarak kullanabileceği bir isim verir. -Bu parametrelerin hepsi "`static`"den farklı olabilir, bunları kendi uygulamanızın ihtiyaçlarına göre belirleyebilirsiniz. +Bu parametrelerin hepsi "`static`" ile aynı olmak zorunda değildir; kendi uygulamanızın ihtiyaçlarına ve özel detaylarına göre ayarlayın. -## Daha Fazla Bilgi +## Daha Fazla Bilgi { #more-info } -Daha fazla detay ve seçenek için Starlette'in Statik Dosyalar hakkındaki dokümantasyonunu incelleyin. +Daha fazla detay ve seçenek için Starlette'in Statik Dosyalar hakkındaki dokümanlarını inceleyin. diff --git a/docs/tr/llm-prompt.md b/docs/tr/llm-prompt.md index 297b0a0e6c..2ba922ec59 100644 --- a/docs/tr/llm-prompt.md +++ b/docs/tr/llm-prompt.md @@ -4,10 +4,16 @@ Translate to Turkish (Türkçe). Language code: tr. +### Core principle + +Don't translate word-by-word. Rewrite naturally in Turkish as if writing the doc from scratch. Preserve meaning, but prioritize fluency over literal accuracy. + ### Grammar and tone - Use instructional Turkish, consistent with existing Turkish docs. -- Use imperative/guide language when appropriate (e.g. “açalım”, “gidin”, “kopyalayalım”). +- Use imperative/guide language (e.g. "açalım", "gidin", "kopyalayalım", "bir bakalım"). +- Avoid filler words and overly long sentences. +- Ensure sentences make sense in Turkish context — adjust structure, conjunctions, and verb forms as needed for natural flow (e.g. use "Ancak" instead of "Ve" when connecting contrasting sentences, use "-maktadır/-mektedir" for formal statements). ### Headings @@ -15,13 +21,23 @@ Language code: tr. ### Quotes -- Alıntı stili mevcut Türkçe dokümanlarla tutarlı tutun (genellikle metin içinde ASCII tırnak işaretleri kullanılır). -- Satır içi kod, kod blokları, URL'ler veya dosya yolları içindeki tırnak işaretlerini asla değiştirmeyin. +- Keep quote style consistent with existing Turkish docs (typically ASCII quotes in text). +- Never modify quotes inside inline code, code blocks, URLs, or file paths. ### Ellipsis -- Üç nokta (...) stili mevcut Türkçe dokümanlarla tutarlı tutun. -- Kod, URL veya CLI örneklerindeki `...` ifadesini asla değiştirmeyin. +- Keep ellipsis style (`...`) consistent with existing Turkish docs. +- Never modify `...` in code, URLs, or CLI examples. + +### Consistency + +- Use the same translation for the same term throughout the document. +- If you translate a concept one way, keep it consistent across all occurrences. + +### Links and references + +- Never modify link syntax like `{.internal-link target=_blank}`. +- Keep markdown link structure intact: `[text](url){.internal-link}`. ### Preferred translations / glossary diff --git a/docs/uk/docs/alternatives.md b/docs/uk/docs/alternatives.md index 786df45c50..d44ca794f8 100644 --- a/docs/uk/docs/alternatives.md +++ b/docs/uk/docs/alternatives.md @@ -1,8 +1,8 @@ -# Альтернативи, натхнення та порівняння +# Альтернативи, натхнення та порівняння { #alternatives-inspiration-and-comparisons } -Що надихнуло на створення **FastAPI**, який він у порінянні з іншими альтернативами та чого він у них навчився. +Що надихнуло **FastAPI**, як він порівнюється з альтернативами та чого він у них навчився. -## Вступ +## Вступ { #intro } **FastAPI** не існувало б, якби не попередні роботи інших. @@ -12,17 +12,17 @@ Але в якийсь момент не було іншого виходу, окрім створення чогось, що надавало б усі ці функції, взявши найкращі ідеї з попередніх інструментів і поєднавши їх найкращим чином, використовуючи мовні функції, які навіть не були доступні раніше (Python 3.6+ підказки типів). -## Попередні інструменти +## Попередні інструменти { #previous-tools } -### Django +### Django { #django } Це найпопулярніший фреймворк Python, який користується широкою довірою. Він використовується для створення таких систем, як Instagram. Він відносно тісно пов’язаний з реляційними базами даних (наприклад, MySQL або PostgreSQL), тому мати базу даних NoSQL (наприклад, Couchbase, MongoDB, Cassandra тощо) як основний механізм зберігання не дуже просто. -Він був створений для створення HTML у серверній частині, а не для створення API, які використовуються сучасним інтерфейсом (як-от React, Vue.js і Angular) або іншими системами (як-от IoT пристрої), які спілкуються з ним. +Він був створений для створення HTML у серверній частині, а не для створення API, які використовуються сучасним інтерфейсом (як-от React, Vue.js і Angular) або іншими системами (як-от IoT пристрої), які спілкуються з ним. -### Django REST Framework +### Django REST Framework { #django-rest-framework } Фреймворк Django REST був створений як гнучкий інструментарій для створення веб-інтерфейсів API використовуючи Django в основі, щоб покращити його можливості API. @@ -42,7 +42,7 @@ Django REST Framework створив Том Крісті. Той самий тв /// -### Flask +### Flask { #flask } Flask — це «мікрофреймворк», він не включає інтеграцію бази даних, а також багато речей, які за замовчуванням є в Django. @@ -64,7 +64,7 @@ Flask — це «мікрофреймворк», він не включає ін /// -### Requests +### Requests { #requests } **FastAPI** насправді не є альтернативою **Requests**. Сфера їх застосування дуже різна. @@ -88,12 +88,12 @@ Requests мають дуже простий та інтуїтивно зрозу response = requests.get("http://example.com/some/url") ``` -Відповідна операція *роуту* API FastAPI може виглядати так: +Відповідна операція шляху API FastAPI може виглядати так: ```Python hl_lines="1" @app.get("/some/url") def read_url(): - return {"message": "Hello World"} + return {"message": "Hello World"} ``` Зверніть увагу на схожість у `requests.get(...)` і `@app.get(...)`. @@ -101,12 +101,12 @@ def read_url(): /// check | Надихнуло **FastAPI** на * Майте простий та інтуїтивно зрозумілий API. - * Використовуйте імена (операції) методів HTTP безпосередньо, простим та інтуїтивно зрозумілим способом. - * Розумні параметри за замовчуванням, але потужні налаштування. +* Використовуйте імена (операції) методів HTTP безпосередньо, простим та інтуїтивно зрозумілим способом. +* Розумні параметри за замовчуванням, але потужні налаштування. /// -### Swagger / OpenAPI +### Swagger / OpenAPI { #swagger-openapi } Головною функцією, яку я хотів від Django REST Framework, була автоматична API документація. @@ -124,18 +124,18 @@ def read_url(): Інтегрувати інструменти інтерфейсу на основі стандартів: - * Інтерфейс Swagger - * ReDoc +* Інтерфейс Swagger +* ReDoc Ці два було обрано через те, що вони досить популярні та стабільні, але, виконавши швидкий пошук, ви можете знайти десятки додаткових альтернативних інтерфейсів для OpenAPI (які можна використовувати з **FastAPI**). /// -### Фреймворки REST для Flask +### Фреймворки REST для Flask { #flask-rest-frameworks } Існує кілька фреймворків Flask REST, але, витративши час і роботу на їх дослідження, я виявив, що багато з них припинено або залишено, з кількома постійними проблемами, які зробили їх непридатними. -### Marshmallow +### Marshmallow { #marshmallow } Однією з головних функцій, необхідних для систем API, є "серіалізація", яка бере дані з коду (Python) і перетворює їх на щось, що можна надіслати через мережу. Наприклад, перетворення об’єкта, що містить дані з бази даних, на об’єкт JSON. Перетворення об’єктів `datetime` на строки тощо. @@ -153,7 +153,7 @@ Marshmallow створено для забезпечення цих функці /// -### Webargs +### Webargs { #webargs } Іншою важливою функцією, необхідною для API, є аналіз даних із вхідних запитів. @@ -175,7 +175,7 @@ Webargs був створений тими ж розробниками Marshmall /// -### APISpec +### APISpec { #apispec } Marshmallow і Webargs забезпечують перевірку, аналіз і серіалізацію як плагіни. @@ -205,7 +205,7 @@ APISpec був створений тими ж розробниками Marshmall /// -### Flask-apispec +### Flask-apispec { #flask-apispec } Це плагін Flask, який об’єднує Webargs, Marshmallow і APISpec. @@ -237,13 +237,13 @@ Flask-apispec був створений тими ж розробниками Mar /// -### NestJS (та Angular) +### NestJS (та Angular) { #nestjs-and-angular } Це навіть не Python, NestJS — це фреймворк NodeJS JavaScript (TypeScript), натхненний Angular. Це досягає чогось подібного до того, що можна зробити з Flask-apispec. -Він має інтегровану систему впровадження залежностей, натхненну Angular two. Він потребує попередньої реєстрації «injectables» (як і всі інші системи впровадження залежностей, які я знаю), тому це збільшує багатослівність та повторення коду. +Він має інтегровану систему впровадження залежностей, натхненну Angular 2. Він потребує попередньої реєстрації «injectables» (як і всі інші системи впровадження залежностей, які я знаю), тому це збільшує багатослівність та повторення коду. Оскільки параметри описані за допомогою типів TypeScript (подібно до підказок типу Python), підтримка редактора досить хороша. @@ -259,7 +259,7 @@ Flask-apispec був створений тими ж розробниками Mar /// -### Sanic +### Sanic { #sanic } Це був один із перших надзвичайно швидких фреймворків Python на основі `asyncio`. Він був дуже схожий на Flask. @@ -279,7 +279,7 @@ Flask-apispec був створений тими ж розробниками Mar /// -### Falcon +### Falcon { #falcon } Falcon — ще один високопродуктивний фреймворк Python, він розроблений як мінімальний і працює як основа інших фреймворків, таких як Hug. @@ -297,7 +297,7 @@ Falcon — ще один високопродуктивний фреймворк /// -### Molten +### Molten { #molten } Я відкрив для себе Molten на перших етапах створення **FastAPI**. І він має досить схожі ідеї: @@ -321,7 +321,7 @@ Falcon — ще один високопродуктивний фреймворк /// -### Hug +### Hug { #hug } Hug був одним із перших фреймворків, який реалізував оголошення типів параметрів API за допомогою підказок типу Python. Це була чудова ідея, яка надихнула інші інструменти зробити те саме. @@ -351,7 +351,7 @@ Hug надихнув частину APIStar і був одним із найбі /// -### APIStar (<= 0,5) +### APIStar (<= 0,5) { #apistar-0-5 } Безпосередньо перед тим, як вирішити створити **FastAPI**, я знайшов сервер **APIStar**. Він мав майже все, що я шукав, і мав чудовий дизайн. @@ -379,9 +379,9 @@ Hug надихнув частину APIStar і був одним із найбі APIStar створив Том Крісті. Той самий хлопець, який створив: - * Django REST Framework - * Starlette (на якому базується **FastAPI**) - * Uvicorn (використовується Starlette і **FastAPI**) +* Django REST Framework +* Starlette (на якому базується **FastAPI**) +* Uvicorn (використовується Starlette і **FastAPI**) /// @@ -393,13 +393,15 @@ APIStar створив Том Крісті. Той самий хлопець, я І після тривалого пошуку подібної структури та тестування багатьох різних альтернатив, APIStar став найкращим доступним варіантом. - Потім APIStar перестав існувати як сервер, і було створено Starlette, який став новою кращою основою для такої системи. Це стало останнім джерелом натхнення для створення **FastAPI**. Я вважаю **FastAPI** «духовним спадкоємцем» APIStar, удосконалюючи та розширюючи функції, систему введення тексту та інші частини на основі досвіду, отриманого від усіх цих попередніх інструментів. + Потім APIStar перестав існувати як сервер, і було створено Starlette, який став новою кращою основою для такої системи. Це стало останнім джерелом натхнення для створення **FastAPI**. + + Я вважаю **FastAPI** «духовним спадкоємцем» APIStar, удосконалюючи та розширюючи функції, систему типізації та інші частини на основі досвіду, отриманого від усіх цих попередніх інструментів. /// -## Використовується **FastAPI** +## Використовується **FastAPI** { #used-by-fastapi } -### Pydantic +### Pydantic { #pydantic } Pydantic — це бібліотека для визначення перевірки даних, серіалізації та документації (за допомогою схеми JSON) на основі підказок типу Python. @@ -415,9 +417,9 @@ Pydantic — це бібліотека для визначення переві /// -### Starlette +### Starlette { #starlette } -Starlette — це легкий фреймворк/набір інструментів ASGI, який ідеально підходить для створення високопродуктивних asyncio сервісів. +Starlette — це легкий фреймворк/набір інструментів ASGI, який ідеально підходить для створення високопродуктивних asyncio сервісів. Він дуже простий та інтуїтивно зрозумілий. Його розроблено таким чином, щоб його можна було легко розширювати та мати модульні компоненти. @@ -460,7 +462,7 @@ ASGI — це новий «стандарт», який розробляєтьс /// -### Uvicorn +### Uvicorn { #uvicorn } Uvicorn — це блискавичний сервер ASGI, побудований на uvloop і httptools. @@ -472,12 +474,12 @@ Uvicorn — це блискавичний сервер ASGI, побудован Основний веб-сервер для запуску програм **FastAPI**. - Ви можете поєднати його з Gunicorn, щоб мати асинхронний багатопроцесний сервер. + Ви також можете використати параметр командного рядка `--workers`, щоб мати асинхронний багатопроцесний сервер. Додаткову інформацію див. у розділі [Розгортання](deployment/index.md){.internal-link target=_blank}. /// -## Орієнтири та швидкість +## Орієнтири та швидкість { #benchmarks-and-speed } Щоб зрозуміти, порівняти та побачити різницю між Uvicorn, Starlette і FastAPI, перегляньте розділ про [Бенчмарки](benchmarks.md){.internal-link target=_blank}. diff --git a/docs/uk/docs/fastapi-cli.md b/docs/uk/docs/fastapi-cli.md index f18b104718..eb55382302 100644 --- a/docs/uk/docs/fastapi-cli.md +++ b/docs/uk/docs/fastapi-cli.md @@ -1,83 +1,75 @@ -# FastAPI CLI +# FastAPI CLI { #fastapi-cli } -**FastAPI CLI** це програма командного рядка, яку Ви можете використовувати, щоб обслуговувати Ваш додаток FastAPI, керувати Вашими FastApi проектами, тощо. +**FastAPI CLI** — це програма командного рядка, яку ви можете використовувати, щоб обслуговувати ваш застосунок FastAPI, керувати вашим проєктом FastAPI тощо. -Коли Ви встановлюєте FastApi (тобто виконуєте `pip install "fastapi[standard]"`), Ви також встановлюєте пакунок `fastapi-cli`, цей пакунок надає команду `fastapi` в терміналі. +Коли ви встановлюєте FastAPI (наприклад, за допомогою `pip install "fastapi[standard]"`), він включає пакет під назвою `fastapi-cli`, цей пакет надає команду `fastapi` у терміналі. -Для запуску Вашого FastAPI проекту для розробки, Ви можете скористатись командою `fastapi dev`: +Щоб запустити ваш застосунок FastAPI для розробки, ви можете використати команду `fastapi dev`:
```console -$ fastapi dev main.py -INFO Using path main.py -INFO Resolved absolute path /home/user/code/awesomeapp/main.py -INFO Searching for package file structure from directories with __init__.py files -INFO Importing from /home/user/code/awesomeapp +$ fastapi dev main.py - ╭─ Python module file ─╮ - │ │ - │ 🐍 main.py │ - │ │ - ╰──────────────────────╯ + FastAPI Starting development server 🚀 -INFO Importing module main -INFO Found importable FastAPI app + Searching for package file structure from directories with + __init__.py files + Importing from /home/user/code/awesomeapp - ╭─ Importable FastAPI app ─╮ - │ │ - │ from main import app │ - │ │ - ╰──────────────────────────╯ + module 🐍 main.py -INFO Using import string main:app + code Importing the FastAPI app object from the module with the + following code: - ╭────────── 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 - │ │ - ╰─────────────────────────────────────────────────────╯ + from main import app -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 [2265862] using WatchFiles -INFO: Started server process [2265873] -INFO: Waiting for application startup. -INFO: Application startup complete. + app Using import string: main:app + + server Server started at http://127.0.0.1:8000 + server Documentation at http://127.0.0.1:8000/docs + + tip Running in development mode, for production use: + fastapi run + + Logs: + + 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 [383138] using WatchFiles + INFO Started server process [383153] + INFO Waiting for application startup. + INFO Application startup complete. ```
-Програма командного рядка `fastapi` це **FastAPI CLI**. +Програма командного рядка під назвою `fastapi` — це **FastAPI CLI**. -FastAPI CLI приймає шлях до Вашої Python програми (напр. `main.py`) і автоматично виявляє екземпляр `FastAPI` (зазвичай названий `app`), обирає коректний процес імпорту, а потім обслуговує його. +FastAPI CLI бере шлях до вашої Python-програми (наприклад, `main.py`) і автоматично виявляє екземпляр `FastAPI` (зазвичай з назвою `app`), визначає правильний процес імпорту, а потім обслуговує його. -Натомість, для запуску у продакшн використовуйте `fastapi run`. 🚀 +Натомість, для продакшн ви використали б `fastapi run`. 🚀 -Всередині **FastAPI CLI** використовує Uvicorn, високопродуктивний, production-ready, ASGI cервер. 😎 +Внутрішньо **FastAPI CLI** використовує Uvicorn, високопродуктивний, production-ready, ASGI сервер. 😎 -## `fastapi dev` +## `fastapi dev` { #fastapi-dev } -Використання `fastapi dev` ініціює режим розробки. +Запуск `fastapi dev` ініціює режим розробки. -За замовчуванням, **автоматичне перезавантаження** увімкнене, автоматично перезавантажуючи сервер кожного разу, коли Ви змінюєте Ваш код. Це ресурсо-затратно, та може бути менш стабільним, ніж коли воно вимкнене. Ви повинні використовувати його тільки під час розробки. Воно також слухає IP-адресу `127.0.0.1`, що є IP Вашого девайсу для самостійної комунікації з самим собою (`localhost`). +За замовчуванням **auto-reload** увімкнено, і сервер автоматично перезавантажується, коли ви вносите зміни у ваш код. Це ресурсоємно та може бути менш стабільним, ніж коли його вимкнено. Вам слід використовувати це лише для розробки. Також він слухає IP-адресу `127.0.0.1`, яка є IP-адресою для того, щоб ваша машина могла взаємодіяти лише сама з собою (`localhost`). -## `fastapi run` +## `fastapi run` { #fastapi-run } -Виконання `fastapi run` запустить FastAPI у продакшн-режимі за замовчуванням. +Виконання `fastapi run` за замовчуванням запускає FastAPI у продакшн-режимі. -За замовчуванням, **автоматичне перезавантаження** вимкнене. Воно також прослуховує IP-адресу `0.0.0.0`, що означає всі доступні IP адреси, тим самим даючи змогу будь-кому комунікувати з девайсом. Так Ви зазвичай будете запускати його у продакшн, наприклад у контейнері. +За замовчуванням **auto-reload** вимкнено. Також він слухає IP-адресу `0.0.0.0`, що означає всі доступні IP-адреси, таким чином він буде публічно доступним для будь-кого, хто може взаємодіяти з машиною. Зазвичай саме так ви запускатимете його в продакшн, наприклад у контейнері. -В більшості випадків Ви можете (і маєте) мати "termination proxy", який обробляє HTTPS для Вас, це залежить від способу розгортання вашого додатку, Ваш провайдер може зробити це для Вас, або Вам потрібно налаштувати його самостійно. +У більшості випадків ви (і вам слід) матимете «termination proxy», який обробляє HTTPS для вас зверху; це залежатиме від того, як ви розгортаєте ваш застосунок: ваш провайдер може зробити це за вас, або вам може знадобитися налаштувати це самостійно. -/// tip +/// tip | Порада -Ви можете дізнатись більше про це у [документації про розгортування](deployment/index.md){.internal-link target=_blank}. +Ви можете дізнатися більше про це в [документації з розгортання](deployment/index.md){.internal-link target=_blank}. /// diff --git a/docs/uk/docs/features.md b/docs/uk/docs/features.md index aa0ef7c79d..d8233115fd 100644 --- a/docs/uk/docs/features.md +++ b/docs/uk/docs/features.md @@ -1,21 +1,21 @@ -# Функціональні можливості +# Функціональні можливості { #features } -## Функціональні можливості FastAPI +## Функціональні можливості FastAPI { #fastapi-features } **FastAPI** надає вам такі можливості: -### Використання відкритих стандартів +### На основі відкритих стандартів { #based-on-open-standards } * OpenAPI для створення API, включаючи оголошення шляхів, операцій, параметрів, тіл запитів, безпеки тощо. * Автоматична документація моделей даних за допомогою JSON Schema (оскільки OpenAPI базується саме на JSON Schema). * Розроблено на основі цих стандартів після ретельного аналізу, а не як додатковий рівень поверх основної архітектури. -* Це також дає змогу автоматично **генерувати код клієнта** багатьма мовами. +* Це також дає змогу використовувати автоматичну **генерацію клієнтського коду** багатьма мовами. -### Автоматична генерація документації +### Автоматична документація { #automatic-docs } -Інтерактивна документація API та вебінтерфейс для його дослідження. Оскільки фреймворк базується на OpenAPI, є кілька варіантів, два з яких включені за замовчуванням. +Інтерактивна документація API та вебінтерфейси для його дослідження. Оскільки фреймворк базується на OpenAPI, є кілька варіантів, 2 з яких включені за замовчуванням. -* Swagger UI — дозволяє інтерактивно переглядати API, викликати та тестувати його прямо у браузері. +* Swagger UI — з інтерактивним дослідженням, викликом і тестуванням вашого API прямо з браузера. ![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) @@ -23,23 +23,25 @@ ![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) -### Тільки сучасний Python +### Лише сучасний Python { #just-modern-python } -FastAPI використовує стандартні **типи Python** (завдяки Pydantic). Вам не потрібно вивчати новий синтаксис — лише стандартний сучасний Python. +Усе базується на стандартних оголошеннях **типів Python** (завдяки Pydantic). Жодного нового синтаксису для вивчення. Лише стандартний сучасний Python. -Якщо вам потрібне коротке нагадування про використання типів у Python (навіть якщо ви не використовуєте FastAPI), перегляньте короткий підручник: [Вступ до типів Python](python-types.md){.internal-link target=_blank}. +Якщо вам потрібно 2-хвилинне нагадування про те, як використовувати типи Python (навіть якщо ви не використовуєте FastAPI), перегляньте короткий підручник: [Типи Python](python-types.md){.internal-link target=_blank}. -Ось приклад стандартного Python-коду з типами: +Ви пишете стандартний Python з типами: ```Python from datetime import date + from pydantic import BaseModel -# Оголошення змінної як str -# з підтримкою автодоповнення у редакторі +# Оголосіть змінну як str +# та отримайте підтримку редактора всередині функції def main(user_id: str): return user_id + # Модель Pydantic class User(BaseModel): id: int @@ -47,7 +49,7 @@ class User(BaseModel): joined: date ``` -Приклад використання цієї моделі: +Далі це можна використовувати так: ```Python my_user: User = User(id=3, name="John Doe", joined="2018-07-19") @@ -65,19 +67,21 @@ my_second_user: User = User(**second_user_data) `**second_user_data` означає: -Передати ключі та значення словника `second_user_data` як аргументи у вигляді "ключ-значення", еквівалентно `User(id=4, name="Mary", joined="2018-11-30")`. +Передати ключі та значення словника `second_user_data` безпосередньо як аргументи у вигляді «ключ-значення», еквівалентно: `User(id=4, name="Mary", joined="2018-11-30")` /// -### Підтримка редакторів (IDE) +### Підтримка редакторів (IDE) { #editor-support } -Фреймворк спроєктований так, щоб бути легким і інтуїтивно зрозумілим. Усі рішення тестувалися у різних редакторах ще до початку розробки, щоб забезпечити найкращий досвід програмування. +Увесь фреймворк спроєктовано так, щоб ним було легко та інтуїтивно користуватися; усі рішення тестувалися у кількох редакторах ще до початку розробки, щоб забезпечити найкращий досвід розробки. -За результатами опитувань розробників Python однією з найпопулярніших функцій є "автодоповнення". +З опитувань розробників Python зрозуміло що однією з найуживаніших функцій є «автодоповнення». -**FastAPI** повністю підтримує автодоповнення у всіх місцях, тому вам рідко доведеться повертатися до документації. +Увесь фреймворк **FastAPI** побудований так, щоб це забезпечити. Автодоповнення працює всюди. -Приклад автодоповнення у редакторах: +Вам рідко доведеться повертатися до документації. + +Ось як ваш редактор може вам допомогти: * у Visual Studio Code: @@ -87,17 +91,25 @@ my_second_user: User = User(**second_user_data) ![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png) -### Короткий код -FastAPI має розумні налаштування **за замовчуванням**, але всі параметри можна налаштовувати відповідно до ваших потреб. Однак за замовчуванням все "просто працює". +Ви отримаєте автодоповнення в коді, який раніше могли вважати навіть неможливим. Наприклад, для ключа `price` всередині JSON body (який міг бути вкладеним), що надходить із запиту. + +Більше не доведеться вводити неправильні назви ключів, постійно повертатися до документації або прокручувати вгору-вниз, щоб знайти, чи ви зрештою використали `username` чи `user_name`. + +### Короткий код { #short } + +FastAPI має розумні **налаштування за замовчуванням** для всього, з можливістю конфігурації всюди. Усі параметри можна точно налаштувати під ваші потреби та визначити потрібний вам API. + +Але за замовчуванням усе **«просто працює»**. + +### Валідація { #validation } -### Валідація * Підтримка валідації для більшості (або всіх?) **типів даних Python**, зокрема: * JSON-об'єктів (`dict`). - * JSON-списків (`list`) з визначенням типів елементів. - * Рядків (`str`) із мінімальною та максимальною довжиною. - * Чисел (`int`, `float`) з обмеженнями мінімальних та максимальних значень тощо. + * JSON-масивів (`list`) із визначенням типів елементів. + * Полів-рядків (`str`) із визначенням мінімальної та максимальної довжини. + * Чисел (`int`, `float`) з мінімальними та максимальними значеннями тощо. -* Валідація складніших типів, таких як: +* Валідація для більш екзотичних типів, як-от: * URL. * Email. * UUID. @@ -105,55 +117,55 @@ FastAPI має розумні налаштування **за замовчува Уся валідація виконується через надійний та перевірений **Pydantic**. -### Безпека та автентифікація +### Безпека та автентифікація { #security-and-authentication } -**FastAPI** підтримує вбудовану автентифікацію та авторизацію, без прив’язки до конкретних баз даних чи моделей даних. +Інтегровані безпека та автентифікація. Без жодних компромісів із базами даних чи моделями даних. -Підтримуються всі схеми безпеки OpenAPI, включаючи: +Підтримуються всі схеми безпеки, визначені в OpenAPI, включно з: * HTTP Basic. -* **OAuth2** (також із підтримкою **JWT-токенів**). Див. підручник: [OAuth2 із JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. +* **OAuth2** (також із підтримкою **JWT tokens**). Перегляньте підручник: [OAuth2 із JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. * Ключі API в: * Заголовках. * Параметрах запиту. * Cookies тощо. -А також усі можливості безпеки від Starlette (зокрема **сесійні cookies**). +А також усі можливості безпеки від Starlette (зокрема **session cookies**). -Усі вони створені як багаторазові інструменти та компоненти, які легко інтегруються з вашими системами, сховищами даних, реляційними та NoSQL базами даних тощо. +Усе це зроблено як багаторазові інструменти та компоненти, які легко інтегруються з вашими системами, сховищами даних, реляційними та NoSQL базами даних тощо. -### Впровадження залежностей +### Впровадження залежностей { #dependency-injection } -**FastAPI** містить надзвичайно просту у використанні, але потужну систему впровадження залежностей. +FastAPI містить надзвичайно просту у використанні, але надзвичайно потужну систему Dependency Injection. -* Залежності можуть мати власні залежності, утворюючи ієрархію або **"граф залежностей"**. -* Усі залежності автоматично керуються фреймворком. -* Усі залежності можуть отримувати дані з запитів і розширювати **обмеження операції за шляхом** та автоматичну документацію. -* **Автоматична валідація** навіть для параметрів *операцій шляху*, визначених у залежностях. -* Підтримка складних систем автентифікації користувачів, **з'єднань із базами даних** тощо. -* **Жодних обмежень** щодо використання баз даних, фронтендів тощо, але водночас проста інтеграція з усіма ними. +* Навіть залежності можуть мати власні залежності, утворюючи ієрархію або **«граф» залежностей**. +* Усе **автоматично обробляється** фреймворком. +* Усі залежності можуть вимагати дані із запитів і **розширювати обмеження операції шляху** та автоматичну документацію. +* **Автоматична валідація** навіть для параметрів *операції шляху*, визначених у залежностях. +* Підтримка складних систем автентифікації користувачів, **підключень до баз даних** тощо. +* **Жодних компромісів** із базами даних, фронтендами тощо. Але проста інтеграція з усіма ними. -### Немає обмежень на "плагіни" +### Необмежені «плагіни» { #unlimited-plug-ins } -Або іншими словами, вони не потрібні – просто імпортуйте та використовуйте необхідний код. +Інакше кажучи, вони не потрібні — імпортуйте та використовуйте код, який вам потрібен. -Будь-яка інтеграція спроєктована настільки просто (з використанням залежностей), що ви можете створити "плагін" для свого застосунку всього у 2 рядках коду, використовуючи ту саму структуру та синтаксис, що й для ваших *операцій шляху*. +Будь-яка інтеграція спроєктована так, щоб її було дуже просто використовувати (із залежностями), тож ви можете створити «плагін» для свого застосунку у 2 рядках коду, використовуючи ту саму структуру та синтаксис, що й для ваших *операцій шляху*. -### Протестовано +### Протестовано { #tested } * 100% покриття тестами. * 100% анотована типами кодова база. -* Використовується у робочих середовищах. +* Використовується в production-застосунках. -## Можливості Starlette +## Можливості Starlette { #starlette-features } **FastAPI** повністю сумісний із (та побудований на основі) Starlette. Тому будь-який додатковий код Starlette, який ви маєте, також працюватиме. -**FastAPI** фактично є підкласом **Starlette**. Тому, якщо ви вже знайомі зі Starlette або використовуєте його, більшість функціональності працюватиме так само. +`FastAPI` фактично є підкласом `Starlette`. Тому, якщо ви вже знайомі зі Starlette або використовуєте його, більшість функціональності працюватиме так само. -З **FastAPI** ви отримуєте всі можливості **Starlette** (адже FastAPI — це, по суті, Starlette на стероїдах): +З **FastAPI** ви отримуєте всі можливості **Starlette** (адже FastAPI — це просто Starlette на стероїдах): -* Разюча продуктивність. Це один із найшвидших фреймворків на Python, на рівні з **NodeJS** і **Go**. +* Разюча продуктивність. Це один із найшвидших доступних Python-фреймворків, на рівні з **NodeJS** і **Go**. * Підтримка **WebSocket**. * Фонові задачі у процесі. * Події запуску та завершення роботи. @@ -163,27 +175,27 @@ FastAPI має розумні налаштування **за замовчува * 100% покриття тестами. * 100% анотована типами кодова база. -## Можливості Pydantic +## Можливості Pydantic { #pydantic-features } **FastAPI** повністю сумісний із (та побудований на основі) Pydantic. Тому будь-який додатковий код Pydantic, який ви маєте, також працюватиме. -Включаючи зовнішні бібліотеки, побудовані також на Pydantic, такі як ORM, ODM для баз даних. +Включно із зовнішніми бібліотеками, які також базуються на Pydantic, як-от ORM-и, ODM-и для баз даних. -Це також означає, що в багатьох випадках ви можете передати той самий об'єкт, який отримуєте з запиту, **безпосередньо в базу даних**, оскільки все автоматично перевіряється. +Це також означає, що в багатьох випадках ви можете передати той самий об'єкт, який отримуєте із запиту, **безпосередньо в базу даних**, оскільки все автоматично перевіряється. -Те ж саме відбувається й у зворотному напрямку — у багатьох випадках ви можете просто передати об'єкт, який отримуєте з бази даних, **безпосередньо клієнту**. +Те саме застосовується й у зворотному напрямку — у багатьох випадках ви можете просто передати об'єкт, який отримуєте з бази даних, **безпосередньо клієнту**. З **FastAPI** ви отримуєте всі можливості **Pydantic** (адже FastAPI базується на Pydantic для обробки всіх даних): -* **Ніякої плутанини** : - * Не потрібно вчити нову мову для визначення схем. +* **Ніякої плутанини**: + * Не потрібно вчити нову мікромову для визначення схем. * Якщо ви знаєте типи Python, ви знаєте, як використовувати Pydantic. -* Легко працює з вашим **IDE/лінтером/мозком**: - * Оскільки структури даних Pydantic є просто екземплярами класів, які ви визначаєте; автодоповнення, лінтинг, mypy і ваша інтуїція повинні добре працювати з вашими перевіреними даними. -* Валідація **складних структур**: - * Використання ієрархічних моделей Pydantic. Python `typing`, `List` і `Dict` тощо. - * Валідатори дозволяють чітко і просто визначати, перевіряти й документувати складні схеми даних у вигляді JSON-схеми. - * Ви можете мати глибоко **вкладені JSON об'єкти** та перевірити та анотувати їх всі. +* Легко працює з вашим **IDE/linter/мозком**: + * Оскільки структури даних pydantic є просто екземплярами класів, які ви визначаєте; автодоповнення, лінтинг, mypy і ваша інтуїція повинні добре працювати з вашими перевіреними даними. +* Валідує **складні структури**: + * Використання ієрархічних моделей Pydantic, Python `typing`’s `List` і `Dict` тощо. + * Валідатори дають змогу складні схеми даних чітко й просто визначати, перевіряти й документувати як JSON Schema. + * Ви можете мати глибоко **вкладені JSON** об'єкти, і всі вони будуть валідовані та анотовані. * **Розширюваність**: - * Pydantic дозволяє визначати користувацькі типи даних або розширювати валідацію методами в моделі декоратором `validator`. + * Pydantic дозволяє визначати користувацькі типи даних або ви можете розширити валідацію методами в моделі, позначеними декоратором validator. * 100% покриття тестами. diff --git a/docs/uk/llm-prompt.md b/docs/uk/llm-prompt.md index f1c5377a48..e8cd3dabc0 100644 --- a/docs/uk/llm-prompt.md +++ b/docs/uk/llm-prompt.md @@ -8,6 +8,7 @@ Language code: uk. - Use polite/formal address consistent with existing Ukrainian docs (use “ви/ваш”). - Keep the tone concise and technical. +- Use one style of dashes. For example, if text contains "-" then use only this symbol to represent a dash. ### Headings @@ -32,6 +33,71 @@ Use the following preferred translations when they apply in documentation prose: - response (HTTP): відповідь - path operation: операція шляху - path operation function: функція операції шляху +- prompt: підсказка +- check: перевірка +- Parallel Server Gateway Interface: Інтерфейс Шлюзу Паралельного Сервера +- Mozilla Developer Network: Мережа Розробників Mozilla +- tutorial: навчальний посібник +- advanced user guide: просунутий посібник користувача +- deep learning: глибоке навчання +- machine learning: машинне навчання +- dependency injection: впровадження залежностей +- digest (HTTP): дайджест +- basic authentication (HTTP): базова автентифікація +- JSON schema: Схема JSON +- password flow: потік паролю +- mobile: мобільний +- body: тіло +- form: форма +- path: шлях +- query: запит +- cookie: кукі +- header: заголовок +- startup: запуск +- shutdown: вимкнення +- lifespan: тривалість життя +- authorization: авторизація +- forwarded header: направлений заголовок +- dependable: залежний +- dependent: залежний +- bound: межа +- concurrency: рівночасність +- parallelism: паралелізм +- multiprocessing: багатопроцесорність +- env var: змінна оточення +- dict: словник +- enum: перелік +- issue: проблема +- server worker: серверний працівник +- worker: працівник +- software development kit: набір для розробки програмного забезпечення +- bearer token: токен носія +- breaking change: несумісна зміна +- bug: помилка +- button: кнопка +- callable: викликаємий +- code: код +- commit: фіксація +- context manager: менеджер контексту +- coroutine: співпрограма +- engine: рушій +- fake X: фальшивий X +- item: предмет +- lock: блокування +- middleware: проміжне програмне забезпечення +- mounting: монтування +- origin: джерело +- override: переписування +- payload: корисне навантаження +- processor: процесор +- property: властивість +- proxy: представник +- pull request: запит на витяг +- random-access memory: пам'ять з довільним доступом +- status code: код статусу +- string: строка +- tag: мітка +- wildcard: дика карта ### `///` admonitions @@ -44,3 +110,4 @@ Use the following preferred translations when they apply in documentation prose: - `/// warning | Попередження` - `/// info | Інформація` - `/// danger | Обережно` +- `/// check | Перевірте` diff --git a/docs_src/app_testing/app_b_an_py310/main.py b/docs_src/app_testing/app_b_an_py310/main.py index c5952be0b3..1b77dd1379 100644 --- a/docs_src/app_testing/app_b_an_py310/main.py +++ b/docs_src/app_testing/app_b_an_py310/main.py @@ -28,11 +28,11 @@ async def read_main(item_id: str, x_token: Annotated[str, Header()]): return fake_db[item_id] -@app.post("/items/", response_model=Item) -async def create_item(item: Item, x_token: Annotated[str, Header()]): +@app.post("/items/") +async def create_item(item: Item, x_token: Annotated[str, Header()]) -> Item: if x_token != fake_secret_token: raise HTTPException(status_code=400, detail="Invalid X-Token header") if item.id in fake_db: raise HTTPException(status_code=409, detail="Item already exists") - fake_db[item.id] = item + fake_db[item.id] = item.model_dump() return item diff --git a/docs_src/app_testing/app_b_an_py39/main.py b/docs_src/app_testing/app_b_an_py39/main.py index 142e23a26a..42026a81a0 100644 --- a/docs_src/app_testing/app_b_an_py39/main.py +++ b/docs_src/app_testing/app_b_an_py39/main.py @@ -28,11 +28,11 @@ async def read_main(item_id: str, x_token: Annotated[str, Header()]): return fake_db[item_id] -@app.post("/items/", response_model=Item) -async def create_item(item: Item, x_token: Annotated[str, Header()]): +@app.post("/items/") +async def create_item(item: Item, x_token: Annotated[str, Header()]) -> Item: if x_token != fake_secret_token: raise HTTPException(status_code=400, detail="Invalid X-Token header") if item.id in fake_db: raise HTTPException(status_code=409, detail="Item already exists") - fake_db[item.id] = item + fake_db[item.id] = item.model_dump() return item diff --git a/docs_src/app_testing/app_b_py310/main.py b/docs_src/app_testing/app_b_py310/main.py index eccedcc7ce..83f6fa142a 100644 --- a/docs_src/app_testing/app_b_py310/main.py +++ b/docs_src/app_testing/app_b_py310/main.py @@ -26,11 +26,11 @@ async def read_main(item_id: str, x_token: str = Header()): return fake_db[item_id] -@app.post("/items/", response_model=Item) -async def create_item(item: Item, x_token: str = Header()): +@app.post("/items/") +async def create_item(item: Item, x_token: str = Header()) -> Item: if x_token != fake_secret_token: raise HTTPException(status_code=400, detail="Invalid X-Token header") if item.id in fake_db: raise HTTPException(status_code=409, detail="Item already exists") - fake_db[item.id] = item + fake_db[item.id] = item.model_dump() return item diff --git a/docs_src/app_testing/app_b_py39/main.py b/docs_src/app_testing/app_b_py39/main.py index 45a1033783..ed38f4721c 100644 --- a/docs_src/app_testing/app_b_py39/main.py +++ b/docs_src/app_testing/app_b_py39/main.py @@ -28,11 +28,11 @@ async def read_main(item_id: str, x_token: str = Header()): return fake_db[item_id] -@app.post("/items/", response_model=Item) -async def create_item(item: Item, x_token: str = Header()): +@app.post("/items/") +async def create_item(item: Item, x_token: str = Header()) -> Item: if x_token != fake_secret_token: raise HTTPException(status_code=400, detail="Invalid X-Token header") if item.id in fake_db: raise HTTPException(status_code=409, detail="Item already exists") - fake_db[item.id] = item + fake_db[item.id] = item.model_dump() return item diff --git a/docs_src/body_updates/tutorial002_py310.py b/docs_src/body_updates/tutorial002_py310.py index e5db711108..d30e41027c 100644 --- a/docs_src/body_updates/tutorial002_py310.py +++ b/docs_src/body_updates/tutorial002_py310.py @@ -25,8 +25,8 @@ async def read_item(item_id: str): return items[item_id] -@app.patch("/items/{item_id}", response_model=Item) -async def update_item(item_id: str, item: Item): +@app.patch("/items/{item_id}") +async def update_item(item_id: str, item: Item) -> Item: stored_item_data = items[item_id] stored_item_model = Item(**stored_item_data) update_data = item.model_dump(exclude_unset=True) diff --git a/docs_src/body_updates/tutorial002_py39.py b/docs_src/body_updates/tutorial002_py39.py index eddd7af716..3714b5a55d 100644 --- a/docs_src/body_updates/tutorial002_py39.py +++ b/docs_src/body_updates/tutorial002_py39.py @@ -27,8 +27,8 @@ async def read_item(item_id: str): return items[item_id] -@app.patch("/items/{item_id}", response_model=Item) -async def update_item(item_id: str, item: Item): +@app.patch("/items/{item_id}") +async def update_item(item_id: str, item: Item) -> Item: stored_item_data = items[item_id] stored_item_model = Item(**stored_item_data) update_data = item.model_dump(exclude_unset=True) diff --git a/docs_src/metadata/tutorial001_1_py39.py b/docs_src/metadata/tutorial001_1_py39.py index a8f5b94588..419232d861 100644 --- a/docs_src/metadata/tutorial001_1_py39.py +++ b/docs_src/metadata/tutorial001_1_py39.py @@ -28,7 +28,7 @@ app = FastAPI( }, license_info={ "name": "Apache 2.0", - "identifier": "MIT", + "identifier": "Apache-2.0", }, ) diff --git a/docs_src/path_operation_advanced_configuration/tutorial004_py310.py b/docs_src/path_operation_advanced_configuration/tutorial004_py310.py index a815a564b7..f222b11dc6 100644 --- a/docs_src/path_operation_advanced_configuration/tutorial004_py310.py +++ b/docs_src/path_operation_advanced_configuration/tutorial004_py310.py @@ -12,8 +12,8 @@ class Item(BaseModel): tags: set[str] = set() -@app.post("/items/", response_model=Item, summary="Create an item") -async def create_item(item: Item): +@app.post("/items/", summary="Create an item") +async def create_item(item: Item) -> Item: """ Create an item with all the information: diff --git a/docs_src/path_operation_advanced_configuration/tutorial004_py39.py b/docs_src/path_operation_advanced_configuration/tutorial004_py39.py index d5fe6705ca..8fabe7cb80 100644 --- a/docs_src/path_operation_advanced_configuration/tutorial004_py39.py +++ b/docs_src/path_operation_advanced_configuration/tutorial004_py39.py @@ -14,8 +14,8 @@ class Item(BaseModel): tags: set[str] = set() -@app.post("/items/", response_model=Item, summary="Create an item") -async def create_item(item: Item): +@app.post("/items/", summary="Create an item") +async def create_item(item: Item) -> Item: """ Create an item with all the information: diff --git a/docs_src/path_operation_configuration/tutorial001_py310.py b/docs_src/path_operation_configuration/tutorial001_py310.py index da078fdf58..2e7488ea4f 100644 --- a/docs_src/path_operation_configuration/tutorial001_py310.py +++ b/docs_src/path_operation_configuration/tutorial001_py310.py @@ -12,6 +12,6 @@ class Item(BaseModel): tags: set[str] = set() -@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED) -async def create_item(item: Item): +@app.post("/items/", status_code=status.HTTP_201_CREATED) +async def create_item(item: Item) -> Item: return item diff --git a/docs_src/path_operation_configuration/tutorial001_py39.py b/docs_src/path_operation_configuration/tutorial001_py39.py index a9dcbf3898..09b3182821 100644 --- a/docs_src/path_operation_configuration/tutorial001_py39.py +++ b/docs_src/path_operation_configuration/tutorial001_py39.py @@ -14,6 +14,6 @@ class Item(BaseModel): tags: set[str] = set() -@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED) -async def create_item(item: Item): +@app.post("/items/", status_code=status.HTTP_201_CREATED) +async def create_item(item: Item) -> Item: return item diff --git a/docs_src/path_operation_configuration/tutorial002_py310.py b/docs_src/path_operation_configuration/tutorial002_py310.py index 9a8af54327..59908ed7c8 100644 --- a/docs_src/path_operation_configuration/tutorial002_py310.py +++ b/docs_src/path_operation_configuration/tutorial002_py310.py @@ -12,8 +12,8 @@ class Item(BaseModel): tags: set[str] = set() -@app.post("/items/", response_model=Item, tags=["items"]) -async def create_item(item: Item): +@app.post("/items/", tags=["items"]) +async def create_item(item: Item) -> Item: return item diff --git a/docs_src/path_operation_configuration/tutorial002_py39.py b/docs_src/path_operation_configuration/tutorial002_py39.py index e7ced7de7e..fca3b0de9e 100644 --- a/docs_src/path_operation_configuration/tutorial002_py39.py +++ b/docs_src/path_operation_configuration/tutorial002_py39.py @@ -14,8 +14,8 @@ class Item(BaseModel): tags: set[str] = set() -@app.post("/items/", response_model=Item, tags=["items"]) -async def create_item(item: Item): +@app.post("/items/", tags=["items"]) +async def create_item(item: Item) -> Item: return item diff --git a/docs_src/path_operation_configuration/tutorial003_py310.py b/docs_src/path_operation_configuration/tutorial003_py310.py index 3d94afe2c0..56bd7e36aa 100644 --- a/docs_src/path_operation_configuration/tutorial003_py310.py +++ b/docs_src/path_operation_configuration/tutorial003_py310.py @@ -14,9 +14,8 @@ class Item(BaseModel): @app.post( "/items/", - response_model=Item, summary="Create an item", description="Create an item with all the information, name, description, price, tax and a set of unique tags", ) -async def create_item(item: Item): +async def create_item(item: Item) -> Item: return item diff --git a/docs_src/path_operation_configuration/tutorial003_py39.py b/docs_src/path_operation_configuration/tutorial003_py39.py index 607c5707e6..a77fb34d89 100644 --- a/docs_src/path_operation_configuration/tutorial003_py39.py +++ b/docs_src/path_operation_configuration/tutorial003_py39.py @@ -16,9 +16,8 @@ class Item(BaseModel): @app.post( "/items/", - response_model=Item, summary="Create an item", description="Create an item with all the information, name, description, price, tax and a set of unique tags", ) -async def create_item(item: Item): +async def create_item(item: Item) -> Item: return item diff --git a/docs_src/path_operation_configuration/tutorial004_py310.py b/docs_src/path_operation_configuration/tutorial004_py310.py index 4cb8bdd438..44404aa083 100644 --- a/docs_src/path_operation_configuration/tutorial004_py310.py +++ b/docs_src/path_operation_configuration/tutorial004_py310.py @@ -12,8 +12,8 @@ class Item(BaseModel): tags: set[str] = set() -@app.post("/items/", response_model=Item, summary="Create an item") -async def create_item(item: Item): +@app.post("/items/", summary="Create an item") +async def create_item(item: Item) -> Item: """ Create an item with all the information: diff --git a/docs_src/path_operation_configuration/tutorial004_py39.py b/docs_src/path_operation_configuration/tutorial004_py39.py index fc25680c5a..31dfbff1d2 100644 --- a/docs_src/path_operation_configuration/tutorial004_py39.py +++ b/docs_src/path_operation_configuration/tutorial004_py39.py @@ -14,8 +14,8 @@ class Item(BaseModel): tags: set[str] = set() -@app.post("/items/", response_model=Item, summary="Create an item") -async def create_item(item: Item): +@app.post("/items/", summary="Create an item") +async def create_item(item: Item) -> Item: """ Create an item with all the information: diff --git a/docs_src/path_operation_configuration/tutorial005_py310.py b/docs_src/path_operation_configuration/tutorial005_py310.py index b176631d84..a4129d600a 100644 --- a/docs_src/path_operation_configuration/tutorial005_py310.py +++ b/docs_src/path_operation_configuration/tutorial005_py310.py @@ -14,11 +14,10 @@ class Item(BaseModel): @app.post( "/items/", - response_model=Item, summary="Create an item", response_description="The created item", ) -async def create_item(item: Item): +async def create_item(item: Item) -> Item: """ Create an item with all the information: diff --git a/docs_src/path_operation_configuration/tutorial005_py39.py b/docs_src/path_operation_configuration/tutorial005_py39.py index ddf29b733d..0a53a8f2dd 100644 --- a/docs_src/path_operation_configuration/tutorial005_py39.py +++ b/docs_src/path_operation_configuration/tutorial005_py39.py @@ -16,11 +16,10 @@ class Item(BaseModel): @app.post( "/items/", - response_model=Item, summary="Create an item", response_description="The created item", ) -async def create_item(item: Item): +async def create_item(item: Item) -> Item: """ Create an item with all the information: diff --git a/docs_src/security/tutorial004_an_py310.py b/docs_src/security/tutorial004_an_py310.py index 18ea96bc5c..368c743bf9 100644 --- a/docs_src/security/tutorial004_an_py310.py +++ b/docs_src/security/tutorial004_an_py310.py @@ -133,10 +133,10 @@ async def login_for_access_token( return Token(access_token=access_token, token_type="bearer") -@app.get("/users/me/", response_model=User) +@app.get("/users/me/") async def read_users_me( current_user: Annotated[User, Depends(get_current_active_user)], -): +) -> User: return current_user diff --git a/docs_src/security/tutorial004_an_py39.py b/docs_src/security/tutorial004_an_py39.py index d3fd29e5a5..73b3d456d1 100644 --- a/docs_src/security/tutorial004_an_py39.py +++ b/docs_src/security/tutorial004_an_py39.py @@ -133,10 +133,10 @@ async def login_for_access_token( return Token(access_token=access_token, token_type="bearer") -@app.get("/users/me/", response_model=User) +@app.get("/users/me/") async def read_users_me( current_user: Annotated[User, Depends(get_current_active_user)], -): +) -> User: return current_user diff --git a/docs_src/security/tutorial004_py310.py b/docs_src/security/tutorial004_py310.py index cd1dcff460..8d0785b404 100644 --- a/docs_src/security/tutorial004_py310.py +++ b/docs_src/security/tutorial004_py310.py @@ -130,8 +130,8 @@ async def login_for_access_token( return Token(access_token=access_token, token_type="bearer") -@app.get("/users/me/", response_model=User) -async def read_users_me(current_user: User = Depends(get_current_active_user)): +@app.get("/users/me/") +async def read_users_me(current_user: User = Depends(get_current_active_user)) -> User: return current_user diff --git a/docs_src/security/tutorial004_py39.py b/docs_src/security/tutorial004_py39.py index 130dc699a0..e67403d5d7 100644 --- a/docs_src/security/tutorial004_py39.py +++ b/docs_src/security/tutorial004_py39.py @@ -131,8 +131,8 @@ async def login_for_access_token( return Token(access_token=access_token, token_type="bearer") -@app.get("/users/me/", response_model=User) -async def read_users_me(current_user: User = Depends(get_current_active_user)): +@app.get("/users/me/") +async def read_users_me(current_user: User = Depends(get_current_active_user)) -> User: return current_user diff --git a/docs_src/security/tutorial005_an_py310.py b/docs_src/security/tutorial005_an_py310.py index df55951c07..fef0ab71ca 100644 --- a/docs_src/security/tutorial005_an_py310.py +++ b/docs_src/security/tutorial005_an_py310.py @@ -160,10 +160,10 @@ async def login_for_access_token( return Token(access_token=access_token, token_type="bearer") -@app.get("/users/me/", response_model=User) +@app.get("/users/me/") async def read_users_me( current_user: Annotated[User, Depends(get_current_active_user)], -): +) -> User: return current_user diff --git a/docs_src/security/tutorial005_an_py39.py b/docs_src/security/tutorial005_an_py39.py index 983c1c22cf..1aeba688a6 100644 --- a/docs_src/security/tutorial005_an_py39.py +++ b/docs_src/security/tutorial005_an_py39.py @@ -160,10 +160,10 @@ async def login_for_access_token( return Token(access_token=access_token, token_type="bearer") -@app.get("/users/me/", response_model=User) +@app.get("/users/me/") async def read_users_me( current_user: Annotated[User, Depends(get_current_active_user)], -): +) -> User: return current_user diff --git a/docs_src/security/tutorial005_py310.py b/docs_src/security/tutorial005_py310.py index d08e2c59f3..412fbf7984 100644 --- a/docs_src/security/tutorial005_py310.py +++ b/docs_src/security/tutorial005_py310.py @@ -159,8 +159,8 @@ async def login_for_access_token( return Token(access_token=access_token, token_type="bearer") -@app.get("/users/me/", response_model=User) -async def read_users_me(current_user: User = Depends(get_current_active_user)): +@app.get("/users/me/") +async def read_users_me(current_user: User = Depends(get_current_active_user)) -> User: return current_user diff --git a/docs_src/security/tutorial005_py39.py b/docs_src/security/tutorial005_py39.py index 5bde47ef47..32280aa48b 100644 --- a/docs_src/security/tutorial005_py39.py +++ b/docs_src/security/tutorial005_py39.py @@ -160,8 +160,8 @@ async def login_for_access_token( return Token(access_token=access_token, token_type="bearer") -@app.get("/users/me/", response_model=User) -async def read_users_me(current_user: User = Depends(get_current_active_user)): +@app.get("/users/me/") +async def read_users_me(current_user: User = Depends(get_current_active_user)) -> User: return current_user diff --git a/docs_src/wsgi/tutorial001_py39.py b/docs_src/wsgi/tutorial001_py39.py index 7f27a85a19..8eeceb829e 100644 --- a/docs_src/wsgi/tutorial001_py39.py +++ b/docs_src/wsgi/tutorial001_py39.py @@ -1,5 +1,5 @@ +from a2wsgi import WSGIMiddleware from fastapi import FastAPI -from fastapi.middleware.wsgi import WSGIMiddleware from flask import Flask, request from markupsafe import escape diff --git a/fastapi/_compat/shared.py b/fastapi/_compat/shared.py index 419b58f7f2..68b9bbdf1e 100644 --- a/fastapi/_compat/shared.py +++ b/fastapi/_compat/shared.py @@ -17,7 +17,7 @@ from pydantic.version import VERSION as PYDANTIC_VERSION from starlette.datastructures import UploadFile from typing_extensions import get_args, get_origin -# Copy from Pydantic v2, compatible with v1 +# Copy from Pydantic: pydantic/_internal/_typing_extra.py if sys.version_info < (3, 10): WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias) # type: ignore[attr-defined] else: @@ -45,7 +45,7 @@ sequence_types = tuple(sequence_annotation_to_type.keys()) Url: type[Any] -# Copy of Pydantic v2, compatible with v1 +# Copy of Pydantic: pydantic/_internal/_utils.py def lenient_issubclass( cls: Any, class_or_tuple: Union[type[Any], tuple[type[Any], ...], None] ) -> bool: diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py index 25b6814536..dae78a32e0 100644 --- a/fastapi/_compat/v2.py +++ b/fastapi/_compat/v2.py @@ -477,7 +477,7 @@ def get_model_fields(model: type[BaseModel]) -> list[ModelField]: @lru_cache def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]: - return get_model_fields(model) # type: ignore[return-value] + return get_model_fields(model) # Duplicate of several schema functions from Pydantic v1 to make them compatible with @@ -500,13 +500,13 @@ def get_model_name_map(unique_models: TypeModelSet) -> dict[TypeModelOrEnum, str def get_compat_model_name_map(fields: list[ModelField]) -> ModelNameMap: - all_flat_models = set() + all_flat_models: TypeModelSet = set() v2_model_fields = [field for field in fields if isinstance(field, ModelField)] v2_flat_models = get_flat_models_from_fields(v2_model_fields, known_models=set()) - all_flat_models = all_flat_models.union(v2_flat_models) # type: ignore[arg-type] + all_flat_models = all_flat_models.union(v2_flat_models) - model_name_map = get_model_name_map(all_flat_models) # type: ignore[arg-type] + model_name_map = get_model_name_map(all_flat_models) return model_name_map diff --git a/fastapi/applications.py b/fastapi/applications.py index 54175cb3b0..340cabfc29 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -672,7 +672,7 @@ class FastAPI(Starlette): in the autogenerated OpenAPI using the `root_path`. Read more about it in the - [FastAPI docs for Behind a Proxy](https://fastapi.tiangolo.com/advanced/behind-a-proxy/#disable-automatic-server-from-root_path). + [FastAPI docs for Behind a Proxy](https://fastapi.tiangolo.com/advanced/behind-a-proxy/#disable-automatic-server-from-root-path). **Example** @@ -739,7 +739,7 @@ class FastAPI(Starlette): It will be added to the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). + [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#deprecate-a-path-operation). """ ), ] = None, @@ -812,6 +812,9 @@ class FastAPI(Starlette): In this case, there would be two different schemas, one for input and another one for output. + + Read more about it in the + [FastAPI docs about how to separate schemas for input and output](https://fastapi.tiangolo.com/how-to/separate-openapi-schemas) """ ), ] = True, diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 45e1ff3ed1..fc5dfed85a 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -204,7 +204,12 @@ def _get_signature(call: Callable[..., Any]) -> inspect.Signature: except NameError: # Handle type annotations with if TYPE_CHECKING, not used by FastAPI # e.g. dependency return types - signature = inspect.signature(call) + if sys.version_info >= (3, 14): + from annotationlib import Format + + signature = inspect.signature(call, annotation_format=Format.FORWARDREF) + else: + signature = inspect.signature(call) else: signature = inspect.signature(call) return signature @@ -399,7 +404,7 @@ def analyze_param( if isinstance(fastapi_annotation, FieldInfo): # Copy `field_info` because we mutate `field_info.default` below. field_info = copy_field_info( - field_info=fastapi_annotation, # type: ignore[arg-type] + field_info=fastapi_annotation, annotation=use_annotation, ) assert ( @@ -433,7 +438,7 @@ def analyze_param( "Cannot specify FastAPI annotations in `Annotated` and default value" f" together for {param_name!r}" ) - field_info = value # type: ignore[assignment] + field_info = value if isinstance(field_info, FieldInfo): field_info.annotation = type_annotation @@ -519,7 +524,7 @@ def analyze_param( # For Pydantic v1 and getattr(field, "shape", 1) == 1 ) - ) + ), f"Query parameter {param_name!r} must be one of the supported types" return ParamDetails(type_annotation=type_annotation, depends=depends, field=field) diff --git a/fastapi/encoders.py b/fastapi/encoders.py index e8610c983b..b4661be4fa 100644 --- a/fastapi/encoders.py +++ b/fastapi/encoders.py @@ -219,9 +219,9 @@ def jsonable_encoder( if isinstance(obj, encoder_type): return encoder_instance(obj) if include is not None and not isinstance(include, (set, dict)): - include = set(include) + include = set(include) # type: ignore[assignment] if exclude is not None and not isinstance(exclude, (set, dict)): - exclude = set(exclude) + exclude = set(exclude) # type: ignore[assignment] if isinstance(obj, BaseModel): obj_dict = obj.model_dump( mode="json", diff --git a/fastapi/exceptions.py b/fastapi/exceptions.py index 1a3abd80c2..62b4674de3 100644 --- a/fastapi/exceptions.py +++ b/fastapi/exceptions.py @@ -49,6 +49,9 @@ class HTTPException(StarletteHTTPException): Doc( """ HTTP status code to send to the client. + + Read more about it in the + [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/#use-httpexception) """ ), ], @@ -58,6 +61,9 @@ class HTTPException(StarletteHTTPException): """ Any data to be sent to the client in the `detail` key of the JSON response. + + Read more about it in the + [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/#use-httpexception) """ ), ] = None, @@ -66,6 +72,10 @@ class HTTPException(StarletteHTTPException): Doc( """ Any headers to send to the client in the response. + + Read more about it in the + [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/#add-custom-headers) + """ ), ] = None, diff --git a/fastapi/middleware/wsgi.py b/fastapi/middleware/wsgi.py index c4c6a797d2..69e4dcab96 100644 --- a/fastapi/middleware/wsgi.py +++ b/fastapi/middleware/wsgi.py @@ -1 +1,3 @@ -from starlette.middleware.wsgi import WSGIMiddleware as WSGIMiddleware # noqa +from starlette.middleware.wsgi import ( + WSGIMiddleware as WSGIMiddleware, +) # pragma: no cover # noqa diff --git a/fastapi/openapi/docs.py b/fastapi/openapi/docs.py index 82380f85d9..bb387c609a 100644 --- a/fastapi/openapi/docs.py +++ b/fastapi/openapi/docs.py @@ -33,6 +33,9 @@ def get_swagger_ui_html( This is normally done automatically by FastAPI using the default URL `/openapi.json`. + + Read more about it in the + [FastAPI docs for Conditional OpenAPI](https://fastapi.tiangolo.com/how-to/conditional-openapi/#conditional-openapi-from-settings-and-env-vars) """ ), ], @@ -41,6 +44,9 @@ def get_swagger_ui_html( Doc( """ The HTML `` content, normally shown in the browser tab. + + Read more about it in the + [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) """ ), ], @@ -51,6 +57,9 @@ def get_swagger_ui_html( The URL to use to load the Swagger UI JavaScript. It is normally set to a CDN URL. + + Read more about it in the + [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) """ ), ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js", @@ -61,6 +70,9 @@ def get_swagger_ui_html( The URL to use to load the Swagger UI CSS. It is normally set to a CDN URL. + + Read more about it in the + [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) """ ), ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css", @@ -77,6 +89,9 @@ def get_swagger_ui_html( Doc( """ The OAuth2 redirect URL, it is normally automatically handled by FastAPI. + + Read more about it in the + [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) """ ), ] = None, @@ -85,6 +100,9 @@ def get_swagger_ui_html( Doc( """ A dictionary with Swagger UI OAuth2 initialization configurations. + + Read more about the available configuration options in the + [Swagger UI docs](https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/). """ ), ] = None, @@ -95,6 +113,9 @@ def get_swagger_ui_html( Configuration parameters for Swagger UI. It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters]. + + Read more about it in the + [FastAPI docs about how to Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/). """ ), ] = None, @@ -118,6 +139,7 @@ def get_swagger_ui_html( <!DOCTYPE html> <html> <head> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link type="text/css" rel="stylesheet" href="{swagger_css_url}"> <link rel="shortcut icon" href="{swagger_favicon_url}"> <title>{title} @@ -168,6 +190,9 @@ def get_redoc_html( This is normally done automatically by FastAPI using the default URL `/openapi.json`. + + Read more about it in the + [FastAPI docs for Conditional OpenAPI](https://fastapi.tiangolo.com/how-to/conditional-openapi/#conditional-openapi-from-settings-and-env-vars) """ ), ], @@ -176,6 +201,9 @@ def get_redoc_html( Doc( """ The HTML `` content, normally shown in the browser tab. + + Read more about it in the + [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) """ ), ], @@ -186,6 +214,9 @@ def get_redoc_html( The URL to use to load the ReDoc JavaScript. It is normally set to a CDN URL. + + Read more about it in the + [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/) """ ), ] = "https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js", diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 75ff261025..5736af3b78 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -1,3 +1,4 @@ +import copy import http.client import inspect import warnings @@ -49,6 +50,8 @@ validation_error_definition = { }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, "required": ["loc", "msg", "type"], } @@ -377,7 +380,7 @@ def get_openapi_path( additional_status_code, additional_response, ) in route.responses.items(): - process_response = additional_response.copy() + process_response = copy.deepcopy(additional_response) process_response.pop("model", None) status_code_key = str(additional_status_code).upper() if status_code_key == "DEFAULT": diff --git a/fastapi/param_functions.py b/fastapi/param_functions.py index 0834fd741a..9bd92be4c7 100644 --- a/fastapi/param_functions.py +++ b/fastapi/param_functions.py @@ -79,6 +79,9 @@ def Path( # noqa: N802 Doc( """ Human-readable title. + + Read more about it in the + [FastAPI docs for Path Parameters and Numeric Validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#declare-metadata) """ ), ] = None, @@ -96,6 +99,9 @@ def Path( # noqa: N802 """ Greater than. If set, value must be greater than this. Only applicable to numbers. + + Read more about it in the + [FastAPI docs about Path parameters numeric validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#number-validations-greater-than-and-less-than-or-equal) """ ), ] = None, @@ -105,6 +111,9 @@ def Path( # noqa: N802 """ Greater than or equal. If set, value must be greater than or equal to this. Only applicable to numbers. + + Read more about it in the + [FastAPI docs about Path parameters numeric validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#number-validations-greater-than-and-less-than-or-equal) """ ), ] = None, @@ -113,6 +122,9 @@ def Path( # noqa: N802 Doc( """ Less than. If set, value must be less than this. Only applicable to numbers. + + Read more about it in the + [FastAPI docs about Path parameters numeric validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#number-validations-greater-than-and-less-than-or-equal) """ ), ] = None, @@ -122,6 +134,9 @@ def Path( # noqa: N802 """ Less than or equal. If set, value must be less than or equal to this. Only applicable to numbers. + + Read more about it in the + [FastAPI docs about Path parameters numeric validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#number-validations-greater-than-and-less-than-or-equal) """ ), ] = None, @@ -213,6 +228,9 @@ def Path( # noqa: N802 Doc( """ Example values for this field. + + Read more about it in the + [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/) """ ), ] = None, @@ -343,6 +361,9 @@ def Query( # noqa: N802 Doc( """ Default value if the parameter field is not set. + + Read more about it in the + [FastAPI docs about Query parameters](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#alternative-old-query-as-the-default-value) """ ), ] = Undefined, @@ -367,6 +388,9 @@ def Query( # noqa: N802 This will be used to extract the data and for the generated OpenAPI. It is particularly useful when you can't use the name you want because it is a Python reserved keyword or similar. + + Read more about it in the + [FastAPI docs about Query parameters](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#alias-parameters) """ ), ] = None, @@ -402,6 +426,9 @@ def Query( # noqa: N802 Doc( """ Human-readable title. + + Read more about it in the + [FastAPI docs about Query parameters](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#declare-more-metadata) """ ), ] = None, @@ -410,6 +437,9 @@ def Query( # noqa: N802 Doc( """ Human-readable description. + + Read more about it in the + [FastAPI docs about Query parameters](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#declare-more-metadata) """ ), ] = None, @@ -419,6 +449,9 @@ def Query( # noqa: N802 """ Greater than. If set, value must be greater than this. Only applicable to numbers. + + Read more about it in the + [FastAPI docs about Path parameters numeric validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#number-validations-greater-than-and-less-than-or-equal) """ ), ] = None, @@ -428,6 +461,9 @@ def Query( # noqa: N802 """ Greater than or equal. If set, value must be greater than or equal to this. Only applicable to numbers. + + Read more about it in the + [FastAPI docs about Path parameters numeric validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#number-validations-greater-than-and-less-than-or-equal) """ ), ] = None, @@ -436,6 +472,9 @@ def Query( # noqa: N802 Doc( """ Less than. If set, value must be less than this. Only applicable to numbers. + + Read more about it in the + [FastAPI docs about Path parameters numeric validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#number-validations-greater-than-and-less-than-or-equal) """ ), ] = None, @@ -445,6 +484,9 @@ def Query( # noqa: N802 """ Less than or equal. If set, value must be less than or equal to this. Only applicable to numbers. + + Read more about it in the + [FastAPI docs about Path parameters numeric validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#number-validations-greater-than-and-less-than-or-equal) """ ), ] = None, @@ -453,6 +495,9 @@ def Query( # noqa: N802 Doc( """ Minimum length for strings. + + Read more about it in the + [FastAPI docs about Query parameters](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/) """ ), ] = None, @@ -461,6 +506,9 @@ def Query( # noqa: N802 Doc( """ Maximum length for strings. + + Read more about it in the + [FastAPI docs about Query parameters](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/) """ ), ] = None, @@ -469,6 +517,9 @@ def Query( # noqa: N802 Doc( """ RegEx pattern for strings. + + Read more about it in the + [FastAPI docs about Query parameters](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#add-regular-expressions """ ), ] = None, @@ -536,6 +587,9 @@ def Query( # noqa: N802 Doc( """ Example values for this field. + + Read more about it in the + [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/) """ ), ] = None, @@ -570,6 +624,9 @@ def Query( # noqa: N802 Mark this parameter field as deprecated. It will affect the generated OpenAPI (e.g. visible at `/docs`). + + Read more about it in the + [FastAPI docs about Query parameters](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#deprecating-parameters) """ ), ] = None, @@ -581,6 +638,9 @@ def Query( # noqa: N802 You probably don't need it, but it's available. This affects the generated OpenAPI (e.g. visible at `/docs`). + + Read more about it in the + [FastAPI docs about Query parameters](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi """ ), ] = True, @@ -849,6 +909,9 @@ def Header( # noqa: N802 Doc( """ Example values for this field. + + Read more about it in the + [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/) """ ), ] = None, @@ -1152,6 +1215,9 @@ def Cookie( # noqa: N802 Doc( """ Example values for this field. + + Read more about it in the + [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/) """ ), ] = None, @@ -1477,6 +1543,9 @@ def Body( # noqa: N802 Doc( """ Example values for this field. + + Read more about it in the + [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/) """ ), ] = None, @@ -1790,6 +1859,9 @@ def Form( # noqa: N802 Doc( """ Example values for this field. + + Read more about it in the + [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/) """ ), ] = None, @@ -2102,6 +2174,9 @@ def File( # noqa: N802 Doc( """ Example values for this field. + + Read more about it in the + [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/) """ ), ] = None, @@ -2215,6 +2290,9 @@ def Depends( # noqa: N802 Don't call it directly, FastAPI will call it for you, just pass the object directly. + + Read more about it in the + [FastAPI docs for Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/) """ ), ] = None, @@ -2230,6 +2308,9 @@ def Depends( # noqa: N802 Set `use_cache` to `False` to disable this behavior and ensure the dependency is called again (if declared more than once) in the same request. + + Read more about it in the + [FastAPI docs about sub-dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/sub-dependencies/#using-the-same-dependency-multiple-times) """ ), ] = True, @@ -2250,6 +2331,9 @@ def Depends( # noqa: N802 that handles the request (similar to when using `"function"`), but end **after** the response is sent back to the client. So, the dependency function will be executed **around** the **request** and response cycle. + + Read more about it in the + [FastAPI docs for FastAPI Dependencies with yield](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#early-exit-and-scope) """ ), ] = None, @@ -2295,6 +2379,9 @@ def Security( # noqa: N802 Don't call it directly, FastAPI will call it for you, just pass the object directly. + + Read more about it in the + [FastAPI docs for Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/) """ ), ] = None, @@ -2312,7 +2399,9 @@ def Security( # noqa: N802 These scopes are integrated with OpenAPI (and the API docs at `/docs`). So they are visible in the OpenAPI specification. - ) + + Read more about it in the + [FastAPI docs about OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/) """ ), ] = None, @@ -2327,6 +2416,9 @@ def Security( # noqa: N802 Set `use_cache` to `False` to disable this behavior and ensure the dependency is called again (if declared more than once) in the same request. + + Read more about it in the + [FastAPI docs about sub-dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/sub-dependencies/#using-the-same-dependency-multiple-times) """ ), ] = True, diff --git a/fastapi/security/oauth2.py b/fastapi/security/oauth2.py index fc49ba1903..58ffc5c762 100644 --- a/fastapi/security/oauth2.py +++ b/fastapi/security/oauth2.py @@ -68,6 +68,9 @@ class OAuth2PasswordRequestForm: "password". Nevertheless, this dependency class is permissive and allows not passing it. If you want to enforce it, use instead the `OAuth2PasswordRequestFormStrict` dependency. + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ] = None, @@ -78,6 +81,9 @@ class OAuth2PasswordRequestForm: """ `username` string. The OAuth2 spec requires the exact field name `username`. + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ], @@ -88,6 +94,9 @@ class OAuth2PasswordRequestForm: """ `password` string. The OAuth2 spec requires the exact field name `password`. + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ], @@ -112,6 +121,9 @@ class OAuth2PasswordRequestForm: * `users:read` * `profile` * `openid` + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ] = "", @@ -222,6 +234,9 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm): "password". This dependency is strict about it. If you want to be permissive, use instead the `OAuth2PasswordRequestForm` dependency class. + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ], @@ -232,6 +247,9 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm): """ `username` string. The OAuth2 spec requires the exact field name `username`. + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ], @@ -242,6 +260,9 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm): """ `password` string. The OAuth2 spec requires the exact field name `password`. + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ], @@ -266,6 +287,9 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm): * `users:read` * `profile` * `openid` + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ] = "", @@ -423,6 +447,9 @@ class OAuth2PasswordBearer(OAuth2): """ The URL to obtain the OAuth2 token. This would be the *path operation* that has `OAuth2PasswordRequestForm` as a dependency. + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ], @@ -442,6 +469,9 @@ class OAuth2PasswordBearer(OAuth2): """ The OAuth2 scopes that would be required by the *path operations* that use this dependency. + + Read more about it in the + [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). """ ), ] = None, diff --git a/fastapi/security/utils.py b/fastapi/security/utils.py index 002e68b445..fd349aec74 100644 --- a/fastapi/security/utils.py +++ b/fastapi/security/utils.py @@ -7,4 +7,4 @@ def get_authorization_scheme_param( if not authorization_header_value: return "", "" scheme, _, param = authorization_header_value.partition(" ") - return scheme, param + return scheme, param.strip() diff --git a/fastapi/types.py b/fastapi/types.py index d3e980cb43..1c3a6de749 100644 --- a/fastapi/types.py +++ b/fastapi/types.py @@ -3,9 +3,9 @@ from enum import Enum from typing import Any, Callable, Optional, TypeVar, Union from pydantic import BaseModel +from pydantic.main import IncEx as IncEx DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any]) UnionType = getattr(types, "UnionType", Union) ModelNameMap = dict[Union[type[BaseModel], type[Enum]], str] -IncEx = Union[set[int], set[str], dict[int, Any], dict[str, Any]] DependencyCacheKey = tuple[Optional[Callable[..., Any]], tuple[str, ...], str] diff --git a/fastapi/utils.py b/fastapi/utils.py index 78fdcbb5b4..1c3a0881f7 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -90,7 +90,7 @@ def create_model_field( field_info = field_info or FieldInfo(annotation=type_, default=default, alias=alias) kwargs = {"mode": mode, "name": name, "field_info": field_info} try: - return v2.ModelField(**kwargs) # type: ignore[return-value,arg-type] + return v2.ModelField(**kwargs) # type: ignore[arg-type] except PydanticSchemaGenerationError: raise fastapi.exceptions.FastAPIError( _invalid_args_message.format(type_=type_) diff --git a/pyproject.toml b/pyproject.toml index fe9b3a7ea1..0f6bf1e4ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -140,18 +140,18 @@ docs = [ "jieba==0.42.1", "markdown-include-variants==0.0.8", "mdx-include>=1.4.1,<2.0.0", - "mkdocs-macros-plugin==1.4.1", + "mkdocs-macros-plugin==1.5.0", "mkdocs-material==9.7.0", "mkdocs-redirects>=1.2.1,<1.3.0", "mkdocstrings[python]==0.30.1", "pillow==11.3.0", "python-slugify==8.0.4", "pyyaml>=5.3.1,<7.0.0", - "typer==0.16.0", + "typer==0.21.1", ] docs-tests = [ "httpx>=0.23.0,<1.0.0", - "ruff==0.14.3", + "ruff==0.14.14", ] github-actions = [ "httpx>=0.27.0,<1.0.0", @@ -174,13 +174,14 @@ tests = [ "pytest>=7.1.3,<9.0.0", "pytest-codspeed==4.2.0", "pyyaml>=5.3.1,<7.0.0", - "sqlmodel==0.0.27", + "sqlmodel==0.0.31", "strawberry-graphql>=0.200.0,<1.0.0", "types-orjson==3.6.2", "types-ujson==5.10.0.20240515", + "a2wsgi>=1.9.0,<=2.0.0", ] translations = [ - "gitpython==3.1.45", + "gitpython==3.1.46", "pydantic-ai==0.4.10", "pygithub==2.8.1", ] @@ -231,7 +232,6 @@ xfail_strict = true junit_family = "xunit2" filterwarnings = [ "error", - 'ignore:starlette.middleware.wsgi is deprecated and will be removed in a future release\..*:DeprecationWarning:starlette', # see https://trio.readthedocs.io/en/stable/history.html#trio-0-22-0-2022-09-28 "ignore:You seem to already have a custom.*:RuntimeWarning:trio", # TODO: remove after upgrading SQLAlchemy to a version that includes the following changes diff --git a/scripts/docs.py b/scripts/docs.py index c350ee8319..ca2baf97dc 100644 --- a/scripts/docs.py +++ b/scripts/docs.py @@ -23,6 +23,7 @@ SUPPORTED_LANGS = { "en", "de", "es", + "ja", "ko", "pt", "ru", @@ -130,14 +131,9 @@ def new_lang(lang: str = typer.Argument(..., callback=lang_callback)): new_path.mkdir() new_config_path: Path = Path(new_path) / mkdocs_name new_config_path.write_text("INHERIT: ../en/mkdocs.yml\n", encoding="utf-8") - new_config_docs_path: Path = new_path / "docs" - new_config_docs_path.mkdir() - en_index_path: Path = en_docs_path / "docs" / "index.md" - new_index_path: Path = new_config_docs_path / "index.md" - en_index_content = en_index_path.read_text(encoding="utf-8") - new_index_content = f"{missing_translation_snippet}\n\n{en_index_content}" - new_index_path.write_text(new_index_content, encoding="utf-8") - typer.secho(f"Successfully initialized: {new_path}", color=typer.colors.GREEN) + new_llm_prompt_path: Path = new_path / "llm-prompt.md" + new_llm_prompt_path.write_text("", encoding="utf-8") + print(f"Successfully initialized: {new_path}") update_languages() diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py index 4b781270a2..567c0111dc 100644 --- a/scripts/mkdocs_hooks.py +++ b/scripts/mkdocs_hooks.py @@ -26,6 +26,17 @@ def get_missing_translation_content(docs_dir: str) -> str: return missing_translation_path.read_text(encoding="utf-8") +@lru_cache +def get_translation_banner_content(docs_dir: str) -> str: + docs_dir_path = Path(docs_dir) + translation_banner_path = docs_dir_path / "translation-banner.md" + if not translation_banner_path.is_file(): + translation_banner_path = ( + docs_dir_path.parent.parent / "en" / "docs" / "translation-banner.md" + ) + return translation_banner_path.read_text(encoding="utf-8") + + @lru_cache def get_mkdocs_material_langs() -> list[str]: material_path = Path(material.__file__).parent @@ -151,4 +162,21 @@ def on_page_markdown( if markdown.startswith("#"): header, _, body = markdown.partition("\n\n") return f"{header}\n\n{missing_translation_content}\n\n{body}" - return markdown + + docs_dir_path = Path(config.docs_dir) + en_docs_dir_path = docs_dir_path.parent.parent / "en/docs" + + if docs_dir_path == en_docs_dir_path: + return markdown + + # For translated pages add translation banner + translation_banner_content = get_translation_banner_content(config.docs_dir) + en_url = "https://fastapi.tiangolo.com/" + page.url.lstrip("/") + translation_banner_content = translation_banner_content.replace( + "ENGLISH_VERSION_URL", en_url + ) + header = "" + body = markdown + if markdown.startswith("#"): + header, _, body = markdown.partition("\n\n") + return f"{header}\n\n{translation_banner_content}\n\n{body}" diff --git a/scripts/translate.py b/scripts/translate.py index eba4ad6a2f..072383514b 100644 --- a/scripts/translate.py +++ b/scripts/translate.py @@ -10,6 +10,7 @@ from typing import Annotated import git import typer import yaml +from doc_parsing_utils import check_translation from github import Github from pydantic_ai import Agent from rich import print @@ -119,9 +120,30 @@ def translate_page( ] ) prompt = "\n\n".join(prompt_segments) - print(f"Running agent for {out_path}") - result = agent.run_sync(prompt) - out_content = f"{result.output.strip()}\n" + + MAX_ATTEMPTS = 3 + for attempt_no in range(1, MAX_ATTEMPTS + 1): + print(f"Running agent for {out_path} (attempt {attempt_no}/{MAX_ATTEMPTS})") + result = agent.run_sync(prompt) + out_content = f"{result.output.strip()}\n" + try: + check_translation( + doc_lines=out_content.splitlines(), + en_doc_lines=original_content.splitlines(), + lang_code=language, + auto_fix=False, + path=str(out_path), + ) + break # Exit loop if no errors + except ValueError as e: + print( + f"Translation check failed on attempt {attempt_no}/{MAX_ATTEMPTS}: {e}" + ) + continue # Retry if not reached max attempts + else: # Max retry attempts reached + print(f"Translation failed for {out_path} after {MAX_ATTEMPTS} attempts") + raise typer.Exit(code=1) + print(f"Saving translation to {out_path}") out_path.write_text(out_content, encoding="utf-8", newline="\n") @@ -389,7 +411,8 @@ def make_pr( print(f"Creating a new branch {branch_name}") subprocess.run(["git", "checkout", "-b", branch_name], check=True) else: - print(f"Committing in place on branch {current_branch}") + branch_name = current_branch + print(f"Committing in place on branch {branch_name}") print("Adding updated files") git_path = Path("docs") subprocess.run(["git", "add", str(git_path)], check=True) diff --git a/tests/test_additional_properties.py b/tests/test_additional_properties.py index 2622366400..935acca42e 100644 --- a/tests/test_additional_properties.py +++ b/tests/test_additional_properties.py @@ -89,6 +89,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_additional_properties_bool.py b/tests/test_additional_properties_bool.py index 063297a3f2..3b323ad463 100644 --- a/tests/test_additional_properties_bool.py +++ b/tests/test_additional_properties_bool.py @@ -101,6 +101,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_additional_responses_custom_model_in_callback.py b/tests/test_additional_responses_custom_model_in_callback.py index 376d7714ed..3a37c924d2 100644 --- a/tests/test_additional_responses_custom_model_in_callback.py +++ b/tests/test_additional_responses_custom_model_in_callback.py @@ -128,6 +128,8 @@ def test_openapi_schema(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_additional_responses_default_validationerror.py b/tests/test_additional_responses_default_validationerror.py index 153f04f579..acc081fb16 100644 --- a/tests/test_additional_responses_default_validationerror.py +++ b/tests/test_additional_responses_default_validationerror.py @@ -66,6 +66,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_additional_responses_union_duplicate_anyof.py b/tests/test_additional_responses_union_duplicate_anyof.py new file mode 100644 index 0000000000..f5d987ca31 --- /dev/null +++ b/tests/test_additional_responses_union_duplicate_anyof.py @@ -0,0 +1,123 @@ +""" +Regression test: Ensure app-level responses with Union models and content/examples +don't accumulate duplicate $ref entries in anyOf arrays. +See https://github.com/fastapi/fastapi/pull/14463 +""" + +from typing import Union + +from fastapi import FastAPI +from fastapi.testclient import TestClient +from pydantic import BaseModel + + +class ModelA(BaseModel): + a: str + + +class ModelB(BaseModel): + b: str + + +app = FastAPI( + responses={ + 500: { + "model": Union[ModelA, ModelB], + "content": {"application/json": {"examples": {"Case A": {"value": "a"}}}}, + } + } +) + + +@app.get("/route1") +async def route1(): + pass # pragma: no cover + + +@app.get("/route2") +async def route2(): + pass # pragma: no cover + + +client = TestClient(app) + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/route1": { + "get": { + "summary": "Route1", + "operationId": "route1_route1_get", + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "anyOf": [ + {"$ref": "#/components/schemas/ModelA"}, + {"$ref": "#/components/schemas/ModelB"}, + ], + "title": "Response 500 Route1 Route1 Get", + }, + "examples": {"Case A": {"value": "a"}}, + } + }, + }, + }, + } + }, + "/route2": { + "get": { + "summary": "Route2", + "operationId": "route2_route2_get", + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "anyOf": [ + {"$ref": "#/components/schemas/ModelA"}, + {"$ref": "#/components/schemas/ModelB"}, + ], + "title": "Response 500 Route2 Route2 Get", + }, + "examples": {"Case A": {"value": "a"}}, + } + }, + }, + }, + } + }, + }, + "components": { + "schemas": { + "ModelA": { + "properties": {"a": {"type": "string", "title": "A"}}, + "type": "object", + "required": ["a"], + "title": "ModelA", + }, + "ModelB": { + "properties": {"b": {"type": "string", "title": "B"}}, + "type": "object", + "required": ["b"], + "title": "ModelB", + }, + } + }, + } diff --git a/tests/test_annotated.py b/tests/test_annotated.py index 39f6f83b29..99bd03ae6f 100644 --- a/tests/test_annotated.py +++ b/tests/test_annotated.py @@ -284,6 +284,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_application.py b/tests/test_application.py index 001586ff78..fe97e674c0 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -1260,6 +1260,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_dependency_duplicates.py b/tests/test_dependency_duplicates.py index a8658e03bb..3ca6a3e891 100644 --- a/tests/test_dependency_duplicates.py +++ b/tests/test_dependency_duplicates.py @@ -223,6 +223,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_enforce_once_required_parameter.py b/tests/test_enforce_once_required_parameter.py index 2e5ac6c062..c46a543576 100644 --- a/tests/test_enforce_once_required_parameter.py +++ b/tests/test_enforce_once_required_parameter.py @@ -42,6 +42,8 @@ expected_schema = { }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "title": "Location", diff --git a/tests/test_extra_routes.py b/tests/test_extra_routes.py index 45734ec28a..251af4a59e 100644 --- a/tests/test_extra_routes.py +++ b/tests/test_extra_routes.py @@ -347,6 +347,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_filter_pydantic_sub_model_pv2.py b/tests/test_filter_pydantic_sub_model_pv2.py index fc5876410d..1de2b50f7f 100644 --- a/tests/test_filter_pydantic_sub_model_pv2.py +++ b/tests/test_filter_pydantic_sub_model_pv2.py @@ -165,6 +165,8 @@ def test_openapi_schema(client: TestClient): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_forms_single_param.py b/tests/test_forms_single_param.py index 67f054b34e..fc163cb1ef 100644 --- a/tests/test_forms_single_param.py +++ b/tests/test_forms_single_param.py @@ -81,6 +81,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_generate_unique_id_function.py b/tests/test_generate_unique_id_function.py index 62ebfbc964..49510d08a9 100644 --- a/tests/test_generate_unique_id_function.py +++ b/tests/test_generate_unique_id_function.py @@ -213,6 +213,8 @@ def test_top_level_generate_unique_id(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", @@ -414,6 +416,8 @@ def test_router_overrides_generate_unique_id(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", @@ -615,6 +619,8 @@ def test_router_include_overrides_generate_unique_id(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", @@ -889,6 +895,8 @@ def test_subrouter_top_level_include_overrides_generate_unique_id(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", @@ -1093,6 +1101,8 @@ def test_router_path_operation_overrides_generate_unique_id(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", @@ -1301,6 +1311,8 @@ def test_app_path_operation_overrides_generate_unique_id(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", @@ -1587,6 +1599,8 @@ def test_callback_override_generate_unique_id(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_get_model_definitions_formfeed_escape.py b/tests/test_get_model_definitions_formfeed_escape.py index eb7939b69a..46f8aa5959 100644 --- a/tests/test_get_model_definitions_formfeed_escape.py +++ b/tests/test_get_model_definitions_formfeed_escape.py @@ -98,6 +98,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_get_request_body.py b/tests/test_get_request_body.py index cc567b88f6..b21889e30f 100644 --- a/tests/test_get_request_body.py +++ b/tests/test_get_request_body.py @@ -100,6 +100,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_include_router_defaults_overrides.py b/tests/test_include_router_defaults_overrides.py index 33baa25e6a..6d2ffc44a5 100644 --- a/tests/test_include_router_defaults_overrides.py +++ b/tests/test_include_router_defaults_overrides.py @@ -7290,6 +7290,8 @@ def test_openapi(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_infer_param_optionality.py b/tests/test_infer_param_optionality.py index 147018996e..b11a1ca433 100644 --- a/tests/test_infer_param_optionality.py +++ b/tests/test_infer_param_optionality.py @@ -325,6 +325,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_invalid_sequence_param.py b/tests/test_invalid_sequence_param.py index 2b8fd059e1..3695344f7a 100644 --- a/tests/test_invalid_sequence_param.py +++ b/tests/test_invalid_sequence_param.py @@ -6,7 +6,10 @@ from pydantic import BaseModel def test_invalid_sequence(): - with pytest.raises(AssertionError): + with pytest.raises( + AssertionError, + match="Query parameter 'q' must be one of the supported types", + ): app = FastAPI() class Item(BaseModel): @@ -18,7 +21,10 @@ def test_invalid_sequence(): def test_invalid_tuple(): - with pytest.raises(AssertionError): + with pytest.raises( + AssertionError, + match="Query parameter 'q' must be one of the supported types", + ): app = FastAPI() class Item(BaseModel): @@ -30,7 +36,10 @@ def test_invalid_tuple(): def test_invalid_dict(): - with pytest.raises(AssertionError): + with pytest.raises( + AssertionError, + match="Query parameter 'q' must be one of the supported types", + ): app = FastAPI() class Item(BaseModel): @@ -42,7 +51,10 @@ def test_invalid_dict(): def test_invalid_simple_dict(): - with pytest.raises(AssertionError): + with pytest.raises( + AssertionError, + match="Query parameter 'q' must be one of the supported types", + ): app = FastAPI() class Item(BaseModel): diff --git a/tests/test_modules_same_name_body/test_main.py b/tests/test_modules_same_name_body/test_main.py index 263d87df26..276de539db 100644 --- a/tests/test_modules_same_name_body/test_main.py +++ b/tests/test_modules_same_name_body/test_main.py @@ -131,6 +131,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_multi_body_errors.py b/tests/test_multi_body_errors.py index 4418c77cb0..fa3e0c6359 100644 --- a/tests/test_multi_body_errors.py +++ b/tests/test_multi_body_errors.py @@ -167,6 +167,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_multi_query_errors.py b/tests/test_multi_query_errors.py index 5df51ba185..7540367a6f 100644 --- a/tests/test_multi_query_errors.py +++ b/tests/test_multi_query_errors.py @@ -97,6 +97,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_no_schema_split.py b/tests/test_no_schema_split.py index 131a3755e7..66bb89902a 100644 --- a/tests/test_no_schema_split.py +++ b/tests/test_no_schema_split.py @@ -154,6 +154,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_openapi_examples.py b/tests/test_openapi_examples.py index bd0d55452e..93e5b366f1 100644 --- a/tests/test_openapi_examples.py +++ b/tests/test_openapi_examples.py @@ -398,6 +398,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_openapi_query_parameter_extension.py b/tests/test_openapi_query_parameter_extension.py index 084cb695d4..b6c3c3d8da 100644 --- a/tests/test_openapi_query_parameter_extension.py +++ b/tests/test_openapi_query_parameter_extension.py @@ -119,6 +119,8 @@ def test_openapi(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_openapi_separate_input_output_schemas.py b/tests/test_openapi_separate_input_output_schemas.py index 1891f0bde0..f941e323bf 100644 --- a/tests/test_openapi_separate_input_output_schemas.py +++ b/tests/test_openapi_separate_input_output_schemas.py @@ -418,6 +418,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] @@ -649,6 +651,8 @@ def test_openapi_schema_no_separate(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_param_in_path_and_dependency.py b/tests/test_param_in_path_and_dependency.py index 08eb0f40f3..6b1f660cb7 100644 --- a/tests/test_param_in_path_and_dependency.py +++ b/tests/test_param_in_path_and_dependency.py @@ -86,6 +86,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_param_include_in_schema.py b/tests/test_param_include_in_schema.py index f461947c97..5060920f14 100644 --- a/tests/test_param_include_in_schema.py +++ b/tests/test_param_include_in_schema.py @@ -151,6 +151,8 @@ openapi_schema = { }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_put_no_body.py b/tests/test_put_no_body.py index 8f4c82532c..2b9299bc58 100644 --- a/tests/test_put_no_body.py +++ b/tests/test_put_no_body.py @@ -78,6 +78,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_regex_deprecated_body.py b/tests/test_regex_deprecated_body.py index 5b4daa450f..6074206ffe 100644 --- a/tests/test_regex_deprecated_body.py +++ b/tests/test_regex_deprecated_body.py @@ -132,6 +132,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_regex_deprecated_params.py b/tests/test_regex_deprecated_params.py index d6eaa45fb1..6074b62828 100644 --- a/tests/test_regex_deprecated_params.py +++ b/tests/test_regex_deprecated_params.py @@ -121,6 +121,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_repeated_dependency_schema.py b/tests/test_repeated_dependency_schema.py index c21829bd97..0fc7e3d3ef 100644 --- a/tests/test_repeated_dependency_schema.py +++ b/tests/test_repeated_dependency_schema.py @@ -35,6 +35,8 @@ schema = { }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "title": "Location", diff --git a/tests/test_repeated_parameter_alias.py b/tests/test_repeated_parameter_alias.py index fd72eaab29..49e4ad4a2e 100644 --- a/tests/test_repeated_parameter_alias.py +++ b/tests/test_repeated_parameter_alias.py @@ -41,6 +41,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_reponse_set_reponse_code_empty.py b/tests/test_reponse_set_reponse_code_empty.py index bf3aa758c3..b31aefa479 100644 --- a/tests/test_reponse_set_reponse_code_empty.py +++ b/tests/test_reponse_set_reponse_code_empty.py @@ -90,6 +90,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_request_body_parameters_media_type.py b/tests/test_request_body_parameters_media_type.py index d1bff9ddf4..8731c3e5d3 100644 --- a/tests/test_request_body_parameters_media_type.py +++ b/tests/test_request_body_parameters_media_type.py @@ -168,6 +168,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_schema_extra_examples.py b/tests/test_schema_extra_examples.py index ac8999c90a..8caf6ce7af 100644 --- a/tests/test_schema_extra_examples.py +++ b/tests/test_schema_extra_examples.py @@ -845,6 +845,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_security_http_base.py b/tests/test_security_http_base.py index 8cf259a750..ac38ee718e 100644 --- a/tests/test_security_http_base.py +++ b/tests/test_security_http_base.py @@ -21,6 +21,12 @@ def test_security_http_base(): assert response.json() == {"scheme": "Other", "credentials": "foobar"} +def test_security_http_base_with_whitespaces(): + response = client.get("/users/me", headers={"Authorization": "Other foobar "}) + assert response.status_code == 200, response.text + assert response.json() == {"scheme": "Other", "credentials": "foobar"} + + def test_security_http_base_no_credentials(): response = client.get("/users/me") assert response.status_code == 401, response.text diff --git a/tests/test_security_oauth2.py b/tests/test_security_oauth2.py index 7ad9369956..bff1226ad9 100644 --- a/tests/test_security_oauth2.py +++ b/tests/test_security_oauth2.py @@ -237,6 +237,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_security_oauth2_authorization_code_bearer.py b/tests/test_security_oauth2_authorization_code_bearer.py index f2097b1490..66f53ab00d 100644 --- a/tests/test_security_oauth2_authorization_code_bearer.py +++ b/tests/test_security_oauth2_authorization_code_bearer.py @@ -37,6 +37,12 @@ def test_token(): assert response.json() == {"token": "testtoken"} +def test_token_with_whitespaces(): + response = client.get("/items", headers={"Authorization": "Bearer testtoken "}) + assert response.status_code == 200, response.text + assert response.json() == {"token": "testtoken"} + + def test_openapi_schema(): response = client.get("/openapi.json") assert response.status_code == 200, response.text diff --git a/tests/test_security_oauth2_optional.py b/tests/test_security_oauth2_optional.py index 57c16058af..5bcd5040fd 100644 --- a/tests/test_security_oauth2_optional.py +++ b/tests/test_security_oauth2_optional.py @@ -240,6 +240,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_security_oauth2_optional_description.py b/tests/test_security_oauth2_optional_description.py index 60c6c242e0..0353ba4c27 100644 --- a/tests/test_security_oauth2_optional_description.py +++ b/tests/test_security_oauth2_optional_description.py @@ -241,6 +241,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_starlette_exception.py b/tests/test_starlette_exception.py index 229fe80163..2be37b8bb8 100644 --- a/tests/test_starlette_exception.py +++ b/tests/test_starlette_exception.py @@ -184,6 +184,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_stringified_annotation_dependency_py314.py b/tests/test_stringified_annotation_dependency_py314.py new file mode 100644 index 0000000000..da9b429fc0 --- /dev/null +++ b/tests/test_stringified_annotation_dependency_py314.py @@ -0,0 +1,30 @@ +from typing import TYPE_CHECKING, Annotated + +from fastapi import Depends, FastAPI +from fastapi.testclient import TestClient + +from .utils import needs_py314 + +if TYPE_CHECKING: # pragma: no cover + + class DummyUser: ... + + +@needs_py314 +def test_stringified_annotation(): + # python3.14: Use forward reference without "from __future__ import annotations" + async def get_current_user() -> DummyUser | None: + return None + + app = FastAPI() + + client = TestClient(app) + + @app.get("/") + async def get( + current_user: Annotated[DummyUser | None, Depends(get_current_user)], + ) -> str: + return "hello world" + + response = client.get("/") + assert response.status_code == 200 diff --git a/tests/test_sub_callbacks.py b/tests/test_sub_callbacks.py index cc7e5f5c6a..442e709fb1 100644 --- a/tests/test_sub_callbacks.py +++ b/tests/test_sub_callbacks.py @@ -277,6 +277,8 @@ def test_openapi_schema(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tuples.py b/tests/test_tuples.py index d3c89045b4..de9487df2a 100644 --- a/tests/test_tuples.py +++ b/tests/test_tuples.py @@ -262,6 +262,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_additional_responses/test_tutorial001.py b/tests/test_tutorial/test_additional_responses/test_tutorial001.py index 1a18db75ce..78ccb84426 100644 --- a/tests/test_tutorial/test_additional_responses/test_tutorial001.py +++ b/tests/test_tutorial/test_additional_responses/test_tutorial001.py @@ -98,6 +98,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_additional_responses/test_tutorial002.py b/tests/test_tutorial/test_additional_responses/test_tutorial002.py index 8208605956..cdab56d7a6 100644 --- a/tests/test_tutorial/test_additional_responses/test_tutorial002.py +++ b/tests/test_tutorial/test_additional_responses/test_tutorial002.py @@ -115,6 +115,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_additional_responses/test_tutorial003.py b/tests/test_tutorial/test_additional_responses/test_tutorial003.py index 90dc4e371e..fda786b398 100644 --- a/tests/test_tutorial/test_additional_responses/test_tutorial003.py +++ b/tests/test_tutorial/test_additional_responses/test_tutorial003.py @@ -102,6 +102,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_additional_responses/test_tutorial004.py b/tests/test_tutorial/test_additional_responses/test_tutorial004.py index c6abf5e466..f36d3d79c2 100644 --- a/tests/test_tutorial/test_additional_responses/test_tutorial004.py +++ b/tests/test_tutorial/test_additional_responses/test_tutorial004.py @@ -118,6 +118,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_bigger_applications/test_main.py b/tests/test_tutorial/test_bigger_applications/test_main.py index f5e243b95a..f80563d142 100644 --- a/tests/test_tutorial/test_bigger_applications/test_main.py +++ b/tests/test_tutorial/test_bigger_applications/test_main.py @@ -593,6 +593,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_body/test_tutorial001.py b/tests/test_tutorial/test_body/test_tutorial001.py index 5a7cae1603..9a837483f2 100644 --- a/tests/test_tutorial/test_body/test_tutorial001.py +++ b/tests/test_tutorial/test_body/test_tutorial001.py @@ -328,6 +328,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body/test_tutorial002.py b/tests/test_tutorial/test_body/test_tutorial002.py index b6d51d5235..e8b23e8f61 100644 --- a/tests/test_tutorial/test_body/test_tutorial002.py +++ b/tests/test_tutorial/test_body/test_tutorial002.py @@ -143,6 +143,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body/test_tutorial003.py b/tests/test_tutorial/test_body/test_tutorial003.py index 227a125e78..7b8b7ea89f 100644 --- a/tests/test_tutorial/test_body/test_tutorial003.py +++ b/tests/test_tutorial/test_body/test_tutorial003.py @@ -153,6 +153,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body/test_tutorial004.py b/tests/test_tutorial/test_body/test_tutorial004.py index 10212843ee..d78c760f5d 100644 --- a/tests/test_tutorial/test_body/test_tutorial004.py +++ b/tests/test_tutorial/test_body/test_tutorial004.py @@ -164,6 +164,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_fields/test_tutorial001.py b/tests/test_tutorial/test_body_fields/test_tutorial001.py index 0ecadbb660..cb6da29085 100644 --- a/tests/test_tutorial/test_body_fields/test_tutorial001.py +++ b/tests/test_tutorial/test_body_fields/test_tutorial001.py @@ -166,6 +166,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py index 63c9c16d62..a4f24627b0 100644 --- a/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py +++ b/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py @@ -162,6 +162,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial002.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial002.py index e98d5860fe..155bda0c99 100644 --- a/tests/test_tutorial/test_body_multiple_params/test_tutorial002.py +++ b/tests/test_tutorial/test_body_multiple_params/test_tutorial002.py @@ -325,6 +325,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py index 76b7ff7099..2f403797fe 100644 --- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py +++ b/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py @@ -202,6 +202,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial004.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial004.py index 979c054cd0..506e55eebc 100644 --- a/tests/test_tutorial/test_body_multiple_params/test_tutorial004.py +++ b/tests/test_tutorial/test_body_multiple_params/test_tutorial004.py @@ -272,6 +272,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial005.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial005.py index d47aa1b4f9..20859d12c8 100644 --- a/tests/test_tutorial/test_body_multiple_params/test_tutorial005.py +++ b/tests/test_tutorial/test_body_multiple_params/test_tutorial005.py @@ -236,6 +236,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial001_tutorial002_tutorial003.py b/tests/test_tutorial/test_body_nested_models/test_tutorial001_tutorial002_tutorial003.py index d452929c38..ae494350b3 100644 --- a/tests/test_tutorial/test_body_nested_models/test_tutorial001_tutorial002_tutorial003.py +++ b/tests/test_tutorial/test_body_nested_models/test_tutorial001_tutorial002_tutorial003.py @@ -233,6 +233,8 @@ def test_openapi_schema(client: TestClient, mod_name: str): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial004.py b/tests/test_tutorial/test_body_nested_models/test_tutorial004.py index ff9596943d..c1410330c4 100644 --- a/tests/test_tutorial/test_body_nested_models/test_tutorial004.py +++ b/tests/test_tutorial/test_body_nested_models/test_tutorial004.py @@ -257,6 +257,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial005.py b/tests/test_tutorial/test_body_nested_models/test_tutorial005.py index 9a07a904e6..c09e0c1b10 100644 --- a/tests/test_tutorial/test_body_nested_models/test_tutorial005.py +++ b/tests/test_tutorial/test_body_nested_models/test_tutorial005.py @@ -283,6 +283,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial006.py b/tests/test_tutorial/test_body_nested_models/test_tutorial006.py index 088177cb95..f26c50167b 100644 --- a/tests/test_tutorial/test_body_nested_models/test_tutorial006.py +++ b/tests/test_tutorial/test_body_nested_models/test_tutorial006.py @@ -251,6 +251,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial007.py b/tests/test_tutorial/test_body_nested_models/test_tutorial007.py index a302819505..dac168e242 100644 --- a/tests/test_tutorial/test_body_nested_models/test_tutorial007.py +++ b/tests/test_tutorial/test_body_nested_models/test_tutorial007.py @@ -326,6 +326,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial008.py b/tests/test_tutorial/test_body_nested_models/test_tutorial008.py index 32eb8ee75c..2101b7bbe5 100644 --- a/tests/test_tutorial/test_body_nested_models/test_tutorial008.py +++ b/tests/test_tutorial/test_body_nested_models/test_tutorial008.py @@ -139,6 +139,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial009.py b/tests/test_tutorial/test_body_nested_models/test_tutorial009.py index f2e56d40fb..f7481a5f7f 100644 --- a/tests/test_tutorial/test_body_nested_models/test_tutorial009.py +++ b/tests/test_tutorial/test_body_nested_models/test_tutorial009.py @@ -98,6 +98,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001.py b/tests/test_tutorial/test_body_updates/test_tutorial001.py index 0401eb7d0d..9c6a90576c 100644 --- a/tests/test_tutorial/test_body_updates/test_tutorial001.py +++ b/tests/test_tutorial/test_body_updates/test_tutorial001.py @@ -168,6 +168,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_body_updates/test_tutorial002.py b/tests/test_tutorial/test_body_updates/test_tutorial002.py index 466e6af8fd..7d79cf5e63 100644 --- a/tests/test_tutorial/test_body_updates/test_tutorial002.py +++ b/tests/test_tutorial/test_body_updates/test_tutorial002.py @@ -189,6 +189,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_cookie_param_models/test_tutorial001.py b/tests/test_tutorial/test_cookie_param_models/test_tutorial001.py index ac8e7bdae1..f391c569a8 100644 --- a/tests/test_tutorial/test_cookie_param_models/test_tutorial001.py +++ b/tests/test_tutorial/test_cookie_param_models/test_tutorial001.py @@ -151,6 +151,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_cookie_param_models/test_tutorial002.py b/tests/test_tutorial/test_cookie_param_models/test_tutorial002.py index d7c3d15f1b..6583045dc8 100644 --- a/tests/test_tutorial/test_cookie_param_models/test_tutorial002.py +++ b/tests/test_tutorial/test_cookie_param_models/test_tutorial002.py @@ -161,6 +161,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_cookie_params/test_tutorial001.py b/tests/test_tutorial/test_cookie_params/test_tutorial001.py index 9b47cbc67a..ab71148764 100644 --- a/tests/test_tutorial/test_cookie_params/test_tutorial001.py +++ b/tests/test_tutorial/test_cookie_params/test_tutorial001.py @@ -101,6 +101,8 @@ def test_openapi_schema(mod: ModuleType): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_dataclasses/test_tutorial001.py b/tests/test_tutorial/test_dataclasses/test_tutorial001.py index 4683062f59..756eacf233 100644 --- a/tests/test_tutorial/test_dataclasses/test_tutorial001.py +++ b/tests/test_tutorial/test_dataclasses/test_tutorial001.py @@ -129,6 +129,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_dataclasses/test_tutorial003.py b/tests/test_tutorial/test_dataclasses/test_tutorial003.py index a6a9fc1c7e..de63a94766 100644 --- a/tests/test_tutorial/test_dataclasses/test_tutorial003.py +++ b/tests/test_tutorial/test_dataclasses/test_tutorial003.py @@ -195,6 +195,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_dependencies/test_tutorial001_tutorial001_02.py b/tests/test_tutorial/test_dependencies/test_tutorial001_tutorial001_02.py index 50d7c4108c..15919c63f7 100644 --- a/tests/test_tutorial/test_dependencies/test_tutorial001_tutorial001_02.py +++ b/tests/test_tutorial/test_dependencies/test_tutorial001_tutorial001_02.py @@ -170,6 +170,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_dependencies/test_tutorial002_tutorial003_tutorial004.py b/tests/test_tutorial/test_dependencies/test_tutorial002_tutorial003_tutorial004.py index f09d6f268d..96300a2599 100644 --- a/tests/test_tutorial/test_dependencies/test_tutorial002_tutorial003_tutorial004.py +++ b/tests/test_tutorial/test_dependencies/test_tutorial002_tutorial003_tutorial004.py @@ -161,6 +161,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_dependencies/test_tutorial005.py b/tests/test_tutorial/test_dependencies/test_tutorial005.py index a914936ba1..e595859cb8 100644 --- a/tests/test_tutorial/test_dependencies/test_tutorial005.py +++ b/tests/test_tutorial/test_dependencies/test_tutorial005.py @@ -121,6 +121,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_dependencies/test_tutorial006.py b/tests/test_tutorial/test_dependencies/test_tutorial006.py index 59202df3bf..cdea27b7c0 100644 --- a/tests/test_tutorial/test_dependencies/test_tutorial006.py +++ b/tests/test_tutorial/test_dependencies/test_tutorial006.py @@ -125,6 +125,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008.py b/tests/test_tutorial/test_dependencies/test_tutorial008.py index 9d7377ebe4..5a2d226bff 100644 --- a/tests/test_tutorial/test_dependencies/test_tutorial008.py +++ b/tests/test_tutorial/test_dependencies/test_tutorial008.py @@ -1,4 +1,5 @@ import importlib +import sys from types import ModuleType from typing import Annotated, Any from unittest.mock import Mock, patch @@ -12,8 +13,13 @@ from fastapi.testclient import TestClient name="module", params=[ "tutorial008_py39", - # Fails with `NameError: name 'DepA' is not defined` - pytest.param("tutorial008_an_py39", marks=pytest.mark.xfail), + pytest.param( + "tutorial008_an_py39", + marks=pytest.mark.xfail( + sys.version_info < (3, 14), + reason="Fails with `NameError: name 'DepA' is not defined`", + ), + ), ], ) def get_module(request: pytest.FixtureRequest): diff --git a/tests/test_tutorial/test_dependencies/test_tutorial011.py b/tests/test_tutorial/test_dependencies/test_tutorial011.py index 4868254c0b..3374c54b5e 100644 --- a/tests/test_tutorial/test_dependencies/test_tutorial011.py +++ b/tests/test_tutorial/test_dependencies/test_tutorial011.py @@ -102,6 +102,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_dependencies/test_tutorial012.py b/tests/test_tutorial/test_dependencies/test_tutorial012.py index d5599ac73a..f342ff842e 100644 --- a/tests/test_tutorial/test_dependencies/test_tutorial012.py +++ b/tests/test_tutorial/test_dependencies/test_tutorial012.py @@ -219,6 +219,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_encoder/test_tutorial001.py b/tests/test_tutorial/test_encoder/test_tutorial001.py index 5c8ee054d8..5a4edbc66e 100644 --- a/tests/test_tutorial/test_encoder/test_tutorial001.py +++ b/tests/test_tutorial/test_encoder/test_tutorial001.py @@ -172,6 +172,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_events/test_tutorial001.py b/tests/test_tutorial/test_events/test_tutorial001.py index 5fe99d50df..48b838d5a0 100644 --- a/tests/test_tutorial/test_events/test_tutorial001.py +++ b/tests/test_tutorial/test_events/test_tutorial001.py @@ -63,6 +63,8 @@ def test_openapi_schema(app: FastAPI): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_events/test_tutorial003.py b/tests/test_tutorial/test_events/test_tutorial003.py index 38710edfea..aed9def7ae 100644 --- a/tests/test_tutorial/test_events/test_tutorial003.py +++ b/tests/test_tutorial/test_events/test_tutorial003.py @@ -76,6 +76,8 @@ def test_openapi_schema(): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_extra_data_types/test_tutorial001.py b/tests/test_tutorial/test_extra_data_types/test_tutorial001.py index 5479e29252..28fe68f285 100644 --- a/tests/test_tutorial/test_extra_data_types/test_tutorial001.py +++ b/tests/test_tutorial/test_extra_data_types/test_tutorial001.py @@ -133,6 +133,8 @@ def test_openapi_schema(client: TestClient): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_extra_models/test_tutorial001_tutorial002.py b/tests/test_tutorial/test_extra_models/test_tutorial001_tutorial002.py index 3f2f508a11..9981699862 100644 --- a/tests/test_tutorial/test_extra_models/test_tutorial001_tutorial002.py +++ b/tests/test_tutorial/test_extra_models/test_tutorial001_tutorial002.py @@ -138,6 +138,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_extra_models/test_tutorial003.py b/tests/test_tutorial/test_extra_models/test_tutorial003.py index 872af53830..38e8741582 100644 --- a/tests/test_tutorial/test_extra_models/test_tutorial003.py +++ b/tests/test_tutorial/test_extra_models/test_tutorial003.py @@ -127,6 +127,8 @@ def test_openapi_schema(client: TestClient): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_generate_clients/test_tutorial001.py b/tests/test_tutorial/test_generate_clients/test_tutorial001.py index bbb66b4516..83ae38c5b5 100644 --- a/tests/test_tutorial/test_generate_clients/test_tutorial001.py +++ b/tests/test_tutorial/test_generate_clients/test_tutorial001.py @@ -135,6 +135,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_generate_clients/test_tutorial002.py b/tests/test_tutorial/test_generate_clients/test_tutorial002.py index ab8bc4c11c..b9255325aa 100644 --- a/tests/test_tutorial/test_generate_clients/test_tutorial002.py +++ b/tests/test_tutorial/test_generate_clients/test_tutorial002.py @@ -180,6 +180,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_generate_clients/test_tutorial003.py b/tests/test_tutorial/test_generate_clients/test_tutorial003.py index bac52e4fd6..d054845879 100644 --- a/tests/test_tutorial/test_generate_clients/test_tutorial003.py +++ b/tests/test_tutorial/test_generate_clients/test_tutorial003.py @@ -180,6 +180,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_generate_clients/test_tutorial004.py b/tests/test_tutorial/test_generate_clients/test_tutorial004.py index e66f6d2a12..eea60e3428 100644 --- a/tests/test_tutorial/test_generate_clients/test_tutorial004.py +++ b/tests/test_tutorial/test_generate_clients/test_tutorial004.py @@ -82,6 +82,8 @@ def test_remove_tags(tmp_path: pathlib.Path): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_handling_errors/test_tutorial001.py b/tests/test_tutorial/test_handling_errors/test_tutorial001.py index c01850fae6..e22f1dafd4 100644 --- a/tests/test_tutorial/test_handling_errors/test_tutorial001.py +++ b/tests/test_tutorial/test_handling_errors/test_tutorial001.py @@ -72,6 +72,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_handling_errors/test_tutorial002.py b/tests/test_tutorial/test_handling_errors/test_tutorial002.py index 09366a86fa..991478a0fe 100644 --- a/tests/test_tutorial/test_handling_errors/test_tutorial002.py +++ b/tests/test_tutorial/test_handling_errors/test_tutorial002.py @@ -72,6 +72,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_handling_errors/test_tutorial003.py b/tests/test_tutorial/test_handling_errors/test_tutorial003.py index 51ac3e7b28..c303960bde 100644 --- a/tests/test_tutorial/test_handling_errors/test_tutorial003.py +++ b/tests/test_tutorial/test_handling_errors/test_tutorial003.py @@ -73,6 +73,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_handling_errors/test_tutorial004.py b/tests/test_tutorial/test_handling_errors/test_tutorial004.py index 376bc8266f..f6ec59b4d0 100644 --- a/tests/test_tutorial/test_handling_errors/test_tutorial004.py +++ b/tests/test_tutorial/test_handling_errors/test_tutorial004.py @@ -78,6 +78,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_handling_errors/test_tutorial005.py b/tests/test_tutorial/test_handling_errors/test_tutorial005.py index 7bd947f194..a7fa4f0b64 100644 --- a/tests/test_tutorial/test_handling_errors/test_tutorial005.py +++ b/tests/test_tutorial/test_handling_errors/test_tutorial005.py @@ -102,6 +102,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_handling_errors/test_tutorial006.py b/tests/test_tutorial/test_handling_errors/test_tutorial006.py index e95e53d5ed..9cb57d857d 100644 --- a/tests/test_tutorial/test_handling_errors/test_tutorial006.py +++ b/tests/test_tutorial/test_handling_errors/test_tutorial006.py @@ -86,6 +86,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_header_param_models/test_tutorial001.py b/tests/test_tutorial/test_header_param_models/test_tutorial001.py index 1fa8aee461..2d14c698e0 100644 --- a/tests/test_tutorial/test_header_param_models/test_tutorial001.py +++ b/tests/test_tutorial/test_header_param_models/test_tutorial001.py @@ -187,6 +187,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_header_param_models/test_tutorial002.py b/tests/test_tutorial/test_header_param_models/test_tutorial002.py index 079a8f5402..478ac84087 100644 --- a/tests/test_tutorial/test_header_param_models/test_tutorial002.py +++ b/tests/test_tutorial/test_header_param_models/test_tutorial002.py @@ -184,6 +184,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_header_param_models/test_tutorial003.py b/tests/test_tutorial/test_header_param_models/test_tutorial003.py index 4c89d80ee2..00636c2b53 100644 --- a/tests/test_tutorial/test_header_param_models/test_tutorial003.py +++ b/tests/test_tutorial/test_header_param_models/test_tutorial003.py @@ -224,6 +224,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_header_params/test_tutorial001.py b/tests/test_tutorial/test_header_params/test_tutorial001.py index 88591b8225..60342f70a4 100644 --- a/tests/test_tutorial/test_header_params/test_tutorial001.py +++ b/tests/test_tutorial/test_header_params/test_tutorial001.py @@ -93,6 +93,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_header_params/test_tutorial002.py b/tests/test_tutorial/test_header_params/test_tutorial002.py index 229f96c1f8..f1ced99b1b 100644 --- a/tests/test_tutorial/test_header_params/test_tutorial002.py +++ b/tests/test_tutorial/test_header_params/test_tutorial002.py @@ -104,6 +104,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_header_params/test_tutorial003.py b/tests/test_tutorial/test_header_params/test_tutorial003.py index cf067ccf9e..382c3ae191 100644 --- a/tests/test_tutorial/test_header_params/test_tutorial003.py +++ b/tests/test_tutorial/test_header_params/test_tutorial003.py @@ -112,6 +112,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_metadata/test_tutorial001_1.py b/tests/test_tutorial/test_metadata/test_tutorial001_1.py index 40878ccfd4..10cb35c546 100644 --- a/tests/test_tutorial/test_metadata/test_tutorial001_1.py +++ b/tests/test_tutorial/test_metadata/test_tutorial001_1.py @@ -28,7 +28,7 @@ def test_openapi_schema(): }, "license": { "name": "Apache 2.0", - "identifier": "MIT", + "identifier": "Apache-2.0", }, "version": "0.0.1", }, diff --git a/tests/test_tutorial/test_openapi_callbacks/test_tutorial001.py b/tests/test_tutorial/test_openapi_callbacks/test_tutorial001.py index 6fde96cb5b..e8c98e8063 100644 --- a/tests/test_tutorial/test_openapi_callbacks/test_tutorial001.py +++ b/tests/test_tutorial/test_openapi_callbacks/test_tutorial001.py @@ -195,6 +195,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_openapi_webhooks/test_tutorial001.py b/tests/test_tutorial/test_openapi_webhooks/test_tutorial001.py index 27619489fa..c58e0fd02c 100644 --- a/tests/test_tutorial/test_openapi_webhooks/test_tutorial001.py +++ b/tests/test_tutorial/test_openapi_webhooks/test_tutorial001.py @@ -98,6 +98,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py index a95540731d..75b08a4e75 100644 --- a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py +++ b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py @@ -118,6 +118,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial001.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial001.py index 085d1f5e19..de81251d04 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial001.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial001.py @@ -150,6 +150,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial002.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial002.py index c7414d756a..28e5e7d8d1 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial002.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial002.py @@ -187,6 +187,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial003_tutorial004.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial003_tutorial004.py index 791db24625..e42c3e2b73 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial003_tutorial004.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial003_tutorial004.py @@ -172,6 +172,8 @@ def test_openapi_schema(client: TestClient, mod_name: str): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py index c5a3aec1d9..b684c9f5c2 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py @@ -117,6 +117,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_path_params/test_tutorial001.py b/tests/test_tutorial/test_path_params/test_tutorial001.py index a898e386fb..f54626f339 100644 --- a/tests/test_tutorial/test_path_params/test_tutorial001.py +++ b/tests/test_tutorial/test_path_params/test_tutorial001.py @@ -80,6 +80,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_params/test_tutorial002.py b/tests/test_tutorial/test_path_params/test_tutorial002.py index 0bfc9f807e..46da41b481 100644 --- a/tests/test_tutorial/test_path_params/test_tutorial002.py +++ b/tests/test_tutorial/test_path_params/test_tutorial002.py @@ -88,6 +88,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_params/test_tutorial003.py b/tests/test_tutorial/test_path_params/test_tutorial003.py index cd2c39ab06..6ac92c87e3 100644 --- a/tests/test_tutorial/test_path_params/test_tutorial003.py +++ b/tests/test_tutorial/test_path_params/test_tutorial003.py @@ -97,6 +97,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_params/test_tutorial004.py b/tests/test_tutorial/test_path_params/test_tutorial004.py index f7f233ccff..8f460fb695 100644 --- a/tests/test_tutorial/test_path_params/test_tutorial004.py +++ b/tests/test_tutorial/test_path_params/test_tutorial004.py @@ -73,6 +73,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_path_params/test_tutorial005.py b/tests/test_tutorial/test_path_params/test_tutorial005.py index 86ccce7b6d..3e3766e845 100644 --- a/tests/test_tutorial/test_path_params/test_tutorial005.py +++ b/tests/test_tutorial/test_path_params/test_tutorial005.py @@ -110,6 +110,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial001.py b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial001.py index f1e3041030..a4d68d01b4 100644 --- a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial001.py +++ b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial001.py @@ -128,6 +128,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial002_tutorial003.py b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial002_tutorial003.py index 467c915dcd..37533bd228 100644 --- a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial002_tutorial003.py +++ b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial002_tutorial003.py @@ -134,6 +134,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial004.py b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial004.py index d3593c984c..a9c111a594 100644 --- a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial004.py +++ b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial004.py @@ -149,6 +149,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial005.py b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial005.py index 296192593b..e0e976d6f3 100644 --- a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial005.py +++ b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial005.py @@ -166,6 +166,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial006.py b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial006.py index 9dc7d7aac2..2004ad1d2b 100644 --- a/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial006.py +++ b/tests/test_tutorial/test_path_params_numeric_validations/test_tutorial006.py @@ -185,6 +185,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_query_param_models/test_tutorial001.py b/tests/test_tutorial/test_query_param_models/test_tutorial001.py index d3ce57121d..38b767154a 100644 --- a/tests/test_tutorial/test_query_param_models/test_tutorial001.py +++ b/tests/test_tutorial/test_query_param_models/test_tutorial001.py @@ -207,6 +207,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_query_param_models/test_tutorial002.py b/tests/test_tutorial/test_query_param_models/test_tutorial002.py index 96abce6ab9..b173a2df45 100644 --- a/tests/test_tutorial/test_query_param_models/test_tutorial002.py +++ b/tests/test_tutorial/test_query_param_models/test_tutorial002.py @@ -213,6 +213,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_query_params/test_tutorial001.py b/tests/test_tutorial/test_query_params/test_tutorial001.py index 4c92b57b8d..84e4557272 100644 --- a/tests/test_tutorial/test_query_params/test_tutorial001.py +++ b/tests/test_tutorial/test_query_params/test_tutorial001.py @@ -108,6 +108,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params/test_tutorial002.py b/tests/test_tutorial/test_query_params/test_tutorial002.py index ae3ee7613d..f725c80b32 100644 --- a/tests/test_tutorial/test_query_params/test_tutorial002.py +++ b/tests/test_tutorial/test_query_params/test_tutorial002.py @@ -109,6 +109,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params/test_tutorial003.py b/tests/test_tutorial/test_query_params/test_tutorial003.py index c0b7e3b133..9f1f2e6e4c 100644 --- a/tests/test_tutorial/test_query_params/test_tutorial003.py +++ b/tests/test_tutorial/test_query_params/test_tutorial003.py @@ -130,6 +130,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params/test_tutorial004.py b/tests/test_tutorial/test_query_params/test_tutorial004.py index 9be18b74df..e834f973a9 100644 --- a/tests/test_tutorial/test_query_params/test_tutorial004.py +++ b/tests/test_tutorial/test_query_params/test_tutorial004.py @@ -138,6 +138,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params/test_tutorial005.py b/tests/test_tutorial/test_query_params/test_tutorial005.py index 1030781472..36129dbc96 100644 --- a/tests/test_tutorial/test_query_params/test_tutorial005.py +++ b/tests/test_tutorial/test_query_params/test_tutorial005.py @@ -86,6 +86,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params/test_tutorial006.py b/tests/test_tutorial/test_query_params/test_tutorial006.py index 157322c7e3..473dc33661 100644 --- a/tests/test_tutorial/test_query_params/test_tutorial006.py +++ b/tests/test_tutorial/test_query_params/test_tutorial006.py @@ -137,6 +137,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial001.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial001.py index f1af7e08c1..069921629e 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial001.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial001.py @@ -103,6 +103,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial002.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial002.py index 62018b80b5..a043b5b2e7 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial002.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial002.py @@ -124,6 +124,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial003.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial003.py index a4ad7a63ba..68c6e6174f 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial003.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial003.py @@ -135,6 +135,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial004.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial004.py index 585989a827..79538f952b 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial004.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial004.py @@ -129,6 +129,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial005.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial005.py index 52462fe33b..fafbd0a7d0 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial005.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial005.py @@ -113,6 +113,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial006.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial006.py index 640cedce19..1d01492c66 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial006.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial006.py @@ -118,6 +118,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial006c.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial006c.py index f287b5dcd8..d31cb5036a 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial006c.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial006c.py @@ -130,6 +130,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial007.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial007.py index b17bc27719..e030902451 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial007.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial007.py @@ -118,6 +118,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial008.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial008.py index c631115744..186de5e062 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial008.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial008.py @@ -120,6 +120,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial009.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial009.py index 7e9d69d41c..b242a75c90 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial009.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial009.py @@ -105,6 +105,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py index 00889c5bf7..6a39130af2 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial010.py @@ -136,6 +136,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py index 11de33ae14..6ab279bf3e 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial011.py @@ -98,6 +98,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py index 1826928611..41bfeb3a7a 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py @@ -93,6 +93,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py index 46c367c86b..52c8147ffb 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial013.py @@ -93,6 +93,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py index 0feaccfa44..bb168f0fc3 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py @@ -93,6 +93,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial015.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial015.py index 82bb606a9b..32a990e748 100644 --- a/tests/test_tutorial/test_query_params_str_validations/test_tutorial015.py +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial015.py @@ -122,6 +122,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_request_files/test_tutorial001.py b/tests/test_tutorial/test_request_files/test_tutorial001.py index e0e1bbe639..db9b83b31a 100644 --- a/tests/test_tutorial/test_request_files/test_tutorial001.py +++ b/tests/test_tutorial/test_request_files/test_tutorial001.py @@ -183,6 +183,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_02.py b/tests/test_tutorial/test_request_files/test_tutorial001_02.py index 18948c5444..feeb5363ed 100644 --- a/tests/test_tutorial/test_request_files/test_tutorial001_02.py +++ b/tests/test_tutorial/test_request_files/test_tutorial001_02.py @@ -173,6 +173,8 @@ def test_openapi_schema(client: TestClient): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_03.py b/tests/test_tutorial/test_request_files/test_tutorial001_03.py index 53a7a0cf85..903452ac76 100644 --- a/tests/test_tutorial/test_request_files/test_tutorial001_03.py +++ b/tests/test_tutorial/test_request_files/test_tutorial001_03.py @@ -163,6 +163,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_request_files/test_tutorial002.py b/tests/test_tutorial/test_request_files/test_tutorial002.py index 03772419ad..4d9ff0e93d 100644 --- a/tests/test_tutorial/test_request_files/test_tutorial002.py +++ b/tests/test_tutorial/test_request_files/test_tutorial002.py @@ -223,6 +223,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_request_files/test_tutorial003.py b/tests/test_tutorial/test_request_files/test_tutorial003.py index fa4bfd5695..c9f7f09940 100644 --- a/tests/test_tutorial/test_request_files/test_tutorial003.py +++ b/tests/test_tutorial/test_request_files/test_tutorial003.py @@ -206,6 +206,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_request_form_models/test_tutorial001.py b/tests/test_tutorial/test_request_form_models/test_tutorial001.py index 0c43dd7b21..c4740ee72d 100644 --- a/tests/test_tutorial/test_request_form_models/test_tutorial001.py +++ b/tests/test_tutorial/test_request_form_models/test_tutorial001.py @@ -159,6 +159,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_request_form_models/test_tutorial002.py b/tests/test_tutorial/test_request_form_models/test_tutorial002.py index 238f8fa2ef..b07fce432a 100644 --- a/tests/test_tutorial/test_request_form_models/test_tutorial002.py +++ b/tests/test_tutorial/test_request_form_models/test_tutorial002.py @@ -177,6 +177,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_request_forms/test_tutorial001.py b/tests/test_tutorial/test_request_forms/test_tutorial001.py index 4276414fc2..f5f76306e9 100644 --- a/tests/test_tutorial/test_request_forms/test_tutorial001.py +++ b/tests/test_tutorial/test_request_forms/test_tutorial001.py @@ -161,6 +161,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_request_forms_and_files/test_tutorial001.py b/tests/test_tutorial/test_request_forms_and_files/test_tutorial001.py index 7fa4c3de57..cd05a1ccf1 100644 --- a/tests/test_tutorial/test_request_forms_and_files/test_tutorial001.py +++ b/tests/test_tutorial/test_request_forms_and_files/test_tutorial001.py @@ -216,6 +216,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_response_directly/test_tutorial001.py b/tests/test_tutorial/test_response_directly/test_tutorial001.py index 2d0c387195..76e7143bda 100644 --- a/tests/test_tutorial/test_response_directly/test_tutorial001.py +++ b/tests/test_tutorial/test_response_directly/test_tutorial001.py @@ -130,6 +130,8 @@ def test_openapi_schema_pv2(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_tutorial/test_response_model/test_tutorial001_tutorial001_01.py b/tests/test_tutorial/test_response_model/test_tutorial001_tutorial001_01.py index 10692f9904..265162f15f 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial001_tutorial001_01.py +++ b/tests/test_tutorial/test_response_model/test_tutorial001_tutorial001_01.py @@ -175,6 +175,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_response_model/test_tutorial002.py b/tests/test_tutorial/test_response_model/test_tutorial002.py index 216d4c420c..17027d3c10 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial002.py +++ b/tests/test_tutorial/test_response_model/test_tutorial002.py @@ -111,6 +111,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_response_model/test_tutorial003.py b/tests/test_tutorial/test_response_model/test_tutorial003.py index 35ed5572dd..a1477b7dfd 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial003.py +++ b/tests/test_tutorial/test_response_model/test_tutorial003.py @@ -126,6 +126,8 @@ def test_openapi_schema(client: TestClient): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_response_model/test_tutorial003_01.py b/tests/test_tutorial/test_response_model/test_tutorial003_01.py index fa1eb62770..a60a14ae8d 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial003_01.py +++ b/tests/test_tutorial/test_response_model/test_tutorial003_01.py @@ -139,6 +139,8 @@ def test_openapi_schema(client: TestClient): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_response_model/test_tutorial003_02.py b/tests/test_tutorial/test_response_model/test_tutorial003_02.py index b7507b7110..fcd5f9a1d4 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial003_02.py +++ b/tests/test_tutorial/test_response_model/test_tutorial003_02.py @@ -86,6 +86,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_response_model/test_tutorial003_05.py b/tests/test_tutorial/test_response_model/test_tutorial003_05.py index 19a7c601bb..e64ed1a804 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial003_05.py +++ b/tests/test_tutorial/test_response_model/test_tutorial003_05.py @@ -101,6 +101,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_response_model/test_tutorial004.py b/tests/test_tutorial/test_response_model/test_tutorial004.py index 9c0d95ebd0..d40bce261b 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial004.py +++ b/tests/test_tutorial/test_response_model/test_tutorial004.py @@ -117,6 +117,8 @@ def test_openapi_schema(client: TestClient): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_response_model/test_tutorial005.py b/tests/test_tutorial/test_response_model/test_tutorial005.py index 63e8535db0..55b2334d46 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial005.py +++ b/tests/test_tutorial/test_response_model/test_tutorial005.py @@ -135,6 +135,8 @@ def test_openapi_schema(client: TestClient): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_response_model/test_tutorial006.py b/tests/test_tutorial/test_response_model/test_tutorial006.py index 08ab659527..5d6f542b5d 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial006.py +++ b/tests/test_tutorial/test_response_model/test_tutorial006.py @@ -135,6 +135,8 @@ def test_openapi_schema(client: TestClient): "required": ["loc", "msg", "type"], "type": "object", "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "title": "Location", "type": "array", diff --git a/tests/test_tutorial/test_response_status_code/test_tutorial001_tutorial002.py b/tests/test_tutorial/test_response_status_code/test_tutorial001_tutorial002.py index ddf55a045d..8b6213e33d 100644 --- a/tests/test_tutorial/test_response_status_code/test_tutorial001_tutorial002.py +++ b/tests/test_tutorial/test_response_status_code/test_tutorial001_tutorial002.py @@ -78,6 +78,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial001.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial001.py index 82f69fd463..7f0105a26d 100644 --- a/tests/test_tutorial/test_schema_extra_example/test_tutorial001.py +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial001.py @@ -120,6 +120,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial002.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial002.py index 4f52408605..32707c2993 100644 --- a/tests/test_tutorial/test_schema_extra_example/test_tutorial002.py +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial002.py @@ -122,6 +122,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial003.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial003.py index 3529a9bf02..4f8f1394c1 100644 --- a/tests/test_tutorial/test_schema_extra_example/test_tutorial003.py +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial003.py @@ -124,6 +124,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial004.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial004.py index 9326e06290..3a0a7704bf 100644 --- a/tests/test_tutorial/test_schema_extra_example/test_tutorial004.py +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial004.py @@ -140,6 +140,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial005.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial005.py index 2d0dee48ca..b10f25e262 100644 --- a/tests/test_tutorial/test_schema_extra_example/test_tutorial005.py +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial005.py @@ -149,6 +149,8 @@ def test_openapi_schema(client: TestClient) -> None: }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, } diff --git a/tests/test_tutorial/test_security/test_tutorial003.py b/tests/test_tutorial/test_security/test_tutorial003.py index 6a786348cf..924b36b3ab 100644 --- a/tests/test_tutorial/test_security/test_tutorial003.py +++ b/tests/test_tutorial/test_security/test_tutorial003.py @@ -182,6 +182,8 @@ def test_openapi_schema(client: TestClient): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_security/test_tutorial004.py b/tests/test_tutorial/test_security/test_tutorial004.py index b5e3d39ef7..2b0df66a2e 100644 --- a/tests/test_tutorial/test_security/test_tutorial004.py +++ b/tests/test_tutorial/test_security/test_tutorial004.py @@ -334,6 +334,8 @@ def test_openapi_schema(mod: ModuleType): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_security/test_tutorial005.py b/tests/test_tutorial/test_security/test_tutorial005.py index 25b47f0adc..76b08860f5 100644 --- a/tests/test_tutorial/test_security/test_tutorial005.py +++ b/tests/test_tutorial/test_security/test_tutorial005.py @@ -379,6 +379,8 @@ def test_openapi_schema(mod: ModuleType): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001.py b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001.py index 275b234877..d0a0d5d388 100644 --- a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001.py +++ b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001.py @@ -121,6 +121,8 @@ def test_openapi_schema(client: TestClient) -> None: }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002.py b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002.py index 8230e39226..a2fa56f932 100644 --- a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002.py +++ b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002.py @@ -121,6 +121,8 @@ def test_openapi_schema(client: TestClient) -> None: }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_sql_databases/test_tutorial001.py b/tests/test_tutorial/test_sql_databases/test_tutorial001.py index 2c628f5257..aec20e42e1 100644 --- a/tests/test_tutorial/test_sql_databases/test_tutorial001.py +++ b/tests/test_tutorial/test_sql_databases/test_tutorial001.py @@ -335,6 +335,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_sql_databases/test_tutorial002.py b/tests/test_tutorial/test_sql_databases/test_tutorial002.py index c72c16e9ae..4ea7d5f647 100644 --- a/tests/test_tutorial/test_sql_databases/test_tutorial002.py +++ b/tests/test_tutorial/test_sql_databases/test_tutorial002.py @@ -416,6 +416,8 @@ def test_openapi_schema(client: TestClient): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_tutorial/test_using_request_directly/test_tutorial001.py b/tests/test_tutorial/test_using_request_directly/test_tutorial001.py index 33e661b164..b55bfb4567 100644 --- a/tests/test_tutorial/test_using_request_directly/test_tutorial001.py +++ b/tests/test_tutorial/test_using_request_directly/test_tutorial001.py @@ -76,6 +76,8 @@ def test_openapi(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [ diff --git a/tests/test_union_body.py b/tests/test_union_body.py index ee7fcc4231..ee56bb6eb1 100644 --- a/tests/test_union_body.py +++ b/tests/test_union_body.py @@ -111,6 +111,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_union_body_discriminator.py b/tests/test_union_body_discriminator.py index 6c31649bcc..4afe7be4b4 100644 --- a/tests/test_union_body_discriminator.py +++ b/tests/test_union_body_discriminator.py @@ -154,6 +154,8 @@ def test_discriminator_pydantic_v2() -> None: }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_union_body_discriminator_annotated.py b/tests/test_union_body_discriminator_annotated.py index 42a6aed24c..6644d106c8 100644 --- a/tests/test_union_body_discriminator_annotated.py +++ b/tests/test_union_body_discriminator_annotated.py @@ -181,6 +181,8 @@ def test_openapi_schema(client: TestClient) -> None: }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_union_forms.py b/tests/test_union_forms.py index 018949f0c7..d90d0753a9 100644 --- a/tests/test_union_forms.py +++ b/tests/test_union_forms.py @@ -136,6 +136,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/test_union_inherited_body.py b/tests/test_union_inherited_body.py index 3c062e7f5a..6b284c68c3 100644 --- a/tests/test_union_inherited_body.py +++ b/tests/test_union_inherited_body.py @@ -117,6 +117,8 @@ def test_openapi_schema(): }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, + "input": {"title": "Input"}, + "ctx": {"title": "Context", "type": "object"}, }, }, "HTTPValidationError": { diff --git a/tests/test_webhooks_security.py b/tests/test_webhooks_security.py index 982ae1e21d..c2c2809b2f 100644 --- a/tests/test_webhooks_security.py +++ b/tests/test_webhooks_security.py @@ -106,6 +106,8 @@ def test_openapi_schema(): }, "ValidationError": { "properties": { + "ctx": {"title": "Context", "type": "object"}, + "input": {"title": "Input"}, "loc": { "items": { "anyOf": [{"type": "string"}, {"type": "integer"}] diff --git a/tests/utils.py b/tests/utils.py index efa0bfd52b..4cbfee79f5 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -6,8 +6,8 @@ needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires pyth needs_py310 = pytest.mark.skipif( sys.version_info < (3, 10), reason="requires python3.10+" ) -needs_py_lt_314 = pytest.mark.skipif( - sys.version_info >= (3, 14), reason="requires python3.13-" +needs_py314 = pytest.mark.skipif( + sys.version_info < (3, 14), reason="requires python3.14+" ) diff --git a/uv.lock b/uv.lock index 9ae2220e0c..931a27021b 100644 --- a/uv.lock +++ b/uv.lock @@ -7,6 +7,18 @@ resolution-markers = [ "python_full_version < '3.10'", ] +[[package]] +name = "a2wsgi" +version = "1.10.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/cb/822c56fbea97e9eee201a2e434a80437f6750ebcb1ed307ee3a0a7505b14/a2wsgi-1.10.10.tar.gz", hash = "sha256:a5bcffb52081ba39df0d5e9a884fc6f819d92e3a42389343ba77cbf809fe1f45", size = 18799, upload-time = "2025-06-18T09:00:10.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/d5/349aba3dc421e73cbd4958c0ce0a4f1aa3a738bc0d7de75d2f40ed43a535/a2wsgi-1.10.10-py3-none-any.whl", hash = "sha256:d2b21379479718539dc15fce53b876251a0efe7615352dfe49f6ad1bc507848d", size = 17389, upload-time = "2025-06-18T09:00:09.676Z" }, +] + [[package]] name = "ag-ui-protocol" version = "0.1.10" @@ -1058,6 +1070,7 @@ standard-no-fastapi-cloud-cli = [ [package.dev-dependencies] dev = [ + { name = "a2wsgi" }, { name = "anyio", extra = ["trio"] }, { name = "black" }, { name = "cairosvg" }, @@ -1131,6 +1144,7 @@ github-actions = [ { name = "smokeshow" }, ] tests = [ + { name = "a2wsgi" }, { name = "anyio", extra = ["trio"] }, { name = "coverage", version = "7.10.7", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version < '3.10'" }, { name = "coverage", version = "7.13.1", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version >= '3.10'" }, @@ -1197,13 +1211,14 @@ provides-extras = ["standard", "standard-no-fastapi-cloud-cli", "all"] [package.metadata.requires-dev] dev = [ + { name = "a2wsgi", specifier = ">=1.9.0,<=2.0.0" }, { name = "anyio", extras = ["trio"], specifier = ">=3.2.1,<5.0.0" }, { name = "black", specifier = "==25.1.0" }, { name = "cairosvg", specifier = "==2.8.2" }, { name = "coverage", extras = ["toml"], specifier = ">=6.5.0,<8.0" }, { name = "dirty-equals", specifier = "==0.9.0" }, { name = "flask", specifier = ">=1.1.2,<4.0.0" }, - { name = "gitpython", specifier = "==3.1.45" }, + { name = "gitpython", specifier = "==3.1.46" }, { name = "griffe-typingdoc", specifier = "==0.3.0" }, { name = "griffe-warnings-deprecated", specifier = "==1.1.0" }, { name = "httpx", specifier = ">=0.23.0,<1.0.0" }, @@ -1211,7 +1226,7 @@ dev = [ { name = "jieba", specifier = "==0.42.1" }, { name = "markdown-include-variants", specifier = "==0.0.8" }, { name = "mdx-include", specifier = ">=1.4.1,<2.0.0" }, - { name = "mkdocs-macros-plugin", specifier = "==1.4.1" }, + { name = "mkdocs-macros-plugin", specifier = "==1.5.0" }, { name = "mkdocs-material", specifier = "==9.7.0" }, { name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" }, { name = "mkdocstrings", extras = ["python"], specifier = "==0.30.1" }, @@ -1227,10 +1242,10 @@ dev = [ { name = "pytest-codspeed", specifier = "==4.2.0" }, { name = "python-slugify", specifier = "==8.0.4" }, { name = "pyyaml", specifier = ">=5.3.1,<7.0.0" }, - { name = "ruff", specifier = "==0.14.3" }, - { name = "sqlmodel", specifier = "==0.0.27" }, + { name = "ruff", specifier = "==0.14.14" }, + { name = "sqlmodel", specifier = "==0.0.31" }, { name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" }, - { name = "typer", specifier = "==0.16.0" }, + { name = "typer", specifier = "==0.21.1" }, { name = "types-orjson", specifier = "==3.6.2" }, { name = "types-ujson", specifier = "==5.10.0.20240515" }, ] @@ -1243,19 +1258,19 @@ docs = [ { name = "jieba", specifier = "==0.42.1" }, { name = "markdown-include-variants", specifier = "==0.0.8" }, { name = "mdx-include", specifier = ">=1.4.1,<2.0.0" }, - { name = "mkdocs-macros-plugin", specifier = "==1.4.1" }, + { name = "mkdocs-macros-plugin", specifier = "==1.5.0" }, { name = "mkdocs-material", specifier = "==9.7.0" }, { name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" }, { name = "mkdocstrings", extras = ["python"], specifier = "==0.30.1" }, { name = "pillow", specifier = "==11.3.0" }, { name = "python-slugify", specifier = "==8.0.4" }, { name = "pyyaml", specifier = ">=5.3.1,<7.0.0" }, - { name = "ruff", specifier = "==0.14.3" }, - { name = "typer", specifier = "==0.16.0" }, + { name = "ruff", specifier = "==0.14.14" }, + { name = "typer", specifier = "==0.21.1" }, ] docs-tests = [ { name = "httpx", specifier = ">=0.23.0,<1.0.0" }, - { name = "ruff", specifier = "==0.14.3" }, + { name = "ruff", specifier = "==0.14.14" }, ] github-actions = [ { name = "httpx", specifier = ">=0.27.0,<1.0.0" }, @@ -1266,6 +1281,7 @@ github-actions = [ { name = "smokeshow", specifier = ">=0.5.0" }, ] tests = [ + { name = "a2wsgi", specifier = ">=1.9.0,<=2.0.0" }, { name = "anyio", extras = ["trio"], specifier = ">=3.2.1,<5.0.0" }, { name = "coverage", extras = ["toml"], specifier = ">=6.5.0,<8.0" }, { name = "dirty-equals", specifier = "==0.9.0" }, @@ -1278,14 +1294,14 @@ tests = [ { name = "pytest", specifier = ">=7.1.3,<9.0.0" }, { name = "pytest-codspeed", specifier = "==4.2.0" }, { name = "pyyaml", specifier = ">=5.3.1,<7.0.0" }, - { name = "ruff", specifier = "==0.14.3" }, - { name = "sqlmodel", specifier = "==0.0.27" }, + { name = "ruff", specifier = "==0.14.14" }, + { name = "sqlmodel", specifier = "==0.0.31" }, { name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" }, { name = "types-orjson", specifier = "==3.6.2" }, { name = "types-ujson", specifier = "==5.10.0.20240515" }, ] translations = [ - { name = "gitpython", specifier = "==3.1.45" }, + { name = "gitpython", specifier = "==3.1.46" }, { name = "pydantic-ai", specifier = "==0.4.10" }, { name = "pygithub", specifier = "==2.8.1" }, ] @@ -1632,15 +1648,15 @@ wheels = [ [[package]] name = "gitpython" -version = "3.1.45" +version = "3.1.46" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", size = 215076, upload-time = "2025-07-24T03:45:54.871Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168, upload-time = "2025-07-24T03:45:52.517Z" }, + { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, ] [[package]] @@ -2652,7 +2668,7 @@ wheels = [ [[package]] name = "mkdocs-macros-plugin" -version = "1.4.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "hjson" }, @@ -2667,9 +2683,9 @@ dependencies = [ { name = "termcolor", version = "3.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "termcolor", version = "3.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1b/42/bb2ceed148c77f82b57c6d3b7f584f0f34ababf7a9a8ff85809380d1f400/mkdocs_macros_plugin-1.4.1.tar.gz", hash = "sha256:55a9c93871e3744cdeb0736316783d60830a6d5d97b1132364e6b491607f2332", size = 35094, upload-time = "2025-10-25T12:37:20.689Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/15/e6a44839841ebc9c5872fa0e6fad1c3757424e4fe026093b68e9f386d136/mkdocs_macros_plugin-1.5.0.tar.gz", hash = "sha256:12aa45ce7ecb7a445c66b9f649f3dd05e9b92e8af6bc65e4acd91d26f878c01f", size = 37730, upload-time = "2025-11-13T08:08:55.545Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/ec/e6e96a7ae8df414f03f43681821234b0d3b86666f7b91f70ab26775a8809/mkdocs_macros_plugin-1.4.1-py3-none-any.whl", hash = "sha256:5a9e483f6056fe7ad0923802affe699233ca468672e20a9640dba237165b3240", size = 40155, upload-time = "2025-10-25T12:37:19.417Z" }, + { url = "https://files.pythonhosted.org/packages/51/62/9fffba5bb9ed3d31a932ad35038ba9483d59850256ee0fea7f1187173983/mkdocs_macros_plugin-1.5.0-py3-none-any.whl", hash = "sha256:c10fabd812bf50f9170609d0ed518e54f1f0e12c334ac29141723a83c881dd6f", size = 44626, upload-time = "2025-11-13T08:08:53.878Z" }, ] [[package]] @@ -3238,11 +3254,11 @@ argon2 = [ [[package]] name = "pyasn1" -version = "0.6.1" +version = "0.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, ] [[package]] @@ -4248,28 +4264,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.3" +version = "0.14.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/62/50b7727004dfe361104dfbf898c45a9a2fdfad8c72c04ae62900224d6ecf/ruff-0.14.3.tar.gz", hash = "sha256:4ff876d2ab2b161b6de0aa1f5bd714e8e9b4033dc122ee006925fbacc4f62153", size = 5558687, upload-time = "2025-10-31T00:26:26.878Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/06/f71e3a86b2df0dfa2d2f72195941cd09b44f87711cb7fa5193732cb9a5fc/ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b", size = 4515732, upload-time = "2026-01-22T22:30:17.527Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/8e/0c10ff1ea5d4360ab8bfca4cb2c9d979101a391f3e79d2616c9bf348cd26/ruff-0.14.3-py3-none-linux_armv6l.whl", hash = "sha256:876b21e6c824f519446715c1342b8e60f97f93264012de9d8d10314f8a79c371", size = 12535613, upload-time = "2025-10-31T00:25:44.302Z" }, - { url = "https://files.pythonhosted.org/packages/d3/c8/6724f4634c1daf52409fbf13fefda64aa9c8f81e44727a378b7b73dc590b/ruff-0.14.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6fd8c79b457bedd2abf2702b9b472147cd860ed7855c73a5247fa55c9117654", size = 12855812, upload-time = "2025-10-31T00:25:47.793Z" }, - { url = "https://files.pythonhosted.org/packages/de/03/db1bce591d55fd5f8a08bb02517fa0b5097b2ccabd4ea1ee29aa72b67d96/ruff-0.14.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:71ff6edca490c308f083156938c0c1a66907151263c4abdcb588602c6e696a14", size = 11944026, upload-time = "2025-10-31T00:25:49.657Z" }, - { url = "https://files.pythonhosted.org/packages/0b/75/4f8dbd48e03272715d12c87dc4fcaaf21b913f0affa5f12a4e9c6f8a0582/ruff-0.14.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:786ee3ce6139772ff9272aaf43296d975c0217ee1b97538a98171bf0d21f87ed", size = 12356818, upload-time = "2025-10-31T00:25:51.949Z" }, - { url = "https://files.pythonhosted.org/packages/ec/9b/506ec5b140c11d44a9a4f284ea7c14ebf6f8b01e6e8917734a3325bff787/ruff-0.14.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cd6291d0061811c52b8e392f946889916757610d45d004e41140d81fb6cd5ddc", size = 12336745, upload-time = "2025-10-31T00:25:54.248Z" }, - { url = "https://files.pythonhosted.org/packages/c7/e1/c560d254048c147f35e7f8131d30bc1f63a008ac61595cf3078a3e93533d/ruff-0.14.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a497ec0c3d2c88561b6d90f9c29f5ae68221ac00d471f306fa21fa4264ce5fcd", size = 13101684, upload-time = "2025-10-31T00:25:56.253Z" }, - { url = "https://files.pythonhosted.org/packages/a5/32/e310133f8af5cd11f8cc30f52522a3ebccc5ea5bff4b492f94faceaca7a8/ruff-0.14.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e231e1be58fc568950a04fbe6887c8e4b85310e7889727e2b81db205c45059eb", size = 14535000, upload-time = "2025-10-31T00:25:58.397Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a1/7b0470a22158c6d8501eabc5e9b6043c99bede40fa1994cadf6b5c2a61c7/ruff-0.14.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:469e35872a09c0e45fecf48dd960bfbce056b5db2d5e6b50eca329b4f853ae20", size = 14156450, upload-time = "2025-10-31T00:26:00.889Z" }, - { url = "https://files.pythonhosted.org/packages/0a/96/24bfd9d1a7f532b560dcee1a87096332e461354d3882124219bcaff65c09/ruff-0.14.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d6bc90307c469cb9d28b7cfad90aaa600b10d67c6e22026869f585e1e8a2db0", size = 13568414, upload-time = "2025-10-31T00:26:03.291Z" }, - { url = "https://files.pythonhosted.org/packages/a7/e7/138b883f0dfe4ad5b76b58bf4ae675f4d2176ac2b24bdd81b4d966b28c61/ruff-0.14.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2f8a0bbcffcfd895df39c9a4ecd59bb80dca03dc43f7fb63e647ed176b741e", size = 13315293, upload-time = "2025-10-31T00:26:05.708Z" }, - { url = "https://files.pythonhosted.org/packages/33/f4/c09bb898be97b2eb18476b7c950df8815ef14cf956074177e9fbd40b7719/ruff-0.14.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:678fdd7c7d2d94851597c23ee6336d25f9930b460b55f8598e011b57c74fd8c5", size = 13539444, upload-time = "2025-10-31T00:26:08.09Z" }, - { url = "https://files.pythonhosted.org/packages/9c/aa/b30a1db25fc6128b1dd6ff0741fa4abf969ded161599d07ca7edd0739cc0/ruff-0.14.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1ec1ac071e7e37e0221d2f2dbaf90897a988c531a8592a6a5959f0603a1ecf5e", size = 12252581, upload-time = "2025-10-31T00:26:10.297Z" }, - { url = "https://files.pythonhosted.org/packages/da/13/21096308f384d796ffe3f2960b17054110a9c3828d223ca540c2b7cc670b/ruff-0.14.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afcdc4b5335ef440d19e7df9e8ae2ad9f749352190e96d481dc501b753f0733e", size = 12307503, upload-time = "2025-10-31T00:26:12.646Z" }, - { url = "https://files.pythonhosted.org/packages/cb/cc/a350bac23f03b7dbcde3c81b154706e80c6f16b06ff1ce28ed07dc7b07b0/ruff-0.14.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7bfc42f81862749a7136267a343990f865e71fe2f99cf8d2958f684d23ce3dfa", size = 12675457, upload-time = "2025-10-31T00:26:15.044Z" }, - { url = "https://files.pythonhosted.org/packages/cb/76/46346029fa2f2078826bc88ef7167e8c198e58fe3126636e52f77488cbba/ruff-0.14.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a65e448cfd7e9c59fae8cf37f9221585d3354febaad9a07f29158af1528e165f", size = 13403980, upload-time = "2025-10-31T00:26:17.81Z" }, - { url = "https://files.pythonhosted.org/packages/9f/a4/35f1ef68c4e7b236d4a5204e3669efdeefaef21f0ff6a456792b3d8be438/ruff-0.14.3-py3-none-win32.whl", hash = "sha256:f3d91857d023ba93e14ed2d462ab62c3428f9bbf2b4fbac50a03ca66d31991f7", size = 12500045, upload-time = "2025-10-31T00:26:20.503Z" }, - { url = "https://files.pythonhosted.org/packages/03/15/51960ae340823c9859fb60c63301d977308735403e2134e17d1d2858c7fb/ruff-0.14.3-py3-none-win_amd64.whl", hash = "sha256:d7b7006ac0756306db212fd37116cce2bd307e1e109375e1c6c106002df0ae5f", size = 13594005, upload-time = "2025-10-31T00:26:22.533Z" }, - { url = "https://files.pythonhosted.org/packages/b7/73/4de6579bac8e979fca0a77e54dec1f1e011a0d268165eb8a9bc0982a6564/ruff-0.14.3-py3-none-win_arm64.whl", hash = "sha256:26eb477ede6d399d898791d01961e16b86f02bc2486d0d1a7a9bb2379d055dc1", size = 12590017, upload-time = "2025-10-31T00:26:24.52Z" }, + { url = "https://files.pythonhosted.org/packages/d2/89/20a12e97bc6b9f9f68343952da08a8099c57237aef953a56b82711d55edd/ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed", size = 10467650, upload-time = "2026-01-22T22:30:08.578Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b1/c5de3fd2d5a831fcae21beda5e3589c0ba67eec8202e992388e4b17a6040/ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c", size = 10883245, upload-time = "2026-01-22T22:30:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7c/3c1db59a10e7490f8f6f8559d1db8636cbb13dccebf18686f4e3c9d7c772/ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de", size = 10231273, upload-time = "2026-01-22T22:30:34.642Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6e/5e0e0d9674be0f8581d1f5e0f0a04761203affce3232c1a1189d0e3b4dad/ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e", size = 10585753, upload-time = "2026-01-22T22:30:31.781Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/754ab09f46ff1884d422dc26d59ba18b4e5d355be147721bb2518aa2a014/ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8", size = 10286052, upload-time = "2026-01-22T22:30:24.827Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cc/e71f88dd2a12afb5f50733851729d6b571a7c3a35bfdb16c3035132675a0/ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906", size = 11043637, upload-time = "2026-01-22T22:30:13.239Z" }, + { url = "https://files.pythonhosted.org/packages/67/b2/397245026352494497dac935d7f00f1468c03a23a0c5db6ad8fc49ca3fb2/ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480", size = 12194761, upload-time = "2026-01-22T22:30:22.542Z" }, + { url = "https://files.pythonhosted.org/packages/5b/06/06ef271459f778323112c51b7587ce85230785cd64e91772034ddb88f200/ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df", size = 12005701, upload-time = "2026-01-22T22:30:20.499Z" }, + { url = "https://files.pythonhosted.org/packages/41/d6/99364514541cf811ccc5ac44362f88df66373e9fec1b9d1c4cc830593fe7/ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b", size = 11282455, upload-time = "2026-01-22T22:29:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/ca/71/37daa46f89475f8582b7762ecd2722492df26421714a33e72ccc9a84d7a5/ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974", size = 11215882, upload-time = "2026-01-22T22:29:57.032Z" }, + { url = "https://files.pythonhosted.org/packages/2c/10/a31f86169ec91c0705e618443ee74ede0bdd94da0a57b28e72db68b2dbac/ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66", size = 11180549, upload-time = "2026-01-22T22:30:27.175Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1e/c723f20536b5163adf79bdd10c5f093414293cdf567eed9bdb7b83940f3f/ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13", size = 10543416, upload-time = "2026-01-22T22:30:01.964Z" }, + { url = "https://files.pythonhosted.org/packages/3e/34/8a84cea7e42c2d94ba5bde1d7a4fae164d6318f13f933d92da6d7c2041ff/ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412", size = 10285491, upload-time = "2026-01-22T22:30:29.51Z" }, + { url = "https://files.pythonhosted.org/packages/55/ef/b7c5ea0be82518906c978e365e56a77f8de7678c8bb6651ccfbdc178c29f/ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3", size = 10733525, upload-time = "2026-01-22T22:30:06.499Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/aaf1dfbcc53a2811f6cc0a1759de24e4b03e02ba8762daabd9b6bd8c59e3/ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b", size = 11315626, upload-time = "2026-01-22T22:30:36.848Z" }, + { url = "https://files.pythonhosted.org/packages/2c/aa/9f89c719c467dfaf8ad799b9bae0df494513fb21d31a6059cb5870e57e74/ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167", size = 10502442, upload-time = "2026-01-22T22:30:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/87/44/90fa543014c45560cae1fffc63ea059fb3575ee6e1cb654562197e5d16fb/ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd", size = 11630486, upload-time = "2026-01-22T22:30:10.852Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, ] [[package]] @@ -4415,15 +4431,15 @@ wheels = [ [[package]] name = "sqlmodel" -version = "0.0.27" +version = "0.0.31" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/5a/693d90866233e837d182da76082a6d4c2303f54d3aaaa5c78e1238c5d863/sqlmodel-0.0.27.tar.gz", hash = "sha256:ad1227f2014a03905aef32e21428640848ac09ff793047744a73dfdd077ff620", size = 118053, upload-time = "2025-10-08T16:39:11.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/56/b8/e7cd6def4a773f25d6e29ffce63ccbfd6cf9488b804ab6fb9b80d334b39d/sqlmodel-0.0.31.tar.gz", hash = "sha256:2d41a8a9ee05e40736e2f9db8ea28cbfe9b5d4e5a18dd139e80605025e0c516c", size = 94952, upload-time = "2025-12-28T12:35:01.436Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/92/c35e036151fe53822893979f8a13e6f235ae8191f4164a79ae60a95d66aa/sqlmodel-0.0.27-py3-none-any.whl", hash = "sha256:667fe10aa8ff5438134668228dc7d7a08306f4c5c4c7e6ad3ad68defa0e7aa49", size = 29131, upload-time = "2025-10-08T16:39:10.917Z" }, + { url = "https://files.pythonhosted.org/packages/6c/72/5aa5be921800f6418a949a73c9bb7054890881143e6bc604a93d228a95a3/sqlmodel-0.0.31-py3-none-any.whl", hash = "sha256:6d946d56cac4c2db296ba1541357cee2e795d68174e2043cd138b916794b1513", size = 27093, upload-time = "2025-12-28T12:35:00.108Z" }, ] [[package]] @@ -4737,7 +4753,7 @@ wheels = [ [[package]] name = "typer" -version = "0.16.0" +version = "0.21.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, @@ -4746,9 +4762,9 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload-time = "2025-05-26T14:30:31.824Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload-time = "2025-05-26T14:30:30.523Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, ] [[package]]