mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-28 16:49:26 -05:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d90030c1e2 | ||
|
|
3639fb00be | ||
|
|
7eabff43de | ||
|
|
a27fb4764b | ||
|
|
7710a34800 | ||
|
|
6320832178 | ||
|
|
26f27982ac | ||
|
|
d974fbdda0 | ||
|
|
ccc7c8fef9 | ||
|
|
b021569913 | ||
|
|
48676b4f11 | ||
|
|
b1102e2388 | ||
|
|
31920eff62 | ||
|
|
4516a48c7c | ||
|
|
f8878f3a98 | ||
|
|
5c8fa58fd0 | ||
|
|
987d2f9a92 | ||
|
|
920110276a | ||
|
|
b397ad9e52 | ||
|
|
498ba94bfc | ||
|
|
6ebe753908 | ||
|
|
3aee64b94f | ||
|
|
001473ab66 | ||
|
|
8c9c536c0a | ||
|
|
70137c0f7d | ||
|
|
286fd694ea | ||
|
|
e157cf4b96 | ||
|
|
235300c1d2 | ||
|
|
c868581ce7 | ||
|
|
7e67a91b08 | ||
|
|
e24299b2ff | ||
|
|
e24a500292 | ||
|
|
5451d05bc8 | ||
|
|
2b3b416122 | ||
|
|
08b817a842 | ||
|
|
2e788bbbdc | ||
|
|
9ec452a154 | ||
|
|
2c937aabef | ||
|
|
f6872dd298 | ||
|
|
cbd7b986e7 | ||
|
|
1e6d95ce6d | ||
|
|
540d8ff398 | ||
|
|
261bc2d387 | ||
|
|
db554ca094 | ||
|
|
ac893a4446 | ||
|
|
b81d29fc00 | ||
|
|
10a13d05c4 | ||
|
|
d51936754d | ||
|
|
030012bf4c | ||
|
|
fc94d904c9 | ||
|
|
39b4692525 | ||
|
|
15afe2e301 | ||
|
|
03b24b5a52 | ||
|
|
3aeaa0a6a8 | ||
|
|
5cbb81cc26 | ||
|
|
eea196f4a5 | ||
|
|
126a9b33c9 | ||
|
|
57a9a64435 | ||
|
|
ad33193f2c | ||
|
|
c6c45ae488 | ||
|
|
828079cb6e | ||
|
|
4740ccdcce | ||
|
|
49b18c87a0 | ||
|
|
42a3b1526e | ||
|
|
83332ff9b2 | ||
|
|
8cde8dc2a9 | ||
|
|
e86ef5e57d | ||
|
|
8a6d81afad | ||
|
|
f9352c18de | ||
|
|
38d409dd67 | ||
|
|
e814707cd1 | ||
|
|
640a5b6fc3 | ||
|
|
6e8da9d00a | ||
|
|
25ee2357d7 | ||
|
|
27d0ccc11c | ||
|
|
b6b031b456 | ||
|
|
495ff5baa9 | ||
|
|
50b307c9f6 | ||
|
|
2bb94fb90b | ||
|
|
2d7d5dafb0 | ||
|
|
701f5791d3 | ||
|
|
eee8d4c58a | ||
|
|
0c24d0607b | ||
|
|
f97c8de41a | ||
|
|
52c1488a37 | ||
|
|
3958e5a113 | ||
|
|
fb19d9895d | ||
|
|
ae724b05ce | ||
|
|
c73e895b86 | ||
|
|
0310af3557 | ||
|
|
633ed1d8af | ||
|
|
133fe8170a | ||
|
|
df8f281674 |
5
.github/workflows/deploy-docs.yml
vendored
5
.github/workflows/deploy-docs.yml
vendored
@@ -62,10 +62,7 @@ jobs:
|
||||
env:
|
||||
PROJECT_NAME: fastapitiangolo
|
||||
BRANCH: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }}
|
||||
# TODO: Use v3 when it's fixed, probably in v3.11
|
||||
# https://github.com/cloudflare/wrangler-action/issues/307
|
||||
uses: cloudflare/wrangler-action@v3.13
|
||||
# uses: cloudflare/wrangler-action@v3
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
@@ -57,11 +57,13 @@ The key features are:
|
||||
<a href="https://zuplo.link/fastapi-gh" target="_blank" title="Zuplo: Scale, Protect, Document, and Monetize your FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/zuplo.png"></a>
|
||||
<a href="https://liblab.com?utm_source=fastapi" target="_blank" title="liblab - Generate SDKs from FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/liblab.png"></a>
|
||||
<a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" target="_blank" title="Deploy & scale any full-stack web app on Render. Focus on building apps, not infra."><img src="https://fastapi.tiangolo.com/img/sponsors/render.svg"></a>
|
||||
<a href="https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=badge&utm_campaign=fastapi" target="_blank" title="Cut Code Review Time & Bugs in Half with CodeRabbit"><img src="https://fastapi.tiangolo.com/img/sponsors/coderabbit.png"></a>
|
||||
<a href="https://github.com/deepset-ai/haystack/" target="_blank" title="Build powerful search from composable, open source building blocks"><img src="https://fastapi.tiangolo.com/img/sponsors/haystack-fastapi.svg"></a>
|
||||
<a href="https://databento.com/" target="_blank" title="Pay as you go for market data"><img src="https://fastapi.tiangolo.com/img/sponsors/databento.svg"></a>
|
||||
<a href="https://speakeasy.com?utm_source=fastapi+repo&utm_medium=github+sponsorship" target="_blank" title="SDKs for your API | Speakeasy"><img src="https://fastapi.tiangolo.com/img/sponsors/speakeasy.png"></a>
|
||||
<a href="https://www.svix.com/" target="_blank" title="Svix - Webhooks as a service"><img src="https://fastapi.tiangolo.com/img/sponsors/svix.svg"></a>
|
||||
<a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" target="_blank" title="Stainless | Generate best-in-class SDKs"><img src="https://fastapi.tiangolo.com/img/sponsors/stainless.png"></a>
|
||||
<a href="https://www.permit.io/blog/implement-authorization-in-fastapi?utm_source=github&utm_medium=referral&utm_campaign=fastapi" target="_blank" title="Fine-Grained Authorization for FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/permit.png"></a>
|
||||
|
||||
<!-- /sponsors -->
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Əhatə">
|
||||
|
||||
@@ -5,15 +5,18 @@
|
||||
<em>FastAPI উচ্চক্ষমতা সম্পন্ন, সহজে শেখার এবং দ্রুত কোড করে প্রোডাকশনের জন্য ফ্রামওয়ার্ক।</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi?color=%2334D058" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -315,22 +315,6 @@ Wenn Sie einen Parameter erforderlich machen wollen, während Sie `Query` verwen
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
|
||||
### Erforderlich mit Ellipse (`...`)
|
||||
|
||||
Es gibt eine Alternative, die explizit deklariert, dass ein Wert erforderlich ist. Sie können als Default das <abbr title='Zeichenfolge, die einen Wert direkt darstellt, etwa 1, "hallowelt", True, None'>Literal</abbr> `...` setzen:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006b_an_py39.py hl[9] *}
|
||||
|
||||
/// info
|
||||
|
||||
Falls Sie das `...` bisher noch nicht gesehen haben: Es ist ein spezieller einzelner Wert, <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">Teil von Python und wird „Ellipsis“ genannt</a> (Deutsch: Ellipse).
|
||||
|
||||
Es wird von Pydantic und FastAPI verwendet, um explizit zu deklarieren, dass ein Wert erforderlich ist.
|
||||
|
||||
///
|
||||
|
||||
Dies wird **FastAPI** wissen lassen, dass dieser Parameter erforderlich ist.
|
||||
|
||||
### Erforderlich, kann `None` sein
|
||||
|
||||
Sie können deklarieren, dass ein Parameter `None` akzeptiert, aber dennoch erforderlich ist. Das zwingt Clients, den Wert zu senden, selbst wenn er `None` ist.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -148,22 +148,6 @@ q: Union[str, None] = Query(default=None, min_length=3)
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006.py hl[7] *}
|
||||
|
||||
### ✔ ⏮️ ❕ (`...`)
|
||||
|
||||
📤 🎛 🌌 🎯 📣 👈 💲 ✔. 👆 💪 ⚒ `default` 🔢 🔑 💲 `...`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006b.py hl[7] *}
|
||||
|
||||
/// info
|
||||
|
||||
🚥 👆 🚫 👀 👈 `...` ⏭: ⚫️ 🎁 👁 💲, ⚫️ <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">🍕 🐍 & 🤙 "❕"</a>.
|
||||
|
||||
⚫️ ⚙️ Pydantic & FastAPI 🎯 📣 👈 💲 ✔.
|
||||
|
||||
///
|
||||
|
||||
👉 🔜 ➡️ **FastAPI** 💭 👈 👉 🔢 ✔.
|
||||
|
||||
### ✔ ⏮️ `None`
|
||||
|
||||
👆 💪 📣 👈 🔢 💪 🚫 `None`, ✋️ 👈 ⚫️ ✔. 👉 🔜 ⚡ 👩💻 📨 💲, 🚥 💲 `None`.
|
||||
@@ -178,18 +162,6 @@ Pydantic, ❔ ⚫️❔ 🏋️ 🌐 💽 🔬 & 🛠️ FastAPI, ✔️
|
||||
|
||||
///
|
||||
|
||||
### ⚙️ Pydantic `Required` ↩️ ❕ (`...`)
|
||||
|
||||
🚥 👆 💭 😬 ⚙️ `...`, 👆 💪 🗄 & ⚙️ `Required` ⚪️➡️ Pydantic:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006d.py hl[2,8] *}
|
||||
|
||||
/// tip
|
||||
|
||||
💭 👈 🌅 💼, 🕐❔ 🕳 🚚, 👆 💪 🎯 🚫 `default` 🔢, 👆 🛎 🚫 ✔️ ⚙️ `...` 🚫 `Required`.
|
||||
|
||||
///
|
||||
|
||||
## 🔢 🔢 📇 / 💗 💲
|
||||
|
||||
🕐❔ 👆 🔬 🔢 🔢 🎯 ⏮️ `Query` 👆 💪 📣 ⚫️ 📨 📇 💲, ⚖️ 🙆♀ 🎏 🌌, 📨 💗 💲.
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
tiangolo:
|
||||
login: tiangolo
|
||||
count: 697
|
||||
count: 713
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
|
||||
url: https://github.com/tiangolo
|
||||
dependabot:
|
||||
login: dependabot
|
||||
count: 89
|
||||
count: 90
|
||||
avatarUrl: https://avatars.githubusercontent.com/in/29110?v=4
|
||||
url: https://github.com/apps/dependabot
|
||||
alejsdev:
|
||||
login: alejsdev
|
||||
count: 47
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=356f39ff3f0211c720b06d3dbb060e98884085e3&v=4
|
||||
url: https://github.com/alejsdev
|
||||
github-actions:
|
||||
login: github-actions
|
||||
count: 26
|
||||
@@ -15,7 +20,7 @@ github-actions:
|
||||
url: https://github.com/apps/github-actions
|
||||
Kludex:
|
||||
login: Kludex
|
||||
count: 22
|
||||
count: 23
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
|
||||
url: https://github.com/Kludex
|
||||
pre-commit-ci:
|
||||
@@ -23,11 +28,6 @@ pre-commit-ci:
|
||||
count: 22
|
||||
avatarUrl: https://avatars.githubusercontent.com/in/68672?v=4
|
||||
url: https://github.com/apps/pre-commit-ci
|
||||
alejsdev:
|
||||
login: alejsdev
|
||||
count: 21
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=356f39ff3f0211c720b06d3dbb060e98884085e3&v=4
|
||||
url: https://github.com/alejsdev
|
||||
dmontagu:
|
||||
login: dmontagu
|
||||
count: 17
|
||||
@@ -108,6 +108,11 @@ hitrust:
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3360631?u=5fa1f475ad784d64eb9666bdd43cc4d285dcc773&v=4
|
||||
url: https://github.com/hitrust
|
||||
ShahriyarR:
|
||||
login: ShahriyarR
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3852029?u=c9a1691e5ebdc94cbf543086099a6ed705cdb873&v=4
|
||||
url: https://github.com/ShahriyarR
|
||||
adriangb:
|
||||
login: adriangb
|
||||
count: 4
|
||||
@@ -208,11 +213,6 @@ graingert:
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/413772?u=64b77b6aa405c68a9c6bcf45f84257c66eea5f32&v=4
|
||||
url: https://github.com/graingert
|
||||
ShahriyarR:
|
||||
login: ShahriyarR
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3852029?u=c9a1691e5ebdc94cbf543086099a6ed705cdb873&v=4
|
||||
url: https://github.com/ShahriyarR
|
||||
jaystone776:
|
||||
login: jaystone776
|
||||
count: 3
|
||||
@@ -433,6 +433,11 @@ imba-tjd:
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/24759802?u=01e901a4fe004b4b126549d3ff1c4000fe3720b5&v=4
|
||||
url: https://github.com/imba-tjd
|
||||
johnthagen:
|
||||
login: johnthagen
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10340167?u=47147fc4e4db1f573bee3fe428deeacb3197bc5f&v=4
|
||||
url: https://github.com/johnthagen
|
||||
paxcodes:
|
||||
login: paxcodes
|
||||
count: 2
|
||||
@@ -443,6 +448,11 @@ kaustubhgupta:
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43691873?u=8dd738718ac7ffad4ef31e86b5d780a1141c695d&v=4
|
||||
url: https://github.com/kaustubhgupta
|
||||
kinuax:
|
||||
login: kinuax
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13321374?u=22dc9873d6d9f2c7e4fc44c6480c3505efb1531f&v=4
|
||||
url: https://github.com/kinuax
|
||||
wakabame:
|
||||
login: wakabame
|
||||
count: 2
|
||||
@@ -503,3 +513,8 @@ AyushSinghal1794:
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/89984761?v=4
|
||||
url: https://github.com/AyushSinghal1794
|
||||
DanielKusyDev:
|
||||
login: DanielKusyDev
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/36250676?u=2ea6114ff751fc48b55f231987a0e2582c6b1bd2&v=4
|
||||
url: https://github.com/DanielKusyDev
|
||||
|
||||
@@ -2,6 +2,9 @@ sponsors:
|
||||
- - login: bump-sh
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/33217836?v=4
|
||||
url: https://github.com/bump-sh
|
||||
- login: renderinc
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/36424661?v=4
|
||||
url: https://github.com/renderinc
|
||||
- login: Nixtla
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/79945230?v=4
|
||||
url: https://github.com/Nixtla
|
||||
@@ -20,9 +23,6 @@ sponsors:
|
||||
- login: zuplo
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/85497839?v=4
|
||||
url: https://github.com/zuplo
|
||||
- login: render-sponsorships
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/189296666?v=4
|
||||
url: https://github.com/render-sponsorships
|
||||
- login: porter-dev
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62078005?v=4
|
||||
url: https://github.com/porter-dev
|
||||
@@ -44,6 +44,9 @@ sponsors:
|
||||
- login: databento
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/64141749?v=4
|
||||
url: https://github.com/databento
|
||||
- login: permitio
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/71775833?v=4
|
||||
url: https://github.com/permitio
|
||||
- - login: mercedes-benz
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/34240465?v=4
|
||||
url: https://github.com/mercedes-benz
|
||||
@@ -95,9 +98,6 @@ sponsors:
|
||||
- - login: samuelcolvin
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4
|
||||
url: https://github.com/samuelcolvin
|
||||
- login: vincentkoc
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25068?u=cbf098fc04c0473523d373b0dd2145b4ec99ef93&v=4
|
||||
url: https://github.com/vincentkoc
|
||||
- login: ProteinQure
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/33707203?v=4
|
||||
url: https://github.com/ProteinQure
|
||||
@@ -107,6 +107,9 @@ sponsors:
|
||||
- login: otosky
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/42260747?u=69d089387c743d89427aa4ad8740cfb34045a9e0&v=4
|
||||
url: https://github.com/otosky
|
||||
- login: khadrawy
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13686061?u=59f25ef42ecf04c22657aac4238ce0e2d3d30304&v=4
|
||||
url: https://github.com/khadrawy
|
||||
- login: mjohnsey
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16784016?u=38fad2e6b411244560b3af99c5f5a4751bc81865&v=4
|
||||
url: https://github.com/mjohnsey
|
||||
@@ -215,6 +218,9 @@ sponsors:
|
||||
- login: anomaly
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3654837?v=4
|
||||
url: https://github.com/anomaly
|
||||
- login: vincentkoc
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25068?u=fbd5b2d51142daa4bdbc21e21953a3b8b8188a4a&v=4
|
||||
url: https://github.com/vincentkoc
|
||||
- login: jstanden
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/63288?u=c3658d57d2862c607a0e19c2101c3c51876e36ad&v=4
|
||||
url: https://github.com/jstanden
|
||||
@@ -248,6 +254,9 @@ sponsors:
|
||||
- login: TrevorBenson
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9167887?u=dccbea3327a57750923333d8ebf1a0b3f1948949&v=4
|
||||
url: https://github.com/TrevorBenson
|
||||
- login: kaangiray26
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11297495?u=e85327a77db45906d44f3ff06dd7f3303c644096&v=4
|
||||
url: https://github.com/kaangiray26
|
||||
- login: wdwinslow
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4
|
||||
url: https://github.com/wdwinslow
|
||||
@@ -263,9 +272,9 @@ sponsors:
|
||||
- login: dannywade
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13680237?u=418ee985bd41577b20fde81417fb2d901e875e8a&v=4
|
||||
url: https://github.com/dannywade
|
||||
- login: khadrawy
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13686061?u=59f25ef42ecf04c22657aac4238ce0e2d3d30304&v=4
|
||||
url: https://github.com/khadrawy
|
||||
- login: gorhack
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4141690?u=ec119ebc4bdf00a7bc84657a71aa17834f4f27f3&v=4
|
||||
url: https://github.com/gorhack
|
||||
- login: Ryandaydev
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=48f68868db8886fce31a1d802c1003914c6cd7c6&v=4
|
||||
url: https://github.com/Ryandaydev
|
||||
@@ -314,9 +323,9 @@ sponsors:
|
||||
- login: mobyw
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/44370805?v=4
|
||||
url: https://github.com/mobyw
|
||||
- login: ArtyomVancyan
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4
|
||||
url: https://github.com/ArtyomVancyan
|
||||
- login: PelicanQ
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/77930606?v=4
|
||||
url: https://github.com/PelicanQ
|
||||
- login: TheR1D
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16740832?u=b0dfdbdb27b79729430c71c6128962f77b7b53f7&v=4
|
||||
url: https://github.com/TheR1D
|
||||
@@ -341,6 +350,9 @@ sponsors:
|
||||
- login: dvlpjrs
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32254642?u=fbd6ad0324d4f1eb6231cf775be1c7bd4404e961&v=4
|
||||
url: https://github.com/dvlpjrs
|
||||
- login: ArtyomVancyan
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4
|
||||
url: https://github.com/ArtyomVancyan
|
||||
- login: caviri
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/45425937?u=4e14bd64282bad8f385eafbdb004b5a279366d6e&v=4
|
||||
url: https://github.com/caviri
|
||||
@@ -356,9 +368,6 @@ sponsors:
|
||||
- login: PunRabbit
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/70463212?u=1a835cfbc99295a60c8282f6aa6199d1b42241a5&v=4
|
||||
url: https://github.com/PunRabbit
|
||||
- login: PelicanQ
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/77930606?v=4
|
||||
url: https://github.com/PelicanQ
|
||||
- login: tochikuji
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/851759?v=4
|
||||
url: https://github.com/tochikuji
|
||||
@@ -380,9 +389,9 @@ sponsors:
|
||||
- login: Alisa-lisa
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4137964?u=e7e393504f554f4ff15863a1e01a5746863ef9ce&v=4
|
||||
url: https://github.com/Alisa-lisa
|
||||
- login: Graeme22
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4185684?u=498182a42300d7bcd4de1215190cb17eb501136c&v=4
|
||||
url: https://github.com/Graeme22
|
||||
- login: hcristea
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7814406?u=61d7a4fcf846983a4606788eac25e1c6c1209ba8&v=4
|
||||
url: https://github.com/hcristea
|
||||
- login: ddanier
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/113563?u=ed1dc79de72f93bd78581f88ebc6952b62f472da&v=4
|
||||
url: https://github.com/ddanier
|
||||
@@ -434,6 +443,9 @@ sponsors:
|
||||
- login: artempronevskiy
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/12235104?u=03df6e1e55c9c6fe5d230adabb8dd7d43d8bbe8f&v=4
|
||||
url: https://github.com/artempronevskiy
|
||||
- login: Graeme22
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4185684?u=498182a42300d7bcd4de1215190cb17eb501136c&v=4
|
||||
url: https://github.com/Graeme22
|
||||
- login: danielunderwood
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4472301?v=4
|
||||
url: https://github.com/danielunderwood
|
||||
@@ -458,9 +470,6 @@ sponsors:
|
||||
- login: harsh183
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7780198?v=4
|
||||
url: https://github.com/harsh183
|
||||
- login: hcristea
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7814406?u=61d7a4fcf846983a4606788eac25e1c6c1209ba8&v=4
|
||||
url: https://github.com/hcristea
|
||||
- - login: larsyngvelundin
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/34173819?u=74958599695bf83ac9f1addd935a51548a10c6b0&v=4
|
||||
url: https://github.com/larsyngvelundin
|
||||
@@ -479,9 +488,15 @@ sponsors:
|
||||
- login: FabulousCodingFox
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/78906517?u=924a27cbee3db7e0ece5cc1509921402e1445e74&v=4
|
||||
url: https://github.com/FabulousCodingFox
|
||||
- login: anqorithm
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/61029571?u=468256fa4e2d9ce2870b608299724bebb7a33f18&v=4
|
||||
url: https://github.com/anqorithm
|
||||
- login: gateremark
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/91592218?u=969314eb2cfb035196f4d19499ec6f5050d7583a&v=4
|
||||
url: https://github.com/gateremark
|
||||
- login: morzan1001
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/47593005?u=c30ab7230f82a12a9b938dcb54f84a996931409a&v=4
|
||||
url: https://github.com/morzan1001
|
||||
- login: Toothwitch
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1710406?u=5eebb23b46cd26e48643b9e5179536cad491c17a&v=4
|
||||
url: https://github.com/Toothwitch
|
||||
- login: ssbarnea
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/102495?u=c7bd9ddf127785286fc939dd18cb02db0a453bce&v=4
|
||||
url: https://github.com/ssbarnea
|
||||
|
||||
@@ -17,3 +17,6 @@ members:
|
||||
- login: patrick91
|
||||
avatar_url: https://avatars.githubusercontent.com/u/667029
|
||||
url: https://github.com/patrick91
|
||||
- login: luzzodev
|
||||
avatar_url: https://avatars.githubusercontent.com/u/27291415
|
||||
url: https://github.com/luzzodev
|
||||
|
||||
@@ -13,7 +13,7 @@ experts:
|
||||
avatarUrl: https://avatars.githubusercontent.com/in/15368?v=4
|
||||
url: https://github.com/apps/github-actions
|
||||
- login: Kludex
|
||||
count: 644
|
||||
count: 645
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: jgould22
|
||||
@@ -116,6 +116,10 @@ experts:
|
||||
count: 39
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4
|
||||
url: https://github.com/sinisaos
|
||||
- login: luzzodev
|
||||
count: 37
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
|
||||
url: https://github.com/luzzodev
|
||||
- login: chbndrhnns
|
||||
count: 37
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
|
||||
@@ -124,10 +128,6 @@ experts:
|
||||
count: 37
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
|
||||
url: https://github.com/STeveShary
|
||||
- login: luzzodev
|
||||
count: 36
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
|
||||
url: https://github.com/luzzodev
|
||||
- login: krishnardt
|
||||
count: 35
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
|
||||
@@ -188,6 +188,10 @@ experts:
|
||||
count: 19
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
|
||||
url: https://github.com/estebanx64
|
||||
- login: sehraramiz
|
||||
count: 18
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
|
||||
url: https://github.com/sehraramiz
|
||||
- login: zoliknemet
|
||||
count: 18
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4
|
||||
@@ -196,10 +200,6 @@ experts:
|
||||
count: 18
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
|
||||
url: https://github.com/retnikt
|
||||
- login: sehraramiz
|
||||
count: 17
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
|
||||
url: https://github.com/sehraramiz
|
||||
- login: caeser1996
|
||||
count: 17
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
|
||||
@@ -224,6 +224,10 @@ experts:
|
||||
count: 16
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
|
||||
url: https://github.com/dstlny
|
||||
- login: ceb10n
|
||||
count: 15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
|
||||
url: https://github.com/ceb10n
|
||||
- login: jorgerpo
|
||||
count: 15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4
|
||||
@@ -240,13 +244,9 @@ experts:
|
||||
count: 15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4
|
||||
url: https://github.com/abhint
|
||||
- login: pythonweb2
|
||||
count: 14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
|
||||
url: https://github.com/pythonweb2
|
||||
last_month_experts:
|
||||
- login: Kludex
|
||||
count: 15
|
||||
count: 14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: YuriiMotov
|
||||
@@ -254,11 +254,11 @@ last_month_experts:
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: sehraramiz
|
||||
count: 8
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
|
||||
url: https://github.com/sehraramiz
|
||||
- login: luzzodev
|
||||
count: 4
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
|
||||
url: https://github.com/luzzodev
|
||||
- login: yokwejuste
|
||||
@@ -269,6 +269,10 @@ last_month_experts:
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
|
||||
url: https://github.com/alv2017
|
||||
- login: Trinkes
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9466879?v=4
|
||||
url: https://github.com/Trinkes
|
||||
- login: PREPONDERANCE
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
|
||||
@@ -287,19 +291,19 @@ last_month_experts:
|
||||
url: https://github.com/iloveitaly
|
||||
three_months_experts:
|
||||
- login: luzzodev
|
||||
count: 34
|
||||
count: 33
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
|
||||
url: https://github.com/luzzodev
|
||||
- login: YuriiMotov
|
||||
count: 33
|
||||
count: 31
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: Kludex
|
||||
count: 23
|
||||
count: 24
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: sehraramiz
|
||||
count: 10
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
|
||||
url: https://github.com/sehraramiz
|
||||
- login: estebanx64
|
||||
@@ -326,6 +330,10 @@ three_months_experts:
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/108818737?u=3d7ffe5808843ee4372f9cc5a559ff1674cf1792&v=4
|
||||
url: https://github.com/viniciusCalcantara
|
||||
- login: Trinkes
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9466879?v=4
|
||||
url: https://github.com/Trinkes
|
||||
- login: PREPONDERANCE
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
|
||||
@@ -358,10 +366,6 @@ three_months_experts:
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/40732698?u=611f39d3c1d2f4207a590937a78c1f10eed6232c&v=4
|
||||
url: https://github.com/gelezo43
|
||||
- login: dbfreem
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9778569?u=f2f1e9135b5e4f1b0c6821a548b17f97572720fc&v=4
|
||||
url: https://github.com/dbfreem
|
||||
- login: AliYmn
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/18416653?u=98c1fca46c7e4dabe8c39d17b5e55d1511d41cf9&v=4
|
||||
@@ -388,47 +392,47 @@ six_months_experts:
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: Kludex
|
||||
count: 39
|
||||
count: 40
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: luzzodev
|
||||
count: 37
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
|
||||
url: https://github.com/luzzodev
|
||||
- login: sinisaos
|
||||
count: 37
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4
|
||||
url: https://github.com/sinisaos
|
||||
- login: luzzodev
|
||||
count: 36
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
|
||||
url: https://github.com/luzzodev
|
||||
- login: JavierSanchezCastro
|
||||
count: 16
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
|
||||
url: https://github.com/JavierSanchezCastro
|
||||
- login: tiangolo
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
|
||||
url: https://github.com/tiangolo
|
||||
- login: Kfir-G
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/57500876?u=0cd29db046a17f12f382d398141319fca7ff230a&v=4
|
||||
url: https://github.com/Kfir-G
|
||||
- login: tiangolo
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
|
||||
url: https://github.com/tiangolo
|
||||
- login: sehraramiz
|
||||
count: 10
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
|
||||
url: https://github.com/sehraramiz
|
||||
- login: estebanx64
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
|
||||
url: https://github.com/estebanx64
|
||||
- login: ceb10n
|
||||
count: 9
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
|
||||
url: https://github.com/ceb10n
|
||||
- login: estebanx64
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
|
||||
url: https://github.com/estebanx64
|
||||
- login: yvallois
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/36999744?v=4
|
||||
url: https://github.com/yvallois
|
||||
- login: n8sty
|
||||
count: 6
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
|
||||
url: https://github.com/n8sty
|
||||
- login: TomFaulkner
|
||||
@@ -483,6 +487,10 @@ six_months_experts:
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4
|
||||
url: https://github.com/svlandeg
|
||||
- login: Trinkes
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9466879?v=4
|
||||
url: https://github.com/Trinkes
|
||||
- login: PREPONDERANCE
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
|
||||
@@ -619,17 +627,13 @@ six_months_experts:
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
|
||||
url: https://github.com/mattmess1221
|
||||
- login: meower1
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109747197?u=0a5cc2a6ae74e558f0afc2874da85132e5953d8b&v=4
|
||||
url: https://github.com/meower1
|
||||
one_year_experts:
|
||||
- login: YuriiMotov
|
||||
count: 223
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: Kludex
|
||||
count: 83
|
||||
count: 81
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: JavierSanchezCastro
|
||||
@@ -645,7 +649,7 @@ one_year_experts:
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4
|
||||
url: https://github.com/sinisaos
|
||||
- login: luzzodev
|
||||
count: 36
|
||||
count: 37
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
|
||||
url: https://github.com/luzzodev
|
||||
- login: tiangolo
|
||||
@@ -660,18 +664,18 @@ one_year_experts:
|
||||
count: 19
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
|
||||
url: https://github.com/estebanx64
|
||||
- login: ceb10n
|
||||
count: 15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
|
||||
url: https://github.com/ceb10n
|
||||
- login: sehraramiz
|
||||
count: 14
|
||||
count: 15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
|
||||
url: https://github.com/sehraramiz
|
||||
- login: PhysicallyActive
|
||||
count: 14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
|
||||
url: https://github.com/PhysicallyActive
|
||||
- login: ceb10n
|
||||
count: 14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
|
||||
url: https://github.com/ceb10n
|
||||
- login: Kfir-G
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/57500876?u=0cd29db046a17f12f382d398141319fca7ff230a&v=4
|
||||
@@ -812,6 +816,18 @@ one_year_experts:
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=d87b866e7c1db970d6f8e8031643818349b046d5&v=4
|
||||
url: https://github.com/ahmedabdou14
|
||||
- login: Trinkes
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9466879?v=4
|
||||
url: https://github.com/Trinkes
|
||||
- login: Leon0824
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1922026?v=4
|
||||
url: https://github.com/Leon0824
|
||||
- login: CarlosOliveira-23
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/102637302?u=cf350a4db956f30cbb2c27d3be0d15c282e32b14&v=4
|
||||
url: https://github.com/CarlosOliveira-23
|
||||
- login: nbx3
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/34649527?u=943812f69e0d40adbd3fa1c9b8ef50dd971a2a45&v=4
|
||||
@@ -832,10 +848,6 @@ one_year_experts:
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4
|
||||
url: https://github.com/slafs
|
||||
- login: CarlosOliveira-23
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/102637302?u=cf350a4db956f30cbb2c27d3be0d15c282e32b14&v=4
|
||||
url: https://github.com/CarlosOliveira-23
|
||||
- login: monchin
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/18521800?v=4
|
||||
@@ -844,10 +856,6 @@ one_year_experts:
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/38752106?u=07f80e451bda00a9492bbc764e49d24ad3ada8cc&v=4
|
||||
url: https://github.com/AmirHmZz
|
||||
- login: Leon0824
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1922026?v=4
|
||||
url: https://github.com/Leon0824
|
||||
- login: iloveitaly
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/150855?v=4
|
||||
@@ -860,7 +868,3 @@ one_year_experts:
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11828278?u=6bcadc5ce4f2f56a514331c9f68eb987d4afe29a&v=4
|
||||
url: https://github.com/shurshilov
|
||||
- login: LincolnPuzey
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/18750802?v=4
|
||||
url: https://github.com/LincolnPuzey
|
||||
|
||||
@@ -32,6 +32,9 @@ gold:
|
||||
- url: https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi
|
||||
title: Deploy & scale any full-stack web app on Render. Focus on building apps, not infra.
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/render.svg
|
||||
- url: https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=badge&utm_campaign=fastapi
|
||||
title: Cut Code Review Time & Bugs in Half with CodeRabbit
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/coderabbit.png
|
||||
silver:
|
||||
- url: https://github.com/deepset-ai/haystack/
|
||||
title: Build powerful search from composable, open source building blocks
|
||||
@@ -48,6 +51,9 @@ silver:
|
||||
- url: https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral
|
||||
title: Stainless | Generate best-in-class SDKs
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/stainless.png
|
||||
- url: https://www.permit.io/blog/implement-authorization-in-fastapi?utm_source=github&utm_medium=referral&utm_campaign=fastapi
|
||||
title: Fine-Grained Authorization for FastAPI
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/permit.png
|
||||
bronze:
|
||||
- url: https://www.exoflare.com/open-source/?utm_source=FastAPI&utm_campaign=open_source
|
||||
title: Biosecurity risk assessments made easy.
|
||||
@@ -55,3 +61,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
|
||||
|
||||
@@ -1,495 +1,495 @@
|
||||
- name: full-stack-fastapi-template
|
||||
html_url: https://github.com/fastapi/full-stack-fastapi-template
|
||||
stars: 28796
|
||||
stars: 29409
|
||||
owner_login: fastapi
|
||||
owner_html_url: https://github.com/fastapi
|
||||
- name: Hello-Python
|
||||
html_url: https://github.com/mouredev/Hello-Python
|
||||
stars: 27554
|
||||
stars: 28113
|
||||
owner_login: mouredev
|
||||
owner_html_url: https://github.com/mouredev
|
||||
- name: serve
|
||||
html_url: https://github.com/jina-ai/serve
|
||||
stars: 21225
|
||||
stars: 21264
|
||||
owner_login: jina-ai
|
||||
owner_html_url: https://github.com/jina-ai
|
||||
- name: sqlmodel
|
||||
html_url: https://github.com/fastapi/sqlmodel
|
||||
stars: 14921
|
||||
stars: 15109
|
||||
owner_login: fastapi
|
||||
owner_html_url: https://github.com/fastapi
|
||||
- name: HivisionIDPhotos
|
||||
html_url: https://github.com/Zeyi-Lin/HivisionIDPhotos
|
||||
stars: 14025
|
||||
stars: 14564
|
||||
owner_login: Zeyi-Lin
|
||||
owner_html_url: https://github.com/Zeyi-Lin
|
||||
- name: Douyin_TikTok_Download_API
|
||||
html_url: https://github.com/Evil0ctal/Douyin_TikTok_Download_API
|
||||
stars: 10001
|
||||
stars: 10701
|
||||
owner_login: Evil0ctal
|
||||
owner_html_url: https://github.com/Evil0ctal
|
||||
- name: fastapi-best-practices
|
||||
html_url: https://github.com/zhanymkanov/fastapi-best-practices
|
||||
stars: 9820
|
||||
stars: 10180
|
||||
owner_login: zhanymkanov
|
||||
owner_html_url: https://github.com/zhanymkanov
|
||||
- name: awesome-fastapi
|
||||
html_url: https://github.com/mjhea0/awesome-fastapi
|
||||
stars: 8899
|
||||
stars: 9061
|
||||
owner_login: mjhea0
|
||||
owner_html_url: https://github.com/mjhea0
|
||||
- name: FastUI
|
||||
html_url: https://github.com/pydantic/FastUI
|
||||
stars: 8400
|
||||
stars: 8644
|
||||
owner_login: pydantic
|
||||
owner_html_url: https://github.com/pydantic
|
||||
- name: nonebot2
|
||||
html_url: https://github.com/nonebot/nonebot2
|
||||
stars: 6235
|
||||
stars: 6312
|
||||
owner_login: nonebot
|
||||
owner_html_url: https://github.com/nonebot
|
||||
- name: serge
|
||||
html_url: https://github.com/serge-chat/serge
|
||||
stars: 5685
|
||||
stars: 5686
|
||||
owner_login: serge-chat
|
||||
owner_html_url: https://github.com/serge-chat
|
||||
- name: fastapi-users
|
||||
html_url: https://github.com/fastapi-users/fastapi-users
|
||||
stars: 4787
|
||||
owner_login: fastapi-users
|
||||
owner_html_url: https://github.com/fastapi-users
|
||||
- name: FileCodeBox
|
||||
html_url: https://github.com/vastsa/FileCodeBox
|
||||
stars: 4479
|
||||
stars: 4933
|
||||
owner_login: vastsa
|
||||
owner_html_url: https://github.com/vastsa
|
||||
- name: fastapi-users
|
||||
html_url: https://github.com/fastapi-users/fastapi-users
|
||||
stars: 4849
|
||||
owner_login: fastapi-users
|
||||
owner_html_url: https://github.com/fastapi-users
|
||||
- name: hatchet
|
||||
html_url: https://github.com/hatchet-dev/hatchet
|
||||
stars: 4413
|
||||
stars: 4514
|
||||
owner_login: hatchet-dev
|
||||
owner_html_url: https://github.com/hatchet-dev
|
||||
- name: chatgpt-web-share
|
||||
html_url: https://github.com/chatpire/chatgpt-web-share
|
||||
stars: 4322
|
||||
stars: 4319
|
||||
owner_login: chatpire
|
||||
owner_html_url: https://github.com/chatpire
|
||||
- name: atrilabs-engine
|
||||
html_url: https://github.com/Atri-Labs/atrilabs-engine
|
||||
stars: 4115
|
||||
owner_login: Atri-Labs
|
||||
owner_html_url: https://github.com/Atri-Labs
|
||||
- name: polar
|
||||
html_url: https://github.com/polarsource/polar
|
||||
stars: 4216
|
||||
owner_login: polarsource
|
||||
owner_html_url: https://github.com/polarsource
|
||||
- name: strawberry
|
||||
html_url: https://github.com/strawberry-graphql/strawberry
|
||||
stars: 4084
|
||||
stars: 4126
|
||||
owner_login: strawberry-graphql
|
||||
owner_html_url: https://github.com/strawberry-graphql
|
||||
- name: atrilabs-engine
|
||||
html_url: https://github.com/Atri-Labs/atrilabs-engine
|
||||
stars: 4114
|
||||
owner_login: Atri-Labs
|
||||
owner_html_url: https://github.com/Atri-Labs
|
||||
- name: dynaconf
|
||||
html_url: https://github.com/dynaconf/dynaconf
|
||||
stars: 3844
|
||||
stars: 3874
|
||||
owner_login: dynaconf
|
||||
owner_html_url: https://github.com/dynaconf
|
||||
- name: poem
|
||||
html_url: https://github.com/poem-web/poem
|
||||
stars: 3698
|
||||
stars: 3746
|
||||
owner_login: poem-web
|
||||
owner_html_url: https://github.com/poem-web
|
||||
- name: polar
|
||||
html_url: https://github.com/polarsource/polar
|
||||
stars: 3355
|
||||
owner_login: polarsource
|
||||
owner_html_url: https://github.com/polarsource
|
||||
- name: opyrator
|
||||
html_url: https://github.com/ml-tooling/opyrator
|
||||
stars: 3114
|
||||
stars: 3117
|
||||
owner_login: ml-tooling
|
||||
owner_html_url: https://github.com/ml-tooling
|
||||
- name: farfalle
|
||||
html_url: https://github.com/rashadphz/farfalle
|
||||
stars: 3022
|
||||
stars: 3094
|
||||
owner_login: rashadphz
|
||||
owner_html_url: https://github.com/rashadphz
|
||||
- name: fastapi-admin
|
||||
html_url: https://github.com/fastapi-admin/fastapi-admin
|
||||
stars: 3002
|
||||
stars: 3040
|
||||
owner_login: fastapi-admin
|
||||
owner_html_url: https://github.com/fastapi-admin
|
||||
- name: docarray
|
||||
html_url: https://github.com/docarray/docarray
|
||||
stars: 2998
|
||||
stars: 3007
|
||||
owner_login: docarray
|
||||
owner_html_url: https://github.com/docarray
|
||||
- name: datamodel-code-generator
|
||||
html_url: https://github.com/koxudaxi/datamodel-code-generator
|
||||
stars: 2845
|
||||
stars: 2914
|
||||
owner_login: koxudaxi
|
||||
owner_html_url: https://github.com/koxudaxi
|
||||
- name: fastapi-realworld-example-app
|
||||
html_url: https://github.com/nsidnev/fastapi-realworld-example-app
|
||||
stars: 2832
|
||||
stars: 2840
|
||||
owner_login: nsidnev
|
||||
owner_html_url: https://github.com/nsidnev
|
||||
- name: uvicorn-gunicorn-fastapi-docker
|
||||
html_url: https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker
|
||||
stars: 2727
|
||||
owner_login: tiangolo
|
||||
owner_html_url: https://github.com/tiangolo
|
||||
- name: WrenAI
|
||||
html_url: https://github.com/Canner/WrenAI
|
||||
stars: 2699
|
||||
owner_login: Canner
|
||||
owner_html_url: https://github.com/Canner
|
||||
- name: LitServe
|
||||
html_url: https://github.com/Lightning-AI/LitServe
|
||||
stars: 2664
|
||||
stars: 2804
|
||||
owner_login: Lightning-AI
|
||||
owner_html_url: https://github.com/Lightning-AI
|
||||
- name: uvicorn-gunicorn-fastapi-docker
|
||||
html_url: https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker
|
||||
stars: 2730
|
||||
owner_login: tiangolo
|
||||
owner_html_url: https://github.com/tiangolo
|
||||
- name: logfire
|
||||
html_url: https://github.com/pydantic/logfire
|
||||
stars: 2495
|
||||
stars: 2620
|
||||
owner_login: pydantic
|
||||
owner_html_url: https://github.com/pydantic
|
||||
- name: huma
|
||||
html_url: https://github.com/danielgtaylor/huma
|
||||
stars: 2479
|
||||
stars: 2567
|
||||
owner_login: danielgtaylor
|
||||
owner_html_url: https://github.com/danielgtaylor
|
||||
- name: tracecat
|
||||
html_url: https://github.com/TracecatHQ/tracecat
|
||||
stars: 2446
|
||||
stars: 2494
|
||||
owner_login: TracecatHQ
|
||||
owner_html_url: https://github.com/TracecatHQ
|
||||
- name: RasaGPT
|
||||
html_url: https://github.com/paulpierre/RasaGPT
|
||||
stars: 2378
|
||||
owner_login: paulpierre
|
||||
owner_html_url: https://github.com/paulpierre
|
||||
- name: best-of-web-python
|
||||
html_url: https://github.com/ml-tooling/best-of-web-python
|
||||
stars: 2374
|
||||
stars: 2433
|
||||
owner_login: ml-tooling
|
||||
owner_html_url: https://github.com/ml-tooling
|
||||
- name: RasaGPT
|
||||
html_url: https://github.com/paulpierre/RasaGPT
|
||||
stars: 2386
|
||||
owner_login: paulpierre
|
||||
owner_html_url: https://github.com/paulpierre
|
||||
- name: fastapi-react
|
||||
html_url: https://github.com/Buuntu/fastapi-react
|
||||
stars: 2274
|
||||
stars: 2293
|
||||
owner_login: Buuntu
|
||||
owner_html_url: https://github.com/Buuntu
|
||||
- name: nextpy
|
||||
html_url: https://github.com/dot-agent/nextpy
|
||||
stars: 2244
|
||||
stars: 2256
|
||||
owner_login: dot-agent
|
||||
owner_html_url: https://github.com/dot-agent
|
||||
- name: 30-Days-of-Python
|
||||
html_url: https://github.com/codingforentrepreneurs/30-Days-of-Python
|
||||
stars: 2154
|
||||
stars: 2155
|
||||
owner_login: codingforentrepreneurs
|
||||
owner_html_url: https://github.com/codingforentrepreneurs
|
||||
- name: FastAPI-template
|
||||
html_url: https://github.com/s3rius/FastAPI-template
|
||||
stars: 2067
|
||||
stars: 2121
|
||||
owner_login: s3rius
|
||||
owner_html_url: https://github.com/s3rius
|
||||
- name: langserve
|
||||
html_url: https://github.com/langchain-ai/langserve
|
||||
stars: 1980
|
||||
owner_login: langchain-ai
|
||||
owner_html_url: https://github.com/langchain-ai
|
||||
- name: sqladmin
|
||||
html_url: https://github.com/aminalaee/sqladmin
|
||||
stars: 1980
|
||||
stars: 2021
|
||||
owner_login: aminalaee
|
||||
owner_html_url: https://github.com/aminalaee
|
||||
- name: langserve
|
||||
html_url: https://github.com/langchain-ai/langserve
|
||||
stars: 2006
|
||||
owner_login: langchain-ai
|
||||
owner_html_url: https://github.com/langchain-ai
|
||||
- name: fastapi-utils
|
||||
html_url: https://github.com/fastapiutils/fastapi-utils
|
||||
stars: 1970
|
||||
stars: 2002
|
||||
owner_login: fastapiutils
|
||||
owner_html_url: https://github.com/fastapiutils
|
||||
- name: solara
|
||||
html_url: https://github.com/widgetti/solara
|
||||
stars: 1950
|
||||
stars: 1967
|
||||
owner_login: widgetti
|
||||
owner_html_url: https://github.com/widgetti
|
||||
- name: python-week-2022
|
||||
html_url: https://github.com/rochacbruno/python-week-2022
|
||||
stars: 1836
|
||||
owner_login: rochacbruno
|
||||
owner_html_url: https://github.com/rochacbruno
|
||||
- name: supabase-py
|
||||
html_url: https://github.com/supabase/supabase-py
|
||||
stars: 1803
|
||||
stars: 1848
|
||||
owner_login: supabase
|
||||
owner_html_url: https://github.com/supabase
|
||||
- name: python-week-2022
|
||||
html_url: https://github.com/rochacbruno/python-week-2022
|
||||
stars: 1832
|
||||
owner_login: rochacbruno
|
||||
owner_html_url: https://github.com/rochacbruno
|
||||
- name: mangum
|
||||
html_url: https://github.com/Kludex/mangum
|
||||
stars: 1760
|
||||
stars: 1789
|
||||
owner_login: Kludex
|
||||
owner_html_url: https://github.com/Kludex
|
||||
- name: manage-fastapi
|
||||
html_url: https://github.com/ycd/manage-fastapi
|
||||
stars: 1704
|
||||
stars: 1711
|
||||
owner_login: ycd
|
||||
owner_html_url: https://github.com/ycd
|
||||
- name: ormar
|
||||
html_url: https://github.com/collerek/ormar
|
||||
stars: 1688
|
||||
stars: 1701
|
||||
owner_login: collerek
|
||||
owner_html_url: https://github.com/collerek
|
||||
- name: agentkit
|
||||
html_url: https://github.com/BCG-X-Official/agentkit
|
||||
stars: 1615
|
||||
stars: 1630
|
||||
owner_login: BCG-X-Official
|
||||
owner_html_url: https://github.com/BCG-X-Official
|
||||
- name: langchain-serve
|
||||
html_url: https://github.com/jina-ai/langchain-serve
|
||||
stars: 1615
|
||||
stars: 1617
|
||||
owner_login: jina-ai
|
||||
owner_html_url: https://github.com/jina-ai
|
||||
- name: termpair
|
||||
html_url: https://github.com/cs01/termpair
|
||||
stars: 1613
|
||||
stars: 1612
|
||||
owner_login: cs01
|
||||
owner_html_url: https://github.com/cs01
|
||||
- name: coronavirus-tracker-api
|
||||
html_url: https://github.com/ExpDev07/coronavirus-tracker-api
|
||||
stars: 1591
|
||||
stars: 1590
|
||||
owner_login: ExpDev07
|
||||
owner_html_url: https://github.com/ExpDev07
|
||||
- name: piccolo
|
||||
html_url: https://github.com/piccolo-orm/piccolo
|
||||
stars: 1477
|
||||
stars: 1519
|
||||
owner_login: piccolo-orm
|
||||
owner_html_url: https://github.com/piccolo-orm
|
||||
- name: fastapi-crudrouter
|
||||
html_url: https://github.com/awtkns/fastapi-crudrouter
|
||||
stars: 1435
|
||||
stars: 1449
|
||||
owner_login: awtkns
|
||||
owner_html_url: https://github.com/awtkns
|
||||
- name: fastapi-cache
|
||||
html_url: https://github.com/long2ice/fastapi-cache
|
||||
stars: 1412
|
||||
stars: 1447
|
||||
owner_login: long2ice
|
||||
owner_html_url: https://github.com/long2ice
|
||||
- name: openapi-python-client
|
||||
html_url: https://github.com/openapi-generators/openapi-python-client
|
||||
stars: 1398
|
||||
stars: 1434
|
||||
owner_login: openapi-generators
|
||||
owner_html_url: https://github.com/openapi-generators
|
||||
- name: awesome-fastapi-projects
|
||||
html_url: https://github.com/Kludex/awesome-fastapi-projects
|
||||
stars: 1386
|
||||
stars: 1398
|
||||
owner_login: Kludex
|
||||
owner_html_url: https://github.com/Kludex
|
||||
- name: awesome-python-resources
|
||||
html_url: https://github.com/DjangoEx/awesome-python-resources
|
||||
stars: 1371
|
||||
stars: 1380
|
||||
owner_login: DjangoEx
|
||||
owner_html_url: https://github.com/DjangoEx
|
||||
- name: budgetml
|
||||
html_url: https://github.com/ebhy/budgetml
|
||||
stars: 1342
|
||||
stars: 1344
|
||||
owner_login: ebhy
|
||||
owner_html_url: https://github.com/ebhy
|
||||
- name: slowapi
|
||||
html_url: https://github.com/laurentS/slowapi
|
||||
stars: 1289
|
||||
stars: 1339
|
||||
owner_login: laurentS
|
||||
owner_html_url: https://github.com/laurentS
|
||||
- name: fastapi-pagination
|
||||
html_url: https://github.com/uriyyo/fastapi-pagination
|
||||
stars: 1240
|
||||
stars: 1263
|
||||
owner_login: uriyyo
|
||||
owner_html_url: https://github.com/uriyyo
|
||||
- name: fastapi-boilerplate
|
||||
html_url: https://github.com/teamhide/fastapi-boilerplate
|
||||
stars: 1173
|
||||
stars: 1206
|
||||
owner_login: teamhide
|
||||
owner_html_url: https://github.com/teamhide
|
||||
- name: fastapi-tutorial
|
||||
html_url: https://github.com/liaogx/fastapi-tutorial
|
||||
stars: 1162
|
||||
stars: 1178
|
||||
owner_login: liaogx
|
||||
owner_html_url: https://github.com/liaogx
|
||||
- name: fastapi-amis-admin
|
||||
html_url: https://github.com/amisadmin/fastapi-amis-admin
|
||||
stars: 1118
|
||||
stars: 1142
|
||||
owner_login: amisadmin
|
||||
owner_html_url: https://github.com/amisadmin
|
||||
- name: fastapi-code-generator
|
||||
html_url: https://github.com/koxudaxi/fastapi-code-generator
|
||||
stars: 1095
|
||||
stars: 1119
|
||||
owner_login: koxudaxi
|
||||
owner_html_url: https://github.com/koxudaxi
|
||||
- name: bolt-python
|
||||
html_url: https://github.com/slackapi/bolt-python
|
||||
stars: 1086
|
||||
stars: 1116
|
||||
owner_login: slackapi
|
||||
owner_html_url: https://github.com/slackapi
|
||||
- name: odmantic
|
||||
html_url: https://github.com/art049/odmantic
|
||||
stars: 1085
|
||||
stars: 1096
|
||||
owner_login: art049
|
||||
owner_html_url: https://github.com/art049
|
||||
- name: langchain-extract
|
||||
html_url: https://github.com/langchain-ai/langchain-extract
|
||||
stars: 1068
|
||||
stars: 1093
|
||||
owner_login: langchain-ai
|
||||
owner_html_url: https://github.com/langchain-ai
|
||||
- name: fastapi_production_template
|
||||
html_url: https://github.com/zhanymkanov/fastapi_production_template
|
||||
stars: 1059
|
||||
stars: 1078
|
||||
owner_login: zhanymkanov
|
||||
owner_html_url: https://github.com/zhanymkanov
|
||||
- name: fastapi-alembic-sqlmodel-async
|
||||
html_url: https://github.com/jonra1993/fastapi-alembic-sqlmodel-async
|
||||
stars: 1031
|
||||
stars: 1055
|
||||
owner_login: jonra1993
|
||||
owner_html_url: https://github.com/jonra1993
|
||||
- name: Kokoro-FastAPI
|
||||
html_url: https://github.com/remsky/Kokoro-FastAPI
|
||||
stars: 1047
|
||||
owner_login: remsky
|
||||
owner_html_url: https://github.com/remsky
|
||||
- name: prometheus-fastapi-instrumentator
|
||||
html_url: https://github.com/trallnag/prometheus-fastapi-instrumentator
|
||||
stars: 1013
|
||||
stars: 1036
|
||||
owner_login: trallnag
|
||||
owner_html_url: https://github.com/trallnag
|
||||
- name: SurfSense
|
||||
html_url: https://github.com/MODSetter/SurfSense
|
||||
stars: 1018
|
||||
owner_login: MODSetter
|
||||
owner_html_url: https://github.com/MODSetter
|
||||
- name: bedrock-claude-chat
|
||||
html_url: https://github.com/aws-samples/bedrock-claude-chat
|
||||
stars: 1010
|
||||
owner_login: aws-samples
|
||||
owner_html_url: https://github.com/aws-samples
|
||||
- name: runhouse
|
||||
html_url: https://github.com/run-house/runhouse
|
||||
stars: 988
|
||||
stars: 1000
|
||||
owner_login: run-house
|
||||
owner_html_url: https://github.com/run-house
|
||||
- name: lanarky
|
||||
html_url: https://github.com/ajndkr/lanarky
|
||||
stars: 982
|
||||
stars: 986
|
||||
owner_login: ajndkr
|
||||
owner_html_url: https://github.com/ajndkr
|
||||
- name: autollm
|
||||
html_url: https://github.com/viddexa/autollm
|
||||
stars: 981
|
||||
stars: 982
|
||||
owner_login: viddexa
|
||||
owner_html_url: https://github.com/viddexa
|
||||
- name: bedrock-claude-chat
|
||||
html_url: https://github.com/aws-samples/bedrock-claude-chat
|
||||
stars: 977
|
||||
owner_login: aws-samples
|
||||
owner_html_url: https://github.com/aws-samples
|
||||
- name: SurfSense
|
||||
html_url: https://github.com/MODSetter/SurfSense
|
||||
stars: 971
|
||||
owner_login: MODSetter
|
||||
owner_html_url: https://github.com/MODSetter
|
||||
- name: restish
|
||||
html_url: https://github.com/danielgtaylor/restish
|
||||
stars: 954
|
||||
stars: 970
|
||||
owner_login: danielgtaylor
|
||||
owner_html_url: https://github.com/danielgtaylor
|
||||
- name: fastcrud
|
||||
html_url: https://github.com/igorbenav/fastcrud
|
||||
stars: 929
|
||||
owner_login: igorbenav
|
||||
owner_html_url: https://github.com/igorbenav
|
||||
- name: secure
|
||||
html_url: https://github.com/TypeError/secure
|
||||
stars: 911
|
||||
stars: 921
|
||||
owner_login: TypeError
|
||||
owner_html_url: https://github.com/TypeError
|
||||
- name: langcorn
|
||||
html_url: https://github.com/msoedov/langcorn
|
||||
stars: 909
|
||||
stars: 915
|
||||
owner_login: msoedov
|
||||
owner_html_url: https://github.com/msoedov
|
||||
- name: energy-forecasting
|
||||
html_url: https://github.com/iusztinpaul/energy-forecasting
|
||||
stars: 884
|
||||
owner_login: iusztinpaul
|
||||
owner_html_url: https://github.com/iusztinpaul
|
||||
- name: vue-fastapi-admin
|
||||
html_url: https://github.com/mizhexiaoxiao/vue-fastapi-admin
|
||||
stars: 863
|
||||
stars: 915
|
||||
owner_login: mizhexiaoxiao
|
||||
owner_html_url: https://github.com/mizhexiaoxiao
|
||||
- name: energy-forecasting
|
||||
html_url: https://github.com/iusztinpaul/energy-forecasting
|
||||
stars: 891
|
||||
owner_login: iusztinpaul
|
||||
owner_html_url: https://github.com/iusztinpaul
|
||||
- name: authx
|
||||
html_url: https://github.com/yezz123/authx
|
||||
stars: 850
|
||||
stars: 862
|
||||
owner_login: yezz123
|
||||
owner_html_url: https://github.com/yezz123
|
||||
- name: titiler
|
||||
html_url: https://github.com/developmentseed/titiler
|
||||
stars: 809
|
||||
stars: 823
|
||||
owner_login: developmentseed
|
||||
owner_html_url: https://github.com/developmentseed
|
||||
- name: marker-api
|
||||
html_url: https://github.com/adithya-s-k/marker-api
|
||||
stars: 792
|
||||
stars: 798
|
||||
owner_login: adithya-s-k
|
||||
owner_html_url: https://github.com/adithya-s-k
|
||||
- name: FastAPI-boilerplate
|
||||
html_url: https://github.com/igorbenav/FastAPI-boilerplate
|
||||
stars: 774
|
||||
owner_login: igorbenav
|
||||
owner_html_url: https://github.com/igorbenav
|
||||
- name: fastapi_best_architecture
|
||||
html_url: https://github.com/fastapi-practices/fastapi_best_architecture
|
||||
stars: 742
|
||||
stars: 766
|
||||
owner_login: fastapi-practices
|
||||
owner_html_url: https://github.com/fastapi-practices
|
||||
- name: fastapi-mail
|
||||
html_url: https://github.com/sabuhish/fastapi-mail
|
||||
stars: 728
|
||||
stars: 735
|
||||
owner_login: sabuhish
|
||||
owner_html_url: https://github.com/sabuhish
|
||||
- name: fastcrud
|
||||
html_url: https://github.com/igorbenav/fastcrud
|
||||
stars: 727
|
||||
owner_login: igorbenav
|
||||
owner_html_url: https://github.com/igorbenav
|
||||
- name: annotated-py-projects
|
||||
html_url: https://github.com/hhstore/annotated-py-projects
|
||||
stars: 722
|
||||
stars: 725
|
||||
owner_login: hhstore
|
||||
owner_html_url: https://github.com/hhstore
|
||||
- name: FastAPI-boilerplate
|
||||
html_url: https://github.com/igorbenav/FastAPI-boilerplate
|
||||
stars: 716
|
||||
owner_login: igorbenav
|
||||
owner_html_url: https://github.com/igorbenav
|
||||
- name: lccn_predictor
|
||||
html_url: https://github.com/baoliay2008/lccn_predictor
|
||||
stars: 707
|
||||
owner_login: baoliay2008
|
||||
owner_html_url: https://github.com/baoliay2008
|
||||
- name: chatGPT-web
|
||||
html_url: https://github.com/mic1on/chatGPT-web
|
||||
stars: 706
|
||||
owner_login: mic1on
|
||||
owner_html_url: https://github.com/mic1on
|
||||
- name: fastapi-do-zero
|
||||
html_url: https://github.com/dunossauro/fastapi-do-zero
|
||||
stars: 702
|
||||
stars: 723
|
||||
owner_login: dunossauro
|
||||
owner_html_url: https://github.com/dunossauro
|
||||
- name: linbing
|
||||
html_url: https://github.com/taomujian/linbing
|
||||
stars: 699
|
||||
owner_login: taomujian
|
||||
owner_html_url: https://github.com/taomujian
|
||||
- name: lccn_predictor
|
||||
html_url: https://github.com/baoliay2008/lccn_predictor
|
||||
stars: 718
|
||||
owner_login: baoliay2008
|
||||
owner_html_url: https://github.com/baoliay2008
|
||||
- name: fastapi-observability
|
||||
html_url: https://github.com/blueswen/fastapi-observability
|
||||
stars: 698
|
||||
stars: 718
|
||||
owner_login: blueswen
|
||||
owner_html_url: https://github.com/blueswen
|
||||
- name: FastAPI-Backend-Template
|
||||
html_url: https://github.com/Aeternalis-Ingenium/FastAPI-Backend-Template
|
||||
stars: 682
|
||||
owner_login: Aeternalis-Ingenium
|
||||
owner_html_url: https://github.com/Aeternalis-Ingenium
|
||||
- name: chatGPT-web
|
||||
html_url: https://github.com/mic1on/chatGPT-web
|
||||
stars: 708
|
||||
owner_login: mic1on
|
||||
owner_html_url: https://github.com/mic1on
|
||||
- name: learn-generative-ai
|
||||
html_url: https://github.com/panaverse/learn-generative-ai
|
||||
stars: 673
|
||||
stars: 701
|
||||
owner_login: panaverse
|
||||
owner_html_url: https://github.com/panaverse
|
||||
- name: linbing
|
||||
html_url: https://github.com/taomujian/linbing
|
||||
stars: 700
|
||||
owner_login: taomujian
|
||||
owner_html_url: https://github.com/taomujian
|
||||
- name: FastAPI-Backend-Template
|
||||
html_url: https://github.com/Aeternalis-Ingenium/FastAPI-Backend-Template
|
||||
stars: 692
|
||||
owner_login: Aeternalis-Ingenium
|
||||
owner_html_url: https://github.com/Aeternalis-Ingenium
|
||||
- name: starlette-admin
|
||||
html_url: https://github.com/jowilf/starlette-admin
|
||||
stars: 692
|
||||
owner_login: jowilf
|
||||
owner_html_url: https://github.com/jowilf
|
||||
- name: fastapi-jwt-auth
|
||||
html_url: https://github.com/IndominusByte/fastapi-jwt-auth
|
||||
stars: 668
|
||||
stars: 674
|
||||
owner_login: IndominusByte
|
||||
owner_html_url: https://github.com/IndominusByte
|
||||
- name: pity
|
||||
html_url: https://github.com/wuranxu/pity
|
||||
stars: 660
|
||||
stars: 663
|
||||
owner_login: wuranxu
|
||||
owner_html_url: https://github.com/wuranxu
|
||||
- name: starlette-admin
|
||||
html_url: https://github.com/jowilf/starlette-admin
|
||||
stars: 653
|
||||
owner_login: jowilf
|
||||
owner_html_url: https://github.com/jowilf
|
||||
- name: fastapi_login
|
||||
html_url: https://github.com/MushroomMaula/fastapi_login
|
||||
stars: 650
|
||||
stars: 656
|
||||
owner_login: MushroomMaula
|
||||
owner_html_url: https://github.com/MushroomMaula
|
||||
|
||||
@@ -5,12 +5,12 @@ s111d:
|
||||
url: https://github.com/s111d
|
||||
Xewus:
|
||||
login: Xewus
|
||||
count: 139
|
||||
count: 140
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
|
||||
url: https://github.com/Xewus
|
||||
ceb10n:
|
||||
login: ceb10n
|
||||
count: 108
|
||||
count: 110
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
|
||||
url: https://github.com/ceb10n
|
||||
tokusumi:
|
||||
@@ -33,21 +33,26 @@ AlertRED:
|
||||
count: 81
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
|
||||
url: https://github.com/AlertRED
|
||||
nazarepiedady:
|
||||
login: nazarepiedady
|
||||
count: 81
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31008635?u=8dc25777dc9cb51fb0dbba2f137988953d330b78&v=4
|
||||
url: https://github.com/nazarepiedady
|
||||
sodaMelon:
|
||||
login: sodaMelon
|
||||
count: 81
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/66295123?u=be939db90f1119efee9e6110cc05066ff1f40f00&v=4
|
||||
url: https://github.com/sodaMelon
|
||||
nazarepiedady:
|
||||
login: nazarepiedady
|
||||
count: 78
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31008635?u=8dc25777dc9cb51fb0dbba2f137988953d330b78&v=4
|
||||
url: https://github.com/nazarepiedady
|
||||
Alexandrhub:
|
||||
login: Alexandrhub
|
||||
count: 68
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
|
||||
url: https://github.com/Alexandrhub
|
||||
alv2017:
|
||||
login: alv2017
|
||||
count: 64
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
|
||||
url: https://github.com/alv2017
|
||||
waynerv:
|
||||
login: waynerv
|
||||
count: 63
|
||||
@@ -55,7 +60,7 @@ waynerv:
|
||||
url: https://github.com/waynerv
|
||||
cassiobotaro:
|
||||
login: cassiobotaro
|
||||
count: 61
|
||||
count: 62
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4
|
||||
url: https://github.com/cassiobotaro
|
||||
mattwang44:
|
||||
@@ -138,26 +143,21 @@ romashevchenko:
|
||||
count: 32
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/132477732?v=4
|
||||
url: https://github.com/romashevchenko
|
||||
alejsdev:
|
||||
login: alejsdev
|
||||
count: 32
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=356f39ff3f0211c720b06d3dbb060e98884085e3&v=4
|
||||
url: https://github.com/alejsdev
|
||||
wdh99:
|
||||
login: wdh99
|
||||
count: 31
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4
|
||||
url: https://github.com/wdh99
|
||||
alv2017:
|
||||
login: alv2017
|
||||
count: 31
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
|
||||
url: https://github.com/alv2017
|
||||
LorhanSohaky:
|
||||
login: LorhanSohaky
|
||||
count: 30
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
|
||||
url: https://github.com/LorhanSohaky
|
||||
alejsdev:
|
||||
login: alejsdev
|
||||
count: 30
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=356f39ff3f0211c720b06d3dbb060e98884085e3&v=4
|
||||
url: https://github.com/alejsdev
|
||||
black-redoc:
|
||||
login: black-redoc
|
||||
count: 29
|
||||
@@ -246,7 +246,7 @@ axel584:
|
||||
wisderfin:
|
||||
login: wisderfin
|
||||
count: 23
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/77553770?u=94478d3e1ef7d36d70479c5bd35d8de28b071c10&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/77553770?u=f3b00a26736ba664e9927a1116c6e8088295e073&v=4
|
||||
url: https://github.com/wisderfin
|
||||
rostik1410:
|
||||
login: rostik1410
|
||||
@@ -353,6 +353,11 @@ mastizada:
|
||||
count: 16
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1975818?u=0751a06d7271c8bf17cb73b1b845644ab4d2c6dc&v=4
|
||||
url: https://github.com/mastizada
|
||||
Joao-Pedro-P-Holanda:
|
||||
login: 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
|
||||
JaeHyuckSa:
|
||||
login: JaeHyuckSa
|
||||
count: 16
|
||||
@@ -363,11 +368,6 @@ Jedore:
|
||||
count: 15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/17944025?u=81d503e1c800eb666b3861ca47a3a773bbc3f539&v=4
|
||||
url: https://github.com/Jedore
|
||||
Joao-Pedro-P-Holanda:
|
||||
login: Joao-Pedro-P-Holanda
|
||||
count: 15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4
|
||||
url: https://github.com/Joao-Pedro-P-Holanda
|
||||
kim-sangah:
|
||||
login: kim-sangah
|
||||
count: 15
|
||||
@@ -386,7 +386,7 @@ dukkee:
|
||||
mkdir700:
|
||||
login: mkdir700
|
||||
count: 14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/56359329?u=0ba13427420e7f6e4c83947736de247326f2c292&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/56359329?u=3d6ea8714f5000829b60dcf7b13a75b1e73aaf47&v=4
|
||||
url: https://github.com/mkdir700
|
||||
BORA040126:
|
||||
login: BORA040126
|
||||
@@ -473,6 +473,11 @@ kwang1215:
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/74170199?u=2a63ff6692119dde3f5e5693365b9fcd6f977b08&v=4
|
||||
url: https://github.com/kwang1215
|
||||
Rishat-F:
|
||||
login: Rishat-F
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/66554797?v=4
|
||||
url: https://github.com/Rishat-F
|
||||
AdrianDeAnda:
|
||||
login: AdrianDeAnda
|
||||
count: 11
|
||||
@@ -513,6 +518,11 @@ KNChiu:
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/36751646?v=4
|
||||
url: https://github.com/KNChiu
|
||||
gitgernit:
|
||||
login: gitgernit
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/129539613?u=d04f10143ab32c93f563ea14bf242d1d2bc991b0&v=4
|
||||
url: https://github.com/gitgernit
|
||||
mariacamilagl:
|
||||
login: mariacamilagl
|
||||
count: 10
|
||||
@@ -538,6 +548,11 @@ RobotToI:
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/44951382?u=e41dbc19191ce7abed86694b1a44ea0523e1c60e&v=4
|
||||
url: https://github.com/RobotToI
|
||||
vitumenezes:
|
||||
login: vitumenezes
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9680878?u=05fd25cfafdc09382bf8907c37293a696c205754&v=4
|
||||
url: https://github.com/vitumenezes
|
||||
fcrozetta:
|
||||
login: fcrozetta
|
||||
count: 10
|
||||
@@ -626,7 +641,7 @@ marcelomarkus:
|
||||
JoaoGustavoRogel:
|
||||
login: JoaoGustavoRogel
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/29525510?u=1dd3096c6c2be2576fd5e818b1be15b2c9768aa5&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/29525510?u=a0a91251f5e43e132608d55d28ccb8645c5ea405&v=4
|
||||
url: https://github.com/JoaoGustavoRogel
|
||||
Zhongheng-Cheng:
|
||||
login: Zhongheng-Cheng
|
||||
@@ -673,11 +688,6 @@ camigomezdev:
|
||||
count: 8
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16061815?u=25b5ebc042fff53fa03dc107ded10e36b1b7a5b9&v=4
|
||||
url: https://github.com/camigomezdev
|
||||
gitgernit:
|
||||
login: gitgernit
|
||||
count: 8
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/129539613?u=d04f10143ab32c93f563ea14bf242d1d2bc991b0&v=4
|
||||
url: https://github.com/gitgernit
|
||||
Serrones:
|
||||
login: Serrones
|
||||
count: 7
|
||||
@@ -698,11 +708,6 @@ anthonycepeda:
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
|
||||
url: https://github.com/anthonycepeda
|
||||
vitumenezes:
|
||||
login: vitumenezes
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9680878?u=e7c6865aec49c3c94b8c8edc1198d1eac3e50b26&v=4
|
||||
url: https://github.com/vitumenezes
|
||||
fabioueno:
|
||||
login: fabioueno
|
||||
count: 7
|
||||
@@ -956,7 +961,7 @@ devluisrodrigues:
|
||||
timothy-jeong:
|
||||
login: timothy-jeong
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=659311b6f6aeb0fbb8b527723fd4c83642f04327&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=db3d0cea2f5fab64d810113c5039a369699a2774&v=4
|
||||
url: https://github.com/timothy-jeong
|
||||
lpdswing:
|
||||
login: lpdswing
|
||||
@@ -1053,6 +1058,11 @@ matiasbertani:
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/65260383?u=d5edd86a6e2ab4fb1aab7751931fe045a963afd7&v=4
|
||||
url: https://github.com/matiasbertani
|
||||
k94-ishi:
|
||||
login: k94-ishi
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32672580?u=bc7c5c07af0656be9fe4f1784a444af8d81ded89&v=4
|
||||
url: https://github.com/k94-ishi
|
||||
javillegasna:
|
||||
login: javillegasna
|
||||
count: 4
|
||||
@@ -1063,6 +1073,16 @@ javillegasna:
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/174453744?v=4
|
||||
url: https://github.com/9zimin9
|
||||
ilhamfadillah:
|
||||
login: ilhamfadillah
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/20577838?u=c56192cf99b55affcaad408b240259c62e633450&v=4
|
||||
url: https://github.com/ilhamfadillah
|
||||
Yarous:
|
||||
login: Yarous
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/61277193?u=5b462347458a373b2d599c6f416d2b75eddbffad&v=4
|
||||
url: https://github.com/Yarous
|
||||
tyronedamasceno:
|
||||
login: tyronedamasceno
|
||||
count: 3
|
||||
@@ -1151,7 +1171,7 @@ rafsaf:
|
||||
frnsimoes:
|
||||
login: frnsimoes
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/66239468?u=771c4b0c403a42ccf2676ac987ac4999e5ad09bc&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/66239468?u=a405e8f10654251e239a4a1d9dd5bda59216727d&v=4
|
||||
url: https://github.com/frnsimoes
|
||||
lieryan:
|
||||
login: lieryan
|
||||
@@ -1283,11 +1303,16 @@ celestywang:
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/184830753?v=4
|
||||
url: https://github.com/celestywang
|
||||
ilhamfadillah:
|
||||
login: ilhamfadillah
|
||||
RyaWcksn:
|
||||
login: RyaWcksn
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/20577838?u=c56192cf99b55affcaad408b240259c62e633450&v=4
|
||||
url: https://github.com/ilhamfadillah
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/42831964?u=0cb4265faf3e3425a89e59b6fddd3eb2de180af0&v=4
|
||||
url: https://github.com/RyaWcksn
|
||||
gerry-sabar:
|
||||
login: gerry-sabar
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4
|
||||
url: https://github.com/gerry-sabar
|
||||
blaisep:
|
||||
login: blaisep
|
||||
count: 2
|
||||
@@ -1633,13 +1658,13 @@ logan2d5:
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/146642263?u=dbd6621f8b0330d6919f6a7131277b92e26fbe87&v=4
|
||||
url: https://github.com/logan2d5
|
||||
RyaWcksn:
|
||||
login: RyaWcksn
|
||||
tiaggo16:
|
||||
login: tiaggo16
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/42831964?u=0cb4265faf3e3425a89e59b6fddd3eb2de180af0&v=4
|
||||
url: https://github.com/RyaWcksn
|
||||
gerry-sabar:
|
||||
login: gerry-sabar
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62227573?u=359f4e2c51a4b13c8553ac5af405d635b07bb61f&v=4
|
||||
url: https://github.com/tiaggo16
|
||||
kiharito:
|
||||
login: kiharito
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4
|
||||
url: https://github.com/gerry-sabar
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/38311245?v=4
|
||||
url: https://github.com/kiharito
|
||||
|
||||
@@ -8,6 +8,11 @@ jaystone776:
|
||||
count: 46
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
|
||||
url: https://github.com/jaystone776
|
||||
ceb10n:
|
||||
login: ceb10n
|
||||
count: 26
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
|
||||
url: https://github.com/ceb10n
|
||||
tokusumi:
|
||||
login: tokusumi
|
||||
count: 23
|
||||
@@ -23,11 +28,6 @@ hasansezertasan:
|
||||
count: 22
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
|
||||
url: https://github.com/hasansezertasan
|
||||
ceb10n:
|
||||
login: ceb10n
|
||||
count: 22
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
|
||||
url: https://github.com/ceb10n
|
||||
waynerv:
|
||||
login: waynerv
|
||||
count: 20
|
||||
@@ -55,7 +55,7 @@ Xewus:
|
||||
url: https://github.com/Xewus
|
||||
Joao-Pedro-P-Holanda:
|
||||
login: Joao-Pedro-P-Holanda
|
||||
count: 12
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4
|
||||
url: https://github.com/Joao-Pedro-P-Holanda
|
||||
Smlep:
|
||||
@@ -78,6 +78,11 @@ Vincy1230:
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/81342412?u=ab5e256a4077a4a91f3f9cd2115ba80780454cbe&v=4
|
||||
url: https://github.com/Vincy1230
|
||||
Zhongheng-Cheng:
|
||||
login: Zhongheng-Cheng
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/95612344?u=a0f7730a3cc7486827965e01a119ad610bda4b0a&v=4
|
||||
url: https://github.com/Zhongheng-Cheng
|
||||
rjNemo:
|
||||
login: rjNemo
|
||||
count: 8
|
||||
@@ -93,11 +98,6 @@ pablocm83:
|
||||
count: 8
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/28315068?u=3310fbb05bb8bfc50d2c48b6cb64ac9ee4a14549&v=4
|
||||
url: https://github.com/pablocm83
|
||||
Zhongheng-Cheng:
|
||||
login: Zhongheng-Cheng
|
||||
count: 8
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/95612344?u=a0f7730a3cc7486827965e01a119ad610bda4b0a&v=4
|
||||
url: https://github.com/Zhongheng-Cheng
|
||||
batlopes:
|
||||
login: batlopes
|
||||
count: 6
|
||||
@@ -188,6 +188,11 @@ kwang1215:
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/74170199?u=2a63ff6692119dde3f5e5693365b9fcd6f977b08&v=4
|
||||
url: https://github.com/kwang1215
|
||||
alv2017:
|
||||
login: alv2017
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
|
||||
url: https://github.com/alv2017
|
||||
jfunez:
|
||||
login: jfunez
|
||||
count: 3
|
||||
@@ -313,11 +318,11 @@ nahyunkeem:
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/174440096?u=e12401d492eee58570f8914d0872b52e421a776e&v=4
|
||||
url: https://github.com/nahyunkeem
|
||||
alv2017:
|
||||
login: alv2017
|
||||
gerry-sabar:
|
||||
login: gerry-sabar
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
|
||||
url: https://github.com/alv2017
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4
|
||||
url: https://github.com/gerry-sabar
|
||||
izaguerreiro:
|
||||
login: izaguerreiro
|
||||
count: 2
|
||||
@@ -481,10 +486,10 @@ saeye:
|
||||
timothy-jeong:
|
||||
login: timothy-jeong
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=659311b6f6aeb0fbb8b527723fd4c83642f04327&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=db3d0cea2f5fab64d810113c5039a369699a2774&v=4
|
||||
url: https://github.com/timothy-jeong
|
||||
gerry-sabar:
|
||||
login: gerry-sabar
|
||||
Rishat-F:
|
||||
login: Rishat-F
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4
|
||||
url: https://github.com/gerry-sabar
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/66554797?v=4
|
||||
url: https://github.com/Rishat-F
|
||||
|
||||
BIN
docs/en/docs/img/sponsors/coderabbit-banner.png
Normal file
BIN
docs/en/docs/img/sponsors/coderabbit-banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
docs/en/docs/img/sponsors/coderabbit.png
Normal file
BIN
docs/en/docs/img/sponsors/coderabbit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/en/docs/img/sponsors/lambdatest.png
Normal file
BIN
docs/en/docs/img/sponsors/lambdatest.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
BIN
docs/en/docs/img/sponsors/permit.png
Normal file
BIN
docs/en/docs/img/sponsors/permit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -7,6 +7,69 @@ hide:
|
||||
|
||||
## Latest Changes
|
||||
|
||||
## 0.115.9
|
||||
|
||||
### Fixes
|
||||
|
||||
* 🐛 Ensure that `HTTPDigest` only raises an exception when `auto_error is True`. PR [#2939](https://github.com/fastapi/fastapi/pull/2939) by [@arthurio](https://github.com/arthurio).
|
||||
|
||||
### Refactors
|
||||
|
||||
* ✅ Simplify tests for `query_params_str_validations`. PR [#13218](https://github.com/fastapi/fastapi/pull/13218) by [@alv2017](https://github.com/alv2017).
|
||||
* ✅ Simplify tests for `app_testing`. PR [#13220](https://github.com/fastapi/fastapi/pull/13220) by [@alv2017](https://github.com/alv2017).
|
||||
* ✅ Simplify tests for `dependency_testing`. PR [#13223](https://github.com/fastapi/fastapi/pull/13223) by [@alv2017](https://github.com/alv2017).
|
||||
|
||||
### Docs
|
||||
|
||||
* 🍱 Update sponsors: CodeRabbit logo. PR [#13424](https://github.com/fastapi/fastapi/pull/13424) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🩺 Unify the badges across all tutorial translations. PR [#13329](https://github.com/fastapi/fastapi/pull/13329) by [@svlandeg](https://github.com/svlandeg).
|
||||
* 📝 Fix typos in virtual environments documentation. PR [#13396](https://github.com/fastapi/fastapi/pull/13396) by [@bullet-ant](https://github.com/bullet-ant).
|
||||
* 🐛 Fix issue with Swagger theme change example in the official tutorial. PR [#13289](https://github.com/fastapi/fastapi/pull/13289) by [@Zerohertz](https://github.com/Zerohertz).
|
||||
* 📝 Add more precise description of HTTP status code range in docs. PR [#13347](https://github.com/fastapi/fastapi/pull/13347) by [@DanielYang59](https://github.com/DanielYang59).
|
||||
* 🔥 Remove manual type annotations in JWT tutorial to avoid typing expectations (JWT doesn't provide more types). PR [#13378](https://github.com/fastapi/fastapi/pull/13378) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Update docs for Query Params and String Validations, remove obsolete Ellipsis docs (`...`). PR [#13377](https://github.com/fastapi/fastapi/pull/13377) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ✏️ Remove duplicate title in docs `body-multiple-params`. PR [#13345](https://github.com/fastapi/fastapi/pull/13345) by [@DanielYang59](https://github.com/DanielYang59).
|
||||
* 📝 Fix test badge. PR [#13313](https://github.com/fastapi/fastapi/pull/13313) by [@esadek](https://github.com/esadek).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/header-params.md`. PR [#13381](https://github.com/fastapi/fastapi/pull/13381) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
|
||||
* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/request-files.md`. PR [#13395](https://github.com/fastapi/fastapi/pull/13395) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
|
||||
* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/request-form-models.md`. PR [#13384](https://github.com/fastapi/fastapi/pull/13384) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
|
||||
* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/request-forms-and-files.md`. PR [#13386](https://github.com/fastapi/fastapi/pull/13386) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
|
||||
* 🌐 Update Korean translation for `docs/ko/docs/help-fastapi.md`. PR [#13262](https://github.com/fastapi/fastapi/pull/13262) by [@Zerohertz](https://github.com/Zerohertz).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/advanced/custom-response.md`. PR [#13265](https://github.com/fastapi/fastapi/pull/13265) by [@11kkw](https://github.com/11kkw).
|
||||
* 🌐 Update Korean translation for `docs/ko/docs/tutorial/security/simple-oauth2.md`. PR [#13335](https://github.com/fastapi/fastapi/pull/13335) by [@yes0ng](https://github.com/yes0ng).
|
||||
* 🌐 Add Russian translation for `docs/ru/docs/advanced/response-cookies.md`. PR [#13327](https://github.com/fastapi/fastapi/pull/13327) by [@Stepakinoyan](https://github.com/Stepakinoyan).
|
||||
* 🌐 Add Vietnamese translation for `docs/vi/docs/tutorial/static-files.md`. PR [#11291](https://github.com/fastapi/fastapi/pull/11291) by [@ptt3199](https://github.com/ptt3199).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md`. PR [#13257](https://github.com/fastapi/fastapi/pull/13257) by [@11kkw](https://github.com/11kkw).
|
||||
* 🌐 Add Vietnamese translation for `docs/vi/docs/virtual-environments.md`. PR [#13282](https://github.com/fastapi/fastapi/pull/13282) by [@ptt3199](https://github.com/ptt3199).
|
||||
* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/static-files.md`. PR [#13285](https://github.com/fastapi/fastapi/pull/13285) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
|
||||
* 🌐 Add Vietnamese translation for `docs/vi/docs/environment-variables.md`. PR [#13287](https://github.com/fastapi/fastapi/pull/13287) by [@ptt3199](https://github.com/ptt3199).
|
||||
* 🌐 Add Vietnamese translation for `docs/vi/docs/fastapi-cli.md`. PR [#13294](https://github.com/fastapi/fastapi/pull/13294) by [@ptt3199](https://github.com/ptt3199).
|
||||
* 🌐 Add Ukrainian translation for `docs/uk/docs/features.md`. PR [#13308](https://github.com/fastapi/fastapi/pull/13308) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
|
||||
* 🌐 Add Ukrainian translation for `docs/uk/docs/learn/index.md`. PR [#13306](https://github.com/fastapi/fastapi/pull/13306) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
|
||||
* 🌐 Update Portuguese Translation for `docs/pt/docs/deployment/https.md`. PR [#13317](https://github.com/fastapi/fastapi/pull/13317) by [@Joao-Pedro-P-Holanda](https://github.com/Joao-Pedro-P-Holanda).
|
||||
* 🌐 Update Portuguese Translation for `docs/pt/docs/index.md`. PR [#13328](https://github.com/fastapi/fastapi/pull/13328) by [@ceb10n](https://github.com/ceb10n).
|
||||
* 🌐 Add Russian translation for `docs/ru/docs/advanced/websockets.md`. PR [#13279](https://github.com/fastapi/fastapi/pull/13279) by [@Rishat-F](https://github.com/Rishat-F).
|
||||
|
||||
### Internal
|
||||
|
||||
* ✅ Fix a minor bug in the test `tests/test_modules_same_name_body/test_main.py`. PR [#13411](https://github.com/fastapi/fastapi/pull/13411) by [@alv2017](https://github.com/alv2017).
|
||||
* 👷 Use `wrangler-action` v3. PR [#13415](https://github.com/fastapi/fastapi/pull/13415) by [@joakimnordling](https://github.com/joakimnordling).
|
||||
* 🔧 Update sponsors: add CodeRabbit. PR [#13402](https://github.com/fastapi/fastapi/pull/13402) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔧 Update team: Add Ludovico. PR [#13390](https://github.com/fastapi/fastapi/pull/13390) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔧 Update sponsors: Add LambdaTest. PR [#13389](https://github.com/fastapi/fastapi/pull/13389) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ Bump cloudflare/wrangler-action from 3.13 to 3.14. PR [#13350](https://github.com/fastapi/fastapi/pull/13350) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump mkdocs-material from 9.5.18 to 9.6.1. PR [#13301](https://github.com/fastapi/fastapi/pull/13301) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump pillow from 11.0.0 to 11.1.0. PR [#13300](https://github.com/fastapi/fastapi/pull/13300) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* 👥 Update FastAPI People - Sponsors. PR [#13295](https://github.com/fastapi/fastapi/pull/13295) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👥 Update FastAPI People - Experts. PR [#13303](https://github.com/fastapi/fastapi/pull/13303) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👥 Update FastAPI GitHub topic repositories. PR [#13302](https://github.com/fastapi/fastapi/pull/13302) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👥 Update FastAPI People - Contributors and Translators. PR [#13293](https://github.com/fastapi/fastapi/pull/13293) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ Bump inline-snapshot from 0.18.1 to 0.19.3. PR [#13298](https://github.com/fastapi/fastapi/pull/13298) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* 🔧 Update sponsors, add Permit. PR [#13288](https://github.com/fastapi/fastapi/pull/13288) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.115.8
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -10,8 +10,6 @@ And you can also declare body parameters as optional, by setting the default to
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
|
||||
|
||||
## Multiple body parameters
|
||||
|
||||
/// note
|
||||
|
||||
Notice that, in this case, the `item` that would be taken from the body is optional. As it has a `None` default value.
|
||||
|
||||
@@ -6,13 +6,13 @@ Let's take this application as example:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
|
||||
|
||||
The query parameter `q` is of type `Union[str, None]` (or `str | None` in Python 3.10), that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required.
|
||||
The query parameter `q` is of type `str | None`, that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required.
|
||||
|
||||
/// note
|
||||
|
||||
FastAPI will know that the value of `q` is not required because of the default value `= None`.
|
||||
|
||||
The `Union` in `Union[str, None]` will allow your editor to give you better support and detect errors.
|
||||
Having `str | None` will allow your editor to give you better support and detect errors.
|
||||
|
||||
///
|
||||
|
||||
@@ -25,29 +25,9 @@ We are going to enforce that even though `q` is optional, whenever it is provide
|
||||
To achieve that, first import:
|
||||
|
||||
* `Query` from `fastapi`
|
||||
* `Annotated` from `typing` (or from `typing_extensions` in Python below 3.9)
|
||||
* `Annotated` from `typing`
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
In Python 3.9 or above, `Annotated` is part of the standard library, so you can import it from `typing`.
|
||||
|
||||
```Python hl_lines="1 3"
|
||||
{!> ../../docs_src/query_params_str_validations/tutorial002_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
In versions of Python below Python 3.9 you import `Annotated` from `typing_extensions`.
|
||||
|
||||
It will already be installed with FastAPI.
|
||||
|
||||
```Python hl_lines="3-4"
|
||||
{!> ../../docs_src/query_params_str_validations/tutorial002_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
|
||||
|
||||
/// info
|
||||
|
||||
@@ -145,54 +125,23 @@ As in this case (without using `Annotated`) we have to replace the default value
|
||||
|
||||
So:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None)
|
||||
```
|
||||
|
||||
...makes the parameter optional, with a default value of `None`, the same as:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
And in Python 3.10 and above:
|
||||
|
||||
```Python
|
||||
q: str | None = Query(default=None)
|
||||
```
|
||||
|
||||
...makes the parameter optional, with a default value of `None`, the same as:
|
||||
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
But the `Query` versions declare it explicitly as being a query parameter.
|
||||
|
||||
/// info
|
||||
|
||||
Keep in mind that the most important part to make a parameter optional is the part:
|
||||
|
||||
```Python
|
||||
= None
|
||||
```
|
||||
|
||||
or the:
|
||||
|
||||
```Python
|
||||
= Query(default=None)
|
||||
```
|
||||
|
||||
as it will use that `None` as the default value, and that way make the parameter **not required**.
|
||||
|
||||
The `Union[str, None]` part allows your editor to provide better support, but it is not what tells FastAPI that this parameter is not required.
|
||||
|
||||
///
|
||||
But the `Query` version declares it explicitly as being a query parameter.
|
||||
|
||||
Then, we can pass more parameters to `Query`. In this case, the `max_length` parameter that applies to strings:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None, max_length=50)
|
||||
q: str | None = Query(default=None, max_length=50)
|
||||
```
|
||||
|
||||
This will validate the data, show a clear error when the data is not valid, and document the parameter in the OpenAPI schema *path operation*.
|
||||
@@ -201,7 +150,7 @@ This will validate the data, show a clear error when the data is not valid, and
|
||||
|
||||
Keep in mind that when using `Query` inside of `Annotated` you cannot use the `default` parameter for `Query`.
|
||||
|
||||
Instead use the actual default value of the function parameter. Otherwise, it would be inconsistent.
|
||||
Instead, use the actual default value of the function parameter. Otherwise, it would be inconsistent.
|
||||
|
||||
For example, this is not allowed:
|
||||
|
||||
@@ -255,7 +204,7 @@ This specific regular expression pattern checks that the received parameter valu
|
||||
|
||||
If you feel lost with all these **"regular expression"** ideas, don't worry. They are a hard topic for many people. You can still do a lot of stuff without needing regular expressions yet.
|
||||
|
||||
But whenever you need them and go and learn them, know that you can already use them directly in **FastAPI**.
|
||||
Now you know that whenever you need them you can use them in **FastAPI**.
|
||||
|
||||
### Pydantic v1 `regex` instead of `pattern`
|
||||
|
||||
@@ -296,7 +245,7 @@ q: str
|
||||
instead of:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
But we are now declaring it with `Query`, for example like:
|
||||
@@ -304,15 +253,7 @@ But we are now declaring it with `Query`, for example like:
|
||||
//// tab | Annotated
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None], Query(min_length=3)] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | non-Annotated
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None, min_length=3)
|
||||
q: Annotated[str | None, Query(min_length=3)] = None
|
||||
```
|
||||
|
||||
////
|
||||
@@ -321,42 +262,14 @@ So, when you need to declare a value as required while using `Query`, you can si
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
|
||||
### Required with Ellipsis (`...`)
|
||||
|
||||
There's an alternative way to explicitly declare that a value is required. You can set the default to the literal value `...`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006b_an_py39.py hl[9] *}
|
||||
|
||||
/// info
|
||||
|
||||
If you hadn't seen that `...` before: it is a special single value, it is <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">part of Python and is called "Ellipsis"</a>.
|
||||
|
||||
It is used by Pydantic and FastAPI to explicitly declare that a value is required.
|
||||
|
||||
///
|
||||
|
||||
This will let **FastAPI** know that this parameter is required.
|
||||
|
||||
### Required, can be `None`
|
||||
|
||||
You can declare that a parameter can accept `None`, but that it's still required. This would force clients to send a value, even if the value is `None`.
|
||||
|
||||
To do that, you can declare that `None` is a valid type but still use `...` as the default:
|
||||
To do that, you can declare that `None` is a valid type but simply do not declare a default value:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
|
||||
|
||||
/// tip
|
||||
|
||||
Pydantic, which is what powers all the data validation and serialization in FastAPI, has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about <a href="https://docs.pydantic.dev/2.3/usage/models/#required-optional-fields" class="external-link" target="_blank">Required fields</a>.
|
||||
|
||||
///
|
||||
|
||||
/// tip
|
||||
|
||||
Remember that in most of the cases, when something is required, you can simply omit the default, so you normally don't have to use `...`.
|
||||
|
||||
///
|
||||
|
||||
## Query parameter list / multiple values
|
||||
|
||||
When you define a query parameter explicitly with `Query` you can also declare it to receive a list of values, or said in another way, to receive multiple values.
|
||||
@@ -396,7 +309,7 @@ The interactive API docs will update accordingly, to allow multiple values:
|
||||
|
||||
### Query parameter list / multiple values with defaults
|
||||
|
||||
And you can also define a default `list` of values if none are provided:
|
||||
You can also define a default `list` of values if none are provided:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
|
||||
|
||||
@@ -419,7 +332,7 @@ the default of `q` will be: `["foo", "bar"]` and your response will be:
|
||||
|
||||
#### Using just `list`
|
||||
|
||||
You can also use `list` directly instead of `List[str]` (or `list[str]` in Python 3.9+):
|
||||
You can also use `list` directly instead of `list[str]`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
|
||||
|
||||
@@ -427,7 +340,7 @@ You can also use `list` directly instead of `List[str]` (or `list[str]` in Pytho
|
||||
|
||||
Keep in mind that in this case, FastAPI won't check the contents of the list.
|
||||
|
||||
For example, `List[int]` would check (and document) that the contents of the list are integers. But `list` alone wouldn't.
|
||||
For example, `list[int]` would check (and document) that the contents of the list are integers. But `list` alone wouldn't.
|
||||
|
||||
///
|
||||
|
||||
|
||||
@@ -53,16 +53,16 @@ These status codes have a name associated to recognize them, but the important p
|
||||
|
||||
In short:
|
||||
|
||||
* `100` and above are for "Information". You rarely use them directly. Responses with these status codes cannot have a body.
|
||||
* **`200`** and above are for "Successful" responses. These are the ones you would use the most.
|
||||
* `100 - 199` are for "Information". You rarely use them directly. Responses with these status codes cannot have a body.
|
||||
* **`200 - 299`** are for "Successful" responses. These are the ones you would use the most.
|
||||
* `200` is the default status code, which means everything was "OK".
|
||||
* Another example would be `201`, "Created". It is commonly used after creating a new record in the database.
|
||||
* A special case is `204`, "No Content". This response is used when there is no content to return to the client, and so the response must not have a body.
|
||||
* **`300`** and above are for "Redirection". Responses with these status codes may or may not have a body, except for `304`, "Not Modified", which must not have one.
|
||||
* **`400`** and above are for "Client error" responses. These are the second type you would probably use the most.
|
||||
* **`300 - 399`** are for "Redirection". Responses with these status codes may or may not have a body, except for `304`, "Not Modified", which must not have one.
|
||||
* **`400 - 499`** are for "Client error" responses. These are the second type you would probably use the most.
|
||||
* An example is `404`, for a "Not Found" response.
|
||||
* For generic errors from the client, you can just use `400`.
|
||||
* `500` and above are for server errors. You almost never use them directly. When something goes wrong at some part in your application code, or server, it will automatically return one of these status codes.
|
||||
* `500 - 599` are for server errors. You almost never use them directly. When something goes wrong at some part in your application code, or server, it will automatically return one of these status codes.
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -668,7 +668,7 @@ After activating the virtual environment, the `PATH` variable would look somethi
|
||||
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
That means that the system will now start looking first look for programs in:
|
||||
That means that the system will now start looking first for programs in:
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin
|
||||
@@ -692,7 +692,7 @@ and use that one.
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
|
||||
```
|
||||
|
||||
That means that the system will now start looking first look for programs in:
|
||||
That means that the system will now start looking first for programs in:
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts
|
||||
|
||||
@@ -88,6 +88,12 @@
|
||||
<img class="sponsor-image" src="/img/sponsors/render-banner.svg" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a title="Cut Code Review Time & Bugs in Half with CodeRabbit" style="display: block; position: relative;" href="https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=banner&utm_campaign=fastapi" target="_blank">
|
||||
<span class="sponsor-badge">sponsor</span>
|
||||
<img class="sponsor-image" src="/img/sponsors/coderabbit-banner.png" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -321,22 +321,6 @@ Así que, cuando necesites declarar un valor como requerido mientras usas `Query
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
|
||||
### Requerido con Puntos suspensivos (`...`)
|
||||
|
||||
Hay una manera alternativa de declarar explícitamente que un valor es requerido. Puedes establecer el valor por defecto al valor literal `...`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006b_an_py39.py hl[9] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
Si no habías visto eso `...` antes: es un valor especial único, es <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">parte de Python y se llama "Ellipsis"</a>.
|
||||
|
||||
Se usa por Pydantic y FastAPI para declarar explícitamente que un valor es requerido.
|
||||
|
||||
///
|
||||
|
||||
Esto le permitirá a **FastAPI** saber que este parámetro es requerido.
|
||||
|
||||
### Requerido, puede ser `None`
|
||||
|
||||
Puedes declarar que un parámetro puede aceptar `None`, pero que aún así es requerido. Esto obligaría a los clientes a enviar un valor, incluso si el valor es `None`.
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
<em>فریمورک FastAPI، کارایی بالا، یادگیری آسان، کدنویسی سریع، آماده برای استفاده در محیط پروداکشن</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi?color=%2334D058" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi?color=%2334D058" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -4,15 +4,19 @@
|
||||
<p align="center">
|
||||
<em>FastAPI framework, alte prestazioni, facile da imparare, rapido da implementare, pronto per il rilascio in produzione</em>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.com/fastapi/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.com/fastapi/fastapi.svg?branch=master" alt="Build Status">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://badge.fury.io/py/fastapi.svg" alt="Package version">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -11,14 +11,17 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.com/fastapi/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.com/fastapi/fastapi.svg?branch=master" alt="Build Status">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://badge.fury.io/py/fastapi.svg" alt="Package version">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
313
docs/ko/docs/advanced/custom-response.md
Normal file
313
docs/ko/docs/advanced/custom-response.md
Normal file
@@ -0,0 +1,313 @@
|
||||
# 사용자 정의 응답 - HTML, Stream, 파일, 기타
|
||||
|
||||
기본적으로, **FastAPI** 응답을 `JSONResponse`를 사용하여 반환합니다.
|
||||
|
||||
이를 재정의 하려면 [응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 본 것처럼 `Response`를 직접 반환하면 됩니다.
|
||||
|
||||
그러나 `Response` (또는 `JSONResponse`와 같은 하위 클래스)를 직접 반환하면, 데이터가 자동으로 변환되지 않으며 (심지어 `response_model`을 선언했더라도), 문서화가 자동으로 생성되지 않습니다(예를 들어, 생성된 OpenAPI의 일부로 HTTP 헤더 `Content-Type`에 특정 "미디어 타입"을 포함하는 경우).
|
||||
|
||||
하지만 *경로 작업 데코레이터*에서 `response_class` 매개변수를 사용하여 원하는 `Response`(예: 모든 `Response` 하위 클래스)를 선언할 수도 있습니다.
|
||||
|
||||
*경로 작업 함수*에서 반환하는 내용은 해당 `Response`안에 포함됩니다.
|
||||
|
||||
그리고 만약 그 `Response`가 `JSONResponse`와 `UJSONResponse`의 경우 처럼 JSON 미디어 타입(`application/json`)을 가지고 있다면, *경로 작업 데코레이터*에서 선언한 Pydantic의 `response_model`을 사용해 자동으로 변환(및 필터링) 됩니다.
|
||||
|
||||
/// note | 참고
|
||||
|
||||
미디어 타입이 없는 응답 클래스를 사용하는 경우, FastAPI는 응답에 내용이 없을 것으로 예상하므로 생성된 OpenAPI 문서에서 응답 형식을 문서화하지 않습니다.
|
||||
|
||||
///
|
||||
|
||||
## `ORJSONResponse` 사용하기
|
||||
|
||||
예를 들어, 성능을 극대화하려는 경우, <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">orjson</a>을 설치하여 사용하고 응답을 `ORJSONResponse`로 설정할 수 있습니다.
|
||||
|
||||
사용하고자 하는 `Response` 클래스(하위 클래스)를 임포트한 후, **경로 작업 데코레이터*에서 선언하세요.
|
||||
|
||||
대규모 응답의 경우, 딕셔너리를 반환하는 것보다 `Response`를 반환하는 것이 훨씬 빠릅니다.
|
||||
|
||||
이유는 기본적으로, FastAPI가 내부의 모든 항목을 검사하고 JSON으로 직렬화할 수 있는지 확인하기 때문입니다. 이는 사용자 안내서에서 설명된 [JSON 호환 가능 인코더](../tutorial/encoder.md){.internal-link target=_blank}를 사용하는 방식과 동일합니다. 이를 통해 데이터베이스 모델과 같은 **임의의 객체**를 반환할 수 있습니다.
|
||||
|
||||
하지만 반환하는 내용이 **JSON으로 직렬화 가능**하다고 확신하는 경우, 해당 내용을 응답 클래스에 직접 전달할 수 있으며, FastAPI가 반환 내용을 `jsonable_encoder`를 통해 처리한 뒤 응답 클래스에 전달하는 오버헤드를 피할 수 있습니다.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
|
||||
|
||||
/// info | 정보
|
||||
|
||||
`response_class` 매개변수는 응답의 "미디어 타입"을 정의하는 데에도 사용됩니다.
|
||||
|
||||
이 경우, HTTP 헤더 `Content-Type`은 `application/json`으로 설정됩니다.
|
||||
|
||||
그리고 이는 OpenAPI에 그대로 문서화됩니다.
|
||||
|
||||
///
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
`ORJSONResponse`는 FastAPI에서만 사용할 수 있고 Starlette에서는 사용할 수 없습니다.
|
||||
|
||||
///
|
||||
|
||||
## HTML 응답
|
||||
|
||||
**FastAPI**에서 HTML 응답을 직접 반환하려면 `HTMLResponse`를 사용하세요.
|
||||
|
||||
* `HTMLResponse`를 임포트 합니다.
|
||||
* *경로 작업 데코레이터*의 `response_class` 매개변수로 `HTMLResponse`를 전달합니다.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
|
||||
|
||||
/// info | 정보
|
||||
|
||||
`response_class` 매개변수는 응답의 "미디어 타입"을 정의하는 데에도 사용됩니다.
|
||||
|
||||
이 경우, HTTP 헤더 `Content-Type`은 `text/html`로 설정됩니다.
|
||||
|
||||
그리고 이는 OpenAPI에 그대로 문서화 됩니다.
|
||||
|
||||
///
|
||||
|
||||
### `Response` 반환하기
|
||||
|
||||
[응답을 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 본 것 처럼, *경로 작업*에서 응답을 직접 반환하여 재정의할 수도 있습니다.
|
||||
|
||||
위의 예제와 동일하게 `HTMLResponse`를 반환하는 코드는 다음과 같을 수 있습니다:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
|
||||
|
||||
/// warning | 경고
|
||||
|
||||
*경로 작업 함수*에서 직접 반환된 `Response`는 OpenAPI에 문서화되지 않습니다(예를들어, `Content-Type`이 문서화되지 않음) 자동 대화형 문서에서도 표시되지 않습니다.
|
||||
|
||||
///
|
||||
|
||||
/// info | 정보
|
||||
|
||||
물론 실제 `Content-Type` 헤더, 상태 코드 등은 반환된 `Response` 객체에서 가져옵니다.
|
||||
|
||||
///
|
||||
|
||||
### OpenAPI에 문서화하고 `Response` 재정의 하기
|
||||
|
||||
함수 내부에서 응답을 재정의하면서 동시에 OpenAPI에서 "미디어 타입"을 문서화하고 싶다면, `response_class` 매게변수를 사용하면서 `Response` 객체를 반환할 수 있습니다.
|
||||
|
||||
이 경우 `response_class`는 OpenAPI *경로 작업*을 문서화하는 데만 사용되고, 실제로는 여러분이 반환한 `Response`가 그대로 사용됩니다.
|
||||
|
||||
### `HTMLResponse`직접 반환하기
|
||||
|
||||
예를 들어, 다음과 같이 작성할 수 있습니다:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
|
||||
|
||||
이 예제에서, `generate_html_response()` 함수는 HTML을 `str`로 반환하는 대신 이미 `Response`를 생성하고 반환합니다.
|
||||
|
||||
`generate_html_response()`를 호출한 결과를 반환함으로써, 기본적인 **FastAPI** 기본 동작을 재정의 하는 `Response`를 이미 반환하고 있습니다.
|
||||
|
||||
하지만 `response_class`에 `HTMLResponse`를 함께 전달했기 때문에, FastAPI는 이를 OpenAPI 및 대화형 문서에서 `text/html`로 HTML을 문서화 하는 방법을 알 수 있습니다.
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## 사용 가능한 응답들
|
||||
|
||||
다음은 사용할 수 있는 몇가지 응답들 입니다.
|
||||
|
||||
`Response`를 사용하여 다른 어떤 것도 반환 할수 있으며, 직접 하위 클래스를 만들 수도 있습니다.
|
||||
|
||||
/// note | 기술 세부사항
|
||||
|
||||
`from starlette.responses import HTMLResponse`를 사용할 수도 있습니다.
|
||||
|
||||
**FastAPI**는 개발자인 여러분의 편의를 위해 `starlette.responses`를 `fastapi.responses`로 제공 하지만, 대부분의 사용 가능한 응답은 Starlette에서 직접 가져옵니다.
|
||||
|
||||
///
|
||||
|
||||
### `Response`
|
||||
|
||||
기본 `Response` 클래스는 다른 모든 응답 클래스의 부모 클래스 입니다.
|
||||
|
||||
이 클래스를 직접 반환할 수 있습니다.
|
||||
|
||||
다음 매개변수를 받을 수 있습니다:
|
||||
|
||||
* `content` - `str` 또는 `bytes`.
|
||||
* `status_code` - HTTP 상태코드를 나타내는 `int`.
|
||||
* `headers` - 문자열로 이루어진 `dict`.
|
||||
* `media_type` - 미디어 타입을 나타내는 `str` 예: `"text/html"`.
|
||||
|
||||
FastAPI (실제로는 Starlette)가 자동으로 `Content-Length` 헤더를 포함시킵니다. 또한 `media_type`에 기반하여 `Content-Type` 헤더를 포함하며, 텍스트 타입의 경우 문자 집합을 추가 합니다.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse`
|
||||
|
||||
텍스트 또는 바이트를 받아 HTML 응답을 반환합니다. 위에서 설명한 내용과 같습니다.
|
||||
|
||||
### `PlainTextResponse`
|
||||
|
||||
텍스트 또는 바이트를 받아 일반 텍스트 응답을 반환합니다.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse`
|
||||
|
||||
데이터를 받아 `application/json`으로 인코딩된 응답을 반환합니다.
|
||||
|
||||
이는 위에서 설명했듯이 **FastAPI**에서 기본적으로 사용되는 응답 형식입니다.
|
||||
|
||||
### `ORJSONResponse`
|
||||
|
||||
<a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>을 사용하여 빠른 JSON 응답을 제공하는 대안입니다. 위에서 설명한 내용과 같습니다.
|
||||
|
||||
/// info | 정보
|
||||
|
||||
이를 사용하려면 `orjson`을 설치해야합니다. 예: `pip install orjson`.
|
||||
|
||||
///
|
||||
|
||||
### `UJSONResponse`
|
||||
|
||||
<a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>을 사용한 또 다른 JSON 응답 형식입니다.
|
||||
|
||||
/// info | 정보
|
||||
|
||||
이 응답을 사용하려면 `ujson`을 설치해야합니다. 예: 'pip install ujson`.
|
||||
|
||||
///
|
||||
|
||||
/// warning | 경고
|
||||
|
||||
`ujson` 은 일부 예외 경우를 처리하는 데 있어 Python 내장 구현보다 덜 엄격합니다.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
`ORJSONResponse`가 더 빠른 대안일 가능성이 있습니다.
|
||||
|
||||
///
|
||||
|
||||
### `RedirectResponse`
|
||||
|
||||
HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 307(임시 리디렉션)으로 설정됩니다.
|
||||
|
||||
`RedirectResponse`를 직접 반환할 수 있습니다.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
또는 `response_class` 매개변수에서 사용할 수도 있습니다:
|
||||
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
|
||||
|
||||
이 경우, *경로 작업* 함수에서 URL을 직접 반환할 수 있습니다.
|
||||
|
||||
이 경우, 사용되는 `status_code`는 `RedirectResponse`의 기본값인 `307` 입니다.
|
||||
|
||||
---
|
||||
|
||||
`status_code` 매개변수를 `response_class` 매개변수와 함께 사용할 수도 있습니다:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse`
|
||||
|
||||
비동기 제너레이터 또는 일반 제너레이터/이터레이터를 받아 응답 본문을 스트리밍 합니다.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
|
||||
|
||||
#### 파일과 같은 객체를 사용한 `StreamingResponse`
|
||||
|
||||
파일과 같은 객체(예: `open()`으로 반환된 객체)가 있는 경우, 해당 파일과 같은 객체를 반복(iterate)하는 제너레이터 함수를 만들 수 있습니다.
|
||||
|
||||
이 방식으로, 파일 전체를 메모리에 먼저 읽어들일 필요 없이, 제너레이터 함수를 `StreamingResponse`에 전달하여 반환할 수 있습니다.
|
||||
|
||||
이 방식은 클라우드 스토리지, 비디오 처리 등의 다양한 라이브러리와 함께 사용할 수 있습니다.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
|
||||
|
||||
1. 이것이 제너레이터 함수입니다. `yield` 문을 포함하고 있으므로 "제너레이터 함수"입니다.
|
||||
2. `with` 블록을 사용함으로써, 제너레이터 함수가 완료된 후 파일과 같은 객체가 닫히도록 합니다. 즉, 응답 전송이 끝난 후 닫힙니다.
|
||||
3. 이 `yield from`은 함수가 `file_like`라는 객체를 반복(iterate)하도록 합니다. 반복된 각 부분은 이 제너레이터 함수(`iterfile`)에서 생성된 것처럼 `yield` 됩니다.
|
||||
|
||||
이렇게 하면 "생성(generating)" 작업을 내부적으로 다른 무언가에 위임하는 제너레이터 함수가 됩니다.
|
||||
|
||||
이 방식을 사용하면 `with` 블록 안에서 파일을 열 수 있어, 작업이 완료된 후 파일과 같은 객체가 닫히는 것을 보장할 수 있습니다.
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
여기서 표준 `open()`을 사용하고 있기 때문에 `async`와 `await`를 지원하지 않습니다. 따라서 경로 작업은 일반 `def`로 선언합니다.
|
||||
|
||||
///
|
||||
|
||||
### `FileResponse`
|
||||
|
||||
파일을 비동기로 스트리밍하여 응답합니다.
|
||||
|
||||
다른 응답 유형과는 다른 인수를 사용하여 객체를 생성합니다:
|
||||
|
||||
* `path` - 스트리밍할 파일의 경로.
|
||||
* `headers` - 딕셔너리 형식의 사용자 정의 헤더.
|
||||
* `media_type` - 미디어 타입을 나타내는 문자열. 설정되지 않은 경우 파일 이름이나 경로를 사용하여 추론합니다.
|
||||
* `filename` - 설정된 경우 응답의 `Content-Disposition`에 포함됩니다.
|
||||
|
||||
파일 응답에는 적절한 `Content-Length`, `Last-Modified`, 및 `ETag` 헤더가 포함됩니다.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
|
||||
|
||||
또한 `response_class` 매개변수를 사용할 수도 있습니다:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
|
||||
|
||||
이 경우, 경로 작업 함수에서 파일 경로를 직접 반환할 수 있습니다.
|
||||
|
||||
## 사용자 정의 응답 클래스
|
||||
|
||||
`Response`를 상속받아 사용자 정의 응답 클래스를 생성하고 사용할 수 있습니다.
|
||||
|
||||
예를 들어, 포함된 `ORJSONResponse` 클래스에서 사용되지 않는 설정으로 <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">orjson</a>을 사용하고 싶다고 가정해봅시다.
|
||||
|
||||
만약 들여쓰기 및 포맷된 JSON을 반환하고 싶다면, `orjson.OPT_INDENT_2` 옵션을 사용할 수 있습니다.
|
||||
|
||||
`CustomORJSONResponse`를 생성할 수 있습니다. 여기서 핵심은 `Response.render(content)` 메서드를 생성하여 내용을 `bytes`로 반환하는 것입니다:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
|
||||
|
||||
이제 다음 대신:
|
||||
|
||||
```json
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
이 응답은 이렇게 반환됩니다:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Hello World"
|
||||
}
|
||||
```
|
||||
|
||||
물론 JSON 포맷팅보다 더 유용하게 활용할 방법을 찾을 수 있을 것입니다. 😉
|
||||
|
||||
## 기본 응답 클래스
|
||||
|
||||
**FastAPI** 클래스 객체 또는 `APIRouter`를 생성할 때 기본적으로 사용할 응답 클래스를 지정할 수 있습니다.
|
||||
|
||||
이를 정의하는 매개변수는 `default_response_class`입니다.
|
||||
|
||||
아래 예제에서 **FastAPI**는 모든 경로 작업에서 기본적으로 `JSONResponse` 대신 `ORJSONResponse`를 사용합니다.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
여전히 이전처럼 *경로 작업*에서 `response_class`를 재정의할 수 있습니다.
|
||||
|
||||
///
|
||||
|
||||
## 추가 문서화
|
||||
|
||||
OpenAPI에서 `responses`를 사용하여 미디어 타입 및 기타 세부 정보를 선언할 수도 있습니다: [OpenAPI에서 추가 응답](additional-responses.md){.internal-link target=_blank}.
|
||||
@@ -1,162 +1,269 @@
|
||||
* # FastAPI 지원 - 도움말 받기
|
||||
# FastAPI 지원 - 도움 받기
|
||||
|
||||
**FastAPI** 가 마음에 드시나요?
|
||||
**FastAPI** 가 마음에 드시나요?
|
||||
|
||||
FastAPI, 다른 사용자, 개발자를 응원하고 싶으신가요?
|
||||
FastAPI, 다른 사용자, 개발자를 응원하고 싶으신가요?
|
||||
|
||||
혹은 **FastAPI** 에 대해 도움이 필요하신가요?
|
||||
혹은 **FastAPI** 에 대해 도움이 필요하신가요?
|
||||
|
||||
아주 간단하게 응원할 수 있습니다 (몇 번의 클릭만으로).
|
||||
아주 간단하게 응원할 수 있습니다 (몇 번의 클릭만으로).
|
||||
|
||||
또한 도움을 받을 수 있는 방법도 몇 가지 있습니다.
|
||||
또한 도움을 받을 수 있는 방법도 몇 가지 있습니다.
|
||||
|
||||
## 뉴스레터 구독
|
||||
## 뉴스레터 구독
|
||||
|
||||
[**FastAPI와 친구** 뉴스레터](https://github.com/fastapi/fastapi/blob/master/newsletter)를 구독하여 최신 정보를 유지할 수 있습니다{.internal-link target=_blank}:
|
||||
[**FastAPI and friends** 뉴스레터](newsletter.md){.internal-link target=\_blank}를 구독하여 최신 정보를 유지할 수 있습니다:
|
||||
|
||||
- FastAPI 와 그 친구들에 대한 뉴스 🚀
|
||||
- 가이드 📝
|
||||
- 특징 ✨
|
||||
- 획기적인 변화 🚨
|
||||
- 팁과 요령 ✅
|
||||
* FastAPI and friends에 대한 뉴스 🚀
|
||||
* 가이드 📝
|
||||
* 기능 ✨
|
||||
* 획기적인 변화 🚨
|
||||
* 팁과 요령 ✅
|
||||
|
||||
## 트위터에서 FastAPI 팔로우하기
|
||||
## 트위터에서 FastAPI 팔로우하기
|
||||
|
||||
[Follow @fastapi on **Twitter**](https://twitter.com/fastapi) 를 팔로우하여 **FastAPI** 에 대한 최신 뉴스를 얻을 수 있습니다. 🐦
|
||||
<a href="https://twitter.com/fastapi" class="external-link" target="_blank">**Twitter**의 @fastapi를 팔로우</a>하여 **FastAPI** 에 대한 최신 뉴스를 얻을 수 있습니다. 🐦
|
||||
|
||||
## Star **FastAPI** in GitHub
|
||||
## Star **FastAPI** in GitHub
|
||||
|
||||
GitHub에서 FastAPI에 "star"를 붙일 수 있습니다(오른쪽 상단의 star 버튼을 클릭): https://github.com/fastapi/fastapi. ⭐️
|
||||
GitHub에서 FastAPI에 "star"를 붙일 수 있습니다 (오른쪽 상단의 star 버튼을 클릭): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. ⭐️
|
||||
|
||||
스타를 늘림으로써, 다른 사용자들이 좀 더 쉽게 찾을 수 있고, 많은 사람들에게 유용한 것임을 나타낼 수 있습니다.
|
||||
스타를 늘림으로써, 다른 사용자들이 좀 더 쉽게 찾을 수 있고, 많은 사람들에게 유용한 것임을 나타낼 수 있습니다.
|
||||
|
||||
## GitHub 저장소에서 릴리즈 확인
|
||||
## GitHub 저장소에서 릴리즈 확인
|
||||
|
||||
GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/fastapi/fastapi. 👀
|
||||
GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
|
||||
|
||||
여기서 "Releases only"을 선택할 수 있습니다.
|
||||
여기서 "Releases only"을 선택할 수 있습니다.
|
||||
|
||||
이렇게하면, **FastAPI** 의 버그 수정 및 새로운 기능의 구현 등의 새로운 자료 (최신 버전)이 있을 때마다 (이메일) 통지를 받을 수 있습니다.
|
||||
이렇게하면, **FastAPI** 의 버그 수정 및 새로운 기능의 구현 등의 새로운 자료 (최신 버전)이 있을 때마다 (이메일) 통지를 받을 수 있습니다.
|
||||
|
||||
## 개발자와의 연결
|
||||
## 개발자와의 연결
|
||||
|
||||
개발자인 [me (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com/) 와 연락을 취할 수 있습니다.
|
||||
<a href="https://tiangolo.com" class="external-link" target="_blank">개발자(Sebastián Ramírez / `tiangolo`)</a>와 연락을 취할 수 있습니다.
|
||||
|
||||
여러분은 할 수 있습니다:
|
||||
여러분은 할 수 있습니다:
|
||||
|
||||
- [**GitHub**에서 팔로우하기](https://github.com/tiangolo).
|
||||
- 당신에게 도움이 될 저의 다른 오픈소스 프로젝트를 확인하십시오.
|
||||
- 새로운 오픈소스 프로젝트를 만들었을 때 확인하려면 팔로우 하십시오.
|
||||
* <a href="https://github.com/tiangolo" class="external-link" target="_blank">**GitHub**에서 팔로우하기.</a>.
|
||||
* 당신에게 도움이 될 저의 다른 오픈소스 프로젝트를 확인하십시오.
|
||||
* 새로운 오픈소스 프로젝트를 만들었을 때 확인하려면 팔로우 하십시오.
|
||||
* <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">**Twitter**</a> 또는 <a href="https://fosstodon.org/@tiangolo" class="external-link" target="_blank">Mastodon</a>에서 팔로우하기.
|
||||
* FastAPI의 사용 용도를 알려주세요 (그것을 듣는 것을 좋아합니다).
|
||||
* 발표나 새로운 툴 출시 소식을 받아보십시오.
|
||||
* <a href="https://twitter.com/fastapi" class="external-link" target="_blank">**Twitter**의 @fastapi를 팔로우</a> (별도 계정에서) 할 수 있습니다.
|
||||
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">**LinkedIn**에서 팔로우하기.</a>.
|
||||
* 새로운 툴의 발표나 출시 소식을 받아보십시오. (단, Twitter를 더 자주 사용합니다 🤷♂).
|
||||
* <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> 또는 <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>에서 제가 작성한 내용을 읽어 보십시오 (또는 팔로우).
|
||||
* 다른 기사나 아이디어들을 읽고, 제가 만들어왔던 툴에 대해서도 읽으십시오.
|
||||
* 새로운 기사를 읽기 위해 팔로우 하십시오.
|
||||
|
||||
- [**Twitter**에서 팔로우하기](https://twitter.com/tiangolo).
|
||||
- FastAPI의 사용 용도를 알려주세요 (그것을 듣는 것을 좋아합니다).
|
||||
- 발표 또는 새로운 툴 출시할 때 들으십시오.
|
||||
- [follow @fastapi on Twitter](https://twitter.com/fastapi) (별도 계정에서) 할 수 있습니다.
|
||||
## **FastAPI**에 대한 트윗
|
||||
|
||||
- [**Linkedin**에서의 연결](https://www.linkedin.com/in/tiangolo/).
|
||||
- 새로운 툴의 발표나 릴리스를 들을 수 있습니다 (단, Twitter를 더 자주 사용합니다 🤷♂).
|
||||
<a href="https://twitter.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi" class="external-link" target="_blank">**FastAPI**에 대해 트윗</a> 하고 FastAPI가 마음에 드는 이유를 알려주세요. 🎉
|
||||
|
||||
- [**Dev.to**](https://dev.to/tiangolo) 또는 [**Medium**](https://medium.com/@tiangolo)에서 제가 작성한 내용을 읽어 보십시오(또는 팔로우).
|
||||
- 다른 기사나 아이디어들을 읽고, 제가 만들어왔던 툴에 대해서도 읽으십시오.
|
||||
- 새로운 기사를 읽기 위해 팔로우 하십시오.
|
||||
**FastAPI**가 어떻게 사용되고 있는지, 어떤 점이 마음에 들었는지, 어떤 프로젝트/회사에서 사용하고 있는지 등에 대해 듣고 싶습니다.
|
||||
|
||||
## **FastAPI**에 대한 트윗
|
||||
## FastAPI에 투표하기
|
||||
|
||||
[**FastAPI**에 대해 트윗](https://twitter.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) 하고 FastAPI가 마음에 드는 이유를 알려주세요. 🎉
|
||||
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Slant에서 **FastAPI** 에 대해 투표하십시오</a>.
|
||||
* <a href="https://alternativeto.net/software/fastapi/about/" class="external-link" target="_blank">AlternativeTo에서 **FastAPI** 에 대해 투표하십시오</a>.
|
||||
* <a href="https://stackshare.io/pypi-fastapi" class="external-link" target="_blank">StackShare에서 **FastAPI** 에 대해 투표하십시오</a>.
|
||||
|
||||
**FastAPI**가 어떻게 사용되고 있는지, 어떤 점이 마음에 들었는지, 어떤 프로젝트/회사에서 사용하고 있는지 등에 대해 듣고 싶습니다.
|
||||
## GitHub의 이슈로 다른사람 돕기
|
||||
|
||||
## FastAPI에 투표하기
|
||||
다른 사람들의 질문에 도움을 줄 수 있습니다:
|
||||
|
||||
- [Slant에서 **FastAPI** 에 대해 투표하십시오](https://www.slant.co/options/34241/~fastapi-review).
|
||||
- [AlternativeTo**FastAPI** 에 대해 투표하십시오](https://alternativeto.net/software/fastapi/).
|
||||
* <a href="https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered" class="external-link" target="_blank">GitHub 디스커션</a>
|
||||
* <a href="https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+" class="external-link" target="_blank">GitHub 이슈</a>
|
||||
|
||||
## GitHub의 이슈로 다른사람 돕기
|
||||
많은 경우, 여러분은 이미 그 질문에 대한 답을 알고 있을 수도 있습니다. 🤓
|
||||
|
||||
[존재하는 이슈](https://github.com/fastapi/fastapi/issues)를 확인하고 그것을 시도하고 도와줄 수 있습니다. 대부분의 경우 이미 답을 알고 있는 질문입니다. 🤓
|
||||
만약 많은 사람들의 문제를 도와준다면, 공식적인 [FastAPI 전문가](fastapi-people.md#fastapi-experts){.internal-link target=\_blank} 가 될 것입니다. 🎉
|
||||
|
||||
많은 사람들의 문제를 도와준다면, 공식적인 [FastAPI 전문가](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/fastapi-people.md#experts) 가 될 수 있습니다{.internal-link target=_blank}. 🎉
|
||||
가장 중요한 점은: 친절하려고 노력하는 것입니다. 사람들은 좌절감을 안고 오며, 많은 경우 최선의 방식으로 질문하지 않을 수도 있습니다. 하지만 최대한 친절하게 대하려고 노력하세요. 🤗
|
||||
|
||||
## GitHub 저장소 보기
|
||||
**FastAPI** 커뮤니티의 목표는 친절하고 환영하는 것입니다. 동시에, 괴롭힘이나 무례한 행동을 받아들이지 마세요. 우리는 서로를 돌봐야 합니다.
|
||||
|
||||
GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/fastapi/fastapi. 👀
|
||||
---
|
||||
|
||||
"Releases only" 대신 "Watching"을 선택하면 다른 사용자가 새로운 issue를 생성할 때 알림이 수신됩니다.
|
||||
다른 사람들의 질문 (디스커션 또는 이슈에서) 해결을 도울 수 있는 방법은 다음과 같습니다.
|
||||
|
||||
그런 다음 이런 issues를 해결 할 수 있도록 도움을 줄 수 있습니다.
|
||||
### 질문 이해하기
|
||||
|
||||
## 이슈 생성하기
|
||||
* 질문하는 사람이 가진 **목적**과 사용 사례를 이해할 수 있는지 확인하세요.
|
||||
|
||||
GitHub 저장소에 [새로운 이슈 생성](https://github.com/fastapi/fastapi/issues/new/choose) 을 할 수 있습니다, 예를들면 다음과 같습니다:
|
||||
* 질문 (대부분은 질문입니다)이 **명확**한지 확인하세요.
|
||||
|
||||
- **질문**을 하거나 **문제**에 대해 질문합니다.
|
||||
- 새로운 **기능**을 제안 합니다.
|
||||
* 많은 경우, 사용자가 가정한 해결책에 대한 질문을 하지만, 더 **좋은** 해결책이 있을 수 있습니다. 문제와 사용 사례를 더 잘 이해하면 더 나은 **대안적인 해결책**을 제안할 수 있습니다.
|
||||
|
||||
**참고**: 만약 이슈를 생성한다면, 저는 여러분에게 다른 사람들을 도와달라고 부탁할 것입니다. 😉
|
||||
* 질문을 이해할 수 없다면, 더 **자세한 정보**를 요청하세요.
|
||||
|
||||
## Pull Request를 만드십시오
|
||||
### 문제 재현하기
|
||||
|
||||
Pull Requests를 이용하여 소스코드에 [컨트리뷰트](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/contributing.md){.internal-link target=_blank} 할 수 있습니다. 예를 들면 다음과 같습니다:
|
||||
대부분의 경우, 질문은 질문자의 **원본 코드**와 관련이 있습니다.
|
||||
|
||||
- 문서에서 찾은 오타를 수정할 때.
|
||||
많은 경우, 코드의 일부만 복사해서 올리지만, 그것만으로는 **문제를 재현**하기에 충분하지 않습니다.
|
||||
|
||||
- FastAPI를 [편집하여](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml) 작성했거나 찾은 문서, 비디오 또는 팟캐스트를 공유할 때.
|
||||
* 질문자에게 <a href="https://stackoverflow.com/help/minimal-reproducible-example" class="external-link" target="_blank">최소한의 재현 가능한 예제</a>를 제공해달라고 요청하세요. 이렇게 하면 코드를 **복사-붙여넣기**하여 직접 실행하고, 동일한 오류나 동작을 확인하거나 사용 사례를 더 잘 이해할 수 있습니다.
|
||||
|
||||
- 해당 섹션의 시작 부분에 링크를 추가했는지 확인하십시오.
|
||||
* 너그러운 마음이 든다면, 문제 설명만을 기반으로 직접 **예제를 만들어**볼 수도 있습니다. 하지만, 이는 시간이 많이 걸릴 수 있으므로, 먼저 질문을 명확히 해달라고 요청하는 것이 좋습니다.
|
||||
|
||||
- 당신의 언어로 [문서 번역하는데](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/contributing.md#translations){.internal-link target=_blank} 기여할 때.
|
||||
### 해결책 제안하기
|
||||
|
||||
- 또한 다른 사용자가 만든 번역을 검토하는데 도움을 줄 수도 있습니다.
|
||||
* 질문을 충분히 이해한 후에는 가능한 **답변**을 제공할 수 있습니다.
|
||||
|
||||
- 새로운 문서의 섹션을 제안할 때.
|
||||
* 많은 경우, 질문자의 **근본적인 문제나 사용 사례**를 이해하는 것이 중요합니다. 그들이 시도하는 방법보다 더 나은 해결책이 있을 수 있기 때문입니다.
|
||||
|
||||
- 기존 문제/버그를 수정할 때.
|
||||
### 해결 요청하기
|
||||
|
||||
- 새로운 feature를 추가할 때.
|
||||
질문자가 답변을 확인하고 나면, 당신이 문제를 해결했을 가능성이 높습니다. 축하합니다, **당신은 영웅입니다**! 🦸
|
||||
|
||||
## 채팅에 참여하십시오
|
||||
* 이제 문제를 해결했다면, 질문자에게 다음을 요청할 수 있습니다.
|
||||
|
||||
👥 [디스코드 채팅 서버](https://discord.gg/VQjSZaeJmf) 👥 에 가입하고 FastAPI 커뮤니티에서 다른 사람들과 어울리세요.
|
||||
* GitHub 디스커션에서: 댓글을 **답변**으로 표시하도록 요청하세요.
|
||||
* GitHub 이슈에서: 이슈를 **닫아달라고** 요청하세요.
|
||||
|
||||
/// tip
|
||||
## GitHub 저장소 보기
|
||||
|
||||
질문이 있는 경우, [GitHub 이슈 ](https://github.com/fastapi/fastapi/issues/new/choose) 에서 질문하십시오, [FastAPI 전문가](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/fastapi-people.md#experts) 의 도움을 받을 가능성이 높습니다{.internal-link target=_blank} .
|
||||
GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
|
||||
|
||||
///
|
||||
"Releases only" 대신 "Watching"을 선택하면, 새로운 이슈나 질문이 생성될 때 알림을 받을 수 있습니다. 또한, 특정하게 새로운 이슈, 디스커션, PR 등만 알림 받도록 설정할 수도 있습니다.
|
||||
|
||||
```
|
||||
다른 일반적인 대화에서만 채팅을 사용하십시오.
|
||||
```
|
||||
그런 다음 이런 이슈들을 해결 할 수 있도록 도움을 줄 수 있습니다.
|
||||
|
||||
기존 [지터 채팅](https://gitter.im/fastapi/fastapi) 이 있지만 채널과 고급기능이 없어서 대화를 하기가 조금 어렵기 때문에 지금은 디스코드가 권장되는 시스템입니다.
|
||||
## 이슈 생성하기
|
||||
|
||||
### 질문을 위해 채팅을 사용하지 마십시오
|
||||
GitHub 저장소에 <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">새로운 이슈 생성</a>을 할 수 있습니다, 예를들면 다음과 같습니다:
|
||||
|
||||
채팅은 더 많은 "자유로운 대화"를 허용하기 때문에, 너무 일반적인 질문이나 대답하기 어려운 질문을 쉽게 질문을 할 수 있으므로, 답변을 받지 못할 수 있습니다.
|
||||
* **질문**을 하거나 **문제**에 대해 질문합니다.
|
||||
* 새로운 **기능**을 제안 합니다.
|
||||
|
||||
GitHub 이슈에서의 템플릿은 올바른 질문을 작성하도록 안내하여 더 쉽게 좋은 답변을 얻거나 질문하기 전에 스스로 문제를 해결할 수도 있습니다. 그리고 GitHub에서는 시간이 조금 걸리더라도 항상 모든 것에 답할 수 있습니다. 채팅 시스템에서는 개인적으로 그렇게 할 수 없습니다. 😅
|
||||
**참고**: 만약 이슈를 생성한다면, 저는 여러분에게 다른 사람들을 도와달라고 부탁할 것입니다. 😉
|
||||
|
||||
채팅 시스템에서의 대화 또한 GitHub에서 처럼 쉽게 검색할 수 없기 때문에 대화 중에 질문과 답변이 손실될 수 있습니다. 그리고 GitHub 이슈에 있는 것만 [FastAPI 전문가](https://github.com/fastapi/fastapi/blob/master/docs/en/docs/fastapi-people.md#experts)가 되는 것으로 간주되므로{.internal-link target=_blank} , GitHub 이슈에서 더 많은 관심을 받을 것입니다.
|
||||
## Pull Requests 리뷰하기
|
||||
|
||||
반면, 채팅 시스템에는 수천 명의 사용자가 있기 때문에, 거의 항상 대화 상대를 찾을 가능성이 높습니다. 😄
|
||||
다른 사람들의 pull request를 리뷰하는 데 도움을 줄 수 있습니다.
|
||||
|
||||
## 개발자 스폰서가 되십시오
|
||||
다시 한번 말하지만, 최대한 친절하게 리뷰해 주세요. 🤗
|
||||
|
||||
[GitHub 스폰서](https://github.com/sponsors/tiangolo) 를 통해 개발자를 경제적으로 지원할 수 있습니다.
|
||||
---
|
||||
|
||||
감사하다는 말로 커피를 ☕️ 한잔 사줄 수 있습니다. 😄
|
||||
Pull Rrquest를 리뷰할 때 고려해야 할 사항과 방법은 다음과 같습니다:
|
||||
|
||||
또한 FastAPI의 실버 또는 골드 스폰서가 될 수 있습니다. 🏅🎉
|
||||
### 문제 이해하기
|
||||
|
||||
## FastAPI를 강화하는 도구의 스폰서가 되십시오
|
||||
* 먼저, 해당 pull request가 해결하려는 **문제를 이해하는지** 확인하세요. GitHub 디스커션 또는 이슈에서 더 긴 논의가 있었을 수도 있습니다.
|
||||
|
||||
문서에서 보았듯이, FastAPI는 Starlette과 Pydantic 라는 거인의 어깨에 타고 있습니다.
|
||||
* Pull request가 필요하지 않을 가능성도 있습니다. **다른 방식**으로 문제를 해결할 수 있다면, 그 방법을 제안하거나 질문할 수 있습니다.
|
||||
|
||||
다음의 스폰서가 될 수 있습니다
|
||||
### 스타일에 너무 신경 쓰지 않기
|
||||
|
||||
- [Samuel Colvin (Pydantic)](https://github.com/sponsors/samuelcolvin)
|
||||
- [Encode (Starlette, Uvicorn)](https://github.com/sponsors/encode)
|
||||
* 커밋 메시지 스타일 같은 것에 너무 신경 쓰지 않아도 됩니다. 저는 직접 커밋을 수정하여 squash and merge를 수행할 것입니다.
|
||||
|
||||
------
|
||||
* 코드 스타일 규칙도 걱정할 필요 없습니다. 이미 자동화된 도구들이 이를 검사하고 있습니다.
|
||||
|
||||
감사합니다! 🚀
|
||||
스타일이나 일관성 관련 요청이 필요한 경우, 제가 직접 요청하거나 필요한 변경 사항을 추가 커밋으로 수정할 것입니다.
|
||||
|
||||
### 코드 확인하기
|
||||
|
||||
* 코드를 읽고, **논리적으로 타당**한지 확인한 후 로컬에서 실행하여 문제가 해결되는지 확인하세요.
|
||||
|
||||
* 그런 다음, 확인했다고 **댓글**을 남겨 주세요. 그래야 제가 검토했음을 알 수 있습니다.
|
||||
|
||||
/// info
|
||||
|
||||
불행히도, 제가 단순히 여러 개의 승인만으로 PR을 신뢰할 수는 없습니다.
|
||||
|
||||
3개, 5개 이상의 승인이 달린 PR이 실제로는 깨져 있거나, 버그가 있거나, 주장하는 문제를 해결하지 못하는 경우가 여러 번 있었습니다. 😅
|
||||
|
||||
따라서, 정말로 코드를 읽고 실행한 뒤, 댓글로 확인 내용을 남겨 주는 것이 매우 중요합니다. 🤓
|
||||
|
||||
///
|
||||
|
||||
* PR을 더 단순하게 만들 수 있다면 그렇게 요청할 수 있지만, 너무 까다로울 필요는 없습니다. 주관적인 견해가 많이 있을 수 있기 때문입니다 (그리고 저도 제 견해가 있을 거예요 🙈). 따라서 핵심적인 부분에 집중하는 것이 좋습니다.
|
||||
|
||||
### 테스트
|
||||
|
||||
* PR에 **테스트**가 포함되어 있는지 확인하는 데 도움을 주세요.
|
||||
|
||||
* PR을 적용하기 전에 테스트가 **실패**하는지 확인하세요. 🚨
|
||||
|
||||
* PR을 적용한 후 테스트가 **통과**하는지 확인하세요. ✅
|
||||
|
||||
* 많은 PR에는 테스트가 없습니다. 테스트를 추가하도록 **상기**시켜줄 수도 있고, 직접 테스트를 **제안**할 수도 있습니다. 이는 시간이 많이 소요되는 부분 중 하나이며, 그 부분을 많이 도와줄 수 있습니다.
|
||||
|
||||
* 그리고 시도한 내용을 댓글로 남겨주세요. 그러면 제가 확인했다는 걸 알 수 있습니다. 🤓
|
||||
|
||||
## Pull Request를 만드십시오
|
||||
|
||||
Pull Requests를 이용하여 소스코드에 [컨트리뷰트](contributing.md){.internal-link target=\_blank} 할 수 있습니다. 예를 들면 다음과 같습니다:
|
||||
|
||||
* 문서에서 발견한 오타를 수정할 때.
|
||||
* FastAPI 관련 문서, 비디오 또는 팟캐스트를 작성했거나 발견하여 <a href="https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">이 파일을 편집하여</a> 공유할 때.
|
||||
* 해당 섹션의 시작 부분에 링크를 추가해야 합니다.
|
||||
* 당신의 언어로 [문서 번역하는데](contributing.md#translations){.internal-link target=\_blank} 기여할 때.
|
||||
* 다른 사람이 작성한 번역을 검토하는 것도 도울 수 있습니다.
|
||||
* 새로운 문서의 섹션을 제안할 때.
|
||||
* 기존 문제/버그를 수정할 때.
|
||||
* 테스트를 반드시 추가해야 합니다.
|
||||
* 새로운 feature를 추가할 때.
|
||||
* 테스트를 반드시 추가해야 합니다.
|
||||
* 관련 문서가 필요하다면 반드시 추가해야 합니다.
|
||||
|
||||
## FastAPI 유지 관리에 도움 주기
|
||||
|
||||
**FastAPI**의 유지 관리를 도와주세요! 🤓
|
||||
|
||||
할 일이 많고, 그 중 대부분은 **여러분**이 할 수 있습니다.
|
||||
|
||||
지금 할 수 있는 주요 작업은:
|
||||
|
||||
* [GitHub에서 다른 사람들의 질문에 도움 주기](#github_1){.internal-link target=_blank} (위의 섹션을 참조하세요).
|
||||
* [Pull Request 리뷰하기](#pull-requests){.internal-link target=_blank} (위의 섹션을 참조하세요).
|
||||
|
||||
이 두 작업이 **가장 많은 시간을 소모**하는 일입니다. 그것이 FastAPI 유지 관리의 주요 작업입니다.
|
||||
|
||||
이 작업을 도와주신다면, **FastAPI 유지 관리에 도움을 주는 것**이며 그것이 **더 빠르고 더 잘 발전하는 것**을 보장하는 것입니다. 🚀
|
||||
|
||||
## 채팅에 참여하십시오
|
||||
|
||||
👥 <a href="https://discord.gg/VQjSZaeJmf" class="external-link" target="_blank">디스코드 채팅 서버</a> 👥 에 가입하고 FastAPI 커뮤니티에서 다른 사람들과 어울리세요.
|
||||
|
||||
/// tip
|
||||
|
||||
질문이 있는 경우, <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub 디스커션</a> 에서 질문하십시오, [FastAPI Experts](fastapi-people.md#fastapi-experts){.internal-link target=_blank} 의 도움을 받을 가능성이 높습니다.
|
||||
|
||||
다른 일반적인 대화에서만 채팅을 사용하십시오.
|
||||
|
||||
///
|
||||
|
||||
### 질문을 위해 채팅을 사용하지 마십시오
|
||||
|
||||
채팅은 더 많은 "자유로운 대화"를 허용하기 때문에, 너무 일반적인 질문이나 대답하기 어려운 질문을 쉽게 질문을 할 수 있으므로, 답변을 받지 못할 수 있습니다.
|
||||
|
||||
GitHub 이슈에서의 템플릿은 올바른 질문을 작성하도록 안내하여 더 쉽게 좋은 답변을 얻거나 질문하기 전에 스스로 문제를 해결할 수도 있습니다. 그리고 GitHub에서는 시간이 조금 걸리더라도 항상 모든 것에 답할 수 있습니다. 채팅 시스템에서는 개인적으로 그렇게 할 수 없습니다. 😅
|
||||
|
||||
채팅 시스템에서의 대화 또한 GitHub에서 처럼 쉽게 검색할 수 없기 때문에 대화 중에 질문과 답변이 손실될 수 있습니다. 그리고 GitHub 이슈에 있는 것만 [FastAPI Expert](fastapi-people.md#fastapi-experts){.internal-link target=_blank}가 되는 것으로 간주되므로, GitHub 이슈에서 더 많은 관심을 받을 것입니다.
|
||||
|
||||
반면, 채팅 시스템에는 수천 명의 사용자가 있기 때문에, 거의 항상 대화 상대를 찾을 가능성이 높습니다. 😄
|
||||
|
||||
## 개발자 스폰서가 되십시오
|
||||
|
||||
<a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub 스폰서</a> 를 통해 개발자를 경제적으로 지원할 수 있습니다.
|
||||
|
||||
감사하다는 말로 커피를 ☕️ 한잔 사줄 수 있습니다. 😄
|
||||
|
||||
또한 FastAPI의 실버 또는 골드 스폰서가 될 수 있습니다. 🏅🎉
|
||||
|
||||
## FastAPI를 강화하는 도구의 스폰서가 되십시오
|
||||
|
||||
문서에서 보았듯이, FastAPI는 Starlette과 Pydantic 라는 거인의 어깨에 타고 있습니다.
|
||||
|
||||
다음의 스폰서가 될 수 있습니다
|
||||
|
||||
* <a href="https://github.com/sponsors/samuelcolvin" class="external-link" target="_blank">Samuel Colvin (Pydantic)</a>
|
||||
* <a href="https://github.com/sponsors/encode" class="external-link" target="_blank">Encode (Starlette, Uvicorn)</a>
|
||||
|
||||
---
|
||||
|
||||
감사합니다! 🚀
|
||||
|
||||
@@ -11,15 +11,18 @@
|
||||
<em>FastAPI 프레임워크, 고성능, 간편한 학습, 빠른 코드 작성, 준비된 프로덕션</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi?color=%2334D058" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
275
docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md
Normal file
275
docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# yield를 사용하는 의존성
|
||||
|
||||
FastAPI는 <abbr title='때로는 "종료 코드", "정리 코드", "종료 처리 코드", "닫기 코드", "컨텍스트 관리자 종료 코드" 등으로도 불립니다'>작업 완료 후 추가 단계를 수행하는</abbr> 의존성을 지원합니다.
|
||||
|
||||
이를 구현하려면 `return` 대신 `yield`를 사용하고, 추가로 실행할 단계 (코드)를 그 뒤에 작성하세요.
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
각 의존성마다 `yield`는 한 번만 사용해야 합니다.
|
||||
|
||||
///
|
||||
|
||||
/// note | 기술 세부사항
|
||||
|
||||
다음과 함께 사용할 수 있는 모든 함수:
|
||||
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> 또는
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
|
||||
|
||||
는 **FastAPI**의 의존성으로 사용할 수 있습니다.
|
||||
|
||||
사실, FastAPI는 내부적으로 이 두 데코레이터를 사용합니다.
|
||||
|
||||
///
|
||||
|
||||
## `yield`를 사용하는 데이터베이스 의존성
|
||||
|
||||
예를 들어, 이 기능을 사용하면 데이터베이스 세션을 생성하고 작업이 끝난 후에 세션을 종료할 수 있습니다.
|
||||
|
||||
응답을 생성하기 전에는 `yield`문을 포함하여 그 이전의 코드만이 실행됩니다:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
|
||||
|
||||
yield된 값은 *경로 작업* 및 다른 의존성들에 주입되는 값 입니다:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
|
||||
|
||||
`yield`문 다음의 코드는 응답을 생성한 후 보내기 전에 실행됩니다:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
`async` 함수와 일반 함수 모두 사용할 수 있습니다.
|
||||
|
||||
**FastAPI**는 일반 의존성과 마찬가지로 각각의 함수를 올바르게 처리할 것입니다.
|
||||
|
||||
///
|
||||
|
||||
## `yield`와 `try`를 사용하는 의존성
|
||||
|
||||
`yield`를 사용하는 의존성에서 `try` 블록을 사용한다면, 의존성을 사용하는 도중 발생한 모든 예외를 받을 수 있습니다.
|
||||
|
||||
예를 들어, 다른 의존성이나 *경로 작업*의 중간에 데이터베이스 트랜잭션 "롤백"이 발생하거나 다른 오류가 발생한다면, 해당 예외를 의존성에서 받을 수 있습니다.
|
||||
|
||||
따라서, 의존성 내에서 `except SomeException`을 사용하여 특정 예외를 처리할 수 있습니다.
|
||||
|
||||
마찬가지로, `finally`를 사용하여 예외 발생 여부와 관계 없이 종료 단계까 실행되도록 할 수 있습니다.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
|
||||
|
||||
## `yield`를 사용하는 하위 의존성
|
||||
|
||||
모든 크기와 형태의 하위 의존성과 하위 의존성의 "트리"도 가질 수 있으며, 이들 모두가 `yield`를 사용할 수 있습니다.
|
||||
|
||||
**FastAPI**는 `yield`를 사용하는 각 의존성의 "종료 코드"가 올바른 순서로 실행되도록 보장합니다.
|
||||
|
||||
예를 들어, `dependency_c`는 `dependency_b`에 의존할 수 있고, `dependency_b`는 `dependency_a`에 의존할 수 있습니다.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
|
||||
|
||||
이들 모두는 `yield`를 사용할 수 있습니다.
|
||||
|
||||
이 경우 `dependency_c`는 종료 코드를 실행하기 위해, `dependency_b`의 값 (여기서는 `dep_b`로 명명)이 여전히 사용 가능해야 합니다.
|
||||
|
||||
그리고, `dependency_b`는 종료 코드를 위해 `dependency_a`의 값 (여기서는 `dep_a`로 명명) 이 사용 가능해야 합니다.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
|
||||
|
||||
같은 방식으로, `yield`를 사용하는 의존성과 `return`을 사용하는 의존성을 함께 사용할 수 있으며, 이들 중 일부가 다른 것들에 의존할 수 있습니다.
|
||||
|
||||
그리고 `yield`를 사용하는 다른 여러 의존성을 필요로 하는 단일 의존성을 가질 수도 있습니다.
|
||||
|
||||
원하는 의존성을 원하는 대로 조합할 수 있습니다.
|
||||
|
||||
**FastAPI**는 모든 것이 올바른 순서로 실행되도록 보장합니다.
|
||||
|
||||
/// note | 기술 세부사항
|
||||
|
||||
파이썬의 <a href=“https://docs.python.org/3/library/contextlib.html” class=“external-link” target=“_blank”>Context Managers</a> 덕분에 이 기능이 작동합니다.
|
||||
|
||||
**FastAPI**는 이를 내부적으로 컨텍스트 관리자를 사용하여 구현합니다.
|
||||
|
||||
///
|
||||
|
||||
## `yield`와 `HTTPException`를 사용하는 의존성
|
||||
|
||||
`yield`와 `try` 블록이 있는 의존성을 사용하여 예외를 처리할 수 있다는 것을 알게 되었습니다.
|
||||
|
||||
같은 방식으로, `yield` 이후의 종료 코드에서 `HTTPException`이나 유사한 예외를 발생시킬 수 있습니다.
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
이는 다소 고급 기술이며, 대부분의 경우 경로 연산 함수 등 나머지 애플리케이션 코드 내부에서 예외 (`HTTPException` 포함)를 발생시킬 수 있으므로 실제로는 필요하지 않을 것입니다.
|
||||
|
||||
하지만 필요한 경우 사용할 수 있습니다. 🤓
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
|
||||
|
||||
예외를 처리하고(또는 추가로 다른 `HTTPException`을 발생시키기 위해) 사용할 수 있는 또 다른 방법은 [사용자 정의 예외 처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}를 생성하는 것 입니다.
|
||||
|
||||
## `yield`와 `except`를 사용하는 의존성
|
||||
|
||||
`yield`를 사용하는 의존성에서 `except`를 사용하여 예외를 포착하고 예외를 다시 발생시키지 않거나 (또는 새 예외를 발생시키지 않으면), FastAPI는 해당 예외가 발생했는지 알 수 없습니다. 이는 일반적인 Python 방식과 동일합니다:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
|
||||
|
||||
이 경우, `HTTPException`이나 유사한 예외를 발생시키지 않기 때문에 클라이언트는 HTTP 500 Internal Server Error 응답을 보게 되지만, 서버는 어떤 오류가 발생했는지에 대한 **로그**나 다른 표시를 전혀 가지지 않게 됩니다. 😱
|
||||
|
||||
### `yield`와 `except`를 사용하는 의존성에서 항상 `raise` 하기
|
||||
|
||||
`yield`가 있는 의존성에서 예외를 잡았을 때는 `HTTPException`이나 유사한 예외를 새로 발생시키지 않는 한, 반드시 원래의 예외를 다시 발생시켜야 합니다.
|
||||
|
||||
`raise`를 사용하여 동일한 예외를 다시 발생시킬 수 있습니다:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
|
||||
|
||||
이제 클라이언트는 동일한 *HTTP 500 Internal Server Error* 오류 응답을 받게 되지만, 서버 로그에는 사용자 정의 예외인 `InternalError"가 기록됩니다. 😎
|
||||
|
||||
## `yield`를 사용하는 의존성의 실행 순서
|
||||
|
||||
실행 순서는 아래 다이어그램과 거의 비슷합니다. 시간은 위에서 아래로 흐릅니다. 그리고 각 열은 상호 작용하거나 코드를 실행하는 부분 중 하나입니다.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
|
||||
participant client as Client
|
||||
participant handler as Exception handler
|
||||
participant dep as Dep with yield
|
||||
participant operation as Path Operation
|
||||
participant tasks as Background tasks
|
||||
|
||||
Note over client,operation: Can raise exceptions, including HTTPException
|
||||
client ->> dep: Start request
|
||||
Note over dep: Run code up to yield
|
||||
opt raise Exception
|
||||
dep -->> handler: Raise Exception
|
||||
handler -->> client: HTTP error response
|
||||
end
|
||||
dep ->> operation: Run dependency, e.g. DB session
|
||||
opt raise
|
||||
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
|
||||
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 -->> tasks: Handle exceptions in the background task code
|
||||
end
|
||||
```
|
||||
|
||||
/// info | 정보
|
||||
|
||||
클라이언트에 **하나의 응답** 만 전송됩니다. 이는 오류 응답 중 하나일 수도 있고,*경로 작업*에서 생성된 응답일 수도 있습니다.
|
||||
|
||||
이러한 응답 중 하나가 전송된 후에는 다른 응답을 보낼 수 없습니다.
|
||||
|
||||
///
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
이 다이어그램은 `HTTPException`을 보여주지만, `yield`를 사용하는 의존성에서 처리한 예외나 [사용자 정의 예외처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.를 사용하여 처리한 다른 예외도 발생시킬 수 있습니다.
|
||||
|
||||
어떤 예외가 발생하든, `HTTPException`을 포함하여 yield를 사용하는 의존성으로 전달됩니다. 대부분의 경우 예외를 다시 발생시키거나 새로운 예외를 발생시켜야 합니다.
|
||||
|
||||
///
|
||||
|
||||
## `yield`, `HTTPException`, `except` 및 백그라운드 작업을 사용하는 의존성
|
||||
|
||||
/// warning | 경고
|
||||
|
||||
이러한 기술적 세부 사항은 대부분 필요하지 않으므로 이 섹션을 건너뛰고 아래에서 계속 진행해도 됩니다.
|
||||
|
||||
이러한 세부 정보는 주로 FastAPI 0.106.0 이전 버전에서 `yield`가 있는 의존성의 리소스를 백그라운드 작업에서 사용했던 경우메 유용합니다.
|
||||
|
||||
///
|
||||
|
||||
### `yield`와 `except`를 사용하는 의존성, 기술 세부사항
|
||||
|
||||
FastAPI 0.110.0 이전에는 `yield`가 포함된 의존성을 사용한 후 해당 의존성에서 `except`가 포함된 예외를 캡처하고 다시 예외를 발생시키지 않으면 예외가 자동으로 예외 핸들러 또는 내부 서버 오류 핸들러로 발생/전달되었습니다.
|
||||
|
||||
이는 처리기 없이 전달된 예외(내부 서버 오류)에서 처리되지 않은 메모리 소비를 수정하고 일반 파이썬 코드의 동작과 일치하도록 하기 위해 0.110.0 버전에서 변경되었습니다.
|
||||
|
||||
### 백그라운드 작업과 `yield`를 사용하는 의존성, 기술 세부사항
|
||||
|
||||
FastAPI 0.106.0 이전에는 `yield` 이후에 예외를 발생시키는 것이 불가능했습니다. `yield`가 있는 의존성 종료 코드는 응답이 전송된 이후에 실행되었기 때문에, [예외 처리기](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}가 이미 실행된 상태였습니다.
|
||||
|
||||
이는 주로 백그라운드 작업 내에서 의존성에서 "yield된" 동일한 객체를 사용할 수 있도록 하기 위해 이런 방식으로 설계되었습니다. 종료 코드는 백그라운드 작업이 완료된 후에 실행되었기 때문입니다
|
||||
|
||||
하지만 이렇게 하면 리소스를 불필요하게 양보한 의존성(예: 데이터베이스 연결)에서 보유하면서 응답이 네트워크를 통해 이동할 때까지 기다리는 것을 의미하기 때문에 FastAPI 0.106.0에서 변경되었습니다.
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
또한 백그라운드 작업은 일반적으로 자체 리소스(예: 자체 데이터베이스 연결)를 사용하여 별도로 처리해야 하는 독립적인 로직 집합입니다.
|
||||
|
||||
따라서 이렇게 하면 코드가 더 깔끔해집니다.
|
||||
|
||||
///
|
||||
|
||||
만약 이전에 이러한 동작에 의존했다면, 이제는 백그라운드 작업 내부에서 백그라운드 작업을 위한 리소스를 생성하고, `yield`가 있는 의존성의 리소스에 의존하지 않는 데이터만 내부적으로 사용해야합니다.
|
||||
|
||||
예를 들어, 동일한 데이터베이스 세션을 사용하는 대신, 백그라운드 작업 내부에서 새로운 데이터베이스 세션을 생성하고 이 새로운 세션을 사용하여 데이터베이스에서 객체를 가져와야 합니다. 그리고 데이터베이스 객체를 백그라운드 작업 함수의 매개변수로 직접 전달하는 대신, 해당 객체의 ID를 전달한 다음 백그라운드 작업 함수 내부에서 객체를 다시 가져와야 합니다
|
||||
|
||||
## 컨텍스트 관리자
|
||||
|
||||
### "컨텍스트 관리자"란?
|
||||
|
||||
"컨텍스트 관리자"는 Python에서 `with` 문에서 사용할 수 있는 모든 객체를 의미합니다.
|
||||
|
||||
예를 들어, <a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank"> `with`를 사용하여 파일을 읽을 수 있습니다</a>:
|
||||
|
||||
```Python
|
||||
with open("./somefile.txt") as f:
|
||||
contents = f.read()
|
||||
print(contents)
|
||||
```
|
||||
|
||||
내부적으로 `open("./somefile.txt")` 는 "컨텍스트 관리자(Context Manager)"라고 불리는 객체를 생성합니다.
|
||||
|
||||
`with` 블록이 끝나면, 예외가 발생했더라도 파일을 닫도록 보장합니다.
|
||||
|
||||
`yield`가 있는 의존성을 생성하면 **FastAPI**는 내부적으로 이를 위한 컨텍스트 매니저를 생성하고 다른 관련 도구들과 결합합니다.
|
||||
|
||||
### `yield`를 사용하는 의존성에서 컨텍스트 관리자 사용하기
|
||||
|
||||
/// warning | 경고
|
||||
|
||||
이것은 어느 정도 "고급" 개념입니다.
|
||||
|
||||
**FastAPI**를 처음 시작하는 경우 지금은 이 부분을 건너뛰어도 좋습니다.
|
||||
|
||||
///
|
||||
|
||||
Python에서는 다음을 통해 컨텍스트 관리자를 생성할 수 있습니다. <a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank"> 두 가지 메서드가 있는 클래스를 생성합니다: `__enter__()` and `__exit__()`</a>.
|
||||
|
||||
**FastAPI**의 `yield`가 있는 의존성 내에서
|
||||
`with` 또는 `async with`문을 사용하여 이들을 활용할 수 있습니다:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
|
||||
|
||||
/// tip | 팁
|
||||
|
||||
컨텍스트 관리자를 생성하는 또 다른 방법은 다음과 같습니다:
|
||||
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> 또는
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
|
||||
|
||||
이들은 단일 `yield`가 있는 함수를 꾸미는 데 사용합니다.
|
||||
|
||||
이것이 **FastAPI**가 `yield`가 있는 의존성을 위해 내부적으로 사용하는 방식입니다.
|
||||
|
||||
하지만 FastAPI 의존성에는 이러한 데코레이터를 사용할 필요가 없습니다(그리고 사용해서도 안됩니다).
|
||||
|
||||
FastAPI가 내부적으로 이를 처리해 줄 것입니다.
|
||||
|
||||
///
|
||||
@@ -32,7 +32,7 @@ OAuth2는 (우리가 사용하고 있는) "패스워드 플로우"을 사용할
|
||||
* `instagram_basic`은 페이스북/인스타그램에서 사용합니다.
|
||||
* `https://www.googleapis.com/auth/drive`는 Google에서 사용합니다.
|
||||
|
||||
/// 정보
|
||||
/// info | 정보
|
||||
|
||||
OAuth2에서 "범위"는 필요한 특정 권한을 선언하는 문자열입니다.
|
||||
|
||||
@@ -61,7 +61,7 @@ OAuth2의 경우 문자열일 뿐입니다.
|
||||
* `scope`는 선택적인 필드로 공백으로 구분된 문자열로 구성된 큰 문자열입니다.
|
||||
* `grant_type`(선택적으로 사용).
|
||||
|
||||
/// 팁
|
||||
/// tip | 팁
|
||||
|
||||
OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type` 필드를 *요구*하지만 `OAuth2PasswordRequestForm`은 이를 강요하지 않습니다.
|
||||
|
||||
@@ -72,7 +72,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
|
||||
* `client_id`(선택적으로 사용) (예제에서는 필요하지 않습니다).
|
||||
* `client_secret`(선택적으로 사용) (예제에서는 필요하지 않습니다).
|
||||
|
||||
/// 정보
|
||||
/// info | 정보
|
||||
|
||||
`OAuth2PasswordRequestForm`은 `OAuth2PasswordBearer`와 같이 **FastAPI**에 대한 특수 클래스가 아닙니다.
|
||||
|
||||
@@ -86,7 +86,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
|
||||
|
||||
### 폼 데이터 사용하기
|
||||
|
||||
/// 팁
|
||||
/// tip | 팁
|
||||
|
||||
종속성 클래스 `OAuth2PasswordRequestForm`의 인스턴스에는 공백으로 구분된 긴 문자열이 있는 `scope` 속성이 없고 대신 전송된 각 범위에 대한 실제 문자열 목록이 있는 `scopes` 속성이 있습니다.
|
||||
|
||||
@@ -126,7 +126,7 @@ OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type`
|
||||
|
||||
따라서 해커는 다른 시스템에서 동일한 암호를 사용하려고 시도할 수 없습니다(많은 사용자가 모든 곳에서 동일한 암호를 사용하므로 이는 위험할 수 있습니다).
|
||||
|
||||
//// tab | P파이썬 3.7 이상
|
||||
//// tab | 파이썬 3.7 이상
|
||||
|
||||
{* ../../docs_src/security/tutorial003.py hl[80:83] *}
|
||||
|
||||
@@ -150,7 +150,7 @@ UserInDB(
|
||||
)
|
||||
```
|
||||
|
||||
/// 정보
|
||||
/// info | 정보
|
||||
|
||||
`**user_dict`에 대한 자세한 설명은 [**추가 모델** 문서](../extra-models.md#about-user_indict){.internal-link target=_blank}를 다시 읽어봅시다.
|
||||
|
||||
@@ -166,7 +166,7 @@ UserInDB(
|
||||
|
||||
이 간단한 예제에서는 완전히 안전하지 않고, 동일한 `username`을 토큰으로 반환합니다.
|
||||
|
||||
/// 팁
|
||||
/// tip | 팁
|
||||
|
||||
다음 장에서는 패스워드 해싱 및 <abbr title="JSON Web Tokens">JWT</abbr> 토큰을 사용하여 실제 보안 구현을 볼 수 있습니다.
|
||||
|
||||
@@ -176,7 +176,7 @@ UserInDB(
|
||||
|
||||
{* ../../docs_src/security/tutorial003.py hl[85] *}
|
||||
|
||||
/// 팁
|
||||
/// tip | 팁
|
||||
|
||||
사양에 따라 이 예제와 동일하게 `access_token` 및 `token_type`이 포함된 JSON을 반환해야 합니다.
|
||||
|
||||
@@ -202,7 +202,7 @@ UserInDB(
|
||||
|
||||
{* ../../docs_src/security/tutorial003.py hl[58:66,69:72,90] *}
|
||||
|
||||
/// 정보
|
||||
/// info | 정보
|
||||
|
||||
여기서 반환하는 값이 `Bearer`인 추가 헤더 `WWW-Authenticate`도 사양의 일부입니다.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -11,15 +11,18 @@
|
||||
<em>FastAPI to szybki, prosty w nauce i gotowy do użycia w produkcji framework</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi?color=%2334D058" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
@@ -14,38 +14,186 @@ Para aprender o básico de HTTPS de uma perspectiva do usuário, verifique <a hr
|
||||
|
||||
Agora, a partir de uma perspectiva do desenvolvedor, aqui estão algumas coisas para ter em mente ao pensar em HTTPS:
|
||||
|
||||
* Para HTTPS, o servidor precisa ter certificados gerados por um terceiro.
|
||||
* Esses certificados são adquiridos de um terceiro, eles não são simplesmente "gerados".
|
||||
* Certificados têm um tempo de vida.
|
||||
* Eles expiram.
|
||||
* E então eles precisam ser renovados, adquirindo-os novamente de um terceiro.
|
||||
* A criptografia da conexão acontece no nível TCP.
|
||||
* Essa é uma camada abaixo do HTTP.
|
||||
* Portanto, o manuseio do certificado e da criptografia é feito antes do HTTP.
|
||||
* O TCP não sabe sobre "domínios". Apenas sobre endereços IP.
|
||||
* As informações sobre o domínio solicitado vão nos dados HTTP.
|
||||
* Os certificados HTTPS “certificam” um determinado domínio, mas o protocolo e a encriptação acontecem ao nível do TCP, antes de sabermos de que domínio se trata.
|
||||
* Por padrão, isso significa que você só pode ter um certificado HTTPS por endereço IP.
|
||||
* Para HTTPS, **o servidor** precisa ter certificados gerados por **um terceiro**.
|
||||
* Esses certificados são na verdade **adquiridos** de um terceiro, eles não são simplesmente "gerados".
|
||||
* Certificados têm um **tempo de vida**.
|
||||
* Eles **expiram**.
|
||||
* E então eles precisam ser **renovados**, **adquirindo-os novamente** de um terceiro.
|
||||
* A criptografia da conexão acontece no **nível TCP**.
|
||||
* Essa é uma camada **abaixo do HTTP**.
|
||||
* Portanto, o manuseio do **certificado e da criptografia** é feito **antes do HTTP**.
|
||||
* **O TCP não sabe sobre "domínios"**. Apenas sobre endereços IP.
|
||||
* As informações sobre o **domínio solicitado** vão nos **dados HTTP**.
|
||||
* Os **certificados HTTPS** “certificam” um **determinado domínio**, mas o protocolo e a encriptação acontecem ao nível do TCP, **antes de sabermos** de que domínio se trata.
|
||||
* **Por padrão**, isso significa que você só pode ter **um certificado HTTPS por endereço IP**.
|
||||
* Não importa o tamanho do seu servidor ou quão pequeno cada aplicativo que você tem nele possa ser.
|
||||
* No entanto, existe uma solução para isso.
|
||||
* Há uma extensão para o protocolo TLS (aquele que lida com a criptografia no nível TCP, antes do HTTP) chamado <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>.
|
||||
* Esta extensão SNI permite que um único servidor (com um único endereço IP) tenha vários certificados HTTPS e atenda a vários domínios / aplicativos HTTPS.
|
||||
* Para que isso funcione, um único componente (programa) em execução no servidor, ouvindo no endereço IP público, deve ter todos os certificados HTTPS no servidor.
|
||||
* Depois de obter uma conexão segura, o protocolo de comunicação ainda é HTTP.
|
||||
* Os conteúdos são criptografados, embora sejam enviados com o protocolo HTTP.
|
||||
* No entanto, existe uma **solução** para isso.
|
||||
* Há uma **extensão** para o protocolo **TLS** (aquele que lida com a criptografia no nível TCP, antes do HTTP) chamado **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication">SNI</abbr></a>**.
|
||||
* Esta extensão SNI permite que um único servidor (com um **único endereço IP**) tenha **vários certificados HTTPS** e atenda a **vários domínios / aplicativos HTTPS**.
|
||||
* Para que isso funcione, um **único** componente (programa) em execução no servidor, ouvindo no **endereço IP público**, deve ter **todos os certificados HTTPS** no servidor.
|
||||
* **Depois** de obter uma conexão segura, o protocolo de comunicação **ainda é HTTP**.
|
||||
* Os conteúdos são **criptografados**, embora sejam enviados com o **protocolo HTTP**.
|
||||
|
||||
É uma prática comum ter um programa/servidor HTTP em execução no servidor (máquina, host, etc.) e gerenciar todas as partes HTTPS: enviando as solicitações HTTP descriptografadas para o aplicativo HTTP real em execução no mesmo servidor (a aplicação **FastAPI**, neste caso), pegue a resposta HTTP do aplicativo, criptografe-a usando o certificado apropriado e envie-a de volta ao cliente usando HTTPS. Este servidor é frequentemente chamado de <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS Termination Proxy</a>.
|
||||
É uma prática comum ter um **programa/servidor HTTP** em execução no servidor (máquina, host, etc.) e **gerenciar todas as partes HTTPS**: **recebendo as requisições encriptadas**, enviando as **solicitações HTTP descriptografadas** para o aplicativo HTTP real em execução no mesmo servidor (a aplicação **FastAPI**, neste caso), pegue a **resposta HTTP** do aplicativo, **criptografe-a** usando o **certificado HTTPS** apropriado e envie-a de volta ao cliente usando **HTTPS**. Este servidor é frequentemente chamado de **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Proxy de Terminação TLS</a>**.
|
||||
|
||||
Algumas das opções que você pode usar como Proxy de Terminação TLS são:
|
||||
|
||||
* Traefik (que também pode gerenciar a renovação de certificados)
|
||||
* Caddy (que também pode gerenciar a renovação de certificados)
|
||||
* Nginx
|
||||
* HAProxy
|
||||
|
||||
## Let's Encrypt
|
||||
|
||||
Antes de Let's Encrypt, esses certificados HTTPS eram vendidos por terceiros confiáveis.
|
||||
Antes de Let's Encrypt, esses **certificados HTTPS** eram vendidos por terceiros confiáveis.
|
||||
|
||||
O processo de aquisição de um desses certificados costumava ser complicado, exigia bastante papelada e os certificados eram bastante caros.
|
||||
|
||||
Mas então <a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a> foi criado.
|
||||
Mas então o **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>** foi criado.
|
||||
|
||||
Ele é um projeto da Linux Foundation que fornece certificados HTTPS gratuitamente. De forma automatizada. Esses certificados usam toda a segurança criptográfica padrão e têm vida curta (cerca de 3 meses), então a segurança é realmente melhor por causa de sua vida útil reduzida.
|
||||
Ele é um projeto da Linux Foundation que fornece **certificados HTTPS gratuitamente** . De forma automatizada. Esses certificados usam toda a segurança criptográfica padrão e têm vida curta (cerca de 3 meses), então a **segurança é, na verdade, melhor** por causa de sua vida útil reduzida.
|
||||
|
||||
Os domínios são verificados com segurança e os certificados são gerados automaticamente. Isso também permite automatizar a renovação desses certificados.
|
||||
|
||||
A ideia é automatizar a aquisição e renovação desses certificados, para que você tenha HTTPS seguro, de graça e para sempre.
|
||||
A ideia é automatizar a aquisição e renovação desses certificados, para que você tenha **HTTPS seguro, de graça e para sempre**.
|
||||
|
||||
## HTTPS para Desenvolvedores
|
||||
|
||||
Aqui está um exemplo de como uma API HTTPS poderia ser estruturada, passo a passo, com foco principal nas ideias relevantes para desenvolvedores.
|
||||
|
||||
### Nome do domínio
|
||||
|
||||
A etapa inicial provavelmente seria **adquirir** algum **nome de domínio**. Então, você iria configurá-lo em um servidor DNS (possivelmente no mesmo provedor em nuvem).
|
||||
|
||||
Você provavelmente usaria um servidor em nuvem (máquina virtual) ou algo parecido, e ele teria <abbr title="Que não muda">fixed</abbr> **Endereço IP público**.
|
||||
|
||||
No(s) servidor(es) DNS, você configuraria um registro (`registro A`) para apontar **seu domínio** para o **endereço IP público do seu servidor**.
|
||||
|
||||
Você provavelmente fará isso apenas uma vez, na primeira vez em que tudo estiver sendo configurado.
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Essa parte do Nome do Domínio se dá muito antes do HTTPS, mas como tudo depende do domínio e endereço IP público, vale a pena mencioná-la aqui.
|
||||
|
||||
///
|
||||
|
||||
### DNS
|
||||
|
||||
Agora vamos focar em todas as partes que realmente fazem parte do HTTPS.
|
||||
|
||||
Primeiro, o navegador iria verificar com os **servidores DNS** qual o **IP do domínio**, nesse caso, `someapp.example.com`.
|
||||
|
||||
Os servidores DNS iriam informar o navegador para utilizar algum **endereço IP** específico. Esse seria o endereço IP público em uso no seu servidor, que você configurou nos servidores DNS.
|
||||
|
||||
<img src="/img/deployment/https/https01.svg">
|
||||
|
||||
### Início do Handshake TLS
|
||||
|
||||
O navegador então irá comunicar-se com esse endereço IP na **porta 443** (a porta HTTPS).
|
||||
|
||||
A primeira parte dessa comunicação é apenas para estabelecer a conexão entre o cliente e o servidor e para decidir as chaves criptográficas a serem utilizadas, etc.
|
||||
|
||||
<img src="/img/deployment/https/https02.svg">
|
||||
|
||||
Esse interação entre o cliente e o servidor para estabelecer uma conexão TLS é chamada de **Handshake TLS**.
|
||||
|
||||
### TLS com a Extensão SNI
|
||||
|
||||
**Apenas um processo** no servidor pode se conectar a uma **porta** em um **endereço IP**. Poderiam existir outros processos conectados em outras portas desse mesmo endereço IP, mas apenas um para cada combinação de endereço IP e porta.
|
||||
|
||||
TLS (HTTPS) usa a porta `443` por padrão. Então essa é a porta que precisamos.
|
||||
|
||||
Como apenas um único processo pode se comunicar com essa porta, o processo que faria isso seria o **Proxy de Terminação TLS**.
|
||||
|
||||
O Proxy de Terminação TLS teria acesso a um ou mais **certificados TLS** (certificados HTTPS).
|
||||
|
||||
Utilizando a **extensão SNI** discutida acima, o Proxy de Terminação TLS iria checar qual dos certificados TLS (HTTPS) disponíveis deve ser usado para essa conexão, utilizando o que corresponda ao domínio esperado pelo cliente.
|
||||
|
||||
Nesse caso, ele usaria o certificado para `someapp.example.com`.
|
||||
|
||||
<img src="/img/deployment/https/https03.svg">
|
||||
|
||||
O cliente já **confia** na entidade que gerou o certificado TLS (nesse caso, o Let's Encrypt, mas veremos sobre isso mais tarde), então ele pode **verificar** que o certificado é válido.
|
||||
|
||||
Então, utilizando o certificado, o cliente e o Proxy de Terminação TLS **decidem como encriptar** o resto da **comunicação TCP**. Isso completa a parte do **Handshake TLS**.
|
||||
|
||||
Após isso, o cliente e o servidor possuem uma **conexão TCP encriptada**, que é provida pelo TLS. E então eles podem usar essa conexão para começar a **comunicação HTTP** propriamente dita.
|
||||
|
||||
E isso resume o que é **HTTPS**, apenas **HTTP** simples dentro de uma **conexão TLS segura** em vez de uma conexão TCP pura (não encriptada).
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Percebe que a encriptação da comunicação acontece no **nível do TCP**, não no nível do HTTP.
|
||||
|
||||
///
|
||||
|
||||
### Solicitação HTTPS
|
||||
|
||||
Agora que o cliente e servidor (especialmente o navegador e o Proxy de Terminação TLS) possuem uma **conexão TCP encriptada**, eles podem iniciar a **comunicação HTTP**.
|
||||
|
||||
Então, o cliente envia uma **solicitação HTTPS**. Que é apenas uma solicitação HTTP sobre uma conexão TLS encriptada.
|
||||
|
||||
<img src="/img/deployment/https/https04.svg">
|
||||
|
||||
### Desencriptando a Solicitação
|
||||
|
||||
O Proxy de Terminação TLS então usaria a encriptação combinada para **desencriptar a solicitação**, e transmitiria a **solicitação básica (desencriptada)** para o processo executando a aplicação (por exemplo, um processo com Uvicorn executando a aplicação FastAPI).
|
||||
|
||||
<img src="/img/deployment/https/https05.svg">
|
||||
|
||||
### Resposta HTTP
|
||||
|
||||
A aplicação processaria a solicitação e retornaria uma **resposta HTTP básica (não encriptada)** para o Proxy de Terminação TLS.
|
||||
|
||||
<img src="/img/deployment/https/https06.svg">
|
||||
|
||||
### Resposta HTTPS
|
||||
|
||||
O Proxy de Terminação TLS iria **encriptar a resposta** utilizando a criptografia combinada anteriormente (que foi definida com o certificado para `someapp.example.com`), e devolveria para o navegador.
|
||||
|
||||
No próximo passo, o navegador verifica que a resposta é válida e encriptada com a chave criptográfica correta, etc. E depois **desencripta a resposta** e a processa.
|
||||
|
||||
<img src="/img/deployment/https/https07.svg">
|
||||
|
||||
O cliente (navegador) saberá que a resposta vem do servidor correto por que ela usa a criptografia que foi combinada entre eles usando o **certificado HTTPS** anterior.
|
||||
|
||||
### Múltiplas Aplicações
|
||||
|
||||
Podem existir **múltiplas aplicações** em execução no mesmo servidor (ou servidores), por exemplo: outras APIs ou um banco de dados.
|
||||
|
||||
Apenas um processo pode estar vinculado a um IP e porta (o Proxy de Terminação TLS, por exemplo), mas outras aplicações/processos também podem estar em execução no(s) servidor(es), desde que não tentem usar a mesma **combinação de IP público e porta**.
|
||||
|
||||
<img src="/img/deployment/https/https08.svg">
|
||||
|
||||
Dessa forma, o Proxy de Terminação TLS pode gerenciar o HTTPS e os certificados de **múltiplos domínios**, para múltiplas aplicações, e então transmitir as requisições para a aplicação correta em cada caso.
|
||||
|
||||
### Renovação de Certificados
|
||||
|
||||
Em algum momento futuro, cada certificado irá **expirar** (aproximadamente 3 meses após a aquisição).
|
||||
|
||||
E então, haverá outro programa (em alguns casos pode ser o próprio Proxy de Terminação TLS) que irá interagir com o Let's Encrypt e renovar o(s) certificado(s).
|
||||
|
||||
<img src="/img/deployment/https/https.svg">
|
||||
|
||||
Os **certificados TLS** são **associados com um nome de domínio**, e não a um endereço IP.
|
||||
|
||||
Então para renovar os certificados, o programa de renovação precisa **provar** para a autoridade (Let's Encrypt) que ele realmente **possui e controla esse domínio**>
|
||||
|
||||
Para fazer isso, e acomodar as necessidades de diferentes aplicações, existem diferentes opções para esse programa. Algumas escolhas populares são:
|
||||
|
||||
* **Modificar alguns registros DNS**
|
||||
* Para isso, o programa de renovação precisa ter suporte as APIs do provedor DNS, então, dependendo do provedor DNS que você utilize, isso pode ou não ser uma opção viável.
|
||||
* **Executar como um servidor** (ao menos durante o processo de aquisição do certificado) no endereço IP público associado com o domínio.
|
||||
* Como dito anteriormente, apenas um processo pode estar ligado a uma porta e IP específicos.
|
||||
* Essa é uma dos motivos que fazem utilizar o mesmo Proxy de Terminação TLS para gerenciar a renovação de certificados ser tão útil.
|
||||
* Caso contrário, você pode ter que parar a execução do Proxy de Terminação TLS momentaneamente, inicializar o programa de renovação para renovar os certificados, e então reiniciar o Proxy de Terminação TLS. Isso não é o ideal, já que sua(s) aplicação(ões) não vão estar disponíveis enquanto o Proxy de Terminação TLS estiver desligado.
|
||||
|
||||
Todo esse processo de renovação, enquanto o aplicativo ainda funciona, é uma das principais razões para preferir um **sistema separado para gerenciar HTTPS** com um Proxy de Terminação TLS em vez de usar os certificados TLS no servidor da aplicação diretamente (e.g. com o Uvicorn).
|
||||
|
||||
## Recapitulando
|
||||
|
||||
Possuir **HTTPS** habilitado na sua aplicação é bastante importante, e até **crítico** na maioria dos casos. A maior parte do esforço que você tem que colocar sobre o HTTPS como desenvolvedor está em **entender esses conceitos** e como eles funcionam.
|
||||
|
||||
Mas uma vez que você saiba o básico de **HTTPS para desenvolvedores**, você pode combinar e configurar diferentes ferramentas facilmente para gerenciar tudo de uma forma simples.
|
||||
|
||||
Em alguns dos próximos capítulos, eu mostrarei para você vários exemplos concretos de como configurar o **HTTPS** para aplicações **FastAPI**. 🔒
|
||||
|
||||
@@ -11,15 +11,18 @@
|
||||
<em>Framework FastAPI, alta performance, fácil de aprender, fácil de codar, pronto para produção</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi?color=%2334D058" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -60,7 +63,7 @@ Os recursos chave são:
|
||||
|
||||
<!-- /sponsors -->
|
||||
|
||||
<a href="https://fastapi.tiangolo.com/pt/fastapi-people/#patrocinadores" class="external-link" target="_blank">Outros patrocinadores</a>
|
||||
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Outros patrocinadores</a>
|
||||
|
||||
## Opiniões
|
||||
|
||||
@@ -70,6 +73,18 @@ Os recursos chave são:
|
||||
|
||||
---
|
||||
|
||||
"_Nós adotamos a biblioteca **FastAPI** para iniciar um servidor **REST** que pode ser consultado para obter **previsões**. [para o Ludwig]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, e Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_A **Netflix** tem o prazer de anunciar o lançamento open-source do nosso framework de orquestração de **gerenciamento de crises**: **Dispatch**! [criado com **FastAPI**]_"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"*Estou extremamente entusiasmado com o **FastAPI**. É tão divertido!*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcaster</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
@@ -90,9 +105,9 @@ Os recursos chave são:
|
||||
|
||||
---
|
||||
|
||||
"*Nós adotamos a biblioteca **FastAPI** para criar um servidor **REST** que possa ser chamado para obter **predições**. [para o Ludwig]*"
|
||||
"_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._"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin e Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
|
||||
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
@@ -113,27 +128,19 @@ FastAPI está nos ombros de gigantes:
|
||||
|
||||
## Instalação
|
||||
|
||||
Crie e ative um <a href="https://fastapi.tiangolo.com/pt/virtual-environments/" class="external-link" target="_blank">ambiente virtual</a>, e então instale o FastAPI:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install fastapi
|
||||
$ pip install "fastapi[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Você também precisará de um servidor ASGI para produção, tal como <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> ou <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "uvicorn[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
**Nota**: Certifique-se de que você colocou `"fastapi[standard]"` com aspas, para garantir que funcione em todos os terminais.
|
||||
|
||||
## Exemplo
|
||||
|
||||
@@ -184,7 +191,7 @@ async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
|
||||
**Nota**:
|
||||
|
||||
Se você não sabe, verifique a seção _"In a hurry?"_ sobre <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` e `await` nas docs</a>.
|
||||
Se você não sabe, verifique a seção _"Com pressa?"_ sobre <a href="https://fastapi.tiangolo.com/pt/async/#com-pressa" target="_blank">`async` e `await` nas docs</a>.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -195,11 +202,24 @@ Rode o servidor com:
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
$ fastapi dev main.py
|
||||
|
||||
╭────────── FastAPI CLI - Development mode ───────────╮
|
||||
│ │
|
||||
│ Serving at: http://127.0.0.1:8000 │
|
||||
│ │
|
||||
│ API docs: http://127.0.0.1:8000/docs │
|
||||
│ │
|
||||
│ Running in development mode, for production use: │
|
||||
│ │
|
||||
│ fastapi run │
|
||||
│ │
|
||||
╰─────────────────────────────────────────────────────╯
|
||||
|
||||
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
INFO: Started reloader process [28720]
|
||||
INFO: Started server process [28722]
|
||||
INFO: Started reloader process [2248755] using WatchFiles
|
||||
INFO: Started server process [2248757]
|
||||
INFO: Waiting for application startup.
|
||||
INFO: Application startup complete.
|
||||
```
|
||||
@@ -207,13 +227,13 @@ INFO: Application startup complete.
|
||||
</div>
|
||||
|
||||
<details markdown="1">
|
||||
<summary>Sobre o comando <code>uvicorn main:app --reload</code>...</summary>
|
||||
<summary>Sobre o comando <code>fastapi dev main.py</code>...</summary>
|
||||
|
||||
O comando `uvicorn main:app` se refere a:
|
||||
O comando `fastapi dev` lê o seu arquivo `main.py`, identifica o aplicativo **FastAPI** nele, e inicia um servidor usando o <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a>.
|
||||
|
||||
* `main`: o arquivo `main.py` (o "módulo" Python).
|
||||
* `app`: o objeto criado dentro de `main.py` com a linha `app = FastAPI()`.
|
||||
* `--reload`: faz o servidor recarregar após mudanças de código. Somente faça isso para desenvolvimento.
|
||||
Por padrão, o `fastapi dev` iniciará com *auto-reload* habilitado para desenvolvimento local.
|
||||
|
||||
Você pode ler mais sobre isso na <a href="https://fastapi.tiangolo.com/pt/fastapi-cli/" target="_blank">documentação do FastAPI CLI</a>.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -268,7 +288,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: Union[bool] = None
|
||||
is_offer: Union[bool, None] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -286,7 +306,7 @@ def update_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
O servidor deverá recarregar automaticamente (porquê você adicionou `--reload` ao comando `uvicorn` acima).
|
||||
O servidor `fastapi dev` deverá recarregar automaticamente.
|
||||
|
||||
### Evoluindo a Documentação Interativa da API
|
||||
|
||||
@@ -316,7 +336,7 @@ E agora, vá para <a href="http://127.0.0.1:8000/redoc" class="external-link" ta
|
||||
|
||||
Resumindo, você declara **uma vez** os tipos dos parâmetros, corpo etc. como parâmetros de função.
|
||||
|
||||
Você faz com tipos padrão do Python moderno.
|
||||
Você faz isso com os tipos padrão do Python moderno.
|
||||
|
||||
Você não terá que aprender uma nova sintaxe, métodos ou classes de uma biblioteca específica etc.
|
||||
|
||||
@@ -383,7 +403,7 @@ Voltando ao código do exemplo anterior, **FastAPI** irá:
|
||||
|
||||
---
|
||||
|
||||
Nós arranhamos apenas a superfície, mas você já tem idéia de como tudo funciona.
|
||||
Nós apenas arranhamos a superfície, mas você já tem idéia de como tudo funciona.
|
||||
|
||||
Experimente mudar a seguinte linha:
|
||||
|
||||
@@ -407,7 +427,7 @@ Experimente mudar a seguinte linha:
|
||||
|
||||

|
||||
|
||||
Para um exemplo mais completo incluindo mais recursos, veja <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - Guia do Usuário</a>.
|
||||
Para um exemplo mais completo incluindo mais recursos, veja <a href="https://fastapi.tiangolo.com/pt/tutorial/">Tutorial - Guia do Usuário</a>.
|
||||
|
||||
**Alerta de Spoiler**: o tutorial - guia do usuário inclui:
|
||||
|
||||
@@ -416,9 +436,9 @@ Para um exemplo mais completo incluindo mais recursos, veja <a href="https://fas
|
||||
* Um poderoso e fácil de usar sistema de **<abbr title="também conhecido como componentes, recursos, fornecedores, serviços, injetáveis">Injeção de Dependência</abbr>**.
|
||||
* Segurança e autenticação, incluindo suporte para **OAuth2** com autenticação **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 <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> e outras bibliotecas.
|
||||
* Muitos recursos extras (graças ao Starlette) como:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* testes extrememamente fáceis baseados em HTTPX e `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
@@ -428,30 +448,49 @@ Para um exemplo mais completo incluindo mais recursos, veja <a href="https://fas
|
||||
|
||||
Testes de performance da _Independent TechEmpower_ mostram aplicações **FastAPI** rodando sob Uvicorn como <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">um dos _frameworks_ Python mais rápidos disponíveis</a>, somente atrás de Starlette e Uvicorn (utilizados internamente pelo FastAPI). (*)
|
||||
|
||||
Para entender mais sobre performance, veja a seção <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
|
||||
Para entender mais sobre performance, veja a seção <a href="https://fastapi.tiangolo.com/pt/benchmarks/" class="internal-link" target="_blank">Comparações</a>.
|
||||
|
||||
## Dependências opcionais
|
||||
## Dependências
|
||||
|
||||
Usados por Pydantic:
|
||||
O FastAPI depende do Pydantic e do Starlette.
|
||||
|
||||
|
||||
### Dependências `standard`
|
||||
|
||||
Quando você instala o FastAPI com `pip install "fastapi[standard]"`, ele vêm com o grupo `standard` (padrão) de dependências opcionais:
|
||||
|
||||
Utilizado pelo Pydantic:
|
||||
|
||||
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - para validação de email.
|
||||
|
||||
Usados por Starlette:
|
||||
Utilizado pelo Starlette:
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Necessário se você quiser utilizar o `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Necessário se você quiser utilizar a configuração padrão de templates.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Necessário se você quiser suporte com <abbr title="converte uma string que chega de uma requisição HTTP para dados Python">"parsing"</abbr> de formulário, com `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Necessário para suporte a `SessionMiddleware`.
|
||||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Necessário para suporte a `SchemaGenerator` da Starlette (você provavelmente não precisará disso com o FastAPI).
|
||||
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Necessário para suporte a `GraphQLApp`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Obrigatório caso você queira utilizar o `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Obrigatório se você quer utilizar a configuração padrão de templates.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Obrigatório se você deseja suporte a <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr> de formulário, com `request.form()`.
|
||||
|
||||
Usados por FastAPI / Starlette:
|
||||
Utilizado pelo FastAPI / Starlette:
|
||||
|
||||
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - para o servidor que carrega e serve sua aplicação.
|
||||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Necessário se você quer utilizar `ORJSONResponse`.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Necessário se você quer utilizar `UJSONResponse`.
|
||||
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - para o servidor que carrega e serve a sua aplicação. Isto inclui `uvicorn[standard]`, que inclui algumas dependências (e.g. `uvloop`) necessárias para servir em alta performance.
|
||||
* `fastapi-cli` - que disponibiliza o comando `fastapi`.
|
||||
|
||||
Você pode instalar todas essas dependências com `pip install fastapi[all]`.
|
||||
### Sem as dependências `standard`
|
||||
|
||||
Se você não deseja incluir as dependências opcionais `standard`, você pode instalar utilizando `pip install fastapi` ao invés de `pip install "fastapi[standard]"`.
|
||||
|
||||
### Dpendências opcionais adicionais
|
||||
|
||||
Existem algumas dependências adicionais que você pode querer instalar.
|
||||
|
||||
Dependências opcionais adicionais do Pydantic:
|
||||
|
||||
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - para gerenciamento de configurações.
|
||||
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - tipos extras para serem utilizados com o Pydantic.
|
||||
|
||||
Dependências opcionais adicionais do FastAPI:
|
||||
|
||||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Obrigatório se você deseja utilizar o `ORJSONResponse`.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Obrigatório se você deseja utilizar o `UJSONResponse`.
|
||||
|
||||
## Licença
|
||||
|
||||
|
||||
48
docs/ru/docs/advanced/response-cookies.md
Normal file
48
docs/ru/docs/advanced/response-cookies.md
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
# Cookies в ответе
|
||||
|
||||
## Использование параметра `Response`
|
||||
|
||||
Вы можете объявить параметр типа `Response` в вашей функции эндпоинта.
|
||||
|
||||
Затем установить cookies в этом временном объекте ответа.
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
|
||||
|
||||
После этого можно вернуть любой объект, как и раньше (например, `dict`, объект модели базы данных и так далее).
|
||||
|
||||
Если вы указали `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта.
|
||||
|
||||
**FastAPI** извлечет cookies (а также заголовки и коды состояния) из временного ответа и включит их в окончательный ответ, содержащий ваше возвращаемое значение, отфильтрованное через `response_model`.
|
||||
|
||||
Вы также можете объявить параметр типа Response в зависимостях и устанавливать cookies (и заголовки) там.
|
||||
|
||||
## Возвращение `Response` напрямую
|
||||
|
||||
Вы также можете установить cookies, если возвращаете `Response` напрямую в вашем коде.
|
||||
|
||||
Для этого создайте объект `Response`, как описано в разделе [Возвращение ответа напрямую](response-directly.md){.target=_blank}.
|
||||
|
||||
Затем установите cookies и верните этот объект:
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
|
||||
|
||||
/// tip | Примечание
|
||||
Имейте в виду, что если вы возвращаете ответ напрямую, вместо использования параметра `Response`, **FastAPI** отправит его без дополнительной обработки.
|
||||
|
||||
Убедитесь, что ваши данные имеют корректный тип. Например, они должны быть совместимы с JSON, если вы используете `JSONResponse`.
|
||||
|
||||
Также убедитесь, что вы не отправляете данные, которые должны были быть отфильтрованы через `response_model`.
|
||||
///
|
||||
|
||||
### Дополнительная информация
|
||||
|
||||
/// note | Технические детали
|
||||
Вы также можете использовать `from starlette.responses import Response` или `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** предоставляет `fastapi.responses`, которые являются теми же объектами, что и `starlette.responses`, просто для удобства. Однако большинство доступных типов ответов поступает непосредственно из **Starlette**.
|
||||
|
||||
Для установки заголовков и cookies `Response` используется часто, поэтому **FastAPI** также предоставляет его через `fastapi.responses`.
|
||||
///
|
||||
|
||||
Чтобы увидеть все доступные параметры и настройки, ознакомьтесь с <a href="https://www.starlette.io/responses/#set-cookie" class="external-link" target="_blank">документацией Starlette</a>.
|
||||
186
docs/ru/docs/advanced/websockets.md
Normal file
186
docs/ru/docs/advanced/websockets.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Веб-сокеты
|
||||
|
||||
Вы можете использовать <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">веб-сокеты</a> в **FastAPI**.
|
||||
|
||||
## Установка `WebSockets`
|
||||
|
||||
Убедитесь, что [виртуальная среда](../virtual-environments.md){.internal-link target=_blank} создана, активируйте её и установите `websockets`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install websockets
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Клиент WebSockets
|
||||
|
||||
### Рабочее приложение
|
||||
|
||||
Скорее всего, в вашей реальной продуктовой системе есть фронтенд, реализованный при помощи современных фреймворков React, Vue.js или Angular.
|
||||
|
||||
И наверняка для взаимодействия с бекендом через веб-сокеты вы будете использовать средства фронтенда.
|
||||
|
||||
Также у вас может быть нативное мобильное приложение, коммуницирующее непосредственно с веб-сокетами на бекенд-сервере.
|
||||
|
||||
Либо вы можете сделать какой-либо другой способ взаимодействия с веб-сокетами.
|
||||
|
||||
---
|
||||
|
||||
Но для этого примера мы воспользуемся очень простым HTML документом с небольшими вставками JavaScript кода.
|
||||
|
||||
Конечно же это неоптимально, и на практике так делать не стоит.
|
||||
|
||||
В реальных приложениях стоит воспользоваться одним из вышеупомянутых способов.
|
||||
|
||||
Для примера нам нужен наиболее простой способ, который позволит сосредоточиться на серверной части веб-сокетов и получить рабочий код:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
|
||||
|
||||
## Создание `websocket`
|
||||
|
||||
Создайте `websocket` в своем **FastAPI** приложении:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
|
||||
|
||||
/// note | Технические детали
|
||||
|
||||
Вы также можете использовать `from starlette.websockets import WebSocket`.
|
||||
|
||||
**FastAPI** напрямую предоставляет тот же самый `WebSocket` просто для удобства. На самом деле это `WebSocket` из Starlette.
|
||||
|
||||
///
|
||||
|
||||
## Ожидание и отправка сообщений
|
||||
|
||||
Через эндпоинт веб-сокета вы можете получать и отправлять сообщения.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
|
||||
|
||||
Вы можете получать и отправлять двоичные, текстовые и JSON данные.
|
||||
|
||||
## Проверка в действии
|
||||
|
||||
Если ваш файл называется `main.py`, то запустите приложение командой:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi dev main.py
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Откройте браузер по адресу <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
|
||||
|
||||
Вы увидите следующую простенькую страницу:
|
||||
|
||||
<img src="/img/tutorial/websockets/image01.png">
|
||||
|
||||
Вы можете набирать сообщения в поле ввода и отправлять их:
|
||||
|
||||
<img src="/img/tutorial/websockets/image02.png">
|
||||
|
||||
И ваше **FastAPI** приложение с веб-сокетами ответит:
|
||||
|
||||
<img src="/img/tutorial/websockets/image03.png">
|
||||
|
||||
Вы можете отправлять и получать множество сообщений:
|
||||
|
||||
<img src="/img/tutorial/websockets/image04.png">
|
||||
|
||||
И все они будут использовать одно и то же веб-сокет соединение.
|
||||
|
||||
## Использование `Depends` и не только
|
||||
|
||||
Вы можете импортировать из `fastapi` и использовать в эндпоинте вебсокета:
|
||||
|
||||
* `Depends`
|
||||
* `Security`
|
||||
* `Cookie`
|
||||
* `Header`
|
||||
* `Path`
|
||||
* `Query`
|
||||
|
||||
Они работают так же, как и в других FastAPI эндпоинтах/*операциях пути*:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *}
|
||||
|
||||
/// info | Примечание
|
||||
|
||||
В веб-сокете вызывать `HTTPException` не имеет смысла. Вместо этого нужно использовать `WebSocketException`.
|
||||
|
||||
Закрывающий статус код можно использовать из <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">valid codes defined in the specification</a>.
|
||||
|
||||
///
|
||||
|
||||
### Веб-сокеты с зависимостями: проверка в действии
|
||||
|
||||
Если ваш файл называется `main.py`, то запустите приложение командой:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi dev main.py
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Откройте браузер по адресу <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
|
||||
|
||||
Там вы можете задать:
|
||||
|
||||
* "Item ID", используемый в пути.
|
||||
* "Token", используемый как query-параметр.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Обратите внимание, что query-параметр `token` будет обработан в зависимости.
|
||||
|
||||
///
|
||||
|
||||
Теперь вы можете подключиться к веб-сокету и начинать отправку и получение сообщений:
|
||||
|
||||
<img src="/img/tutorial/websockets/image05.png">
|
||||
|
||||
## Обработка отключений и работа с несколькими клиентами
|
||||
|
||||
Если веб-сокет соединение закрыто, то `await websocket.receive_text()` вызовет исключение `WebSocketDisconnect`, которое можно поймать и обработать как в этом примере:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
|
||||
|
||||
Чтобы воспроизвести пример:
|
||||
|
||||
* Откройте приложение в нескольких вкладках браузера.
|
||||
* Отправьте из них сообщения.
|
||||
* Затем закройте одну из вкладок.
|
||||
|
||||
Это вызовет исключение `WebSocketDisconnect`, и все остальные клиенты получат следующее сообщение:
|
||||
|
||||
```
|
||||
Client #1596980209979 left the chat
|
||||
```
|
||||
|
||||
/// tip | Примечание
|
||||
|
||||
Приложение выше - это всего лишь простой минимальный пример, демонстрирующий обработку и передачу сообщений нескольким веб-сокет соединениям.
|
||||
|
||||
Но имейте в виду, что это будет работать только в одном процессе и только пока он активен, так как всё обрабатывается в простом списке в оперативной памяти.
|
||||
|
||||
Если нужно что-то легко интегрируемое с FastAPI, но более надежное и с поддержкой Redis, PostgreSQL или другого, то можно воспользоваться <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a>.
|
||||
|
||||
///
|
||||
|
||||
## Дополнительная информация
|
||||
|
||||
Для более глубокого изучения темы воспользуйтесь документацией Starlette:
|
||||
|
||||
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank">The `WebSocket` class</a>.
|
||||
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">Class-based WebSocket handling</a>.
|
||||
@@ -12,10 +12,10 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi?color=%2334D058" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
|
||||
@@ -291,22 +291,6 @@ q: Union[str, None] = Query(default=None, min_length=3)
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
|
||||
### Обязательный параметр с Ellipsis (`...`)
|
||||
|
||||
Альтернативный способ указать обязательность параметра запроса - это указать параметр `default` через многоточие `...`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006b_an_py39.py hl[9] *}
|
||||
|
||||
/// info | Дополнительная информация
|
||||
|
||||
Если вы ранее не сталкивались с `...`: это специальное значение, <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">часть языка Python и называется "Ellipsis"</a>.
|
||||
|
||||
Используется в Pydantic и FastAPI для определения, что значение требуется обязательно.
|
||||
|
||||
///
|
||||
|
||||
Таким образом, **FastAPI** определяет, что параметр является обязательным.
|
||||
|
||||
### Обязательный параметр с `None`
|
||||
|
||||
Вы можете определить, что параметр может принимать `None`, но всё ещё является обязательным. Это может потребоваться для того, чтобы пользователи явно указали параметр, даже если его значение будет `None`.
|
||||
@@ -321,18 +305,6 @@ Pydantic, мощь которого используется в FastAPI для
|
||||
|
||||
///
|
||||
|
||||
### Использование Pydantic's `Required` вместо Ellipsis (`...`)
|
||||
|
||||
Если вас смущает `...`, вы можете использовать `Required` из Pydantic:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006d_an_py39.py hl[4,10] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Запомните, когда вам необходимо объявить query-параметр обязательным, вы можете просто не указывать параметр `default`. Таким образом, вам редко придётся использовать `...` или `Required`.
|
||||
|
||||
///
|
||||
|
||||
## Множество значений для query-параметра
|
||||
|
||||
Для query-параметра `Query` можно указать, что он принимает список значений (множество значений).
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
189
docs/uk/docs/features.md
Normal file
189
docs/uk/docs/features.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Функціональні можливості
|
||||
|
||||
## Функціональні можливості FastAPI
|
||||
|
||||
**FastAPI** надає вам такі можливості:
|
||||
|
||||
### Використання відкритих стандартів
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> для створення API, включаючи оголошення <abbr title="також відомі як: endpoints, маршрути">шляхів</abbr>, <abbr title="також відомі як HTTP-методи, наприклад, POST, GET, PUT, DELETE">операцій</abbr>, параметрів, тіл запитів, безпеки тощо.
|
||||
* Автоматична документація моделей даних за допомогою <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (оскільки OpenAPI базується саме на JSON Schema).
|
||||
* Розроблено на основі цих стандартів після ретельного аналізу, а не як додатковий рівень поверх основної архітектури.
|
||||
* Це також дає змогу автоматично **генерувати код клієнта** багатьма мовами.
|
||||
|
||||
### Автоматична генерація документації
|
||||
|
||||
Інтерактивна документація API та вебінтерфейс для його дослідження. Оскільки фреймворк базується на OpenAPI, є кілька варіантів, два з яких включені за замовчуванням.
|
||||
|
||||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a> — дозволяє інтерактивно переглядати API, викликати та тестувати його прямо у браузері.
|
||||
|
||||

|
||||
|
||||
* Альтернативна документація API за допомогою <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
|
||||
|
||||

|
||||
|
||||
### Тільки сучасний Python
|
||||
|
||||
FastAPI використовує стандартні **типи Python** (завдяки Pydantic). Вам не потрібно вивчати новий синтаксис — лише стандартний сучасний Python.
|
||||
|
||||
Якщо вам потрібне коротке нагадування про використання типів у Python (навіть якщо ви не використовуєте FastAPI), перегляньте короткий підручник: [Вступ до типів Python](python-types.md){.internal-link target=_blank}.
|
||||
|
||||
Ось приклад стандартного Python-коду з типами:
|
||||
|
||||
```Python
|
||||
from datetime import date
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Оголошення змінної як str
|
||||
# з підтримкою автодоповнення у редакторі
|
||||
def main(user_id: str):
|
||||
return user_id
|
||||
|
||||
# Модель Pydantic
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
joined: date
|
||||
```
|
||||
|
||||
Приклад використання цієї моделі:
|
||||
|
||||
```Python
|
||||
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
|
||||
|
||||
second_user_data = {
|
||||
"id": 4,
|
||||
"name": "Mary",
|
||||
"joined": "2018-11-30",
|
||||
}
|
||||
|
||||
my_second_user: User = User(**second_user_data)
|
||||
```
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
`**second_user_data` означає:
|
||||
|
||||
Передати ключі та значення словника `second_user_data` як аргументи у вигляді "ключ-значення", еквівалентно `User(id=4, name="Mary", joined="2018-11-30")`.
|
||||
|
||||
///
|
||||
|
||||
### Підтримка редакторів (IDE)
|
||||
|
||||
Фреймворк спроєктований так, щоб бути легким і інтуїтивно зрозумілим. Усі рішення тестувалися у різних редакторах ще до початку розробки, щоб забезпечити найкращий досвід програмування.
|
||||
|
||||
За результатами опитувань розробників Python <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">однією з найпопулярніших функцій є "автодоповнення"</a>.
|
||||
|
||||
**FastAPI** повністю підтримує автодоповнення у всіх місцях, тому вам рідко доведеться повертатися до документації.
|
||||
|
||||
Приклад автодоповнення у редакторах:
|
||||
|
||||
* у <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>:
|
||||
|
||||

|
||||
|
||||
* у <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>:
|
||||
|
||||

|
||||
|
||||
### Короткий код
|
||||
FastAPI має розумні налаштування **за замовчуванням**, але всі параметри можна налаштовувати відповідно до ваших потреб. Однак за замовчуванням все "просто працює".
|
||||
|
||||
### Валідація
|
||||
* Підтримка валідації для більшості (або всіх?) **типів даних Python**, зокрема:
|
||||
* JSON-об'єктів (`dict`).
|
||||
* JSON-списків (`list`) з визначенням типів елементів.
|
||||
* Рядків (`str`) із мінімальною та максимальною довжиною.
|
||||
* Чисел (`int`, `float`) з обмеженнями мінімальних та максимальних значень тощо.
|
||||
|
||||
* Валідація складніших типів, таких як:
|
||||
* URL.
|
||||
* Email.
|
||||
* UUID.
|
||||
* ...та інші.
|
||||
|
||||
Уся валідація виконується через надійний та перевірений **Pydantic**.
|
||||
|
||||
### Безпека та автентифікація
|
||||
|
||||
**FastAPI** підтримує вбудовану автентифікацію та авторизацію, без прив’язки до конкретних баз даних чи моделей даних.
|
||||
|
||||
Підтримуються всі схеми безпеки OpenAPI, включаючи:
|
||||
|
||||
* HTTP Basic.
|
||||
* **OAuth2** (також із підтримкою **JWT-токенів**). Див. підручник: [OAuth2 із JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
|
||||
* Ключі API в:
|
||||
* Заголовках.
|
||||
* Параметрах запиту.
|
||||
* Cookies тощо.
|
||||
|
||||
А також усі можливості безпеки від Starlette (зокрема **сесійні cookies**).
|
||||
|
||||
Усі вони створені як багаторазові інструменти та компоненти, які легко інтегруються з вашими системами, сховищами даних, реляційними та NoSQL базами даних тощо.
|
||||
|
||||
### Впровадження залежностей
|
||||
|
||||
**FastAPI** містить надзвичайно просту у використанні, але потужну систему впровадження залежностей.
|
||||
|
||||
* Залежності можуть мати власні залежності, утворюючи ієрархію або **"граф залежностей"**.
|
||||
* Усі залежності автоматично керуються фреймворком.
|
||||
* Усі залежності можуть отримувати дані з запитів і розширювати **обмеження операції за шляхом** та автоматичну документацію.
|
||||
* **Автоматична валідація** навіть для параметрів *операцій шляху*, визначених у залежностях.
|
||||
* Підтримка складних систем автентифікації користувачів, **з'єднань із базами даних** тощо.
|
||||
* **Жодних обмежень** щодо використання баз даних, фронтендів тощо, але водночас проста інтеграція з усіма ними.
|
||||
|
||||
### Немає обмежень на "плагіни"
|
||||
|
||||
Або іншими словами, вони не потрібні – просто імпортуйте та використовуйте необхідний код.
|
||||
|
||||
Будь-яка інтеграція спроєктована настільки просто (з використанням залежностей), що ви можете створити "плагін" для свого застосунку всього у 2 рядках коду, використовуючи ту саму структуру та синтаксис, що й для ваших *операцій шляху*.
|
||||
|
||||
### Протестовано
|
||||
|
||||
* 100% <abbr title="Обсяг коду, що автоматично тестується">покриття тестами</abbr>.
|
||||
* 100% <abbr title="Анотації типів у Python, завдяки яким ваш редактор і зовнішні інструменти можуть надавати кращу підтримку">анотована типами</abbr> кодова база.
|
||||
* Використовується у робочих середовищах.
|
||||
|
||||
## Можливості Starlette
|
||||
|
||||
**FastAPI** повністю сумісний із (та побудований на основі) <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a>. Тому будь-який додатковий код Starlette, який ви маєте, також працюватиме.
|
||||
|
||||
**FastAPI** фактично є підкласом **Starlette**. Тому, якщо ви вже знайомі зі Starlette або використовуєте його, більшість функціональності працюватиме так само.
|
||||
|
||||
З **FastAPI** ви отримуєте всі можливості **Starlette** (адже FastAPI — це, по суті, Starlette на стероїдах):
|
||||
|
||||
* Разюча продуктивність. Це <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">один із найшвидших фреймворків на Python</a>, на рівні з **NodeJS** і **Go**.
|
||||
* Підтримка **WebSocket**.
|
||||
* Фонові задачі у процесі.
|
||||
* Події запуску та завершення роботи.
|
||||
* Клієнт для тестування, побудований на HTTPX.
|
||||
* Підтримка **CORS**, **GZip**, статичних файлів, потокових відповідей.
|
||||
* Підтримка **сесій** і **cookie**.
|
||||
* 100% покриття тестами.
|
||||
* 100% анотована типами кодова база.
|
||||
|
||||
## Можливості Pydantic
|
||||
|
||||
**FastAPI** повністю сумісний із (та побудований на основі) <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Тому будь-який додатковий код Pydantic, який ви маєте, також працюватиме.
|
||||
|
||||
Включаючи зовнішні бібліотеки, побудовані також на Pydantic, такі як <abbr title="Object-Relational Mapper">ORM</abbr>, <abbr title="Object-Document Mapper">ODM</abbr> для баз даних.
|
||||
|
||||
Це також означає, що в багатьох випадках ви можете передати той самий об'єкт, який отримуєте з запиту, **безпосередньо в базу даних**, оскільки все автоматично перевіряється.
|
||||
|
||||
Те ж саме відбувається й у зворотному напрямку — у багатьох випадках ви можете просто передати об'єкт, який отримуєте з бази даних, **безпосередньо клієнту**.
|
||||
|
||||
З **FastAPI** ви отримуєте всі можливості **Pydantic** (адже FastAPI базується на Pydantic для обробки всіх даних):
|
||||
|
||||
* **Ніякої плутанини** :
|
||||
* Не потрібно вчити нову мову для визначення схем.
|
||||
* Якщо ви знаєте типи Python, ви знаєте, як використовувати Pydantic.
|
||||
* Легко працює з вашим **<abbr title="Інтегроване середовище розробки, схоже на редактор коду">IDE</abbr>/<abbr title="Програма, яка перевіряє помилки в коді">лінтером</abbr>/мозком**:
|
||||
* Оскільки структури даних Pydantic є просто екземплярами класів, які ви визначаєте; автодоповнення, лінтинг, mypy і ваша інтуїція повинні добре працювати з вашими перевіреними даними.
|
||||
* Валідація **складних структур**:
|
||||
* Використання ієрархічних моделей Pydantic. Python `typing`, `List` і `Dict` тощо.
|
||||
* Валідатори дозволяють чітко і просто визначати, перевіряти й документувати складні схеми даних у вигляді JSON-схеми.
|
||||
* Ви можете мати глибоко **вкладені JSON об'єкти** та перевірити та анотувати їх всі.
|
||||
* **Розширюваність**:
|
||||
* Pydantic дозволяє визначати користувацькі типи даних або розширювати валідацію методами в моделі декоратором `validator`.
|
||||
* 100% покриття тестами.
|
||||
@@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
5
docs/uk/docs/learn/index.md
Normal file
5
docs/uk/docs/learn/index.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Навчання
|
||||
|
||||
У цьому розділі надані вступні та навчальні матеріали для вивчення FastAPI.
|
||||
|
||||
Це можна розглядати як **книгу**, **курс**, або **офіційний** та рекомендований спосіб освоїти FastAPI. 😎
|
||||
91
docs/uk/docs/tutorial/header-params.md
Normal file
91
docs/uk/docs/tutorial/header-params.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Header-параметри
|
||||
|
||||
Ви можете визначати параметри заголовків, так само як визначаєте `Query`, `Path` і `Cookie` параметри.
|
||||
|
||||
## Імпорт `Header`
|
||||
|
||||
Спочатку імпортуйте `Header`:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Оголошення параметрів `Header`
|
||||
|
||||
Потім оголосіть параметри заголовків, використовуючи ту ж структуру, що й для `Path`, `Query` та `Cookie`.
|
||||
|
||||
Ви можете визначити значення за замовчуванням, а також усі додаткові параметри валідації або анотації:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
`Header`є "сестринським" класом для `Path`, `Query` і `Cookie`. Він також успадковується від загального класу `Param`.
|
||||
|
||||
Але пам’ятайте, що при імпорті `Query`, `Path`, `Header` та інших із `fastapi`, то насправді вони є функціями, які повертають спеціальні класи.
|
||||
|
||||
///
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб оголосити заголовки, потрібно використовувати `Header`, інакше параметри будуть інтерпретуватися як параметри запиту.
|
||||
|
||||
///
|
||||
|
||||
## Автоматичне перетворення
|
||||
|
||||
`Header` має додатковий функціонал порівняно з `Path`, `Query` та `Cookie`.
|
||||
|
||||
Більшість стандартних заголовків розділяються символом «дефіс», також відомим як «мінус» (`-`).
|
||||
|
||||
Але змінна, така як `user-agent`, є недійсною в Python.
|
||||
|
||||
Тому, за замовчуванням, `Header` автоматично перетворює символи підкреслення (`_`) на дефіси (`-`) для отримання та документування заголовків.
|
||||
|
||||
Оскільки заголовки HTTP не чутливі до регістру, Ви можете використовувати стандартний стиль Python ("snake_case").
|
||||
|
||||
Тому Ви можете використовувати `user_agent`, як зазвичай у коді Python, замість того щоб писати з великої літери, як `User_Agent` або щось подібне.
|
||||
|
||||
Якщо Вам потрібно вимкнути автоматичне перетворення підкреслень у дефіси, встановіть `convert_underscores` в `Header` значення `False`:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
|
||||
|
||||
/// warning | Увага
|
||||
|
||||
Перед тим як встановити значення `False` для `convert_underscores` пам’ятайте, що деякі HTTP-проксі та сервери не підтримують заголовки з підкресленнями.
|
||||
|
||||
///
|
||||
|
||||
## Дубльовані заголовки
|
||||
|
||||
Можливо отримати дубльовані заголовки, тобто той самий заголовок із кількома значеннями.
|
||||
|
||||
Це можна визначити, використовуючи список у типізації параметра.
|
||||
|
||||
Ви отримаєте всі значення дубльованого заголовка у вигляді `list` у Python.
|
||||
|
||||
Наприклад, щоб оголосити заголовок `X-Token`, який може з’являтися більше ніж один раз:
|
||||
|
||||
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
|
||||
|
||||
Якщо Ви взаємодієте з цією операцією шляху, надсилаючи два HTTP-заголовки, наприклад:
|
||||
|
||||
```
|
||||
X-Token: foo
|
||||
X-Token: bar
|
||||
```
|
||||
|
||||
Відповідь буде така:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"X-Token values": [
|
||||
"bar",
|
||||
"foo"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Підсумок
|
||||
|
||||
Оголошуйте заголовки за допомогою `Header`, використовуючи той самий підхід, що й для `Query`, `Path` та `Cookie`.
|
||||
|
||||
Не хвилюйтеся про підкреслення у змінних — **FastAPI** автоматично конвертує їх.
|
||||
175
docs/uk/docs/tutorial/request-files.md
Normal file
175
docs/uk/docs/tutorial/request-files.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Запит файлів
|
||||
|
||||
Ви можете визначити файли, які будуть завантажуватися клієнтом, використовуючи `File`.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб отримувати завантажені файли, спочатку встановіть <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">python-multipart</a>.
|
||||
|
||||
Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його та встановили пакет, наприклад:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
Це необхідно, оскільки завантажені файли передаються у вигляді "форматованих даних форми".
|
||||
|
||||
///
|
||||
|
||||
## Імпорт `File`
|
||||
|
||||
Імпортуйте `File` та `UploadFile` з `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
|
||||
|
||||
## Визначення параметрів `File`
|
||||
|
||||
Створіть параметри файлів так само як Ви б створювали `Body` або `Form`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
`File` — це клас, який безпосередньо успадковує `Form`.
|
||||
|
||||
Але пам’ятайте, що коли Ви імпортуєте `Query`, `Path`, `File` та інші з `fastapi`, це насправді функції, які повертають спеціальні класи.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Підказка
|
||||
|
||||
Щоб оголосити тіла файлів, Вам потрібно використовувати `File`, тому що інакше параметри будуть інтерпретовані як параметри запиту або параметри тіла (JSON).
|
||||
|
||||
///
|
||||
|
||||
Файли будуть завантажені у вигляді "форматованих даних форми".
|
||||
|
||||
Якщо Ви оголосите тип параметра функції обробника маршруту як `bytes`, **FastAPI** прочитає файл за Вас, і Ви отримаєте його вміст у вигляді `bytes`.
|
||||
|
||||
Однак майте на увазі, що весь вміст буде збережено в пам'яті. Це працюватиме добре для малих файлів.
|
||||
|
||||
Але в деяких випадках Вам може знадобитися `UploadFile`.
|
||||
|
||||
## Параметри файлу з `UploadFile`
|
||||
|
||||
Визначте параметр файлу з типом `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
|
||||
|
||||
Використання `UploadFile` має кілька переваг перед `bytes`:
|
||||
|
||||
* Вам не потрібно використовувати `File()` у значенні за замовчуванням параметра.
|
||||
* Використовується "буферизований" файл:
|
||||
* Файл зберігається в пам'яті до досягнення певного обмеження, після чого він записується на диск.
|
||||
* Це означає, що він добре працює для великих файлів, таких як зображення, відео, великі двійкові файли тощо, не споживаючи всю пам'ять.
|
||||
Ви можете отримати метадані про завантажений файл.
|
||||
* Він має <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> `асинхронний файловий інтерфейс` interface.
|
||||
* Він надає фактичний об'єкт Python <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a>, який можна передавати безпосередньо іншим бібліотекам.
|
||||
|
||||
### `UploadFile`
|
||||
|
||||
`UploadFile` має такі атрибути:
|
||||
|
||||
* `filename`: Рядок `str` з оригінальною назвою файлу, який був завантажений (наприклад, `myimage.jpg`).
|
||||
* `content_type`: Рядок `str` з MIME-типом (наприклад, `image/jpeg`).
|
||||
* `file`: Об'єкт <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">SpooledTemporaryFile</a> (<a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">файлоподібний</a> об'єкт). Це фактичний файловий об'єкт Python, який можна безпосередньо передавати іншим функціям або бібліотекам, що очікують "файлоподібний" об'єкт.
|
||||
|
||||
`UploadFile` має такі асинхронні `async` методи. Вони викликають відповідні методи файлу під капотом (використовуючи внутрішній `SpooledTemporaryFile`).
|
||||
|
||||
* `write(data)`: Записує `data` (`str` або `bytes`) у файл.
|
||||
* `read(size)`: Читає `size` (`int`) байтів/символів з файлу.
|
||||
* `seek(offset)`: Переміщується до позиції `offset` (`int`) у файлі.
|
||||
* Наприклад, `await myfile.seek(0)` поверне курсор на початок файлу.
|
||||
* This is especially useful if you run `await myfile.read()` once and then need to read the contents again. Це особливо корисно, якщо Ви виконуєте await `await myfile.read()` один раз, а потім потрібно знову прочитати вміст.
|
||||
* `close()`: Закриває файл.
|
||||
|
||||
Оскільки всі ці методи є асинхронними `async`, Вам потрібно використовувати "await":
|
||||
|
||||
Наприклад, всередині `async` *функції обробки шляху* Ви можете отримати вміст за допомогою:
|
||||
|
||||
```Python
|
||||
contents = await myfile.read()
|
||||
```
|
||||
Якщо Ви знаходитесь у звичайній `def` *функції обробки шляху*, Ви можете отримати доступ до `UploadFile.file` безпосередньо, наприклад:
|
||||
|
||||
```Python
|
||||
contents = myfile.file.read()
|
||||
```
|
||||
|
||||
/// note | Технічні деталі `async`
|
||||
|
||||
Коли Ви використовуєте `async` методи, **FastAPI** виконує файлові операції у пулі потоків та очікує їх завершення.
|
||||
|
||||
///
|
||||
|
||||
/// note | Технічні деталі Starlette
|
||||
|
||||
`UploadFile` у **FastAPI** успадковується безпосередньо від `UploadFile` у **Starlette**, але додає деякі необхідні частини, щоб зробити його сумісним із **Pydantic** та іншими компонентами FastAPI.
|
||||
|
||||
///
|
||||
|
||||
## Що таке "Form Data"
|
||||
|
||||
Спосіб, у який HTML-форми (`<form></form>`) надсилають дані на сервер, зазвичай використовує "спеціальне" кодування, відмінне від JSON.
|
||||
|
||||
**FastAPI** забезпечує правильне зчитування цих даних з відповідної частини запиту, а не з JSON.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Дані з форм зазвичай кодуються за допомогою "media type" `application/x-www-form-urlencoded`, якщо вони не містять файлів.
|
||||
|
||||
Але якщо форма містить файли, вона кодується у форматі `multipart/form-data`. Якщо Ви використовуєте `File`, **FastAPI** визначить, що потрібно отримати файли з відповідної частини тіла запиту.
|
||||
|
||||
Щоб дізнатися більше про ці типи кодування та формові поля, ознайомтеся з <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">документацією MDN</abbr> щодо <code>POST</code></a>.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Увага
|
||||
|
||||
Ви можете оголосити кілька параметрів `File` і `Form` в *операції шляху*, але Ви не можете одночасно оголошувати поля `Body`, які мають надходити у форматі JSON, оскільки тіло запиту буде закодоване у форматі `multipart/form-data`, а не `application/json`.
|
||||
|
||||
Це не обмеження **FastAPI**, а особливість протоколу HTTP.
|
||||
|
||||
///
|
||||
|
||||
## Опціональне Завантаження Файлів
|
||||
|
||||
Файл можна зробити необов’язковим, використовуючи стандартні анотації типів і встановлюючи значення за замовчуванням `None`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
|
||||
|
||||
## `UploadFile` із Додатковими Мета Даними
|
||||
|
||||
Ви також можете використовувати `File()` разом із `UploadFile`, наприклад, для встановлення додаткових метаданих:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
|
||||
|
||||
## Завантаження Кількох Файлів
|
||||
|
||||
Можна завантажувати кілька файлів одночасно.
|
||||
|
||||
Вони будуть пов’язані з одним і тим самим "form field", який передається у вигляді "form data".
|
||||
|
||||
Щоб це реалізувати, потрібно оголосити список `bytes` або `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
|
||||
|
||||
Ви отримаєте, як і було оголошено, `list` із `bytes` або `UploadFile`.
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Ви також можете використати `from starlette.responses import HTMLResponse`.
|
||||
|
||||
**FastAPI** надає ті ж самі `starlette.responses`, що й `fastapi.responses`, для зручності розробників. Однак більшість доступних відповідей надходять безпосередньо від Starlette.
|
||||
|
||||
///
|
||||
|
||||
### Завантаження декількох файлів із додатковими метаданими
|
||||
|
||||
Так само як і раніше, Ви можете використовувати `File()`, щоб встановити додаткові параметри навіть для `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
|
||||
|
||||
## Підсумок
|
||||
|
||||
Використовуйте `File`, `bytes`та `UploadFile`, щоб оголошувати файли для завантаження у запитах, які надсилаються у вигляді form data.
|
||||
78
docs/uk/docs/tutorial/request-form-models.md
Normal file
78
docs/uk/docs/tutorial/request-form-models.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Моделі форм (Form Models)
|
||||
|
||||
У FastAPI Ви можете використовувати **Pydantic-моделі** для оголошення **полів форми**.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб використовувати форми, спочатку встановіть <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">python-multipart</a>.
|
||||
|
||||
Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили бібліотеку, наприклад:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
/// note | Підказка
|
||||
|
||||
Ця функція підтримується, починаючи з FastAPI версії `0.113.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Використання Pydantic-моделей для форм
|
||||
|
||||
Вам просто потрібно оголосити **Pydantic-модель** з полями, які Ви хочете отримати як **поля форми**, а потім оголосити параметр як `Form`:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
|
||||
|
||||
**FastAPI** **витягне** дані для **кожного поля** з **формових даних** у запиті та надасть вам Pydantic-модель, яку Ви визначили.
|
||||
|
||||
## Перевірка документації
|
||||
|
||||
Ви можете перевірити це в UI документації за `/docs`:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/request-form-models/image01.png">
|
||||
</div>
|
||||
|
||||
## Заборона додаткових полів форми
|
||||
|
||||
У деяких особливих випадках (ймовірно, рідко) Ви можете **обмежити** форму лише тими полями, які були оголошені в Pydantic-моделі, і **заборонити** будь-які **додаткові** поля.
|
||||
|
||||
/// note | Підказка
|
||||
|
||||
Ця функція підтримується, починаючи з FastAPI версії `0.114.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
Ви можете використати конфігурацію Pydantic-моделі, щоб заборонити `forbid` будь-які додаткові `extra` поля:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
|
||||
|
||||
Якщо клієнт спробує надіслати додаткові дані, він отримає **відповідь з помилкою**.
|
||||
|
||||
Наприклад, якщо клієнт спробує надіслати наступні поля форми:
|
||||
|
||||
* `username`: `Rick`
|
||||
* `password`: `Portal Gun`
|
||||
* `extra`: `Mr. Poopybutthole`
|
||||
|
||||
Він отримає відповідь із помилкою, яка повідомляє, що поле `extra` не дозволено:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "extra_forbidden",
|
||||
"loc": ["body", "extra"],
|
||||
"msg": "Extra inputs are not permitted",
|
||||
"input": "Mr. Poopybutthole"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Підсумок
|
||||
|
||||
Ви можете використовувати Pydantic-моделі для оголошення полів форми у FastAPI. 😎
|
||||
41
docs/uk/docs/tutorial/request-forms-and-files.md
Normal file
41
docs/uk/docs/tutorial/request-forms-and-files.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Запити з формами та файлами
|
||||
|
||||
У FastAPI Ви можете одночасно отримувати файли та поля форми, використовуючи `File` і `Form`.
|
||||
|
||||
/// info | Інформація
|
||||
|
||||
Щоб отримувати завантажені файли та/або дані форми, спочатку встановіть <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">python-multipart</a>.
|
||||
|
||||
Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили бібліотеку, наприклад:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
## Імпорт `File` та `Form`
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
|
||||
|
||||
## Оголошення параметрів `File` та `Form`
|
||||
|
||||
Створіть параметри файлів та форми так само як і для `Body` або `Query`:
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
|
||||
|
||||
Файли та поля форми будуть завантажені як формові дані, і Ви отримаєте як файли, так і введені користувачем поля.
|
||||
|
||||
Ви також можете оголосити деякі файли як `bytes`, а деякі як `UploadFile`.
|
||||
|
||||
/// warning | Увага
|
||||
|
||||
Ви можете оголосити кілька параметрів `File` і `Form` в операції *шляху*, але не можете одночасно оголошувати `Body`-поля, які очікуєте отримати у форматі JSON, оскільки запит матиме тіло, закодоване за допомогою `multipart/form-data`, а не `application/json`.
|
||||
|
||||
Це не обмеження **FastAPI**, а частина протоколу HTTP.
|
||||
|
||||
///
|
||||
|
||||
## Підсумок
|
||||
|
||||
Використовуйте `File` та `Form` разом, коли вам потрібно отримувати дані форми та файли в одному запиті.
|
||||
40
docs/uk/docs/tutorial/static-files.md
Normal file
40
docs/uk/docs/tutorial/static-files.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Статичні файли
|
||||
|
||||
Ви можете автоматично надавати статичні файли з каталогу, використовуючи `StaticFiles`.
|
||||
|
||||
## Використання `StaticFiles`
|
||||
|
||||
* Імпортуйте `StaticFiles`.
|
||||
* "Під'єднати" екземпляр `StaticFiles()` з вказанням необхідного шляху.
|
||||
|
||||
{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
|
||||
|
||||
/// note | Технічні деталі
|
||||
|
||||
Ви також можете використовувати `from starlette.staticfiles import StaticFiles`.
|
||||
|
||||
**FastAPI** надає той самий `starlette.staticfiles`, що й `fastapi.staticfiles` для зручності розробників. Але фактично він безпосередньо походить із Starlette.
|
||||
|
||||
///
|
||||
|
||||
### Що таке "Під'єднання"
|
||||
|
||||
"Під'єднання" означає додавання повноцінного "незалежного" застосунку за певним шляхом, який потім обробляє всі під шляхи.
|
||||
|
||||
Це відрізняється від використання `APIRouter`, оскільки під'єднаний застосунок є повністю незалежним. OpenAPI та документація вашого основного застосунку не будуть знати нічого про ваш під'єднаний застосунок.
|
||||
|
||||
Ви можете дізнатися більше про це в [Посібнику для просунутих користувачів](../advanced/index.md){.internal-link target=_blank}.
|
||||
|
||||
## Деталі
|
||||
|
||||
Перше `"/static"` вказує на під шлях, за яким буде "під'єднано" цей новий "застосунок". Тому будь-який шлях, який починається з `"/static"`, буде оброблятися ним.
|
||||
|
||||
`directory="static"` визначає каталог, що містить ваші статичні файли.
|
||||
|
||||
`name="static"` це ім'я, яке можна використовувати всередині **FastAPI**.
|
||||
|
||||
Усі ці параметри можуть бути змінені відповідно до потреб і особливостей вашого застосунку.
|
||||
|
||||
## Додаткова інформація
|
||||
|
||||
Детальніше про налаштування та можливості можна дізнатися в <a href="https://www.starlette.io/staticfiles/" class="external-link" target="_blank">документації Starlette про статичні файли</a>.
|
||||
300
docs/vi/docs/environment-variables.md
Normal file
300
docs/vi/docs/environment-variables.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# Biến môi trường (Environment Variables)
|
||||
|
||||
/// tip
|
||||
|
||||
Nếu bạn đã biết về "biến môi trường" và cách sử dụng chúng, bạn có thể bỏ qua phần này.
|
||||
|
||||
///
|
||||
|
||||
Một biến môi trường (còn được gọi là "**env var**") là một biến mà tồn tại **bên ngoài** đoạn mã Python, ở trong **hệ điều hành**, và có thể được đọc bởi đoạn mã Python của bạn (hoặc bởi các chương trình khác).
|
||||
|
||||
Các biến môi trường có thể được sử dụng để xử lí **các thiết lập** của ứng dụng, như một phần của **các quá trình cài đặt** Python, v.v.
|
||||
|
||||
## Tạo và Sử dụng các Biến Môi Trường
|
||||
|
||||
Bạn có thể **tạo** và sử dụng các biến môi trường trong **shell (terminal)**, mà không cần sử dụng Python:
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Bạn có thể tạo một biến môi trường MY_NAME với
|
||||
$ export MY_NAME="Wade Wilson"
|
||||
|
||||
// Sau đó bạn có thể sử dụng nó với các chương trình khác, như
|
||||
$ echo "Hello $MY_NAME"
|
||||
|
||||
Hello Wade Wilson
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Tạo một biến môi trường MY_NAME
|
||||
$ $Env:MY_NAME = "Wade Wilson"
|
||||
|
||||
// Sử dụng nó với các chương trình khác, như là
|
||||
$ echo "Hello $Env:MY_NAME"
|
||||
|
||||
Hello Wade Wilson
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
## Đọc các Biến Môi Trường trong Python
|
||||
|
||||
Bạn cũng có thể tạo các biến môi trường **bên ngoài** đoạn mã Python, trong terminal (hoặc bằng bất kỳ phương pháp nào khác), và sau đó **đọc chúng trong Python**.
|
||||
|
||||
Ví dụ, bạn có một file `main.py` với:
|
||||
|
||||
```Python hl_lines="3"
|
||||
import os
|
||||
|
||||
name = os.getenv("MY_NAME", "World")
|
||||
print(f"Hello {name} from Python")
|
||||
```
|
||||
|
||||
/// tip
|
||||
|
||||
Tham số thứ hai cho <a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> là giá trị mặc định để trả về.
|
||||
|
||||
Nếu không được cung cấp, nó mặc định là `None`, ở đây chúng ta cung cấp `"World"` là giá trị mặc định để sử dụng.
|
||||
|
||||
///
|
||||
|
||||
Sau đó bạn có thể gọi chương trình Python:
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Ở đây chúng ta chưa cài đặt biến môi trường
|
||||
$ python main.py
|
||||
|
||||
// Vì chúng ta chưa cài đặt biến môi trường, chúng ta nhận được giá trị mặc định
|
||||
|
||||
Hello World from Python
|
||||
|
||||
// Nhưng nếu chúng ta tạo một biến môi trường trước đó
|
||||
$ export MY_NAME="Wade Wilson"
|
||||
|
||||
// Và sau đó gọi chương trình lại
|
||||
$ python main.py
|
||||
|
||||
// Bây giờ nó có thể đọc biến môi trường
|
||||
|
||||
Hello Wade Wilson from Python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Ở đây chúng ta chưa cài đặt biến môi trường
|
||||
$ python main.py
|
||||
|
||||
// Vì chúng ta chưa cài đặt biến môi trường, chúng ta nhận được giá trị mặc định
|
||||
|
||||
Hello World from Python
|
||||
|
||||
// Nhưng nếu chúng ta tạo một biến môi trường trước đó
|
||||
$ $Env:MY_NAME = "Wade Wilson"
|
||||
|
||||
// Và sau đó gọi chương trình lại
|
||||
$ python main.py
|
||||
|
||||
// Bây giờ nó có thể đọc biến môi trường
|
||||
|
||||
Hello Wade Wilson from Python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
Vì các biến môi trường có thể được tạo bên ngoài đoạn mã Python, nhưng có thể được đọc bởi đoạn mã Python, và không cần được lưu trữ (commit vào `git`) cùng với các file khác, nên chúng thường được sử dụng để lưu các thiết lập hoặc **cấu hình**.
|
||||
|
||||
Bạn cũng có thể tạo ra một biến môi trường dành riêng cho một **lần gọi chương trình**, chỉ có thể được sử dụng bởi chương trình đó, và chỉ trong thời gian chạy của chương trình.
|
||||
|
||||
Để làm điều này, tạo nó ngay trước chương trình đó, trên cùng một dòng:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Tạo một biến môi trường MY_NAME cho lần gọi chương trình này
|
||||
$ MY_NAME="Wade Wilson" python main.py
|
||||
|
||||
// Bây giờ nó có thể đọc biến môi trường
|
||||
|
||||
Hello Wade Wilson from Python
|
||||
|
||||
// Biến môi trường không còn tồn tại sau đó
|
||||
$ python main.py
|
||||
|
||||
Hello World from Python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// tip
|
||||
|
||||
Bạn có thể đọc thêm về điều này tại <a href="https://12factor.net/config" class="external-link" target="_blank">The Twelve-Factor App: Config</a>.
|
||||
|
||||
///
|
||||
|
||||
## Các Kiểu (Types) và Kiểm tra (Validation)
|
||||
|
||||
Các biến môi trường có thể chỉ xử lí **chuỗi ký tự**, vì chúng nằm bên ngoài đoạn mã Python và phải tương thích với các chương trình khác và phần còn lại của hệ thống (và thậm chí với các hệ điều hành khác, như Linux, Windows, macOS).
|
||||
|
||||
Điều này có nghĩa là **bất kỳ giá trị nào** được đọc trong Python từ một biến môi trường **sẽ là một `str`**, và bất kỳ hành động chuyển đổi sang kiểu dữ liệu khác hoặc hành động kiểm tra nào cũng phải được thực hiện trong đoạn mã.
|
||||
|
||||
Bạn sẽ học thêm về việc sử dụng biến môi trường để xử lí **các thiết lập ứng dụng** trong [Hướng dẫn nâng cao - Các thiết lập và biến môi trường](./advanced/settings.md){.internal-link target=_blank}.
|
||||
|
||||
## Biến môi trường `PATH`
|
||||
|
||||
Có một biến môi trường **đặc biệt** được gọi là **`PATH`** được sử dụng bởi các hệ điều hành (Linux, macOS, Windows) nhằm tìm các chương trình để thực thi.
|
||||
|
||||
Giá trị của biến môi trường `PATH` là một chuỗi dài được tạo bởi các thư mục được phân tách bởi dấu hai chấm `:` trên Linux và macOS, và bởi dấu chấm phẩy `;` trên Windows.
|
||||
|
||||
Ví dụ, biến môi trường `PATH` có thể có dạng như sau:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
```plaintext
|
||||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
Điều này có nghĩa là hệ thống sẽ tìm kiếm các chương trình trong các thư mục:
|
||||
|
||||
* `/usr/local/bin`
|
||||
* `/usr/bin`
|
||||
* `/bin`
|
||||
* `/usr/sbin`
|
||||
* `/sbin`
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
```plaintext
|
||||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
|
||||
```
|
||||
|
||||
Điều này có nghĩa là hệ thống sẽ tìm kiếm các chương trình trong các thư mục:
|
||||
|
||||
* `C:\Program Files\Python312\Scripts`
|
||||
* `C:\Program Files\Python312`
|
||||
* `C:\Windows\System32`
|
||||
|
||||
////
|
||||
|
||||
Khi bạn gõ một **lệnh** trong terminal, hệ điều hành **tìm kiếm** chương trình trong **mỗi thư mục** được liệt kê trong biến môi trường `PATH`.
|
||||
|
||||
Ví dụ, khi bạn gõ `python` trong terminal, hệ điều hành tìm kiếm một chương trình được gọi `python` trong **thư mục đầu tiên** trong danh sách đó.
|
||||
|
||||
Nếu tìm thấy, nó sẽ **sử dụng** nó. Nếu không tìm thấy, nó sẽ tiếp tục tìm kiếm trong **các thư mục khác**.
|
||||
|
||||
### Cài đặt Python và cập nhật biến môi trường `PATH`
|
||||
|
||||
Khi bạn cài đặt Python, bạn có thể được hỏi nếu bạn muốn cập nhật biến môi trường `PATH`.
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
Giả sử bạn cài đặt Python vào thư mục `/opt/custompython/bin`.
|
||||
|
||||
Nếu bạn chọn cập nhật biến môi trường `PATH`, thì cài đặt sẽ thêm `/opt/custompython/bin` vào biến môi trường `PATH`.
|
||||
|
||||
Nó có thể có dạng như sau:
|
||||
|
||||
```plaintext
|
||||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
|
||||
```
|
||||
|
||||
Như vậy, khi bạn gõ `python` trong terminal, hệ thống sẽ tìm thấy chương trình Python trong `/opt/custompython/bin` (thư mục cuối) và sử dụng nó.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
Giả sử bạn cài đặt Python vào thư mục `C:\opt\custompython\bin`.
|
||||
|
||||
Nếu bạn chọn cập nhật biến môi trường `PATH`, thì cài đặt sẽ thêm `C:\opt\custompython\bin` vào biến môi trường `PATH`.
|
||||
|
||||
Nó có thể có dạng như sau:
|
||||
|
||||
```plaintext
|
||||
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
|
||||
```
|
||||
|
||||
Như vậy, khi bạn gõ `python` trong terminal, hệ thống sẽ tìm thấy chương trình Python trong `C:\opt\custompython\bin` (thư mục cuối) và sử dụng nó.
|
||||
|
||||
////
|
||||
|
||||
Vậy, nếu bạn gõ:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
Hệ thống sẽ **tìm kiếm** chương trình `python` trong `/opt/custompython/bin` và thực thi nó.
|
||||
|
||||
Nó tương đương với việc bạn gõ:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ /opt/custompython/bin/python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
Hệ thống sẽ **tìm kiếm** chương trình `python` trong `C:\opt\custompython\bin\python` và thực thi nó.
|
||||
|
||||
Nó tương đương với việc bạn gõ:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ C:\opt\custompython\bin\python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
Thông tin này sẽ hữu ích khi bạn học về [Môi trường ảo](virtual-environments.md){.internal-link target=_blank}.
|
||||
|
||||
## Kết luận
|
||||
|
||||
Với những thông tin này, bạn có thể hiểu được **các biến môi trường là gì** và **cách sử dụng chúng trong Python**.
|
||||
|
||||
Bạn có thể đọc thêm về chúng tại <a href="https://en.wikipedia.org/wiki/Environment_variable" class="external-link" target="_blank">Wikipedia cho Biến môi trường</a>.
|
||||
|
||||
Trong nhiều trường hợp, cách các biến môi trường trở nên hữu ích và có thể áp dụng không thực sự rõ ràng ngay từ đầu, nhưng chúng sẽ liên tục xuất hiện trong rất nhiều tình huống khi bạn phát triển ứng dụng, vì vậy việc hiểu biết về chúng là hữu ích.
|
||||
|
||||
Chẳng hạn, bạn sẽ cần những thông tin này khi bạn học về [Môi trường ảo](virtual-environments.md).
|
||||
75
docs/vi/docs/fastapi-cli.md
Normal file
75
docs/vi/docs/fastapi-cli.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# FastAPI CLI
|
||||
|
||||
**FastAPI CLI** là một chương trình dòng lệnh có thể được sử dụng để phục vụ ứng dụng FastAPI của bạn, quản lý dự án FastAPI của bạn và nhiều hoạt động khác.
|
||||
|
||||
Khi bạn cài đặt FastAPI (vd với `pip install "fastapi[standard]"`), nó sẽ bao gồm một gói được gọi là `fastapi-cli`, gói này cung cấp lệnh `fastapi` trong terminal.
|
||||
|
||||
Để chạy ứng dụng FastAPI của bạn cho quá trình phát triển (development), bạn có thể sử dụng lệnh `fastapi dev`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
|
||||
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
|
||||
|
||||
Searching for package file structure from directories with
|
||||
<font color="#3465A4">__init__.py</font> files
|
||||
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
|
||||
following code:
|
||||
|
||||
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
|
||||
<b>fastapi run</b>
|
||||
|
||||
Logs:
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
|
||||
<b>[</b><font color="#4E9A06">'/home/user/code/awesomeapp'</font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C to
|
||||
quit<b>)</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Chương trình dòng lệnh `fastapi` là **FastAPI CLI**.
|
||||
|
||||
FastAPI CLI nhận đường dẫn đến chương trình Python của bạn (vd `main.py`) và tự động phát hiện đối tượng `FastAPI` (thường được gọi là `app`), xác định quá trình nhập đúng, và sau đó chạy nó (serve).
|
||||
|
||||
Đối với vận hành thực tế (production), bạn sẽ sử dụng `fastapi run` thay thế. 🚀
|
||||
|
||||
Ở bên trong, **FastAPI CLI** sử dụng <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a>, một server ASGI có hiệu suất cao, sẵn sàng cho vận hành thực tế (production). 😎
|
||||
|
||||
## `fastapi dev`
|
||||
|
||||
Chạy `fastapi dev` sẽ khởi động quá trình phát triển.
|
||||
|
||||
Mặc định, **auto-reload** được bật, tự động tải lại server khi bạn thay đổi code của bạn. Điều này tốn nhiều tài nguyên và có thể kém ổn định hơn khi nó bị tắt. Bạn nên sử dụng nó cho quá trình phát triển. Nó cũng lắng nghe địa chỉ IP `127.0.0.1`, đó là địa chỉ IP của máy tính để tự giao tiếp với chính nó (`localhost`).
|
||||
|
||||
## `fastapi run`
|
||||
|
||||
Chạy `fastapi run` mặc định sẽ khởi động FastAPI cho quá trình vận hành thực tế.
|
||||
|
||||
Mặc định, **auto-reload** bị tắt. Nó cũng lắng nghe địa chỉ IP `0.0.0.0`, đó là tất cả các địa chỉ IP có sẵn, như vậy nó sẽ được truy cập công khai bởi bất kỳ ai có thể giao tiếp với máy tính. Đây là cách bạn thường chạy nó trong sản phẩm hoàn thiện, ví dụ trong một container.
|
||||
|
||||
Trong hầu hết các trường hợp, bạn sẽ (và nên) có một "proxy điểm cuối (termination proxy)" xử lý HTTPS cho bạn, điều này sẽ phụ thuộc vào cách bạn triển khai ứng dụng của bạn, nhà cung cấp có thể làm điều này cho bạn, hoặc bạn có thể cần thiết lập nó.
|
||||
|
||||
/// tip
|
||||
|
||||
Bạn có thể tìm hiểu thêm về FastAPI CLI trong [tài liệu triển khai](deployment/index.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
40
docs/vi/docs/tutorial/static-files.md
Normal file
40
docs/vi/docs/tutorial/static-files.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Tệp tĩnh (Static Files)
|
||||
|
||||
Bạn có thể triển khai tệp tĩnh tự động từ một thư mục bằng cách sử dụng StaticFiles.
|
||||
|
||||
## Sử dụng `Tệp tĩnh`
|
||||
|
||||
- Nhập `StaticFiles`.
|
||||
- "Mount" a `StaticFiles()` instance in a specific path.
|
||||
|
||||
{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
|
||||
|
||||
/// note | Chi tiết kỹ thuật
|
||||
|
||||
Bạn cũng có thể sử dụng `from starlette.staticfiles import StaticFiles`.
|
||||
|
||||
**FastAPI** cung cấp cùng `starlette.staticfiles` như `fastapi.staticfiles` giúp đơn giản hóa việc sử dụng, nhưng nó thực sự đến từ Starlette.
|
||||
|
||||
///
|
||||
|
||||
### "Mounting" là gì
|
||||
|
||||
"Mounting" có nghĩa là thêm một ứng dụng "độc lập" hoàn chỉnh vào một đường dẫn cụ thể, sau đó ứng dụng đó sẽ chịu trách nhiệm xử lý tất cả các đường dẫn con.
|
||||
|
||||
Điều này khác với việc sử dụng `APIRouter` vì một ứng dụng được gắn kết là hoàn toàn độc lập. OpenAPI và tài liệu từ ứng dụng chính của bạn sẽ không bao gồm bất kỳ thứ gì từ ứng dụng được gắn kết, v.v.
|
||||
|
||||
Bạn có thể đọc thêm về điều này trong [Hướng dẫn Người dùng Nâng cao](../advanced/index.md){.internal-link target=\_blank}.
|
||||
|
||||
## Chi tiết
|
||||
|
||||
Đường dẫn đầu tiên `"/static"` là đường dẫn con mà "ứng dụng con" này sẽ được "gắn" vào. Vì vậy, bất kỳ đường dẫn nào bắt đầu bằng `"/static"` sẽ được xử lý bởi nó.
|
||||
|
||||
Đường dẫn `directory="static"` là tên của thư mục chứa tệp tĩnh của bạn.
|
||||
|
||||
Tham số `name="static"` đặt tên cho nó để có thể được sử dụng bên trong **FastAPI**.
|
||||
|
||||
Tất cả các tham số này có thể khác với `static`, điều chỉnh chúng với phù hợp với ứng dụng của bạn.
|
||||
|
||||
## Thông tin thêm
|
||||
|
||||
Để biết thêm chi tiết và tùy chọn, hãy xem <a href="https://www.starlette.io/staticfiles/" class="external-link" target="_blank">Starlette's docs about Static Files</a>.
|
||||
842
docs/vi/docs/virtual-environments.md
Normal file
842
docs/vi/docs/virtual-environments.md
Normal file
@@ -0,0 +1,842 @@
|
||||
# Môi trường ảo (Virtual Environments)
|
||||
|
||||
Khi bạn làm việc trong các dự án Python, bạn có thể sử dụng một **môi trường ảo** (hoặc một cơ chế tương tự) để cách ly các gói bạn cài đặt cho mỗi dự án.
|
||||
|
||||
/// info
|
||||
Nếu bạn đã biết về các môi trường ảo, cách tạo chúng và sử dụng chúng, bạn có thể bỏ qua phần này. 🤓
|
||||
|
||||
///
|
||||
|
||||
/// tip
|
||||
|
||||
Một **môi trường ảo** khác với một **biến môi trường (environment variable)**.
|
||||
|
||||
Một **biến môi trường** là một biến trong hệ thống có thể được sử dụng bởi các chương trình.
|
||||
|
||||
Một **môi trường ảo** là một thư mục với một số tệp trong đó.
|
||||
|
||||
///
|
||||
|
||||
/// info
|
||||
|
||||
Trang này sẽ hướng dẫn bạn cách sử dụng các **môi trường ảo** và cách chúng hoạt động.
|
||||
|
||||
Nếu bạn đã sẵn sàng sử dụng một **công cụ có thể quản lý tất cả mọi thứ** cho bạn (bao gồm cả việc cài đặt Python), hãy thử <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
|
||||
|
||||
///
|
||||
|
||||
## Tạo một Dự án
|
||||
|
||||
Đầu tiên, tạo một thư mục cho dự án của bạn.
|
||||
|
||||
Cách tôi thường làm là tạo một thư mục có tên `code` trong thư mục `home/user`.
|
||||
|
||||
Và trong thư mục đó, tôi tạo một thư mục cho mỗi dự án.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Đi đến thư mục home
|
||||
$ cd
|
||||
// Tạo một thư mục cho tất cả các dự án của bạn
|
||||
$ mkdir code
|
||||
// Vào thư mục code
|
||||
$ cd code
|
||||
// Tạo một thư mục cho dự án này
|
||||
$ mkdir awesome-project
|
||||
// Vào thư mục dự án
|
||||
$ cd awesome-project
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Tạo một Môi trường ảo
|
||||
|
||||
Khi bạn bắt đầu làm việc với một dự án Python **trong lần đầu**, hãy tạo một môi trường ảo **<abbr title="có nhiều cách thực hiện khác nhau, đây là một hướng dẫn đơn giản">trong thư mục dự án của bạn</abbr>**.
|
||||
|
||||
/// tip
|
||||
|
||||
Bạn cần làm điều này **một lần cho mỗi dự án**, không phải mỗi khi bạn làm việc.
|
||||
///
|
||||
|
||||
//// tab | `venv`
|
||||
|
||||
Để tạo một môi trường ảo, bạn có thể sử dụng module `venv` có sẵn của Python.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python -m venv .venv
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// details | Cách các lệnh hoạt động
|
||||
|
||||
* `python`: sử dụng chương trình `python`
|
||||
* `-m`: gọi một module như một script, chúng ta sẽ nói về module đó sau
|
||||
* `venv`: sử dụng module `venv` được cài đặt sẵn của Python
|
||||
* `.venv`: tạo môi trường ảo trong thư mục mới `.venv`
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | `uv`
|
||||
|
||||
Nếu bạn có <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> được cài đặt, bạn có thể sử dụng nó để tạo một môi trường ảo.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uv venv
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// tip
|
||||
|
||||
Mặc định, `uv` sẽ tạo một môi trường ảo trong một thư mục có tên `.venv`.
|
||||
|
||||
Nhưng bạn có thể tùy chỉnh nó bằng cách thêm một đối số với tên thư mục.
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
Lệnh này tạo một môi trường ảo mới trong một thư mục có tên `.venv`.
|
||||
|
||||
/// details | `.venv` hoặc tên khác
|
||||
|
||||
Bạn có thể tạo môi trường ảo trong một thư mục khác, nhưng thường người ta quy ước đặt nó là `.venv`.
|
||||
|
||||
///
|
||||
|
||||
## Kích hoạt Môi trường ảo
|
||||
|
||||
Kích hoạt môi trường ảo mới để bất kỳ lệnh Python nào bạn chạy hoặc gói nào bạn cài đặt sẽ sử dụng nó.
|
||||
|
||||
/// tip
|
||||
|
||||
Làm điều này **mỗi khi** bạn bắt đầu một **phiên terminal mới** để làm việc trên dự án.
|
||||
|
||||
///
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/bin/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ .venv\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows Bash
|
||||
|
||||
Nếu bạn sử dụng Bash cho Windows (ví dụ: <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/Scripts/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
/// tip
|
||||
|
||||
Mỗi khi bạn cài đặt thêm một **package mới** trong môi trường đó, hãy **kích hoạt** môi trường đó lại.
|
||||
|
||||
Điều này đảm bảo rằng khi bạn sử dụng một **chương trình dòng lệnh (<abbr title="command line interface">CLI</abbr>)** được cài đặt từ gói đó, bạn sẽ dùng bản cài đặt từ môi trường ảo của mình thay vì bản được cài đặt toàn cục khác có thể có phiên bản khác với phiên bản bạn cần.
|
||||
|
||||
///
|
||||
|
||||
## Kiểm tra xem Môi trường ảo đã được Kích hoạt chưa
|
||||
|
||||
Kiểm tra xem môi trường ảo đã được kích hoạt chưa (lệnh trước đó đã hoạt động).
|
||||
|
||||
/// tip
|
||||
|
||||
Điều này là **không bắt buộc**, nhưng nó là một cách tốt để **kiểm tra** rằng mọi thứ đang hoạt động như mong đợi và bạn đang sử dụng đúng môi trường ảo mà bạn đã định.
|
||||
|
||||
///
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ which python
|
||||
|
||||
/home/user/code/awesome-project/.venv/bin/python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Nếu nó hiển thị `python` binary tại `.venv/bin/python`, trong dự án của bạn (trong trường hợp `awesome-project`), thì tức là nó hoạt động. 🎉
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ Get-Command python
|
||||
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts\python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Nếu nó hiển thị `python` binary tại `.venv\Scripts\python`, trong dự án của bạn (trong trường hợp `awesome-project`), thì tức là nó hoạt động. 🎉
|
||||
|
||||
////
|
||||
|
||||
## Nâng cấp `pip`
|
||||
|
||||
/// tip
|
||||
|
||||
Nếu bạn sử dụng <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> bạn sử dụng nó để cài đặt thay vì `pip`, thì bạn không cần cập nhật `pip`. 😎
|
||||
|
||||
///
|
||||
|
||||
Nếu bạn sử dụng `pip` để cài đặt gói (nó được cài đặt mặc định với Python), bạn nên **nâng cấp** nó lên phiên bản mới nhất.
|
||||
|
||||
Nhiều lỗi khác nhau trong khi cài đặt gói được giải quyết chỉ bằng cách nâng cấp `pip` trước.
|
||||
|
||||
/// tip
|
||||
|
||||
Bạn thường làm điều này **một lần**, ngay sau khi bạn tạo môi trường ảo.
|
||||
|
||||
///
|
||||
|
||||
Đảm bảo rằng môi trường ảo đã được kích hoạt (với lệnh trên) và sau đó chạy:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python -m pip install --upgrade pip
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Thêm `.gitignore`
|
||||
|
||||
Nếu bạn sử dụng **Git** (nên làm), hãy thêm một file `.gitignore` để Git bỏ qua mọi thứ trong `.venv`.
|
||||
|
||||
/// tip
|
||||
|
||||
Nếu bạn sử dụng <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> để tạo môi trường ảo, nó đã tự động làm điều này cho bạn, bạn có thể bỏ qua bước này. 😎
|
||||
|
||||
///
|
||||
|
||||
/// tip
|
||||
|
||||
Làm điều này **một lần**, ngay sau khi bạn tạo môi trường ảo.
|
||||
|
||||
///
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ echo "*" > .venv/.gitignore
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
/// details | Cách lệnh hoạt động
|
||||
|
||||
* `echo "*"`: sẽ "in" văn bản `*` trong terminal (phần tiếp theo sẽ thay đổi điều đó một chút)
|
||||
* `>`: bất kỳ văn bản nào được in ra terminal bởi lệnh trước `>` không được in ra mà thay vào đó được viết vào file ở phía bên phải của `>`
|
||||
* `.gitignore`: tên của file mà văn bản sẽ được viết vào
|
||||
|
||||
Và `*` với Git có nghĩa là "mọi thứ". Vì vậy, nó sẽ bỏ qua mọi thứ trong thư mục `.venv`.
|
||||
|
||||
Lệnh này sẽ tạo một file `.gitignore` với nội dung:
|
||||
|
||||
```gitignore
|
||||
*
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
## Cài đặt gói (packages)
|
||||
|
||||
Sau khi kích hoạt môi trường, bạn có thể cài đặt các gói trong đó.
|
||||
|
||||
/// tip
|
||||
|
||||
Thực hiện điều này **một lần** khi cài đặt hoặc cập nhật gói cần thiết cho dự án của bạn.
|
||||
|
||||
Nếu bạn cần cập nhật phiên bản hoặc thêm một gói mới, bạn sẽ **thực hiện điều này lại**.
|
||||
|
||||
///
|
||||
|
||||
### Cài đặt gói trực tiếp
|
||||
|
||||
Nếu bạn cần cập nhật phiên bản hoặc thêm một gói mới, bạn sẽ **thực hiện điều này lại**.
|
||||
|
||||
/// tip
|
||||
Để quản lý dự án tốt hơn, hãy liệt kê tất cả các gói và phiên bản cần thiết trong một file (ví dụ `requirements.txt` hoặc `pyproject.toml`).
|
||||
|
||||
///
|
||||
|
||||
//// tab | `pip`
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "fastapi[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | `uv`
|
||||
|
||||
Nếu bạn có <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uv pip install "fastapi[standard]"
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
### Cài đặt từ `requirements.txt`
|
||||
|
||||
Nếu bạn có một tệp `requirements.txt`, bạn có thể sử dụng nó để cài đặt các gói.
|
||||
|
||||
//// tab | `pip`
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install -r requirements.txt
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | `uv`
|
||||
|
||||
Nếu bạn có <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a>:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uv pip install -r requirements.txt
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
/// details | `requirements.txt`
|
||||
|
||||
Một tệp `requirements.txt` với một số gói sẽ trông như thế này:
|
||||
|
||||
```requirements.txt
|
||||
fastapi[standard]==0.113.0
|
||||
pydantic==2.8.0
|
||||
```
|
||||
|
||||
///
|
||||
|
||||
## Chạy Chương trình của bạn
|
||||
|
||||
Sau khi kích hoạt môi trường ảo, bạn có thể chạy chương trình của mình, nó sẽ sử dụng Python trong môi trường ảo của bạn với các gói bạn đã cài đặt.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python main.py
|
||||
|
||||
Hello World
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Cấu hình Trình soạn thảo của bạn
|
||||
|
||||
Nếu bạn sử dụng một trình soạn thảo, hãy đảm bảo bạn cấu hình nó để sử dụng cùng môi trường ảo mà bạn đã tạo (trình soạn thảo sẽ tự động phát hiện môi trường ảo) để bạn có thể nhận được tính năng tự động hoàn thành câu lệnh (autocomplete) và in lỗi trực tiếp trong trình soạn thảo (inline errors).
|
||||
|
||||
Ví dụ:
|
||||
|
||||
* <a href="https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment" class="external-link" target="_blank">VS Code</a>
|
||||
* <a href="https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html" class="external-link" target="_blank">PyCharm</a>
|
||||
|
||||
/// tip
|
||||
|
||||
Bạn thường chỉ cần làm điều này **một lần**, khi bạn tạo môi trường ảo.
|
||||
|
||||
///
|
||||
|
||||
## Huỷ kích hoạt Môi trường ảo
|
||||
|
||||
Khi bạn hoàn tất việc làm trên dự án của bạn, bạn có thể **huỷ kích hoạt** môi trường ảo.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ deactivate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Như vậy, khi bạn chạy `python`, nó sẽ không chạy từ môi trường ảo đó với các gói đã cài đặt.
|
||||
|
||||
## Sẵn sàng để Làm việc
|
||||
|
||||
Bây giờ bạn đã sẵn sàng để làm việc trên dự án của mình rồi đấy.
|
||||
|
||||
/// tip
|
||||
|
||||
Bạn muốn hiểu tất cả những gì ở trên?
|
||||
|
||||
Tiếp tục đọc. 👇🤓
|
||||
|
||||
///
|
||||
|
||||
## Tại sao cần Môi trường ảo
|
||||
|
||||
Để làm việc với FastAPI, bạn cần cài đặt <a href="https://www.python.org/" class="external-link" target="_blank">Python</a>.
|
||||
|
||||
Sau đó, bạn sẽ cần **cài đặt** FastAPI và bất kỳ **gói** nào mà bạn muốn sử dụng.
|
||||
|
||||
Để cài đặt gói, bạn thường sử dụng lệnh `pip` có sẵn với Python (hoặc các phiên bản tương tự).
|
||||
|
||||
Tuy nhiên, nếu bạn sử dụng `pip` trực tiếp, các gói sẽ được cài đặt trong **môi trường Python toàn cục** của bạn (phần cài đặt toàn cục của Python).
|
||||
|
||||
### Vấn đề
|
||||
|
||||
Vậy, vấn đề gì khi cài đặt gói trong môi trường Python toàn cục?
|
||||
|
||||
Trong một vài thời điểm, bạn sẽ phải viết nhiều chương trình khác nhau phụ thuộc vào **các gói khác nhau**. Và một số dự án bạn thực hiện lại phụ thuộc vào **các phiên bản khác nhau** của cùng một gói. 😱
|
||||
|
||||
Ví dụ, bạn có thể tạo một dự án được gọi là `philosophers-stone`, chương trình này phụ thuộc vào một gói khác được gọi là **`harry`, sử dụng phiên bản `1`**. Vì vậy, bạn cần cài đặt `harry`.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
stone(philosophers-stone) -->|phụ thuộc| harry-1[harry v1]
|
||||
```
|
||||
|
||||
Sau đó, vào một vài thời điểm sau, bạn tạo một dự án khác được gọi là `prisoner-of-azkaban`, và dự án này cũng phụ thuộc vào `harry`, nhưng dự án này cần **`harry` phiên bản `3`**.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
azkaban(prisoner-of-azkaban) --> |phụ thuộc| harry-3[harry v3]
|
||||
```
|
||||
|
||||
Bây giờ, vấn đề là, nếu bạn cài đặt các gói toàn cục (trong môi trường toàn cục) thay vì trong một **môi trường ảo cục bộ**, bạn sẽ phải chọn phiên bản `harry` nào để cài đặt.
|
||||
|
||||
Nếu bạn muốn chạy `philosophers-stone` bạn sẽ cần phải cài đặt `harry` phiên bản `1`, ví dụ với:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "harry==1"
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Và sau đó bạn sẽ có `harry` phiên bản `1` được cài đặt trong môi trường Python toàn cục của bạn.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph global[môi trường toàn cục]
|
||||
harry-1[harry v1]
|
||||
end
|
||||
subgraph stone-project[dự án philosophers-stone ]
|
||||
stone(philosophers-stone) -->|phụ thuộc| harry-1
|
||||
end
|
||||
```
|
||||
|
||||
Nhưng sau đó, nếu bạn muốn chạy `prisoner-of-azkaban`, bạn sẽ cần phải gỡ bỏ `harry` phiên bản `1` và cài đặt `harry` phiên bản `3` (hoặc chỉ cần cài đặt phiên bản `3` sẽ tự động gỡ bỏ phiên bản `1`).
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "harry==3"
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Và sau đó bạn sẽ có `harry` phiên bản `3` được cài đặt trong môi trường Python toàn cục của bạn.
|
||||
|
||||
Và nếu bạn cố gắng chạy `philosophers-stone` lại, có khả năng nó sẽ **không hoạt động** vì nó cần `harry` phiên bản `1`.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph global[môi trường toàn cục]
|
||||
harry-1[<strike>harry v1</strike>]
|
||||
style harry-1 fill:#ccc,stroke-dasharray: 5 5
|
||||
harry-3[harry v3]
|
||||
end
|
||||
subgraph stone-project[dự án philosophers-stone ]
|
||||
stone(philosophers-stone) -.-x|⛔️| harry-1
|
||||
end
|
||||
subgraph azkaban-project[dự án prisoner-of-azkaban ]
|
||||
azkaban(prisoner-of-azkaban) --> |phụ thuộc| harry-3
|
||||
end
|
||||
```
|
||||
|
||||
/// tip
|
||||
|
||||
Mặc dù các gói Python thường cố gắng **tránh các thay đổi làm hỏng code** trong **phiên bản mới**, nhưng để đảm bảo an toàn, bạn nên chủ động cài đặt phiên bản mới và chạy kiểm thử để xác nhận mọi thứ vẫn hoạt động đúng.
|
||||
|
||||
///
|
||||
|
||||
Bây giờ, hãy hình dung về **nhiều** gói khác nhau mà tất cả các dự án của bạn phụ thuộc vào. Rõ ràng rất khó để quản lý. Điều này dẫn tới việc là bạn sẽ có nhiều dự án với **các phiên bản không tương thích** của các gói, và bạn có thể không biết tại sao một số thứ không hoạt động.
|
||||
|
||||
Hơn nữa, tuỳ vào hệ điều hành của bạn (vd Linux, Windows, macOS), có thể đã có Python được cài đặt sẵn. Trong trường hợp ấy, một vài gói nhiều khả năng đã được cài đặt trước với các phiên bản **cần thiết cho hệ thống của bạn**. Nếu bạn cài đặt các gói trong môi trường Python toàn cục, bạn có thể sẽ **phá vỡ** một số chương trình đã được cài đặt sẵn cùng hệ thống.
|
||||
|
||||
## Nơi các Gói được Cài đặt
|
||||
|
||||
Khi bạn cài đặt Python, nó sẽ tạo ra một vài thư mục và tệp trong máy tính của bạn.
|
||||
|
||||
Một vài thư mục này là những thư mục chịu trách nhiệm có tất cả các gói bạn cài đặt.
|
||||
|
||||
Khi bạn chạy:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
// Đừng chạy lệnh này ngay, đây chỉ là một ví dụ 🤓
|
||||
$ pip install "fastapi[standard]"
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Lệnh này sẽ tải xuống một tệp nén với mã nguồn FastAPI, thường là từ <a href="https://pypi.org/project/fastapi/" class="external-link" target="_blank">PyPI</a>.
|
||||
|
||||
Nó cũng sẽ **tải xuống** các tệp cho các gói khác mà FastAPI phụ thuộc vào.
|
||||
|
||||
Sau đó, nó sẽ **giải nén** tất cả các tệp đó và đưa chúng vào một thư mục trong máy tính của bạn.
|
||||
|
||||
Mặc định, nó sẽ đưa các tệp đã tải xuống và giải nén vào thư mục được cài đặt cùng Python của bạn, đó là **môi trường toàn cục**.
|
||||
|
||||
## Những Môi trường ảo là gì?
|
||||
|
||||
Cách giải quyết cho vấn đề có tất cả các gói trong môi trường toàn cục là sử dụng một **môi trường ảo cho mỗi dự án** bạn làm việc.
|
||||
|
||||
Một môi trường ảo là một **thư mục**, rất giống với môi trường toàn cục, trong đó bạn có thể cài đặt các gói cho một dự án.
|
||||
|
||||
Vì vậy, mỗi dự án sẽ có một môi trường ảo riêng của nó (thư mục `.venv`) với các gói riêng của nó.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph stone-project[dự án philosophers-stone ]
|
||||
stone(philosophers-stone) --->|phụ thuộc| harry-1
|
||||
subgraph venv1[.venv]
|
||||
harry-1[harry v1]
|
||||
end
|
||||
end
|
||||
subgraph azkaban-project[dự án prisoner-of-azkaban ]
|
||||
azkaban(prisoner-of-azkaban) --->|phụ thuộc| harry-3
|
||||
subgraph venv2[.venv]
|
||||
harry-3[harry v3]
|
||||
end
|
||||
end
|
||||
stone-project ~~~ azkaban-project
|
||||
```
|
||||
|
||||
## Kích hoạt Môi trường ảo nghĩa là gì
|
||||
|
||||
Khi bạn kích hoạt một môi trường ảo, ví dụ với:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/bin/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ .venv\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows Bash
|
||||
|
||||
Nếu bạn sử dụng Bash cho Windows (ví dụ <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ source .venv/Scripts/activate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
Lệnh này sẽ tạo hoặc sửa đổi một số [biến môi trường](environment-variables.md){.internal-link target=_blank} mà sẽ được sử dụng cho các lệnh tiếp theo.
|
||||
|
||||
Một trong số đó là biến `PATH`.
|
||||
|
||||
/// tip
|
||||
|
||||
Bạn có thể tìm hiểu thêm về biến `PATH` trong [Biến môi trường](environment-variables.md#path-environment-variable){.internal-link target=_blank} section.
|
||||
|
||||
///
|
||||
|
||||
Kích hoạt môi trường ảo thêm đường dẫn `.venv/bin` (trên Linux và macOS) hoặc `.venv\Scripts` (trên Windows) vào biến `PATH`.
|
||||
|
||||
Giả sử rằng trước khi kích hoạt môi trường, biến `PATH` như sau:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
```plaintext
|
||||
/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
Nghĩa là hệ thống sẽ tìm kiếm chương trình trong:
|
||||
|
||||
* `/usr/bin`
|
||||
* `/bin`
|
||||
* `/usr/sbin`
|
||||
* `/sbin`
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
```plaintext
|
||||
C:\Windows\System32
|
||||
```
|
||||
|
||||
Nghĩa là hệ thống sẽ tìm kiếm chương trình trong:
|
||||
|
||||
* `C:\Windows\System32`
|
||||
|
||||
////
|
||||
|
||||
Sau khi kích hoạt môi trường ảo, biến `PATH` sẽ như sau:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
```
|
||||
|
||||
Nghĩa là hệ thống sẽ bắt đầu tìm kiếm chương trình trong:
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin
|
||||
```
|
||||
|
||||
trước khi tìm kiếm trong các thư mục khác.
|
||||
|
||||
Vì vậy, khi bạn gõ `python` trong terminal, hệ thống sẽ tìm thấy chương trình Python trong:
|
||||
|
||||
```plaintext
|
||||
/home/user/code/awesome-project/.venv/bin/python
|
||||
```
|
||||
|
||||
và sử dụng chương trình đó.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
|
||||
```
|
||||
|
||||
Nghĩa là hệ thống sẽ bắt đầu tìm kiếm chương trình trong:
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts
|
||||
```
|
||||
|
||||
trước khi tìm kiếm trong các thư mục khác.
|
||||
|
||||
Vì vậy, khi bạn gõ `python` trong terminal, hệ thống sẽ tìm thấy chương trình Python trong:
|
||||
|
||||
```plaintext
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts\python
|
||||
```
|
||||
|
||||
và sử dụng chương trình đó.
|
||||
|
||||
////
|
||||
|
||||
Một chi tiết quan trọng là nó sẽ đưa địa chỉ của môi trường ảo vào **đầu** của biến `PATH`. Hệ thống sẽ tìm kiếm nó **trước** khi tìm kiếm bất kỳ Python nào khác có sẵn. Vì vậy, khi bạn chạy `python`, nó sẽ sử dụng Python **từ môi trường ảo** thay vì bất kỳ Python nào khác (ví dụ, Python từ môi trường toàn cục).
|
||||
|
||||
Kích hoạt một môi trường ảo cũng thay đổi một vài thứ khác, nhưng đây là một trong những điều quan trọng nhất mà nó thực hiện.
|
||||
|
||||
## Kiểm tra một Môi trường ảo
|
||||
|
||||
Khi bạn kiểm tra một môi trường ảo đã được kích hoạt chưa, ví dụ với:
|
||||
|
||||
//// tab | Linux, macOS, Windows Bash
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ which python
|
||||
|
||||
/home/user/code/awesome-project/.venv/bin/python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Windows PowerShell
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ Get-Command python
|
||||
|
||||
C:\Users\user\code\awesome-project\.venv\Scripts\python
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
|
||||
Điều đó có nghĩa là chương trình `python` sẽ được sử dụng là chương trình **trong môi trường ảo**.
|
||||
|
||||
Bạn sử dụng `which` trên Linux và macOS và `Get-Command` trên Windows PowerShell.
|
||||
|
||||
Cách hoạt động của lệnh này là nó sẽ đi và kiểm tra biến `PATH`, đi qua **mỗi đường dẫn theo thứ tự**, tìm kiếm chương trình được gọi là `python`. Khi nó tìm thấy nó, nó sẽ **hiển thị cho bạn đường dẫn** đến chương trình đó.
|
||||
|
||||
Điều quan trọng nhất là khi bạn gọi `python`, đó chính là chương trình `python` được thực thi.
|
||||
|
||||
Vì vậy, bạn có thể xác nhận nếu bạn đang ở trong môi trường ảo đúng.
|
||||
|
||||
/// tip
|
||||
|
||||
Dễ dàng kích hoạt một môi trường ảo, cài đặt Python, và sau đó **chuyển đến một dự án khác**.
|
||||
|
||||
Và dự án thứ hai **sẽ không hoạt động** vì bạn đang sử dụng **Python không đúng**, từ một môi trường ảo cho một dự án khác.
|
||||
|
||||
Thật tiện lợi khi có thể kiểm tra `python` nào đang được sử dụng 🤓
|
||||
|
||||
///
|
||||
|
||||
## Tại sao lại Huỷ kích hoạt một Môi trường ảo
|
||||
|
||||
Ví dụ, bạn có thể làm việc trên một dự án `philosophers-stone`, **kích hoạt môi trường ảo**, cài đặt các gói và làm việc với môi trường ảo đó.
|
||||
|
||||
Sau đó, bạn muốn làm việc trên **dự án khác** `prisoner-of-azkaban`.
|
||||
|
||||
Bạn đi đến dự án đó:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ cd ~/code/prisoner-of-azkaban
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Nếu bạn không tắt môi trường ảo cho `philosophers-stone`, khi bạn chạy `python` trong terminal, nó sẽ cố gắng sử dụng Python từ `philosophers-stone`.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ cd ~/code/prisoner-of-azkaban
|
||||
|
||||
$ python main.py
|
||||
|
||||
// Lỗi khi import sirius, nó không được cài đặt 😱
|
||||
Traceback (most recent call last):
|
||||
File "main.py", line 1, in <module>
|
||||
import sirius
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Nếu bạn huỷ kích hoạt môi trường ảo hiện tại và kích hoạt môi trường ảo mới cho `prisoner-of-azkaban`, khi bạn chạy `python`, nó sẽ sử dụng Python từ môi trường ảo trong `prisoner-of-azkaban`.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ cd ~/code/prisoner-of-azkaban
|
||||
|
||||
// Bạn không cần phải ở trong thư mục trước để huỷ kích hoạt, bạn có thể làm điều đó ở bất kỳ đâu, ngay cả sau khi đi đến dự án khác 😎
|
||||
$ deactivate
|
||||
|
||||
// Kích hoạt môi trường ảo trong prisoner-of-azkaban/.venv 🚀
|
||||
$ source .venv/bin/activate
|
||||
|
||||
// Bây giờ khi bạn chạy python, nó sẽ tìm thấy gói sirius được cài đặt trong môi trường ảo này ✨
|
||||
$ python main.py
|
||||
|
||||
I solemnly swear 🐺
|
||||
|
||||
(Tôi long trọng thề 🐺 - câu này được lấy từ Harry Potter, chú thích của người dịch)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Các cách làm tương tự
|
||||
|
||||
Đây là một hướng dẫn đơn giản để bạn có thể bắt đầu và hiểu cách mọi thứ hoạt động **bên trong**.
|
||||
|
||||
Có nhiều **cách khác nhau** để quản lí các môi trường ảo, các gói phụ thuộc (requirements), và các dự án.
|
||||
|
||||
Một khi bạn đã sẵn sàng và muốn sử dụng một công cụ để **quản lí cả dự án**, các gói phụ thuộc, các môi trường ảo, v.v. Tôi sẽ khuyên bạn nên thử <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a>.
|
||||
|
||||
`uv` có thể làm nhiều thứ, chẳng hạn:
|
||||
|
||||
* **Cài đặt Python** cho bạn, bao gồm nhiều phiên bản khác nhau
|
||||
* Quản lí **các môi trường ảo** cho các dự án của bạn
|
||||
* Cài đặt **các gói (packages)**
|
||||
* Quản lí **các thành phần phụ thuộc và phiên bản** của các gói cho dự án của bạn
|
||||
* Đảm bảo rằng bạn có một **tập hợp chính xác** các gói và phiên bản để cài đặt, bao gồm các thành phần phụ thuộc của chúng, để bạn có thể đảm bảo rằng bạn có thể chạy dự án của bạn trong sản xuất chính xác như trong máy tính của bạn trong khi phát triển, điều này được gọi là **locking**
|
||||
* Và còn nhiều thứ khác nữa
|
||||
|
||||
## Kết luận
|
||||
|
||||
Nếu bạn đã đọc và hiểu hết những điều này, khá chắc là bây giờ bạn đã **biết nhiều hơn** về môi trường ảo so với kha khá lập trình viên khác đấy. 🤓
|
||||
|
||||
Những hiểu biết chi tiết này có thể sẽ hữu ích với bạn trong tương lai khi mà bạn cần gỡ lỗi một vài thứ phức tạp, và bạn đã có những hiểu biết về **ngọn ngành gốc rễ cách nó hoạt động**. 😎
|
||||
@@ -12,7 +12,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
<em>FastAPI 框架,高性能,易于学习,高效编码,生产可用</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/fastapi/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/fastapi/fastapi?color=%2334D058" alt="Coverage">
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
|
||||
@@ -108,21 +108,6 @@ q: Union[str, None] = Query(default=None, min_length=3)
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006.py hl[7] *}
|
||||
|
||||
### 使用省略号(`...`)声明必需参数
|
||||
|
||||
有另一种方法可以显式的声明一个值是必需的,即将默认参数的默认值设为 `...` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006b.py hl[7] *}
|
||||
|
||||
/// info
|
||||
|
||||
如果你之前没见过 `...` 这种用法:它是一个特殊的单独值,它是 <a href="https://docs.python.org/zh-cn/3/library/constants.html#Ellipsis" class="external-link" target="_blank">Python 的一部分并且被称为“Ellipsis”(意为省略号 —— 译者注)</a>。
|
||||
Pydantic 和 FastAPI 使用它来显式的声明需要一个值。
|
||||
|
||||
///
|
||||
|
||||
这将使 **FastAPI** 知道此查询参数是必需的。
|
||||
|
||||
### 使用`None`声明必需参数
|
||||
|
||||
你可以声明一个参数可以接收`None`值,但它仍然是必需的。这将强制客户端发送一个值,即使该值是`None`。
|
||||
@@ -137,18 +122,6 @@ Pydantic 是 FastAPI 中所有数据验证和序列化的核心,当你在没
|
||||
|
||||
///
|
||||
|
||||
### 使用Pydantic中的`Required`代替省略号(`...`)
|
||||
|
||||
如果你觉得使用 `...` 不舒服,你也可以从 Pydantic 导入并使用 `Required`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006d.py hl[2,8] *}
|
||||
|
||||
/// tip
|
||||
|
||||
请记住,在大多数情况下,当你需要某些东西时,可以简单地省略 `default` 参数,因此你通常不必使用 `...` 或 `Required`
|
||||
|
||||
///
|
||||
|
||||
## 查询参数列表 / 多个值
|
||||
|
||||
当你使用 `Query` 显式地定义查询参数时,你还可以声明它去接收一组值,或换句话来说,接收多个值。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI(swagger_ui_parameters={"syntaxHighlight.theme": "obsidian"})
|
||||
app = FastAPI(swagger_ui_parameters={"syntaxHighlight": {"theme": "obsidian"}})
|
||||
|
||||
|
||||
@app.get("/users/{username}")
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: str = Query(default=..., min_length=3)):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
return results
|
||||
@@ -1,12 +0,0 @@
|
||||
from fastapi import FastAPI, Query
|
||||
from typing_extensions import Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
return results
|
||||
@@ -1,13 +0,0 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
return results
|
||||
@@ -6,7 +6,7 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Union[str, None] = Query(default=..., min_length=3)):
|
||||
async def read_items(q: Union[str, None] = Query(min_length=3)):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
|
||||
@@ -7,7 +7,7 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)] = ...):
|
||||
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)]):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
|
||||
@@ -6,7 +6,7 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Annotated[str | None, Query(min_length=3)] = ...):
|
||||
async def read_items(q: Annotated[str | None, Query(min_length=3)]):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
|
||||
@@ -6,7 +6,7 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)] = ...):
|
||||
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)]):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
|
||||
@@ -4,7 +4,7 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: str | None = Query(default=..., min_length=3)):
|
||||
async def read_items(q: str | None = Query(min_length=3)):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: str = Query(default=..., min_length=3)):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
return results
|
||||
@@ -1,12 +0,0 @@
|
||||
from fastapi import FastAPI, Query
|
||||
from typing_extensions import Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
return results
|
||||
@@ -1,13 +0,0 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
|
||||
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
if q:
|
||||
results.update({"q": q})
|
||||
return results
|
||||
@@ -95,7 +95,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
|
||||
@@ -96,7 +96,7 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
|
||||
@@ -95,7 +95,7 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
|
||||
@@ -95,7 +95,7 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
|
||||
@@ -94,7 +94,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
|
||||
@@ -117,7 +117,7 @@ async def get_current_user(
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_scopes = payload.get("scopes", [])
|
||||
|
||||
@@ -116,7 +116,7 @@ async def get_current_user(
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_scopes = payload.get("scopes", [])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Annotated, List, Union
|
||||
from typing import Annotated, Union
|
||||
|
||||
import jwt
|
||||
from fastapi import Depends, FastAPI, HTTPException, Security, status
|
||||
@@ -44,7 +44,7 @@ class Token(BaseModel):
|
||||
|
||||
class TokenData(BaseModel):
|
||||
username: Union[str, None] = None
|
||||
scopes: List[str] = []
|
||||
scopes: list[str] = []
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
@@ -116,7 +116,7 @@ async def get_current_user(
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_scopes = payload.get("scopes", [])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.115.8"
|
||||
__version__ = "0.115.9"
|
||||
|
||||
from starlette import status as status
|
||||
|
||||
|
||||
@@ -413,8 +413,11 @@ class HTTPDigest(HTTPBase):
|
||||
else:
|
||||
return None
|
||||
if scheme.lower() != "digest":
|
||||
raise HTTPException(
|
||||
status_code=HTTP_403_FORBIDDEN,
|
||||
detail="Invalid authentication credentials",
|
||||
)
|
||||
if self.auto_error:
|
||||
raise HTTPException(
|
||||
status_code=HTTP_403_FORBIDDEN,
|
||||
detail="Invalid authentication credentials",
|
||||
)
|
||||
else:
|
||||
return None
|
||||
return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-e .
|
||||
-r requirements-docs-tests.txt
|
||||
mkdocs-material==9.5.18
|
||||
mkdocs-material==9.6.1
|
||||
mdx-include >=1.4.1,<2.0.0
|
||||
mkdocs-redirects>=1.2.1,<1.3.0
|
||||
typer == 0.12.5
|
||||
@@ -8,7 +8,7 @@ pyyaml >=5.3.1,<7.0.0
|
||||
# For Material for MkDocs, Chinese search
|
||||
jieba==0.42.1
|
||||
# For image processing by Material for MkDocs
|
||||
pillow==11.0.0
|
||||
pillow==11.1.0
|
||||
# For image processing by Material for MkDocs
|
||||
cairosvg==2.7.1
|
||||
mkdocstrings[python]==0.26.1
|
||||
|
||||
@@ -10,7 +10,7 @@ anyio[trio] >=3.2.1,<5.0.0
|
||||
PyJWT==2.8.0
|
||||
pyyaml >=5.3.1,<7.0.0
|
||||
passlib[bcrypt] >=1.7.2,<2.0.0
|
||||
inline-snapshot==0.18.1
|
||||
inline-snapshot==0.19.3
|
||||
# types
|
||||
types-ujson ==5.10.0.20240515
|
||||
types-orjson ==3.6.2
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from .app.main import app
|
||||
@@ -5,29 +6,22 @@ from .app.main import app
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_post_a():
|
||||
@pytest.mark.parametrize(
|
||||
"path", ["/a/compute", "/a/compute/", "/b/compute", "/b/compute/"]
|
||||
)
|
||||
def test_post(path):
|
||||
data = {"a": 2, "b": "foo"}
|
||||
response = client.post("/a/compute", json=data)
|
||||
response = client.post(path, json=data)
|
||||
assert response.status_code == 200, response.text
|
||||
data = response.json()
|
||||
assert data == response.json()
|
||||
|
||||
|
||||
def test_post_a_invalid():
|
||||
@pytest.mark.parametrize(
|
||||
"path", ["/a/compute", "/a/compute/", "/b/compute", "/b/compute/"]
|
||||
)
|
||||
def test_post_invalid(path):
|
||||
data = {"a": "bar", "b": "foo"}
|
||||
response = client.post("/a/compute", json=data)
|
||||
assert response.status_code == 422, response.text
|
||||
|
||||
|
||||
def test_post_b():
|
||||
data = {"a": 2, "b": "foo"}
|
||||
response = client.post("/b/compute/", json=data)
|
||||
assert response.status_code == 200, response.text
|
||||
data = response.json()
|
||||
|
||||
|
||||
def test_post_b_invalid():
|
||||
data = {"a": "bar", "b": "foo"}
|
||||
response = client.post("/b/compute/", json=data)
|
||||
response = client.post(path, json=data)
|
||||
assert response.status_code == 422, response.text
|
||||
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ def test_security_http_digest_incorrect_scheme_credentials():
|
||||
response = client.get(
|
||||
"/users/me", headers={"Authorization": "Other invalidauthorization"}
|
||||
)
|
||||
assert response.status_code == 403, response.text
|
||||
assert response.json() == {"detail": "Invalid authentication credentials"}
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"msg": "Create an account first"}
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
|
||||
@@ -12,7 +12,7 @@ def test_swagger_ui():
|
||||
'"syntaxHighlight": false' not in response.text
|
||||
), "not used parameters should not be included"
|
||||
assert (
|
||||
'"syntaxHighlight.theme": "obsidian"' in response.text
|
||||
'"syntaxHighlight": {"theme": "obsidian"}' in response.text
|
||||
), "parameters with middle dots should be included in a JSON compatible way"
|
||||
assert (
|
||||
'"dom_id": "#swagger-ui"' in response.text
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
import importlib
|
||||
|
||||
import pytest
|
||||
from dirty_equals import IsDict
|
||||
from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_py39, needs_py310
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client():
|
||||
from docs_src.query_params_str_validations.tutorial010 import app
|
||||
|
||||
client = TestClient(app)
|
||||
@pytest.fixture(
|
||||
name="client",
|
||||
params=[
|
||||
"tutorial010",
|
||||
pytest.param("tutorial010_py310", marks=needs_py310),
|
||||
"tutorial010_an",
|
||||
pytest.param("tutorial010_an_py39", marks=needs_py39),
|
||||
pytest.param("tutorial010_an_py310", marks=needs_py310),
|
||||
],
|
||||
)
|
||||
def get_client(request: pytest.FixtureRequest):
|
||||
mod = importlib.import_module(
|
||||
f"docs_src.query_params_str_validations.{request.param}"
|
||||
)
|
||||
|
||||
client = TestClient(mod.app)
|
||||
return client
|
||||
|
||||
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
import pytest
|
||||
from dirty_equals import IsDict
|
||||
from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client():
|
||||
from docs_src.query_params_str_validations.tutorial010_an import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
def test_query_params_str_validations_no_query(client: TestClient):
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
|
||||
|
||||
def test_query_params_str_validations_item_query_fixedquery(client: TestClient):
|
||||
response = client.get("/items/", params={"item-query": "fixedquery"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
|
||||
"q": "fixedquery",
|
||||
}
|
||||
|
||||
|
||||
def test_query_params_str_validations_q_fixedquery(client: TestClient):
|
||||
response = client.get("/items/", params={"q": "fixedquery"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
|
||||
|
||||
def test_query_params_str_validations_item_query_nonregexquery(client: TestClient):
|
||||
response = client.get("/items/", params={"item-query": "nonregexquery"})
|
||||
assert response.status_code == 422
|
||||
assert response.json() == IsDict(
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "string_pattern_mismatch",
|
||||
"loc": ["query", "item-query"],
|
||||
"msg": "String should match pattern '^fixedquery$'",
|
||||
"input": "nonregexquery",
|
||||
"ctx": {"pattern": "^fixedquery$"},
|
||||
}
|
||||
]
|
||||
}
|
||||
) | IsDict(
|
||||
# TODO: remove when deprecating Pydantic v1
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"ctx": {"pattern": "^fixedquery$"},
|
||||
"loc": ["query", "item-query"],
|
||||
"msg": 'string does not match regex "^fixedquery$"',
|
||||
"type": "value_error.str.regex",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_openapi_schema(client: TestClient):
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
"required": False,
|
||||
"deprecated": True,
|
||||
"schema": IsDict(
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 50,
|
||||
"pattern": "^fixedquery$",
|
||||
},
|
||||
{"type": "null"},
|
||||
],
|
||||
"title": "Query string",
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
# See https://github.com/pydantic/pydantic/blob/80353c29a824c55dea4667b328ba8f329879ac9f/tests/test_fastapi.sh#L25-L34.
|
||||
**(
|
||||
{"deprecated": True}
|
||||
if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 10)
|
||||
else {}
|
||||
),
|
||||
}
|
||||
)
|
||||
| IsDict(
|
||||
# TODO: remove when deprecating Pydantic v1
|
||||
{
|
||||
"title": "Query string",
|
||||
"maxLength": 50,
|
||||
"minLength": 3,
|
||||
"pattern": "^fixedquery$",
|
||||
"type": "string",
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
}
|
||||
),
|
||||
"name": "item-query",
|
||||
"in": "query",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ValidationError": {
|
||||
"title": "ValidationError",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loc": {
|
||||
"title": "Location",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
},
|
||||
"msg": {"title": "Message", "type": "string"},
|
||||
"type": {"title": "Error Type", "type": "string"},
|
||||
},
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
import pytest
|
||||
from dirty_equals import IsDict
|
||||
from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_py310
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client():
|
||||
from docs_src.query_params_str_validations.tutorial010_an_py310 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_query_params_str_validations_no_query(client: TestClient):
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_query_params_str_validations_item_query_fixedquery(client: TestClient):
|
||||
response = client.get("/items/", params={"item-query": "fixedquery"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
|
||||
"q": "fixedquery",
|
||||
}
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_query_params_str_validations_q_fixedquery(client: TestClient):
|
||||
response = client.get("/items/", params={"q": "fixedquery"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_query_params_str_validations_item_query_nonregexquery(client: TestClient):
|
||||
response = client.get("/items/", params={"item-query": "nonregexquery"})
|
||||
assert response.status_code == 422
|
||||
assert response.json() == IsDict(
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "string_pattern_mismatch",
|
||||
"loc": ["query", "item-query"],
|
||||
"msg": "String should match pattern '^fixedquery$'",
|
||||
"input": "nonregexquery",
|
||||
"ctx": {"pattern": "^fixedquery$"},
|
||||
}
|
||||
]
|
||||
}
|
||||
) | IsDict(
|
||||
# TODO: remove when deprecating Pydantic v1
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"ctx": {"pattern": "^fixedquery$"},
|
||||
"loc": ["query", "item-query"],
|
||||
"msg": 'string does not match regex "^fixedquery$"',
|
||||
"type": "value_error.str.regex",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_openapi_schema(client: TestClient):
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
"required": False,
|
||||
"deprecated": True,
|
||||
"schema": IsDict(
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 50,
|
||||
"pattern": "^fixedquery$",
|
||||
},
|
||||
{"type": "null"},
|
||||
],
|
||||
"title": "Query string",
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
# See https://github.com/pydantic/pydantic/blob/80353c29a824c55dea4667b328ba8f329879ac9f/tests/test_fastapi.sh#L25-L34.
|
||||
**(
|
||||
{"deprecated": True}
|
||||
if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 10)
|
||||
else {}
|
||||
),
|
||||
}
|
||||
)
|
||||
| IsDict(
|
||||
# TODO: remove when deprecating Pydantic v1
|
||||
{
|
||||
"title": "Query string",
|
||||
"maxLength": 50,
|
||||
"minLength": 3,
|
||||
"pattern": "^fixedquery$",
|
||||
"type": "string",
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
}
|
||||
),
|
||||
"name": "item-query",
|
||||
"in": "query",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ValidationError": {
|
||||
"title": "ValidationError",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loc": {
|
||||
"title": "Location",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
},
|
||||
"msg": {"title": "Message", "type": "string"},
|
||||
"type": {"title": "Error Type", "type": "string"},
|
||||
},
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
import pytest
|
||||
from dirty_equals import IsDict
|
||||
from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_py39
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client():
|
||||
from docs_src.query_params_str_validations.tutorial010_an_py39 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_py39
|
||||
def test_query_params_str_validations_no_query(client: TestClient):
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
|
||||
|
||||
@needs_py39
|
||||
def test_query_params_str_validations_item_query_fixedquery(client: TestClient):
|
||||
response = client.get("/items/", params={"item-query": "fixedquery"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
|
||||
"q": "fixedquery",
|
||||
}
|
||||
|
||||
|
||||
@needs_py39
|
||||
def test_query_params_str_validations_q_fixedquery(client: TestClient):
|
||||
response = client.get("/items/", params={"q": "fixedquery"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
|
||||
|
||||
@needs_py39
|
||||
def test_query_params_str_validations_item_query_nonregexquery(client: TestClient):
|
||||
response = client.get("/items/", params={"item-query": "nonregexquery"})
|
||||
assert response.status_code == 422
|
||||
assert response.json() == IsDict(
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "string_pattern_mismatch",
|
||||
"loc": ["query", "item-query"],
|
||||
"msg": "String should match pattern '^fixedquery$'",
|
||||
"input": "nonregexquery",
|
||||
"ctx": {"pattern": "^fixedquery$"},
|
||||
}
|
||||
]
|
||||
}
|
||||
) | IsDict(
|
||||
# TODO: remove when deprecating Pydantic v1
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"ctx": {"pattern": "^fixedquery$"},
|
||||
"loc": ["query", "item-query"],
|
||||
"msg": 'string does not match regex "^fixedquery$"',
|
||||
"type": "value_error.str.regex",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@needs_py39
|
||||
def test_openapi_schema(client: TestClient):
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
"required": False,
|
||||
"deprecated": True,
|
||||
"schema": IsDict(
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 50,
|
||||
"pattern": "^fixedquery$",
|
||||
},
|
||||
{"type": "null"},
|
||||
],
|
||||
"title": "Query string",
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
# See https://github.com/pydantic/pydantic/blob/80353c29a824c55dea4667b328ba8f329879ac9f/tests/test_fastapi.sh#L25-L34.
|
||||
**(
|
||||
{"deprecated": True}
|
||||
if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 10)
|
||||
else {}
|
||||
),
|
||||
}
|
||||
)
|
||||
| IsDict(
|
||||
# TODO: remove when deprecating Pydantic v1
|
||||
{
|
||||
"title": "Query string",
|
||||
"maxLength": 50,
|
||||
"minLength": 3,
|
||||
"pattern": "^fixedquery$",
|
||||
"type": "string",
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
}
|
||||
),
|
||||
"name": "item-query",
|
||||
"in": "query",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ValidationError": {
|
||||
"title": "ValidationError",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loc": {
|
||||
"title": "Location",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
},
|
||||
"msg": {"title": "Message", "type": "string"},
|
||||
"type": {"title": "Error Type", "type": "string"},
|
||||
},
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
import pytest
|
||||
from dirty_equals import IsDict
|
||||
from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_py310
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client():
|
||||
from docs_src.query_params_str_validations.tutorial010_py310 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_query_params_str_validations_no_query(client: TestClient):
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_query_params_str_validations_item_query_fixedquery(client: TestClient):
|
||||
response = client.get("/items/", params={"item-query": "fixedquery"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
|
||||
"q": "fixedquery",
|
||||
}
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_query_params_str_validations_q_fixedquery(client: TestClient):
|
||||
response = client.get("/items/", params={"q": "fixedquery"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_query_params_str_validations_item_query_nonregexquery(client: TestClient):
|
||||
response = client.get("/items/", params={"item-query": "nonregexquery"})
|
||||
assert response.status_code == 422
|
||||
assert response.json() == IsDict(
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "string_pattern_mismatch",
|
||||
"loc": ["query", "item-query"],
|
||||
"msg": "String should match pattern '^fixedquery$'",
|
||||
"input": "nonregexquery",
|
||||
"ctx": {"pattern": "^fixedquery$"},
|
||||
}
|
||||
]
|
||||
}
|
||||
) | IsDict(
|
||||
# TODO: remove when deprecating Pydantic v1
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"ctx": {"pattern": "^fixedquery$"},
|
||||
"loc": ["query", "item-query"],
|
||||
"msg": 'string does not match regex "^fixedquery$"',
|
||||
"type": "value_error.str.regex",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_openapi_schema(client: TestClient):
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
"required": False,
|
||||
"deprecated": True,
|
||||
"schema": IsDict(
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 50,
|
||||
"pattern": "^fixedquery$",
|
||||
},
|
||||
{"type": "null"},
|
||||
],
|
||||
"title": "Query string",
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
# See https://github.com/pydantic/pydantic/blob/80353c29a824c55dea4667b328ba8f329879ac9f/tests/test_fastapi.sh#L25-L34.
|
||||
**(
|
||||
{"deprecated": True}
|
||||
if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 10)
|
||||
else {}
|
||||
),
|
||||
}
|
||||
)
|
||||
| IsDict(
|
||||
# TODO: remove when deprecating Pydantic v1
|
||||
{
|
||||
"title": "Query string",
|
||||
"maxLength": 50,
|
||||
"minLength": 3,
|
||||
"pattern": "^fixedquery$",
|
||||
"type": "string",
|
||||
"description": "Query string for the items to search in the database that have a good match",
|
||||
}
|
||||
),
|
||||
"name": "item-query",
|
||||
"in": "query",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ValidationError": {
|
||||
"title": "ValidationError",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loc": {
|
||||
"title": "Location",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
},
|
||||
"msg": {"title": "Message", "type": "string"},
|
||||
"type": {"title": "Error Type", "type": "string"},
|
||||
},
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,26 +1,47 @@
|
||||
import importlib
|
||||
|
||||
import pytest
|
||||
from dirty_equals import IsDict
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.query_params_str_validations.tutorial011 import app
|
||||
|
||||
client = TestClient(app)
|
||||
from ...utils import needs_py39, needs_py310
|
||||
|
||||
|
||||
def test_multi_query_values():
|
||||
@pytest.fixture(
|
||||
name="client",
|
||||
params=[
|
||||
"tutorial011",
|
||||
pytest.param("tutorial011_py39", marks=needs_py310),
|
||||
pytest.param("tutorial011_py310", marks=needs_py310),
|
||||
"tutorial011_an",
|
||||
pytest.param("tutorial011_an_py39", marks=needs_py39),
|
||||
pytest.param("tutorial011_an_py310", marks=needs_py310),
|
||||
],
|
||||
)
|
||||
def get_client(request: pytest.FixtureRequest):
|
||||
mod = importlib.import_module(
|
||||
f"docs_src.query_params_str_validations.{request.param}"
|
||||
)
|
||||
|
||||
client = TestClient(mod.app)
|
||||
return client
|
||||
|
||||
|
||||
def test_multi_query_values(client: TestClient):
|
||||
url = "/items/?q=foo&q=bar"
|
||||
response = client.get(url)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"q": ["foo", "bar"]}
|
||||
|
||||
|
||||
def test_query_no_values():
|
||||
def test_query_no_values(client: TestClient):
|
||||
url = "/items/"
|
||||
response = client.get(url)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"q": None}
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
def test_openapi_schema(client: TestClient):
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user